diff --git a/hotel_roommatik/__init__.py b/hotel_roommatik/__init__.py new file mode 100755 index 000000000..0650744f6 --- /dev/null +++ b/hotel_roommatik/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/hotel_roommatik/__manifest__.py b/hotel_roommatik/__manifest__.py new file mode 100755 index 000000000..de79f8b56 --- /dev/null +++ b/hotel_roommatik/__manifest__.py @@ -0,0 +1,28 @@ +# Copyright 2018 Jose Luis Algara (Alda hotels) +# 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) ', + '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': [ + ], +} diff --git a/hotel_roommatik/models/__init__.py b/hotel_roommatik/models/__init__.py new file mode 100755 index 000000000..9b5209f6d --- /dev/null +++ b/hotel_roommatik/models/__init__.py @@ -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 diff --git a/hotel_roommatik/models/inherited_hotel_checkin_partner.py b/hotel_roommatik/models/inherited_hotel_checkin_partner.py new file mode 100644 index 000000000..7243fa943 --- /dev/null +++ b/hotel_roommatik/models/inherited_hotel_checkin_partner.py @@ -0,0 +1,122 @@ +# Copyright 2019 Jose Luis Algara (Alda hotels) +# 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 diff --git a/hotel_roommatik/models/inherited_hotel_folio.py b/hotel_roommatik/models/inherited_hotel_folio.py new file mode 100755 index 000000000..4d2cdabdd --- /dev/null +++ b/hotel_roommatik/models/inherited_hotel_folio.py @@ -0,0 +1,45 @@ +# Copyright 2019 Jose Luis Algara (Alda hotels) +# 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) diff --git a/hotel_roommatik/models/inherited_hotel_reservation.py b/hotel_roommatik/models/inherited_hotel_reservation.py new file mode 100644 index 000000000..856e12ec8 --- /dev/null +++ b/hotel_roommatik/models/inherited_hotel_reservation.py @@ -0,0 +1,23 @@ +# Copyright 2019 Jose Luis Algara (Alda hotels) +# 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') diff --git a/hotel_roommatik/models/inherited_hotel_room_type.py b/hotel_roommatik/models/inherited_hotel_room_type.py new file mode 100644 index 000000000..e281243c3 --- /dev/null +++ b/hotel_roommatik/models/inherited_hotel_room_type.py @@ -0,0 +1,49 @@ +# Copyright 2019 Jose Luis Algara (Alda hotels) +# 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} diff --git a/hotel_roommatik/models/inherited_res_partner.py b/hotel_roommatik/models/inherited_res_partner.py new file mode 100755 index 000000000..7a0354b3b --- /dev/null +++ b/hotel_roommatik/models/inherited_res_partner.py @@ -0,0 +1,117 @@ +# Copyright 2019 Jose Luis Algara (Alda hotels) +# 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 diff --git a/hotel_roommatik/models/roommatik.py b/hotel_roommatik/models/roommatik.py new file mode 100755 index 000000000..3b0c0f5cb --- /dev/null +++ b/hotel_roommatik/models/roommatik.py @@ -0,0 +1,74 @@ +# Copyright 2019 Jose Luis Algara (Alda hotels) +# 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 ------------------- diff --git a/hotel_roommatik/static/description/icon.png b/hotel_roommatik/static/description/icon.png new file mode 100755 index 000000000..2f06baf55 Binary files /dev/null and b/hotel_roommatik/static/description/icon.png differ diff --git a/hotel_roommatik/views/inherit_hotel_reservation.xml b/hotel_roommatik/views/inherit_hotel_reservation.xml new file mode 100644 index 000000000..bbfd71953 --- /dev/null +++ b/hotel_roommatik/views/inherit_hotel_reservation.xml @@ -0,0 +1,17 @@ + + + + + + + roommatik.reservation_form + hotel.reservation + + + + + + + + +