From 7dbe789e01761aa4244144bb592c5e5ba36fe198 Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Mon, 29 Apr 2019 12:55:54 +0200 Subject: [PATCH 01/14] [ADD] Data BI --- hotel_data_bi/README.rst | 74 ++++++++++++++ hotel_data_bi/__init__.py | 1 + hotel_data_bi/__manifest__.py | 32 ++++++ hotel_data_bi/models/__init__.py | 3 + hotel_data_bi/models/budget.py | 48 +++++++++ hotel_data_bi/models/data_bi.py | 107 ++++++++++++++++++++ hotel_data_bi/models/inherit_res_company.py | 13 +++ hotel_data_bi/security/data_bi.xml | 10 ++ hotel_data_bi/security/ir.model.access.csv | 4 + hotel_data_bi/static/description/icon.png | Bin 0 -> 66994 bytes hotel_data_bi/views/budget.xml | 71 +++++++++++++ hotel_data_bi/views/inherit_res_company.xml | 19 ++++ 12 files changed, 382 insertions(+) create mode 100644 hotel_data_bi/README.rst create mode 100644 hotel_data_bi/__init__.py create mode 100644 hotel_data_bi/__manifest__.py create mode 100644 hotel_data_bi/models/__init__.py create mode 100644 hotel_data_bi/models/budget.py create mode 100644 hotel_data_bi/models/data_bi.py create mode 100644 hotel_data_bi/models/inherit_res_company.py create mode 100644 hotel_data_bi/security/data_bi.xml create mode 100644 hotel_data_bi/security/ir.model.access.csv create mode 100644 hotel_data_bi/static/description/icon.png create mode 100644 hotel_data_bi/views/budget.xml create mode 100644 hotel_data_bi/views/inherit_res_company.xml diff --git a/hotel_data_bi/README.rst b/hotel_data_bi/README.rst new file mode 100644 index 000000000..accc16833 --- /dev/null +++ b/hotel_data_bi/README.rst @@ -0,0 +1,74 @@ +REVENUE EXPORTER +============= + +Export Odoo data for Revenue MyDataBI + +Usage +======= +To use this module, you need to: + +Create a user and give the "Hotel Management / Export data BI" permission. + +To connect to Odoo via xmlrpc there are examples in https://www.odoo.com/documentation/10.0/api_integration.html in the "Calling methods" section with examples in several languages. + +A python example: +import xmlrpclib + +url = 'https://www.example.org' + +username = 'username@example.org' + +password = '123passwordexample' + +db = 'example_db_name' + +common = xmlrpclib.ServerProxy('{}/xmlrpc/2/common'.format(url)) + +uid = common.authenticate(db, username, password, {}) + +models = xmlrpclib.ServerProxy('{}/xmlrpc/2/object'.format(url)) + +models.execute_kw(db, uid, password,'data_bi','export_data_bi', [ 8, '2018-01-01']) + +In the parameters of export_data_bi: + +archivo == 1 'Tarifa' + +archivo == 2 'Canal' + +archivo == 3 'Hotel' + +archivo == 4 'Pais' + +archivo == 5 'Regimen' + +archivo == 6 'Reservas' + +archivo == 7 'Capacidad' + +archivo == 8 'Tipo Habitación' + +archivo == 9 'Budget' + +archivo == 10 'Bloqueos' + +archivo == 11 'Motivo Bloqueo' + +archivo == 12 'Segmentos' + +archivo == 13 'Clientes' + +archivo == 14 'Estado Reservas' + +fechafoto = start date to take data + +in the example recive 8 'Tipo Habitación' from '2018-01-01' + + +Credits +======= + +Creator +------------ + +* Jose Luis Algara (Alda hotels) diff --git a/hotel_data_bi/__init__.py b/hotel_data_bi/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/hotel_data_bi/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/hotel_data_bi/__manifest__.py b/hotel_data_bi/__manifest__.py new file mode 100644 index 000000000..e0ca740b2 --- /dev/null +++ b/hotel_data_bi/__manifest__.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Copyright 2018-2019 Jose Luis Algara Toledo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Hotel Data Bi', + 'description': """ + Export hotel data for business intelligence + + To use this module you need to: + + Create a user and give the 'Hotel Management/Export data BI' permission. + """, + 'summary': "Export hotel data for business intelligence", + 'version': '2.0', + 'license': 'AGPL-3', + 'author': "Jose Luis Algara (Alda hotels) ", + 'website': 'www.aldahotels.com', + 'depends': ['hotel', 'hotel_l10n_es'], + 'category': 'hotel/revenue', + 'data': [ + 'views/budget.xml', + 'views/inherit_res_company.xml', + 'security/data_bi.xml', + 'security/ir.model.access.csv', + ], + 'demo': [ + ], + 'installable': True, + 'auto_install': False, + 'application': False, +} diff --git a/hotel_data_bi/models/__init__.py b/hotel_data_bi/models/__init__.py new file mode 100644 index 000000000..1de849e11 --- /dev/null +++ b/hotel_data_bi/models/__init__.py @@ -0,0 +1,3 @@ +from . import inherit_res_company +from . import budget +from . import data_bi diff --git a/hotel_data_bi/models/budget.py b/hotel_data_bi/models/budget.py new file mode 100644 index 000000000..b184fbad4 --- /dev/null +++ b/hotel_data_bi/models/budget.py @@ -0,0 +1,48 @@ +# Copyright 2019 Jose Luis Algara (Alda hotels) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from datetime import date + + +def get_years(): + """Return a year list, to select in year field.""" + year_list = [] + for i in range(2018, 2036): + year_list.append((i, str(i))) + return year_list + + +class Budget(models.Model): + """Establish and save the budget for DataBI control by revenue""" + + _name = 'budget' + + # fecha Primer día del mes + month = fields.Selection([(1, 'January'), (2, 'February'), (3, 'March'), + (4, 'April'), (5, 'May'), (6, 'June'), + (7, 'July'), (8, 'August'), (9, 'September'), + (10, 'October'), (11, 'November'), + (12, 'December'), ], + string='Month', required=True) + year = fields.Selection(get_years(), string='Year', required=True) + room_nights = fields.Float("Room Nights", required=True, digits=(6, 2)) + # Número de Room Nights + room_revenue = fields.Float("Room Revenue", required=True, digits=(6, 2)) + # Ingresos por Reservas + estancias = fields.Integer("Number of Stays") # Número de Estancias + # ID_Tarifa numérico Código de la Tarifa + # ID_Canal numérico Código del Canal + # ID_Pais numérico Código del País + # ID_Regimen numérico Cóigo del Régimen + # ID_Tipo_Habitacion numérico Código del Tipo de Habitación + # iD_Segmento numérico Código del Segmento + # ID_Cliente numérico Código del Cliente + # Pension_Revenue numérico con dos decimales Ingresos por Pensión + + @api.model + def export_data_bi(self, + archivo=False, + fechafoto=date.today().strftime('%Y-%m-%d')): + apidata = self.env['data_bi'] + return apidata.export_data_bi(self) diff --git a/hotel_data_bi/models/data_bi.py b/hotel_data_bi/models/data_bi.py new file mode 100644 index 000000000..9812cd70c --- /dev/null +++ b/hotel_data_bi/models/data_bi.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2018 -2019 Alda Hotels +# Jose Luis Algara +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp import models, fields, api, _ +from datetime import date, datetime, timedelta +import json +import logging +_logger = logging.getLogger(__name__) + + +def inv_percent(amount, percent): + """Return the amount to which a percentage was applied.""" + return round(amount*(100/float(100-percent)) - amount, 2) + + +class Data_Bi(models.Model): + """Management and export data for MopSolution MyDataBI.""" + + _name = 'data_bi' + + @api.model + def export_data_bi(self, + archivo=False, + fechafoto=date.today().strftime('%Y-%m-%d')): + u"""Prepare a Json Objet to export data for MyDataBI. + + Generate a dicctionary to by send in JSON + archivo = response file type + archivo == 1 'Tarifa' + archivo == 2 'Canal' + archivo == 3 'Hotel' + archivo == 4 'Pais' + archivo == 5 'Regimen' + archivo == 6 'Reservas' + archivo == 7 'Capacidad' + archivo == 8 'Tipo Habitación' + archivo == 9 'Budget' + archivo == 10 'Bloqueos' + archivo == 11 'Motivo Bloqueo' + archivo == 12 'Segmentos' + archivo == 13 'Clientes' + archivo == 14 'Estado Reservas' + fechafoto = start date to take data + """ + + if type(fechafoto) is dict: + fechafoto = date.today() + else: + fechafoto = datetime.strptime(fechafoto, '%Y-%m-%d').date() + + _logger.warning("Init Export Data_Bi Module") + + dic_export = [] # Diccionario con todo lo necesario para exportar. + # if (archivo == 0) or (archivo == 1): + # dic_export.append({'Tarifa': dic_tarifa}) + # if (archivo == 0) or (archivo == 2): + # dic_export.append({'Canal': dic_canal}) + # if (archivo == 0) or (archivo == 3): + # dic_export.append({'Hotel': dic_hotel}) + # if (archivo == 0) or (archivo == 4): + # dic_export.append({'Pais': dic_pais}) + # if (archivo == 0) or (archivo == 5): + # dic_export.append({'Regimen': dic_regimen}) + # if (archivo == 0) or (archivo == 6): + # dic_export.append({'Reservas': dic_reservas}) + # if (archivo == 0) or (archivo == 7): + # dic_export.append({'Capacidad': dic_capacidad}) + # if (archivo == 0) or (archivo == 8): + # dic_export.append({'Tipo Habitación': dic_tipo_habitacion}) + # if (archivo == 0) or (archivo == 9): + # dic_export.append({'Budget': dic_budget}) + # if (archivo == 0) or (archivo == 10): + # dic_export.append({'Bloqueos': dic_bloqueos}) + # if (archivo == 0) or (archivo == 11): + # dic_export.append({'Motivo Bloqueo': dic_moti_bloq}) + # if (archivo == 0) or (archivo == 12): + # dic_export.append({'Segmentos': dic_segmentos}) + # if (archivo == 0) or (archivo == 13): + # dic_export.append({'Clientes': dic_clientes}) + # if (archivo == 0) or (archivo == 14): + # dic_export.append({'Estado Reservas': dic_estados}) + + dictionaryToJson = json.dumps(dic_export) + _logger.warning("End Export Data_Bi Module to Json") + + # Debug Stop ------------------- + # import wdb; wdb.set_trace() + # Debug Stop ------------------- + return dictionaryToJson diff --git a/hotel_data_bi/models/inherit_res_company.py b/hotel_data_bi/models/inherit_res_company.py new file mode 100644 index 000000000..f33265dc0 --- /dev/null +++ b/hotel_data_bi/models/inherit_res_company.py @@ -0,0 +1,13 @@ +# Copyright 2019 Jose Luis Algara (Alda hotels) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class Inherit_res_company(models.Model): + _inherit = 'res.company' + + id_hotel = fields.Integer( + 'Unique ID for DataBI', default=0, + help='It must be unique to be able to identify the hotel, \ + within a hotel group.') diff --git a/hotel_data_bi/security/data_bi.xml b/hotel_data_bi/security/data_bi.xml new file mode 100644 index 000000000..521038794 --- /dev/null +++ b/hotel_data_bi/security/data_bi.xml @@ -0,0 +1,10 @@ + + + + + + + Hotel Management / Export data BI + + + diff --git a/hotel_data_bi/security/ir.model.access.csv b/hotel_data_bi/security/ir.model.access.csv new file mode 100644 index 000000000..a8f8fab40 --- /dev/null +++ b/hotel_data_bi/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +ir_model_hotel_budget,hoteldatabi.budget.manager,hotel_data_bi.model_budget,hotel.group_hotel_manager,1,1,1,1 +ir_model_hotel_budget_user,hoteldatabi.budget.user,hotel_data_bi.model_budget,hotel.group_hotel_user,0,0,0,0 +ir_model_hotel_data_bi_export_data,hoteldatabi.data_bi.export_data,hotel_data_bi.model_data_bi,hotel_data_bi.group_hotel_export_data,1,0,0,0 diff --git a/hotel_data_bi/static/description/icon.png b/hotel_data_bi/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0ad91d0d3ac40828ba5a8b003dd8b90fd44c658f GIT binary patch literal 66994 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 + + + + + + + + + + budget.form (in hotel_data_bi) + budget + +
+ + + + +
+
+
+ + + + + +
+
+
+
+
+
+
+
+
+ + + + budget.tree (in hotel_data_bi) + budget + + + + + + + + + + + +
diff --git a/hotel_data_bi/views/inherit_res_company.xml b/hotel_data_bi/views/inherit_res_company.xml new file mode 100644 index 000000000..2f829839b --- /dev/null +++ b/hotel_data_bi/views/inherit_res_company.xml @@ -0,0 +1,19 @@ + + + + + + + + databi.config.view_company_form + res.company + + + + + + + + + + From ab6c5b0cd82861ad53a33022f5493cf3c11cbf2b Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Tue, 30 Apr 2019 11:28:25 +0200 Subject: [PATCH 02/14] [ADD] Tarifa & Canal --- hotel_data_bi/models/data_bi.py | 46 ++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/hotel_data_bi/models/data_bi.py b/hotel_data_bi/models/data_bi.py index 9812cd70c..42db9834a 100644 --- a/hotel_data_bi/models/data_bi.py +++ b/hotel_data_bi/models/data_bi.py @@ -44,6 +44,7 @@ class Data_Bi(models.Model): Generate a dicctionary to by send in JSON archivo = response file type + archivo == 0 'ALL' archivo == 1 'Tarifa' archivo == 2 'Canal' archivo == 3 'Hotel' @@ -68,11 +69,21 @@ class Data_Bi(models.Model): _logger.warning("Init Export Data_Bi Module") + if not isinstance(archivo, int): + archivo = 0 + dic_param = [] + dic_param.append({'Archivo': archivo, + 'Fechafoto': fechafoto.strftime('%Y-%m-%d')}) + compan = self.env.user.company_id + dic_export = [] # Diccionario con todo lo necesario para exportar. - # if (archivo == 0) or (archivo == 1): - # dic_export.append({'Tarifa': dic_tarifa}) - # if (archivo == 0) or (archivo == 2): - # dic_export.append({'Canal': dic_canal}) + if (archivo == 0) or (archivo == 1): + dic_tarifa = self.data_bi_tarifa(compan.id_hotel) + dic_export.append({'Tarifa': dic_tarifa}) + + if (archivo == 0) or (archivo == 2): + dic_canal = self.data_bi_canal(compan.id_hotel) + dic_export.append({'Canal': dic_canal}) # if (archivo == 0) or (archivo == 3): # dic_export.append({'Hotel': dic_hotel}) # if (archivo == 0) or (archivo == 4): @@ -98,10 +109,31 @@ class Data_Bi(models.Model): # if (archivo == 0) or (archivo == 14): # dic_export.append({'Estado Reservas': dic_estados}) + # Debug Stop ------------------- + import wdb; wdb.set_trace() + # Debug Stop ------------------- dictionaryToJson = json.dumps(dic_export) _logger.warning("End Export Data_Bi Module to Json") - # Debug Stop ------------------- - # import wdb; wdb.set_trace() - # Debug Stop ------------------- return dictionaryToJson + + @api.model + def data_bi_tarifa(self, compan): + dic_tarifa = [] # Diccionario con las tarifas + tarifas = self.env['product.pricelist'].search_read([], ['name']) + for tarifa in tarifas: + dic_tarifa.append({'ID_Hotel': compan, + 'ID_Tarifa': tarifa['id'], + 'Descripcion': tarifa['name']}) + return dic_tarifa + + @api.model + def data_bi_canal(self, compan): + dic_canal = [] # Diccionario con los Canales + canal_array = ['Directo', 'OTA', 'Call-Center', 'Agencia', + 'Touroperador'] + for i in range(0, len(canal_array)): + dic_canal.append({'ID_Hotel': compan, + 'ID_Canal': i, + 'Descripcion': canal_array[i]}) + return dic_canal From 96668b71ca82a5df3bfc5b23bdd3f853fffc3817 Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Tue, 30 Apr 2019 11:48:22 +0200 Subject: [PATCH 03/14] [ADD] Hotel & Countries --- hotel_data_bi/models/data_bi.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/hotel_data_bi/models/data_bi.py b/hotel_data_bi/models/data_bi.py index 42db9834a..1c3ecaba0 100644 --- a/hotel_data_bi/models/data_bi.py +++ b/hotel_data_bi/models/data_bi.py @@ -84,10 +84,12 @@ class Data_Bi(models.Model): if (archivo == 0) or (archivo == 2): dic_canal = self.data_bi_canal(compan.id_hotel) dic_export.append({'Canal': dic_canal}) - # if (archivo == 0) or (archivo == 3): - # dic_export.append({'Hotel': dic_hotel}) - # if (archivo == 0) or (archivo == 4): - # dic_export.append({'Pais': dic_pais}) + if (archivo == 0) or (archivo == 3): + dic_hotel = self.data_bi_hotel(compan) + dic_export.append({'Hotel': dic_hotel}) + if (archivo == 0) or (archivo == 4): + dic_pais = self.data_bi_pais(compan.id_hotel) + dic_export.append({'Pais': dic_pais}) # if (archivo == 0) or (archivo == 5): # dic_export.append({'Regimen': dic_regimen}) # if (archivo == 0) or (archivo == 6): @@ -137,3 +139,21 @@ class Data_Bi(models.Model): 'ID_Canal': i, 'Descripcion': canal_array[i]}) return dic_canal + + @api.model + def data_bi_hotel(self, compan): + dic_hotel = [] # Diccionario con el/los nombre de los hoteles + dic_hotel.append({'ID_Hotel': compan.id_hotel, + 'Descripcion': compan.property_name}) + return dic_hotel + + @api.model + def data_bi_pais(self, compan): + dic_pais = [] + # Diccionario con los nombre de los Paises usando los del INE + paises = self.env['code.ine'].search_read([], ['code', 'name']) + for pais in paises: + dic_pais.append({'ID_Hotel': compan, + 'ID_Pais': pais['code'], + 'Descripcion': pais['name']}) + return dic_pais From 68c98764849df35b24634d1640eede3c2eec2455 Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Tue, 30 Apr 2019 12:01:38 +0200 Subject: [PATCH 04/14] [ADD] Board Services --- hotel_data_bi/models/data_bi.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/hotel_data_bi/models/data_bi.py b/hotel_data_bi/models/data_bi.py index 1c3ecaba0..c0e5d49ef 100644 --- a/hotel_data_bi/models/data_bi.py +++ b/hotel_data_bi/models/data_bi.py @@ -90,8 +90,9 @@ class Data_Bi(models.Model): if (archivo == 0) or (archivo == 4): dic_pais = self.data_bi_pais(compan.id_hotel) dic_export.append({'Pais': dic_pais}) - # if (archivo == 0) or (archivo == 5): - # dic_export.append({'Regimen': dic_regimen}) + if (archivo == 0) or (archivo == 5): + dic_regimen = self.data_bi_regimen(compan.id_hotel) + dic_export.append({'Regimen': dic_regimen}) # if (archivo == 0) or (archivo == 6): # dic_export.append({'Reservas': dic_reservas}) # if (archivo == 0) or (archivo == 7): @@ -157,3 +158,16 @@ class Data_Bi(models.Model): 'ID_Pais': pais['code'], 'Descripcion': pais['name']}) return dic_pais + + @api.model + def data_bi_regimen(self, compan): + dic_regimen = [] # Diccionario con los Board Services + board_services = self.env['hotel.board.service'].search_read([]) + dic_regimen.append({'ID_Hotel': compan, + 'ID_Regimen': 0, + 'Descripcion': 'Sin régimen'}) + for board_service in board_services: + dic_regimen.append({'ID_Hotel': compan, + 'ID_Regimen': board_service['id'], + 'Descripcion': board_service['name']}) + return dic_regimen From 0c51c26cfb8c0bc48ef6ccaced6a95edeeafb1bc Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Tue, 30 Apr 2019 12:29:29 +0200 Subject: [PATCH 05/14] [ADD] Estados --- hotel_data_bi/models/data_bi.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/hotel_data_bi/models/data_bi.py b/hotel_data_bi/models/data_bi.py index c0e5d49ef..fce35333c 100644 --- a/hotel_data_bi/models/data_bi.py +++ b/hotel_data_bi/models/data_bi.py @@ -80,7 +80,6 @@ class Data_Bi(models.Model): if (archivo == 0) or (archivo == 1): dic_tarifa = self.data_bi_tarifa(compan.id_hotel) dic_export.append({'Tarifa': dic_tarifa}) - if (archivo == 0) or (archivo == 2): dic_canal = self.data_bi_canal(compan.id_hotel) dic_export.append({'Canal': dic_canal}) @@ -109,8 +108,9 @@ class Data_Bi(models.Model): # dic_export.append({'Segmentos': dic_segmentos}) # if (archivo == 0) or (archivo == 13): # dic_export.append({'Clientes': dic_clientes}) - # if (archivo == 0) or (archivo == 14): - # dic_export.append({'Estado Reservas': dic_estados}) + if (archivo == 0) or (archivo == 14): + dic_estados = self.data_bi_estados(compan.id_hotel) + dic_export.append({'Estado Reservas': dic_estados}) # Debug Stop ------------------- import wdb; wdb.set_trace() @@ -171,3 +171,15 @@ class Data_Bi(models.Model): 'ID_Regimen': board_service['id'], 'Descripcion': board_service['name']}) return dic_regimen + + @api.model + def data_bi_estados(self, compan): + dic_estados = [] # Diccionario con los Estados Reserva + estado_array_txt = ['Borrador', 'Confirmada', 'Hospedandose', + 'Checkout', 'Cancelada'] + estado_array = ['draft', 'confirm', 'booking', 'done', 'cancelled'] + for i in range(0, len(estado_array)): + dic_estados.append({'ID_Hotel': compan, + 'ID_EstadoReserva': i, + 'Descripcion': estado_array_txt[i]}) + return dic_estados From 7ed090290fc5051963b65176019e69184a6c9bb6 Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Tue, 30 Apr 2019 13:35:46 +0200 Subject: [PATCH 06/14] [ADD] Room tyes & Capacity & Budget --- hotel_data_bi/models/data_bi.py | 70 ++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/hotel_data_bi/models/data_bi.py b/hotel_data_bi/models/data_bi.py index fce35333c..3ef31ddb0 100644 --- a/hotel_data_bi/models/data_bi.py +++ b/hotel_data_bi/models/data_bi.py @@ -94,12 +94,18 @@ class Data_Bi(models.Model): dic_export.append({'Regimen': dic_regimen}) # if (archivo == 0) or (archivo == 6): # dic_export.append({'Reservas': dic_reservas}) - # if (archivo == 0) or (archivo == 7): - # dic_export.append({'Capacidad': dic_capacidad}) - # if (archivo == 0) or (archivo == 8): - # dic_export.append({'Tipo Habitación': dic_tipo_habitacion}) - # if (archivo == 0) or (archivo == 9): - # dic_export.append({'Budget': dic_budget}) + if (archivo == 0) or (archivo == 7) or (archivo == 8): + room_types = self.env['hotel.room.type'].search([]) + if (archivo == 0) or (archivo == 7): + dic_capacidad = self.data_bi_capacidad(compan.id_hotel, room_types) + dic_export.append({'Capacidad': dic_capacidad}) + if (archivo == 0) or (archivo == 8): + dic_tipo_habitacion = self.data_bi_habitacione(compan.id_hotel, + room_types) + dic_export.append({'Tipo Habitación': dic_tipo_habitacion}) + if (archivo == 0) or (archivo == 9): + dic_budget = self.data_bi_budget(compan.id_hotel) + dic_export.append({'Budget': dic_budget}) # if (archivo == 0) or (archivo == 10): # dic_export.append({'Bloqueos': dic_bloqueos}) # if (archivo == 0) or (archivo == 11): @@ -183,3 +189,55 @@ class Data_Bi(models.Model): 'ID_EstadoReserva': i, 'Descripcion': estado_array_txt[i]}) return dic_estados + + @api.model + def data_bi_habitacione(self, compan, rooms): + dic_tipo_habitacion = [] # Diccionario con Virtuals Rooms + for room in rooms: + dic_tipo_habitacion.append({ + 'ID_Hotel': compan, + 'ID_Tipo_Habitacion': room['id'], + 'Descripcion': room['name'], + 'Estancias': room['capacity']}) + return dic_tipo_habitacion + + @api.model + def data_bi_capacidad(self, compan, rooms): + dic_capacidad = [] # Diccionario con las capacidades + for room in rooms: + dic_capacidad.append({ + 'ID_Hotel': compan, + 'Hasta_Fecha': + (date.today() + timedelta(days=365 * 3)).strftime("%Y-%m-%d"), + 'ID_Tipo_Habitacion': room['id'], + 'Nro_Habitaciones': room['total_rooms_count']}) + return dic_capacidad + + @api.model + def data_bi_budget(self, compan): + budgets = self.env['budget'].search([]) + dic_budget = [] # Diccionario con las previsiones Budget + for budget in budgets: + dic_budget.append({'ID_Hotel': compan, + 'Fecha': str(budget.year) + '-' + + str(budget.month).zfill(2) + '-01', + # 'ID_Tarifa': 0, + # 'ID_Canal': 0, + # 'ID_Pais': 0, + # 'ID_Regimen': 0, + # 'ID_Tipo_Habitacion': 0, + # 'ID_Cliente': 0, + 'Room_Nights': budget.room_nights, + 'Room_Revenue': budget.room_revenue, + # 'Pension_Revenue': 0, + 'Estancias': budget.estancias}) + # Fecha fecha Primer día del mes + # ID_Tarifa numérico Código de la Tarifa + # ID_Canal numérico Código del Canal + # ID_Pais numérico Código del País + # ID_Regimen numérico Cóigo del Régimen + # ID_Tipo_Habitacion numérico Código del Tipo de Habitación + # iD_Segmento numérico Código del Segmento + # ID_Cliente numérico Código del Cliente + # Pension_Revenue numérico con dos decimales Ingresos por Pensión + return dic_budget From f7fb1cf47724316941282846eea08e7b5a57c332 Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Tue, 30 Apr 2019 13:42:28 +0200 Subject: [PATCH 07/14] [ADD] Reason for blocking --- hotel_data_bi/models/data_bi.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/hotel_data_bi/models/data_bi.py b/hotel_data_bi/models/data_bi.py index 3ef31ddb0..a0bfacdad 100644 --- a/hotel_data_bi/models/data_bi.py +++ b/hotel_data_bi/models/data_bi.py @@ -108,8 +108,9 @@ class Data_Bi(models.Model): dic_export.append({'Budget': dic_budget}) # if (archivo == 0) or (archivo == 10): # dic_export.append({'Bloqueos': dic_bloqueos}) - # if (archivo == 0) or (archivo == 11): - # dic_export.append({'Motivo Bloqueo': dic_moti_bloq}) + if (archivo == 0) or (archivo == 11): + dic_moti_bloq = self.data_bi_moti_bloq(compan.id_hotel) + dic_export.append({'Motivo Bloqueo': dic_moti_bloq}) # if (archivo == 0) or (archivo == 12): # dic_export.append({'Segmentos': dic_segmentos}) # if (archivo == 0) or (archivo == 13): @@ -241,3 +242,14 @@ class Data_Bi(models.Model): # ID_Cliente numérico Código del Cliente # Pension_Revenue numérico con dos decimales Ingresos por Pensión return dic_budget + + @api.model + def data_bi_moti_bloq(self, compan): + dic_moti_bloq = [] # Diccionario con Motivo de Bloqueos + bloqeo_array = ['Staff', _('Out of Service')] + for i in range(0, len(bloqeo_array)): + dic_moti_bloq.append({'ID_Hotel': compan, + 'ID_Motivo_Bloqueo': i, + 'Descripcion': bloqeo_array[i].encode( + 'ascii', 'xmlcharrefreplace')}) + return dic_moti_bloq From 478a4b1ed4e151dd9db4c32f37ac508c2ff90108 Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Tue, 30 Apr 2019 14:08:29 +0200 Subject: [PATCH 08/14] [ADD] Segmentation --- hotel_data_bi/models/data_bi.py | 37 ++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/hotel_data_bi/models/data_bi.py b/hotel_data_bi/models/data_bi.py index a0bfacdad..843cf9945 100644 --- a/hotel_data_bi/models/data_bi.py +++ b/hotel_data_bi/models/data_bi.py @@ -111,24 +111,26 @@ class Data_Bi(models.Model): if (archivo == 0) or (archivo == 11): dic_moti_bloq = self.data_bi_moti_bloq(compan.id_hotel) dic_export.append({'Motivo Bloqueo': dic_moti_bloq}) - # if (archivo == 0) or (archivo == 12): - # dic_export.append({'Segmentos': dic_segmentos}) + if (archivo == 0) or (archivo == 12): + dic_segmentos = self.data_bi_segment(compan.id_hotel) + dic_export.append({'Segmentos': dic_segmentos}) # if (archivo == 0) or (archivo == 13): # dic_export.append({'Clientes': dic_clientes}) if (archivo == 0) or (archivo == 14): dic_estados = self.data_bi_estados(compan.id_hotel) dic_export.append({'Estado Reservas': dic_estados}) + dictionaryToJson = json.dumps(dic_export) + _logger.warning("End Export Data_Bi Module to Json") # Debug Stop ------------------- import wdb; wdb.set_trace() # Debug Stop ------------------- - dictionaryToJson = json.dumps(dic_export) - _logger.warning("End Export Data_Bi Module to Json") return dictionaryToJson @api.model def data_bi_tarifa(self, compan): + _logger.info("DataBi: Calculating all fees") dic_tarifa = [] # Diccionario con las tarifas tarifas = self.env['product.pricelist'].search_read([], ['name']) for tarifa in tarifas: @@ -139,6 +141,7 @@ class Data_Bi(models.Model): @api.model def data_bi_canal(self, compan): + _logger.info("DataBi: Calculating all channels") dic_canal = [] # Diccionario con los Canales canal_array = ['Directo', 'OTA', 'Call-Center', 'Agencia', 'Touroperador'] @@ -150,6 +153,7 @@ class Data_Bi(models.Model): @api.model def data_bi_hotel(self, compan): + _logger.info("DataBi: Calculating hotel names") dic_hotel = [] # Diccionario con el/los nombre de los hoteles dic_hotel.append({'ID_Hotel': compan.id_hotel, 'Descripcion': compan.property_name}) @@ -157,6 +161,7 @@ class Data_Bi(models.Model): @api.model def data_bi_pais(self, compan): + _logger.info("DataBi: Calculating all countries") dic_pais = [] # Diccionario con los nombre de los Paises usando los del INE paises = self.env['code.ine'].search_read([], ['code', 'name']) @@ -168,6 +173,7 @@ class Data_Bi(models.Model): @api.model def data_bi_regimen(self, compan): + _logger.info("DataBi: Calculating all board services") dic_regimen = [] # Diccionario con los Board Services board_services = self.env['hotel.board.service'].search_read([]) dic_regimen.append({'ID_Hotel': compan, @@ -181,6 +187,7 @@ class Data_Bi(models.Model): @api.model def data_bi_estados(self, compan): + _logger.info("DataBi: Calculating all the states of the reserves") dic_estados = [] # Diccionario con los Estados Reserva estado_array_txt = ['Borrador', 'Confirmada', 'Hospedandose', 'Checkout', 'Cancelada'] @@ -193,7 +200,8 @@ class Data_Bi(models.Model): @api.model def data_bi_habitacione(self, compan, rooms): - dic_tipo_habitacion = [] # Diccionario con Virtuals Rooms + _logger.info("DataBi: Calculating all room types") + dic_tipo_habitacion = [] # Diccionario con Rooms types for room in rooms: dic_tipo_habitacion.append({ 'ID_Hotel': compan, @@ -204,6 +212,7 @@ class Data_Bi(models.Model): @api.model def data_bi_capacidad(self, compan, rooms): + _logger.info("DataBi: Calculating all the capacitys") dic_capacidad = [] # Diccionario con las capacidades for room in rooms: dic_capacidad.append({ @@ -216,6 +225,7 @@ class Data_Bi(models.Model): @api.model def data_bi_budget(self, compan): + _logger.info("DataBi: Calculating budget") budgets = self.env['budget'].search([]) dic_budget = [] # Diccionario con las previsiones Budget for budget in budgets: @@ -245,11 +255,24 @@ class Data_Bi(models.Model): @api.model def data_bi_moti_bloq(self, compan): + _logger.info("DataBi: Calculating all blocking reasons") dic_moti_bloq = [] # Diccionario con Motivo de Bloqueos bloqeo_array = ['Staff', _('Out of Service')] for i in range(0, len(bloqeo_array)): dic_moti_bloq.append({'ID_Hotel': compan, 'ID_Motivo_Bloqueo': i, - 'Descripcion': bloqeo_array[i].encode( - 'ascii', 'xmlcharrefreplace')}) + 'Descripcion': bloqeo_array[i]}) return dic_moti_bloq + + @api.model + def data_bi_segment(self, compan): + _logger.info("DataBi: Calculating all the segmentations") + dic_segmentos = [] # Diccionario con Segmentación + lineas = self.env['res.partner.category'].search([]) + for linea in lineas: + if linea.parent_id.name: + seg_desc = linea.parent_id.name + " / " + linea.name + dic_segmentos.append({'ID_Hotel': compan, + 'ID_Segmento': linea.id, + 'Descripcion': seg_desc}) + return dic_segmentos From 48d55d48eae33bc101c4d621361b8294ca2af073 Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Tue, 30 Apr 2019 20:28:14 +0200 Subject: [PATCH 09/14] [ADD] Clients --- hotel_data_bi/models/data_bi.py | 57 +++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/hotel_data_bi/models/data_bi.py b/hotel_data_bi/models/data_bi.py index 843cf9945..2e9623940 100644 --- a/hotel_data_bi/models/data_bi.py +++ b/hotel_data_bi/models/data_bi.py @@ -114,8 +114,9 @@ class Data_Bi(models.Model): if (archivo == 0) or (archivo == 12): dic_segmentos = self.data_bi_segment(compan.id_hotel) dic_export.append({'Segmentos': dic_segmentos}) - # if (archivo == 0) or (archivo == 13): - # dic_export.append({'Clientes': dic_clientes}) + if (archivo == 0) or (archivo == 13): + dic_clientes = self.data_bi_client(compan.id_hotel) + dic_export.append({'Clientes': dic_clientes}) if (archivo == 0) or (archivo == 14): dic_estados = self.data_bi_estados(compan.id_hotel) dic_export.append({'Estado Reservas': dic_estados}) @@ -276,3 +277,55 @@ class Data_Bi(models.Model): 'ID_Segmento': linea.id, 'Descripcion': seg_desc}) return dic_segmentos + + @api.model + def data_bi_client(self, compan): + dic_clientes = [] # Diccionario con Clientes (OTAs y agencias) + dic_clientes.append({'ID_Hotel': compan, + 'ID_Cliente': 0, + 'Descripcion': u'Ninguno'}) + lineas = self.env['channel.ota.info'].search([]) + + for linea in lineas: + dic_clientes.append({'ID_Hotel': compan, + 'ID_Cliente': linea.id, + 'Descripcion': linea.name}) + + lineas = self.env['res.partner'].search([('is_tour_operator', + '=', True)]) + id_cli_count = 700 + for linea in lineas: + dic_clientes.append({'ID_Hotel': compan, + 'ID_Cliente': id_cli_count, + 'Descripcion': linea.name}) + id_cli_count += 1 + + dic_clientes.append({'ID_Hotel': compan, + 'ID_Cliente': 999, + 'Descripcion': u'Web Propia'}) + dic_clientes.append({'ID_Hotel': compan, + 'ID_Cliente': 901, + 'Descripcion': u'Expedia Empaquedata'}) + dic_clientes.append({'ID_Hotel': compan, + 'ID_Cliente': 902, + 'Descripcion': u'Expedia Sin Comisión'}) + dic_clientes.append({'ID_Hotel': compan, + 'ID_Cliente': 903, + 'Descripcion': u'Puerta'}) + dic_clientes.append({'ID_Hotel': compan, + 'ID_Cliente': 904, + 'Descripcion': u'E-Mail'}) + dic_clientes.append({'ID_Hotel': compan, + 'ID_Cliente': 905, + 'Descripcion': u'Teléfono'}) + dic_clientes.append({'ID_Hotel': compan, + 'ID_Cliente': 906, + 'Descripcion': u'Call-Center'}) + dic_clientes.append({'ID_Hotel': compan, + 'ID_Cliente': 907, + 'Descripcion': u'Agencia'}) + dic_clientes.append({'ID_Hotel': compan, + 'ID_Cliente': 908, + 'Descripcion': u'Touroperador'}) + + return dic_clientes From 5c7903c99b1d0f0b4c47f3529cc49d2b6f4edc3d Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Thu, 2 May 2019 13:47:38 +0200 Subject: [PATCH 10/14] [ADD] calculation of locked rooms --- hotel_data_bi/models/data_bi.py | 38 ++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/hotel_data_bi/models/data_bi.py b/hotel_data_bi/models/data_bi.py index 2e9623940..4abd18212 100644 --- a/hotel_data_bi/models/data_bi.py +++ b/hotel_data_bi/models/data_bi.py @@ -68,7 +68,6 @@ class Data_Bi(models.Model): fechafoto = datetime.strptime(fechafoto, '%Y-%m-%d').date() _logger.warning("Init Export Data_Bi Module") - if not isinstance(archivo, int): archivo = 0 dic_param = [] @@ -76,6 +75,9 @@ class Data_Bi(models.Model): 'Fechafoto': fechafoto.strftime('%Y-%m-%d')}) compan = self.env.user.company_id + days_ago = 60 + limit_ago = (fechafoto - timedelta(days=days_ago)).strftime('%Y-%m-%d') + dic_export = [] # Diccionario con todo lo necesario para exportar. if (archivo == 0) or (archivo == 1): dic_tarifa = self.data_bi_tarifa(compan.id_hotel) @@ -106,8 +108,11 @@ class Data_Bi(models.Model): if (archivo == 0) or (archivo == 9): dic_budget = self.data_bi_budget(compan.id_hotel) dic_export.append({'Budget': dic_budget}) - # if (archivo == 0) or (archivo == 10): - # dic_export.append({'Bloqueos': dic_bloqueos}) + if (archivo == 0) or (archivo == 10): + line_res = self.env['hotel.reservation.line'].search( + [('date', '>=', limit_ago)], order="date") + dic_bloqueos = self.data_bi_bloqueos(compan.id_hotel, line_res) + dic_export.append({'Bloqueos': dic_bloqueos}) if (archivo == 0) or (archivo == 11): dic_moti_bloq = self.data_bi_moti_bloq(compan.id_hotel) dic_export.append({'Motivo Bloqueo': dic_moti_bloq}) @@ -329,3 +334,30 @@ class Data_Bi(models.Model): 'Descripcion': u'Touroperador'}) return dic_clientes + + @api.model + def data_bi_bloqueos(self, compan, lines): + _logger.info("DataBi: Calculating all reservations blocked") + dic_bloqueos = [] # Diccionario con Bloqueos + lines = lines.filtered( + lambda n: (n.reservation_id.reservation_type != 'normal') and ( + n.reservation_id.state != 'cancelled')) + for line in lines: + # if linea.reservation_id.state != 'cancelled': + if line.reservation_id.reservation_type == 'out': + id_m_b = 1 + else: + id_m_b = 0 + dic_bloqueos.append({ + 'ID_Hotel': compan, + 'Fecha_desde': line.date, + 'Fecha_hasta': (datetime.strptime(line.date, "%Y-%m-%d") + + timedelta(days=1)).strftime("%Y-%m-%d"), + 'ID_Tipo_Habitacion': line.reservation_id.room_type_id.id, + 'ID_Motivo_Bloqueo': id_m_b, + 'Nro_Habitaciones': 1}) + return dic_bloqueos + + # # Debug Stop ------------------- + # import wdb; wdb.set_trace() + # # Debug Stop ------------------- From c86ff3f6f44b1d0f515383d115552106ba3d1dea Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Thu, 9 May 2019 18:54:35 +0200 Subject: [PATCH 11/14] [ADD] Expedia comision rate field --- hotel_data_bi/models/inherit_res_company.py | 11 +++++++++++ hotel_data_bi/views/inherit_res_company.xml | 6 +++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/hotel_data_bi/models/inherit_res_company.py b/hotel_data_bi/models/inherit_res_company.py index f33265dc0..303bf541a 100644 --- a/hotel_data_bi/models/inherit_res_company.py +++ b/hotel_data_bi/models/inherit_res_company.py @@ -11,3 +11,14 @@ class Inherit_res_company(models.Model): 'Unique ID for DataBI', default=0, help='It must be unique to be able to identify the hotel, \ within a hotel group.') + expedia_rate = fields.Integer( + 'Expedia Rate DataBI', + default=18, required=True, digits=(2), + help='It is the commission percentage negotiated with the \ + Expedia company, expressed with two digits. \ + Example: 18 = 18% commission.') + data_bi_days = fields.Integer( + 'Days to download', + default=60, required=True, digits=(3), + help='Number of days, which are downloaded data, \ + backwards, by default are 60 days to download.') diff --git a/hotel_data_bi/views/inherit_res_company.xml b/hotel_data_bi/views/inherit_res_company.xml index 2f829839b..05b3865ab 100644 --- a/hotel_data_bi/views/inherit_res_company.xml +++ b/hotel_data_bi/views/inherit_res_company.xml @@ -10,7 +10,11 @@ - + + + + + From cbaf0fd8fa17d4caeec2445920c05e5e689c3287 Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Thu, 9 May 2019 21:17:06 +0200 Subject: [PATCH 12/14] [ADD] reservations calculus --- hotel_data_bi/models/data_bi.py | 221 +++++++++++++++++++++++++++++--- 1 file changed, 203 insertions(+), 18 deletions(-) diff --git a/hotel_data_bi/models/data_bi.py b/hotel_data_bi/models/data_bi.py index 4abd18212..bd7ce8ec4 100644 --- a/hotel_data_bi/models/data_bi.py +++ b/hotel_data_bi/models/data_bi.py @@ -19,7 +19,7 @@ # along with this program. If not, see . # ############################################################################## -from openerp import models, fields, api, _ +from openerp import models, api, _ from datetime import date, datetime, timedelta import json import logging @@ -74,11 +74,17 @@ class Data_Bi(models.Model): dic_param.append({'Archivo': archivo, 'Fechafoto': fechafoto.strftime('%Y-%m-%d')}) compan = self.env.user.company_id - - days_ago = 60 - limit_ago = (fechafoto - timedelta(days=days_ago)).strftime('%Y-%m-%d') + limit_ago = (fechafoto - timedelta( + days=self.env.user.company_id.data_bi_days)).strftime('%Y-%m-%d') dic_export = [] # Diccionario con todo lo necesario para exportar. + if (archivo == 0) or (archivo == 7) or (archivo == 8): + room_types = self.env['hotel.room.type'].search([]) + if (archivo == 0) or (archivo == 10) or (archivo == 6): + line_res = self.env['hotel.reservation.line'].search( + [('date', '>=', limit_ago)], order="id") + estado_array = ['draft', 'confirm', 'booking', 'done', 'cancelled'] + if (archivo == 0) or (archivo == 1): dic_tarifa = self.data_bi_tarifa(compan.id_hotel) dic_export.append({'Tarifa': dic_tarifa}) @@ -94,10 +100,6 @@ class Data_Bi(models.Model): if (archivo == 0) or (archivo == 5): dic_regimen = self.data_bi_regimen(compan.id_hotel) dic_export.append({'Regimen': dic_regimen}) - # if (archivo == 0) or (archivo == 6): - # dic_export.append({'Reservas': dic_reservas}) - if (archivo == 0) or (archivo == 7) or (archivo == 8): - room_types = self.env['hotel.room.type'].search([]) if (archivo == 0) or (archivo == 7): dic_capacidad = self.data_bi_capacidad(compan.id_hotel, room_types) dic_export.append({'Capacidad': dic_capacidad}) @@ -109,8 +111,6 @@ class Data_Bi(models.Model): dic_budget = self.data_bi_budget(compan.id_hotel) dic_export.append({'Budget': dic_budget}) if (archivo == 0) or (archivo == 10): - line_res = self.env['hotel.reservation.line'].search( - [('date', '>=', limit_ago)], order="date") dic_bloqueos = self.data_bi_bloqueos(compan.id_hotel, line_res) dic_export.append({'Bloqueos': dic_bloqueos}) if (archivo == 0) or (archivo == 11): @@ -119,17 +119,23 @@ class Data_Bi(models.Model): if (archivo == 0) or (archivo == 12): dic_segmentos = self.data_bi_segment(compan.id_hotel) dic_export.append({'Segmentos': dic_segmentos}) - if (archivo == 0) or (archivo == 13): + if (archivo == 0) or (archivo == 13) or (archivo == 6): dic_clientes = self.data_bi_client(compan.id_hotel) dic_export.append({'Clientes': dic_clientes}) if (archivo == 0) or (archivo == 14): - dic_estados = self.data_bi_estados(compan.id_hotel) + dic_estados = self.data_bi_estados(compan.id_hotel, estado_array) dic_export.append({'Estado Reservas': dic_estados}) + if (archivo == 0) or (archivo == 6): + dic_reservas = self.data_bi_reservas(compan.id_hotel, + line_res, + estado_array, + dic_clientes) + dic_export.append({'Reservas': dic_reservas}) dictionaryToJson = json.dumps(dic_export) _logger.warning("End Export Data_Bi Module to Json") # Debug Stop ------------------- - import wdb; wdb.set_trace() + # import wdb; wdb.set_trace() # Debug Stop ------------------- return dictionaryToJson @@ -149,8 +155,8 @@ class Data_Bi(models.Model): def data_bi_canal(self, compan): _logger.info("DataBi: Calculating all channels") dic_canal = [] # Diccionario con los Canales - canal_array = ['Directo', 'OTA', 'Call-Center', 'Agencia', - 'Touroperador'] + canal_array = ['Puerta', 'Mail', 'Telefono', 'Call Center', 'Web', + 'Agencia', 'Touroperador', 'Virtual Door'] for i in range(0, len(canal_array)): dic_canal.append({'ID_Hotel': compan, 'ID_Canal': i, @@ -192,12 +198,12 @@ class Data_Bi(models.Model): return dic_regimen @api.model - def data_bi_estados(self, compan): + def data_bi_estados(self, compan, estado_array): _logger.info("DataBi: Calculating all the states of the reserves") dic_estados = [] # Diccionario con los Estados Reserva estado_array_txt = ['Borrador', 'Confirmada', 'Hospedandose', 'Checkout', 'Cancelada'] - estado_array = ['draft', 'confirm', 'booking', 'done', 'cancelled'] + # estado_array = ['draft', 'confirm', 'booking', 'done', 'cancelled'] for i in range(0, len(estado_array)): dic_estados.append({'ID_Hotel': compan, 'ID_EstadoReserva': i, @@ -332,7 +338,6 @@ class Data_Bi(models.Model): dic_clientes.append({'ID_Hotel': compan, 'ID_Cliente': 908, 'Descripcion': u'Touroperador'}) - return dic_clientes @api.model @@ -358,6 +363,186 @@ class Data_Bi(models.Model): 'Nro_Habitaciones': 1}) return dic_bloqueos + @api.model + def data_bi_reservas(self, compan, lineas, estado_array, dic_clientes): + dic_reservas = [] + lineas = lineas.filtered( + lambda n: (n.reservation_id.reservation_type == 'normal') and ( + n.price > 0)) + channels = {'door': 0, + 'mail': 1, + 'phone': 2, + 'call': 3, + 'web': 4, + 'agency': 5, + 'operator': 6, + 'virtualdoor': 7} + + for linea in lineas: + id_segmen = 0 + if len(linea.reservation_id.segmentation_ids) > 0: + id_segmen = linea.reservation_id.segmentation_ids[0].id + elif len(linea.reservation_id.partner_id.category_id) > 0: + id_segmen = ( + linea.reservation_id.partner_id.category_id[0].id) + precio_neto = linea.price + precio_dto = 0 + precio_iva = 0 + precio_comision = 0 + + if linea.reservation_id.ota_id.id: + ota_prices = self.data_bi_comisiones_ota(linea) + precio_neto = ota_prices[0]['precio_neto'] + precio_dto = ota_prices[0]['precio_dto'] + precio_iva = ota_prices[0]['precio_iva'] + precio_comision = ota_prices[0]['precio_comision'] + + # if linea.reservation_id.id == 6742: + # # # Debug Stop ------------------- + # import wdb; wdb.set_trace() + # # # Debug Stop ------------------- + if linea.reservation_id.discount != 0: + precio_dto = linea.price * ( + linea.reservation_id.discount/100) + + dic_reservas.append({ + 'ID_Reserva': linea.reservation_id.folio_id.name, + 'ID_Hotel': compan, + 'ID_EstadoReserva': estado_array.index( + linea.reservation_id.state), + 'FechaVenta': linea.reservation_id.create_date[0:10], + 'ID_Segmento': id_segmen, + # 'ID_Cliente': channel_c, + 'ID_Canal': channels[linea.reservation_id.channel_type], + 'FechaExtraccion': date.today().strftime('%Y-%m-%d'), + 'Entrada': linea.date, + 'Salida': (datetime.strptime(linea.date, "%Y-%m-%d") + + timedelta(days=1)).strftime("%Y-%m-%d"), + 'Noches': 1, + 'ID_TipoHabitacion': linea.reservation_id.room_type_id.id, + 'ID_HabitacionDuerme': + linea.reservation_id.room_id.room_type_id.id, + 'ID_Regimen': 0, + 'Adultos': linea.reservation_id.adults, + 'Menores': linea.reservation_id.children, + 'Cunas': 0, + 'PrecioDiario': precio_neto, + 'PrecioComision': precio_comision, + 'PrecioIva': precio_iva, + 'PrecioDto': precio_dto, + 'ID_Tarifa': linea.reservation_id.pricelist_id.id, + # 'ID_Pais': id_codeine + }) + # ID_Reserva numérico Código único de la reserva + # ID_Hotel numérico Código del Hotel + # ID_EstadoReserva numérico Código del estado de la reserva + # FechaVenta fecha Fecha de la venta de la reserva + # ID_Segmento numérico Código del Segmento de la reserva + # ID_Cliente Numérico Código del Cliente de la reserva + # ID_Canal numérico Código del Canal + # FechaExtraccion fecha Fecha de la extracción de los datos (Foto) + # Entrada fecha Fecha de entrada + # Salida fecha Fecha de salida + # Noches numérico Nro. de noches de la reserva + # ID_TipoHabitacion numérico Código del Tipo de Habitación + # ID_Regimen numérico Código del Tipo de Régimen + # Adultos numérico Nro. de adultos + # Menores numérico Nro. de menores + # Cunas numérico Nro. de cunas + # PrecioDiario numérico con 2 decimales Precio por noche de la reserva + # ID_Tarifa numérico Código de la tarifa aplicada a la reserva + # ID_Pais numérico Código del país + return dic_reservas + + @api.model + def data_bi_comisiones_ota(self, reserva): + response_dic = [] + precio_neto = reserva.price + precio_comision = 0 + precio_iva = 0 + precio_dto = 0 + if reserva.reservation_id.ota_id.ota_id == "2": + # Booking. 15% comision + precio_comision = (precio_neto*15/100) + precio_neto -= precio_comision + precio_iva = (precio_neto*10/100) + precio_neto -= precio_iva + + if reserva.reservation_id.ota_id.ota_id == "9": + # Hotelbeds 20% comision + precio_comision = (precio_neto*20/100) + precio_neto -= precio_comision + precio_iva = (precio_neto*10/100) + precio_neto -= precio_iva + + if reserva.reservation_id.ota_id.ota_id == "11": + # HRS 20% comision + precio_comision = (precio_neto*20/100) + precio_neto -= precio_comision + precio_iva = (precio_neto*10/100) + precio_neto -= precio_iva + + if reserva.reservation_id.ota_id.ota_id == "1": + # Expedia. + precio_comision = (precio_neto*15/100) + precio_neto -= precio_comision + precio_iva = (precio_neto*10/100) + precio_neto -= precio_iva + data = json.loads( + reserva.reservation_id.channel_bind_ids.channel_raw_data) + + jsonBooked = data['booked_rooms'][0] + if jsonBooked.get('ancillary').get( + 'channel_rate_name') is not None: + jsonRate = jsonBooked.get('ancillary').get( + 'channel_rate_name') + # _logger.warning("EXPEDIA ancillary : %s - %s", + # jsonRate, reserva.id) + + elif jsonBooked.get('roomdays')[0].get( + 'ancillary').get( + 'channel_rate_name') is not None: + jsonRate = jsonBooked.get( + 'roomdays')[0].get( + 'ancillary').get('channel_rate_name') + # _logger.warning("EXPEDIA roomdays : %s - %s", + # jsonRate, reserva.id) + + else: + _logger.critical( + "EXPEDIA Tarifa No Contemplada : " + + jsonBooked) + + jsonRefundable = jsonRate.upper().find('REFUNDABLE') + # _logger.warning("EXPEDIA Tarifa : %s", jsonRate) + # _logger.warning("EXPEDIA Tarifa : %s y %s", + # jsonRate, str(jsonRefundable)) + + # 10 % Iva + precio_iva = round((precio_neto-(precio_neto/1.1)), 2) + # 18 % comision ? + precio_comision = inv_percent( + precio_neto, self.env.user.company_id.expedia_rate) + precio_neto += precio_comision + # 3% Refundable ? + if jsonRefundable >= 0: + precio_dto = inv_percent(precio_neto, 3) + precio_neto += precio_dto + # _logger.warning("ODOO: %s , precio_neto: %s , precio_comision: \ + # %s , precio_iva: %s , precio_dto: %s", reserva.price, + # precio_neto, precio_comision, precio_iva, + # precio_dto) + + response_dic.append({'ota': reserva.reservation_id.ota_id.id, + 'ota_id': reserva.reservation_id.ota_id.ota_id, + 'precio_odoo': reserva.price, + 'precio_neto': precio_neto, + 'precio_comision': precio_comision, + 'precio_iva': precio_iva, + 'precio_dto': precio_dto, + }) + return response_dic + # # Debug Stop ------------------- # import wdb; wdb.set_trace() # # Debug Stop ------------------- From 6069513d02e8160075a119e430091615e8d23c3a Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Fri, 10 May 2019 14:24:01 +0200 Subject: [PATCH 13/14] [ADD] Security in DataBi --- hotel_data_bi/security/ir.model.access.csv | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hotel_data_bi/security/ir.model.access.csv b/hotel_data_bi/security/ir.model.access.csv index a8f8fab40..99a2df307 100644 --- a/hotel_data_bi/security/ir.model.access.csv +++ b/hotel_data_bi/security/ir.model.access.csv @@ -2,3 +2,13 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink ir_model_hotel_budget,hoteldatabi.budget.manager,hotel_data_bi.model_budget,hotel.group_hotel_manager,1,1,1,1 ir_model_hotel_budget_user,hoteldatabi.budget.user,hotel_data_bi.model_budget,hotel.group_hotel_user,0,0,0,0 ir_model_hotel_data_bi_export_data,hoteldatabi.data_bi.export_data,hotel_data_bi.model_data_bi,hotel_data_bi.group_hotel_export_data,1,0,0,0 +access_room_type,hoteldatabi.hotel_room_type,hotel.model_hotel_room_type,hotel_data_bi.group_hotel_export_data,1,0,0,0 +access_hotel_reservation_line,hoteldatabi.hotel_reservation_line,hotel.model_hotel_reservation_line,hotel_data_bi.group_hotel_export_data,1,0,0,0 +access_hotel_reservation,hoteldatabi.hotel_reservation,hotel.model_hotel_reservation,hotel_data_bi.group_hotel_export_data,1,0,0,0 +access_hotel_board_service,hoteldatabi.hotel_board_service,hotel.model_hotel_board_service,hotel_data_bi.group_hotel_export_data,1,0,0,0 +access_code_ine,hoteldatabi.code_ine,hotel_l10n_es.model_code_ine,hotel_data_bi.group_hotel_export_data,1,0,0,0 +access_budget ,hoteldatabi.budget,hotel_data_bi.model_budget,hotel_data_bi.group_hotel_export_data,1,0,0,0 +access_channel_ota_info,hoteldatabi.channel_ota_info,hotel_channel_connector.model_channel_ota_info,hotel_data_bi.group_hotel_export_data,1,0,0,0 +access_hotel_folio,hoteldatabi.hotel_folio,hotel.model_hotel_folio,hotel_data_bi.group_hotel_export_data,1,0,0,0 +access_hotel_room,hoteldatabi.hotel_room,hotel.model_hotel_room,hotel_data_bi.group_hotel_export_data,1,0,0,0 +access_channel_hotel_reservation,hoteldatabi.channel_hotel_reservation,hotel_channel_connector.model_channel_hotel_reservation,hotel_data_bi.group_hotel_export_data,1,0,0,0 From c31a1a3554abf1790d59e2f21bbcd320abdbdd79 Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Fri, 10 May 2019 14:24:36 +0200 Subject: [PATCH 14/14] [FIX] depends --- hotel_data_bi/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hotel_data_bi/__manifest__.py b/hotel_data_bi/__manifest__.py index e0ca740b2..2b398871e 100644 --- a/hotel_data_bi/__manifest__.py +++ b/hotel_data_bi/__manifest__.py @@ -16,7 +16,7 @@ 'license': 'AGPL-3', 'author': "Jose Luis Algara (Alda hotels) ", 'website': 'www.aldahotels.com', - 'depends': ['hotel', 'hotel_l10n_es'], + 'depends': ['hotel', 'hotel_l10n_es', 'hotel_channel_connector'], 'category': 'hotel/revenue', 'data': [ 'views/budget.xml',