mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
Merge commit 'refs/pull/104/head' of https://github.com/hootel/hootel into 11.0
This commit is contained in:
1
hotel_roommatik/__init__.py
Executable file
1
hotel_roommatik/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
28
hotel_roommatik/__manifest__.py
Executable file
28
hotel_roommatik/__manifest__.py
Executable file
@@ -0,0 +1,28 @@
|
||||
# Copyright 2018 Jose Luis Algara (Alda hotels) <osotranquilo@gmail.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
{
|
||||
'name': 'Hotel RoomMatik',
|
||||
'description': """
|
||||
Integration of Hootel with the RoomMatik kiosk""",
|
||||
'summary': """
|
||||
The integration of Hootel with the RoomMatik kiosk.
|
||||
A series of requests/responses that provide the basic
|
||||
information needed by the kiosk.""",
|
||||
'version': '11.0.1.0.0',
|
||||
'license': 'AGPL-3',
|
||||
'author': 'Jose Luis Algara (Alda hotels) <osotranquilo@gmail.com>',
|
||||
'website': 'https://www.aldahotels.com',
|
||||
'category': 'Generic Modules/Hotel Management',
|
||||
'depends': [
|
||||
'hotel',
|
||||
'partner_contact_gender',
|
||||
'partner_second_lastname',
|
||||
'partner_contact_birthdate'
|
||||
],
|
||||
'data': [
|
||||
'views/inherit_hotel_reservation.xml',
|
||||
],
|
||||
'demo': [
|
||||
],
|
||||
}
|
||||
6
hotel_roommatik/models/__init__.py
Executable file
6
hotel_roommatik/models/__init__.py
Executable file
@@ -0,0 +1,6 @@
|
||||
from . import inherited_hotel_folio
|
||||
from . import inherited_hotel_checkin_partner
|
||||
from . import inherited_res_partner
|
||||
from . import inherited_hotel_room_type
|
||||
from . import roommatik
|
||||
from . import inherited_hotel_reservation
|
||||
122
hotel_roommatik/models/inherited_hotel_checkin_partner.py
Normal file
122
hotel_roommatik/models/inherited_hotel_checkin_partner.py
Normal file
@@ -0,0 +1,122 @@
|
||||
# Copyright 2019 Jose Luis Algara (Alda hotels) <osotranquilo@gmail.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import json
|
||||
from odoo import api, models
|
||||
from datetime import datetime
|
||||
import logging
|
||||
|
||||
class HotelFolio(models.Model):
|
||||
|
||||
_inherit = 'hotel.checkin.partner'
|
||||
|
||||
@api.model
|
||||
def rm_checkin_partner(self, stay):
|
||||
_logger = logging.getLogger(__name__)
|
||||
# CHECK-IN
|
||||
reservation_rm = self.env['hotel.reservation'].search([('id', '=',
|
||||
stay['Code'])])
|
||||
# Need checkin?
|
||||
|
||||
total_chekins = reservation_rm.checkin_partner_pending_count
|
||||
if total_chekins > 0 and len(stay["Customers"]) <= total_chekins:
|
||||
_logger.info('ROOMMATIK checkin %s customer in %s Reservation.',
|
||||
total_chekins,
|
||||
reservation_rm.id)
|
||||
for room_partner in stay["Customers"]:
|
||||
# ADD costumer ?
|
||||
# costumer = self.env['res.partner'].rm_add_customer(room_partner["Customer"])
|
||||
|
||||
checkin_partner_val = {
|
||||
'folio_id': reservation_rm.folio_id.id,
|
||||
'reservation_id': reservation_rm.id,
|
||||
'enter_date': datetime.strptime(stay["Arrival"],
|
||||
"%d%m%Y").date(),
|
||||
'exit_date': datetime.strptime(stay["Departure"],
|
||||
"%d%m%Y").date(),
|
||||
'partner_id': room_partner["Customer"]["Id"],
|
||||
'email': room_partner["Customer"]["Contact"]["Email"],
|
||||
'mobile': room_partner["Customer"]["Contact"]["Mobile"],
|
||||
'document_type': room_partner["Customer"][
|
||||
"IdentityDocument"]["Type"],
|
||||
'document_number': room_partner["Customer"][
|
||||
"IdentityDocument"]["Number"],
|
||||
'document_expedition_date': datetime.strptime(room_partner[
|
||||
"Customer"]["IdentityDocument"][
|
||||
"ExpiryDate"], "%d%m%Y").date(),
|
||||
'gender': room_partner["Customer"]["Sex"],
|
||||
'birthdate_date': datetime.strptime(room_partner[
|
||||
"Customer"]["Birthday"], "%d%m%Y").date(),
|
||||
'code_ine_id': room_partner["Customer"][
|
||||
"Address"]["Province"],
|
||||
'state': 'booking',
|
||||
}
|
||||
try:
|
||||
record = self.env['hotel.checkin.partner'].create(
|
||||
checkin_partner_val)
|
||||
_logger.info('ROOMMATIK check-in Document: %s in \
|
||||
(%s Reservation) ID:%s.',
|
||||
checkin_partner_val['document_number'],
|
||||
checkin_partner_val['reservation_id'],
|
||||
record.id)
|
||||
stay['Id'] = record.id
|
||||
json_response = stay
|
||||
except:
|
||||
# Debug Stop -------------------
|
||||
import wdb; wdb.set_trace()
|
||||
# Debug Stop -------------------
|
||||
json_response = {'Estate': 'Error not create Checkin'}
|
||||
_logger.error('ROOMMATIK writing %s in reservation: %s).',
|
||||
checkin_partner_val['document_number'],
|
||||
checkin_partner_val['reservation_id'])
|
||||
return json_response
|
||||
|
||||
else:
|
||||
json_response = {'Estate': 'Error checkin_partner_pending_count \
|
||||
values do not match.'}
|
||||
_logger.error('ROOMMATIK checkin pending count do not match for \
|
||||
Reservation ID %s.', reservation_rm.id)
|
||||
json_response = json.dumps(json_response)
|
||||
return json_response
|
||||
|
||||
@api.model
|
||||
def rm_get_stay(self, code):
|
||||
# BUSQUEDA POR LOCALIZADOR
|
||||
reserva = self.search([('id', '=', code)])
|
||||
if any(reserva):
|
||||
stay = {'Code': reserva.reservation_id.localizator}
|
||||
stay['Id'] = reserva.folio_id.id
|
||||
stay['Room'] = {}
|
||||
stay['Room']['Id'] = reserva.reservation_id.room_id.id
|
||||
stay['Room']['Name'] = reserva.reservation_id.room_id.name
|
||||
stay['RoomType'] = {}
|
||||
stay['RoomType']['Id'] = reserva.reservation_id.room_type_id.id
|
||||
stay['RoomType']['Name'] = reserva.reservation_id.room_type_id.name
|
||||
stay['RoomType']['GuestNumber'] = "xxxxxxx"
|
||||
stay['Arrival'] = (reserva.reservation_id.real_checkin +
|
||||
'T' + reserva.reservation_id.arrival_hour + ':00')
|
||||
stay['Departure'] = (reserva.reservation_id.real_checkout +
|
||||
'T' +
|
||||
reserva.reservation_id.departure_hour + ':00')
|
||||
stay['Customers'] = []
|
||||
for idx, cpi in enumerate(reserva.reservation_id.checkin_partner_ids):
|
||||
stay['Customers'].append({'Customer': {}})
|
||||
stay['Customers'][idx]['Customer'] = self.env[
|
||||
'res.partner'].rm_get_a_customer(cpi.partner_id.id)
|
||||
stay['TimeInterval'] = {}
|
||||
stay['TimeInterval']['Id'] = {}
|
||||
stay['TimeInterval']['Name'] = {}
|
||||
stay['TimeInterval']['Minutes'] = {}
|
||||
stay['Adults'] = reserva.reservation_id.adults
|
||||
stay['ReservationCode'] = {}
|
||||
stay['Total'] = reserva.reservation_id.price_total
|
||||
stay['Paid'] = (stay['Total'] -
|
||||
reserva.reservation_id.folio_pending_amount)
|
||||
stay['Outstanding'] = {}
|
||||
stay['Taxable'] = reserva.reservation_id.price_tax
|
||||
|
||||
else:
|
||||
stay = {'Code': ""}
|
||||
|
||||
json_response = json.dumps(stay)
|
||||
return json_response
|
||||
45
hotel_roommatik/models/inherited_hotel_folio.py
Executable file
45
hotel_roommatik/models/inherited_hotel_folio.py
Executable file
@@ -0,0 +1,45 @@
|
||||
# Copyright 2019 Jose Luis Algara (Alda hotels) <osotranquilo@gmail.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import json
|
||||
from odoo import api, models
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HotelFolio(models.Model):
|
||||
|
||||
_inherit = 'hotel.folio'
|
||||
|
||||
@api.model
|
||||
def rm_get_reservation(self, Code):
|
||||
# BÚSQUEDA DE RESERVA POR LOCALIZADOR
|
||||
folio_res = self.env['hotel.folio'].search([('id', '=', Code)])
|
||||
if any(folio_res):
|
||||
_logger.info('ROOMMATIK serving Folio: %s', folio_res.id)
|
||||
folio_lin = folio_res.room_lines
|
||||
json_response = {
|
||||
'Id': folio_res.id,
|
||||
'Arrival': folio_lin[0]['checkin'],
|
||||
'Departure': folio_lin[0]['checkout'],
|
||||
'Deposit': folio_res.amount_total,
|
||||
}
|
||||
for i, line in enumerate(folio_lin):
|
||||
total_chekins = folio_lin.checkin_partner_pending_count
|
||||
json_response.setdefault('Rooms', [i]).append({
|
||||
'Id': line.id,
|
||||
'Adults': line.adults,
|
||||
'IsAvailable': True if total_chekins > 0 else False,
|
||||
# IsAvailable “false” Rooms not need check-in
|
||||
'Price': line.price_total,
|
||||
'RoomTypeId': line.room_type_id.id,
|
||||
'RoomTypeName': line.room_type_id.name,
|
||||
'RoomName': line.room_id.name,
|
||||
})
|
||||
# Debug Stop -------------------
|
||||
# import wdb; wdb.set_trace()
|
||||
# Debug Stop -------------------
|
||||
else:
|
||||
_logger.warning('ROOMMATIK Not Found Folio search %s', Code)
|
||||
json_response = {'Error': 'Not Found ' + str(Code)}
|
||||
return json.dumps(json_response)
|
||||
23
hotel_roommatik/models/inherited_hotel_reservation.py
Normal file
23
hotel_roommatik/models/inherited_hotel_reservation.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Copyright 2019 Jose Luis Algara (Alda hotels) <osotranquilo@gmail.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, models, fields
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
import random
|
||||
|
||||
class HotelReservation(models.Model):
|
||||
|
||||
_inherit = 'hotel.reservation'
|
||||
|
||||
def _compute_localizator(self):
|
||||
random.seed(self.id)
|
||||
number = str(random.random())
|
||||
leters = "ABCEFGHJKL"
|
||||
locali = str(self.folio_id.id) + leters[int(number[11])]
|
||||
locali += number[2:10] + leters[int(number[12])]
|
||||
locali += str(self.id)
|
||||
self.localizator = locali
|
||||
return
|
||||
|
||||
localizator = fields.Char('Localizator', compute='_compute_localizator')
|
||||
49
hotel_roommatik/models/inherited_hotel_room_type.py
Normal file
49
hotel_roommatik/models/inherited_hotel_room_type.py
Normal file
@@ -0,0 +1,49 @@
|
||||
# Copyright 2019 Jose Luis Algara (Alda hotels) <osotranquilo@gmail.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, models
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
|
||||
|
||||
class HotelRoomType(models.Model):
|
||||
|
||||
_inherit = "hotel.room.type"
|
||||
|
||||
@api.model
|
||||
def rm_get_all_room_type_rates(self):
|
||||
# types = self.env['hotel.room.type'].search(['active', '=', True])
|
||||
types = self.env['hotel.room.type'].search([])
|
||||
dfrom = datetime.now()
|
||||
dto = (dfrom + timedelta(hours=24))
|
||||
|
||||
room_type_rates = []
|
||||
for i, type in enumerate(types):
|
||||
frees = self.check_availability_room_type(dfrom, dto, type.id)
|
||||
if any(frees):
|
||||
room_type_rates.append({
|
||||
"RoomType": {
|
||||
"Id": type.id,
|
||||
"Name": type.product_id.name,
|
||||
"GuestNumber": type.get_capacity()
|
||||
},
|
||||
"TimeInterval": {
|
||||
"Id": "1",
|
||||
"Name": "1 day",
|
||||
"Minutes": "1440"
|
||||
},
|
||||
"Price": "",
|
||||
"IsAvailable": "",
|
||||
})
|
||||
|
||||
return room_type_rates
|
||||
|
||||
@api.model
|
||||
def rm_get_prices(self, start_date, time_interval, number_intervals, room_type, guest_number):
|
||||
# TODO: FALTA POR COMPLETO
|
||||
_logger = logging.getLogger(__name__)
|
||||
_logger.info('ROOMMATIK get prices date %s Room: %s for %s Guests',
|
||||
start_date,
|
||||
room_type,
|
||||
guest_number)
|
||||
return {'start_date': start_date, 'time_interval': time_interval, 'number_intervals': number_intervals}
|
||||
117
hotel_roommatik/models/inherited_res_partner.py
Executable file
117
hotel_roommatik/models/inherited_res_partner.py
Executable file
@@ -0,0 +1,117 @@
|
||||
# Copyright 2019 Jose Luis Algara (Alda hotels) <osotranquilo@gmail.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import json
|
||||
from odoo import api, models
|
||||
from datetime import datetime
|
||||
import logging
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
|
||||
_inherit = 'res.partner'
|
||||
|
||||
@api.model
|
||||
def rm_add_customer(self, customer):
|
||||
# RoomMatik API CREACIÓN DE CLIENTE
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
partner_res = self.env['res.partner'].search([(
|
||||
'document_number', '=',
|
||||
customer['IdentityDocument']['Number'])])
|
||||
|
||||
json_response = {'Id': 0}
|
||||
if any(partner_res):
|
||||
# Change customer data
|
||||
_logger.warning('ROOMMATIK %s exist in BD [ %s ] Rewriting',
|
||||
partner_res[0].document_number,
|
||||
partner_res[0].id,)
|
||||
try:
|
||||
partner_res[0].update(self.rm_preare_customer(customer))
|
||||
write_custumer = partner_res[0]
|
||||
except:
|
||||
_logger.error('ROOMMATIK Rewriting [%s] in BD [ %s ] ID',
|
||||
partner_res[0].document_number,
|
||||
partner_res[0].id,)
|
||||
else:
|
||||
# Create new customer
|
||||
try:
|
||||
write_custumer = self.create(self.rm_preare_customer(customer))
|
||||
_logger.info('ROOMMATIK Writing %s Name: %s',
|
||||
customer['IdentityDocument']['Number'],
|
||||
customer['FirstName'])
|
||||
except:
|
||||
_logger.error('ROOMMATIK Creating %s %s in BD',
|
||||
customer['IdentityDocument']['Number'],
|
||||
customer['FirstName'])
|
||||
json_response = self.rm_get_a_customer(write_custumer.id)
|
||||
json_response = json.dumps(json_response)
|
||||
return json_response
|
||||
|
||||
def rm_preare_customer(self, customer):
|
||||
# Check Sex string
|
||||
if customer['Sex'] not in {'male', 'female'}:
|
||||
customer['Sex'] = ''
|
||||
# Check state_id
|
||||
city_srch = self.env['res.country.state'].search([
|
||||
('name', 'ilike', customer['Address']['Province'])])
|
||||
# Create Street2
|
||||
street_2 = customer['Address']['House']
|
||||
street_2 += ' ' + customer['Address']['Flat']
|
||||
street_2 += ' ' + customer['Address']['Number']
|
||||
metadata = {
|
||||
'firstname': customer['FirstName'],
|
||||
'lastname': customer['LastName1'],
|
||||
'lastname2': customer['LastName2'],
|
||||
'birthdate_date': datetime.strptime(customer['Birthday'],
|
||||
"%d%m%Y").date(),
|
||||
'gender': customer['Sex'],
|
||||
'zip': customer['Address']['ZipCode'],
|
||||
'city': customer['Address']['City'],
|
||||
'street': customer['Address']['Street'],
|
||||
'street2': street_2,
|
||||
'state_id': city_srch.id,
|
||||
'phone': customer['Contact']['Telephone'],
|
||||
'mobile': customer['Contact']['Mobile'],
|
||||
'email': customer['Contact']['Email'],
|
||||
'document_number': customer['IdentityDocument']['Number'],
|
||||
'document_type': customer['IdentityDocument']['Type'],
|
||||
'document_expedition_date': datetime.strptime(customer[
|
||||
'IdentityDocument']['ExpeditionDate'],
|
||||
"%d%m%Y").date(),
|
||||
}
|
||||
return {k: v for k, v in metadata.items() if v is not ""}
|
||||
|
||||
def rm_get_a_customer(self, customer):
|
||||
# Prepare a Customer for RoomMatik
|
||||
partner = self.search([('id', '=', customer)])
|
||||
response = {}
|
||||
response['Id'] = partner.id
|
||||
response['FirstName'] = partner.firstname
|
||||
response['LastName1'] = partner.lastname
|
||||
response['LastName2'] = partner.lastname2
|
||||
response['Birthday'] = partner.birthdate_date
|
||||
response['Sex'] = partner.gender
|
||||
response['Address'] = {'Nationality': {},
|
||||
'Country': partner.country_id.name,
|
||||
'ZipCode': partner.zip,
|
||||
'City': partner.city,
|
||||
'Street': partner.street,
|
||||
'House': partner.street2,
|
||||
# 'Flat': "xxxxxxx",
|
||||
# 'Number': "xxxxxxx",
|
||||
'Province': partner.state_id.name,
|
||||
}
|
||||
response['IdentityDocument'] = {
|
||||
'Number': partner.document_number,
|
||||
'Type': partner.document_type,
|
||||
'ExpiryDate': "",
|
||||
'ExpeditionDate': partner.document_expedition_date,
|
||||
}
|
||||
response['Contact'] = {
|
||||
'Telephone': partner.phone,
|
||||
# 'Fax': 'xxxxxxx',
|
||||
'Mobile': partner.mobile,
|
||||
'Email': partner.email,
|
||||
}
|
||||
return response
|
||||
74
hotel_roommatik/models/roommatik.py
Executable file
74
hotel_roommatik/models/roommatik.py
Executable file
@@ -0,0 +1,74 @@
|
||||
# Copyright 2019 Jose Luis Algara (Alda hotels) <osotranquilo@gmail.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
from odoo import api, models
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RoomMatik(models.Model):
|
||||
_name = 'roommatik.api'
|
||||
|
||||
@api.model
|
||||
def rm_get_date(self):
|
||||
# RoomMatik API Gets the current business date/time. (MANDATORY)
|
||||
utc_s = '+01:00'
|
||||
# TODO Need know UTC in the machine/hotel
|
||||
json_response = {
|
||||
'dateTime': datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f") + utc_s
|
||||
}
|
||||
json_response = json.dumps(json_response)
|
||||
return json_response
|
||||
|
||||
@api.model
|
||||
def rm_get_reservation(self, reservation_code):
|
||||
# RoomMatik Gets a reservation ready for check-in
|
||||
# through the provided code. (MANDATORY)
|
||||
apidata = self.env['hotel.folio']
|
||||
return apidata.rm_get_reservation(reservation_code)
|
||||
|
||||
@api.model
|
||||
def rm_add_customer(self, customer):
|
||||
# RoomMatik API Adds a new PMS customer through the provided parameters
|
||||
# Addition will be ok if the returned customer has ID. (MANDATORY)
|
||||
_logger.info('ROOMMATIK Customer Creation')
|
||||
apidata = self.env['res.partner']
|
||||
return apidata.rm_add_customer(customer)
|
||||
|
||||
@api.model
|
||||
def rm_checkin_partner(self, stay):
|
||||
# RoomMatik API Check-in a stay.
|
||||
# Addition will be ok if the returned stay has ID. (MANDATORY)
|
||||
_logger.info('ROOMMATIK Check-IN')
|
||||
apidata = self.env['hotel.checkin.partner']
|
||||
return apidata.rm_checkin_partner(stay)
|
||||
|
||||
@api.model
|
||||
def rm_get_stay(self, check_in_code):
|
||||
# RoomMatik API Gets stay information through check-in code
|
||||
# (if code is related to a current stay)
|
||||
# (MANDATORY for check-out kiosk)
|
||||
apidata = self.env['hotel.checkin.partner']
|
||||
return apidata.rm_get_stay(check_in_code)
|
||||
|
||||
@api.model
|
||||
def rm_get_all_room_type_rates(self):
|
||||
# Gets the current room rates and availability. (MANDATORY)
|
||||
# return ArrayOfRoomTypeRate
|
||||
_logger.info('ROOMMATIK Get Rooms and Rates')
|
||||
apidata = self.env['hotel.room.type']
|
||||
return apidata.rm_get_all_room_type_rates()
|
||||
|
||||
@api.model
|
||||
def rm_get_prices(self, start_date, time_interval, number_intervals, room_type, guest_number):
|
||||
# Gets some prices related to different dates of the same stay.
|
||||
# return ArrayOfDecimal
|
||||
_logger.info('ROOMMATIK Get Prices')
|
||||
apidata = self.env['hotel.room.type']
|
||||
return apidata.rm_get_prices(start_date, time_interval, number_intervals, room_type, guest_number)
|
||||
|
||||
# Debug Stop -------------------
|
||||
# import wdb; wdb.set_trace()
|
||||
# Debug Stop -------------------
|
||||
BIN
hotel_roommatik/static/description/icon.png
Executable file
BIN
hotel_roommatik/static/description/icon.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
17
hotel_roommatik/views/inherit_hotel_reservation.xml
Normal file
17
hotel_roommatik/views/inherit_hotel_reservation.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Hotel Settings -->
|
||||
<data>
|
||||
<!-- Inherit view to add 'localizator' in Reservation Form -->
|
||||
<record id="roommatik_code_reservation_form" model="ir.ui.view">
|
||||
<field name="name">roommatik.reservation_form</field>
|
||||
<field name="model">hotel.reservation</field>
|
||||
<field name="inherit_id" ref="hotel.hotel_reservation_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='reservation_type']" position="after">
|
||||
<field name="localizator" />
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user