diff --git a/hibou_professional/__init__.py b/hibou_professional/__init__.py new file mode 100644 index 00000000..09434554 --- /dev/null +++ b/hibou_professional/__init__.py @@ -0,0 +1,3 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from . import models diff --git a/hibou_professional/__manifest__.py b/hibou_professional/__manifest__.py new file mode 100644 index 00000000..cc1eaec2 --- /dev/null +++ b/hibou_professional/__manifest__.py @@ -0,0 +1,24 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +{ + 'name': 'Hibou Professional', + 'author': 'Hibou Corp. ', + 'category': 'Tools', + 'depends': ['mail'], + 'version': '11.0.1.0.0', + 'description': """ +Hibou Professional Support and Billing +====================================== + + """, + 'website': 'https://hibou.io/', + 'data': [ + 'views/webclient_templates.xml', + ], + 'qweb': [ + 'static/src/xml/templates.xml', + ], + 'installable': True, + 'auto_install': True, + 'license': 'OPL-1', +} diff --git a/hibou_professional/models/__init__.py b/hibou_professional/models/__init__.py new file mode 100644 index 00000000..1ae9ad83 --- /dev/null +++ b/hibou_professional/models/__init__.py @@ -0,0 +1,3 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from . import update diff --git a/hibou_professional/models/update.py b/hibou_professional/models/update.py new file mode 100644 index 00000000..a52e2717 --- /dev/null +++ b/hibou_professional/models/update.py @@ -0,0 +1,210 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +import datetime +import requests + +from odoo import api, fields, models, release +from odoo.exceptions import UserError + + +class PublisherWarrantyContract(models.AbstractModel): + _inherit = 'publisher_warranty.contract' + # CONFIG_HIBOU_URL = 'https://hibou.io/hibouapi/v1/professional' + CONFIG_HIBOU_URL = 'https://odoo-hibou-test12.hibou-test-odoo.us-w-p1.hibou.me/hibouapi/v1/professional' + # CONFIG_HIBOU_MESSAGE_URL = 'https://hibou.io/hibouapi/v1/professional/message' + CONFIG_HIBOU_MESSAGE_URL = 'https://odoo-hibou-test12.hibou-test-odoo.us-w-p1.hibou.me/hibouapi/v1/professional/message' + # CONFIG_HIBOU_QUOTE_URL = 'https://hibou.io/hibouapi/v1/professional/quote' + CONFIG_HIBOU_QUOTE_URL = 'https://odoo-hibou-test12.hibou-test-odoo.us-w-p1.hibou.me/hibouapi/v1/professional/quote' + DAYS_ENDING_SOON = 7 + + @api.model + def hibou_professional_status(self): + get_param = self.env['ir.config_parameter'].sudo().get_param + expiration_date = get_param('database.hibou_professional_expiration_date') + expiration_reason = get_param('database.hibou_professional_expiration_reason') + dbuuid = get_param('database.uuid') + expiring = False + expired = False + if expiration_date: + expiration_date_date = fields.Date.from_string(expiration_date) + today = fields.Date.from_string(fields.Date.today()) + if expiration_date_date < today: + if expiration_reason == 'trial': + expired = 'Your trial of Hibou Professional has ended.' + else: + expired = 'Your Hibou Professional subscription has ended.' + elif expiration_date_date < (today + datetime.timedelta(days=self.DAYS_ENDING_SOON)): + if expiration_reason == 'trial': + expiring = 'Your trial of Hibou Professional is ending soon.' + else: + expiring = 'Your Hibou Professional subscription is ending soon.' + + is_admin = self.env.user.has_group('base.group_erp_manager') + allow_admin_message = get_param('database.hibou_allow_admin_message') + allow_message = get_param('database.hibou_allow_message') + + return { + 'expiration_date': get_param('database.hibou_professional_expiration_date'), + 'expiration_reason': get_param('database.hibou_professional_expiration_reason'), + 'expiring': expiring, + 'expired': expired, + 'professional_code': get_param('database.hibou_professional_code'), + 'dbuuid': dbuuid, + 'is_admin': is_admin, + 'allow_admin_message': allow_admin_message, + 'allow_message': allow_message, + } + + @api.model + def hibou_professional_update_message_preferences(self, allow_admin_message, allow_message): + if self.env.user.has_group('base.group_erp_manager'): + set_param = self.env['ir.config_parameter'].sudo().set_param + set_param('database.hibou_allow_admin_message', allow_admin_message and '1') + set_param('database.hibou_allow_message', allow_message and '1') + return self.hibou_professional_status() + + def _check_message_allow(self): + get_param = self.env['ir.config_parameter'].sudo().get_param + allow_message = get_param('database.hibou_allow_message') + if not allow_message: + allow_message = get_param('database.hibou_allow_admin_message') and self.env.user.has_group( + 'base.group_erp_manager') + if not allow_message: + raise UserError('You are not allowed to send messages at this time.') + + @api.model + def hibou_professional_quote(self): + get_param = self.env['ir.config_parameter'].sudo().get_param + try: + self._hibou_install() + except: + pass + dbuuid = get_param('database.uuid') + dbtoken = get_param('database.hibou_token') + if dbuuid and dbtoken: + return {'url': self.CONFIG_HIBOU_QUOTE_URL + '/%s/%s' % (dbuuid, dbtoken)} + return {} + + @api.model + def hibou_professional_send_message(self, type, priority, subject, body, user_url, res_id): + self._check_message_allow() + get_param = self.env['ir.config_parameter'].sudo().get_param + dbuuid = get_param('database.uuid') + dbtoken = get_param('database.hibou_token') + user_name = self.env.user.name + user_email = self.env.user.email or self.env.user.login + company_name = self.env.user.company_id.name + data = { + 'jsonrpc': '2.0', + 'method': 'call', + 'params': { + 'dbuuid': dbuuid, + 'user_name': user_name, + 'user_email': user_email, + 'user_url': user_url, + 'company_name': company_name, + 'type': type, + 'priority': priority, + 'subject': subject, + 'body': body, + 'res_id': res_id, + }, + } + if dbtoken: + data['params']['dbtoken'] = dbtoken + try: + r = requests.post(self.CONFIG_HIBOU_MESSAGE_URL + '/new', json=data, timeout=30) + r.raise_for_status() + wrapper = r.json() + return wrapper.get('result', {}) + except: + return {'error': 'Error sending message.'} + + @api.model + def hibou_professional_get_messages(self): + self._check_message_allow() + get_param = self.env['ir.config_parameter'].sudo().get_param + dbuuid = get_param('database.uuid') + dbtoken = get_param('database.hibou_token') + try: + r = requests.get(self.CONFIG_HIBOU_MESSAGE_URL + '/get/%s/%s' % (dbuuid, dbtoken), timeout=30) + r.raise_for_status() + # not jsonrpc + return r.json() + except: + return {'error': 'Error retrieving messages, maybe the token is wrong.'} + + @api.model + def hibou_professional_update(self, professional_code): + set_param = self.env['ir.config_parameter'].sudo().set_param + set_param('database.hibou_professional_code', professional_code) + try: + self._hibou_install() + except: + pass + return self.hibou_professional_status() + + def _get_hibou_modules(self): + domain = [('state', 'in', ['installed', 'to upgrade', 'to remove']), ('author', 'ilike', 'hibou')] + module_list = self.env['ir.module.module'].sudo().search_read(domain, ['name']) + return {module['name']: 1 for module in module_list} + + def _get_hibou_message(self): + IrParamSudo = self.env['ir.config_parameter'].sudo() + + dbuuid = IrParamSudo.get_param('database.uuid') + dbtoken = IrParamSudo.get_param('database.hibou_token') + db_create_date = IrParamSudo.get_param('database.create_date') + user = self.env.user.sudo() + professional_code = IrParamSudo.get_param('database.hibou_professional_code') + + module_dictionary = self._get_hibou_modules() + modules = [] + for module, qty in module_dictionary.items(): + modules.append(module if qty == 1 else '%s,%s' % (module, qty)) + + web_base_url = IrParamSudo.get_param('web.base.url') + msg = { + "dbuuid": dbuuid, + "dbname": self._cr.dbname, + "db_create_date": db_create_date, + "version": release.version, + "language": user.lang, + "web_base_url": web_base_url, + "modules": '\n'.join(modules), + "professional_code": professional_code, + } + if dbtoken: + msg['dbtoken'] = dbtoken + msg.update({'company_' + key: value for key, value in user.company_id.read(["name", "email", "phone"])[0].items() if key != 'id'}) + return msg + + def _process_hibou_message(self, result): + if result.get('professional_info'): + set_param = self.env['ir.config_parameter'].sudo().set_param + set_param('database.hibou_professional_expiration_date', result['professional_info'].get('expiration_date')) + set_param('database.hibou_professional_expiration_reason', result['professional_info'].get('expiration_reason', 'trial')) + if result['professional_info'].get('professional_code'): + set_param('database.hibou_professional_code', result['professional_info'].get('professional_code')) + if result['professional_info'].get('dbtoken'): + set_param('database.hibou_token', result['professional_info'].get('dbtoken')) + + def _hibou_install(self): + data = self._get_hibou_message() + data = { + 'jsonrpc': '2.0', + 'method': 'call', + 'params': data, + } + r = requests.post(self.CONFIG_HIBOU_URL, json=data, timeout=30) + r.raise_for_status() + wrapper = r.json() + self._process_hibou_message(wrapper.get('result', {})) + + @api.model + def _get_sys_logs(self): + try: + self._hibou_install() + except: + pass + return super(PublisherWarrantyContract, self)._get_sys_logs() diff --git a/hibou_professional/static/src/css/web.css b/hibou_professional/static/src/css/web.css new file mode 100644 index 00000000..05211e2a --- /dev/null +++ b/hibou_professional/static/src/css/web.css @@ -0,0 +1,30 @@ +.hibou_professional_systray { + color: #666; +} +.hibou_professional_systray .o_mail_systray_dropdown { + max-height: 580px !important; +} +.hibou_professional_systray .o_mail_systray_dropdown_items { + max-height: 578px !important; +} +.hibou_professional_systray .subscription_form button { + margin-top: 6px; +} + +.hibou_professional_systray .hibou-icon-small { + margin-top: -2px; +} + +.hibou_professional_systray .hibou_professional_help { + color: #0a6fa2; +} + +.hibou_professional_systray .o_preview_title span { + font-weight: bold; +} + +.hibou_professional_systray .hibou_message_form_container, +.hibou_professional_systray .subscription_form, +.hibou_professional_systray .message_preferences_form { + padding: 3px 20px; +} \ No newline at end of file diff --git a/hibou_professional/static/src/img/hibou_icon_small.png b/hibou_professional/static/src/img/hibou_icon_small.png new file mode 100644 index 00000000..2ea52f7e Binary files /dev/null and b/hibou_professional/static/src/img/hibou_icon_small.png differ diff --git a/hibou_professional/static/src/js/core.js b/hibou_professional/static/src/js/core.js new file mode 100644 index 00000000..2545025a --- /dev/null +++ b/hibou_professional/static/src/js/core.js @@ -0,0 +1,275 @@ +odoo.define('hibou_professional.core', function (require) { +"use strict"; + +var Widget = require('web.Widget'); +var SystrayMenu = require('web.SystrayMenu'); + +var HibouProfessionalSystrayWidget = Widget.extend({ + template: 'HibouProfessionalSystrayWidget', + + start: function() { + var self = this; + self.expiration_date = false; + self.expiration_reason = false; + self.professional_code = false; + this.types = [['lead', 'Sales'], ['ticket', 'Support']]; + this.message_subjects = {'lead': [], 'ticket': [], 'task': []}; + self.expiring = false; + self.expired = false; + self.dbuuid = false; + self.quote_url = false; + self.is_admin = false; + self.allow_admin_message = false; + self.allow_message = false; + this._rpc({ + model: 'publisher_warranty.contract', + method: 'hibou_professional_status', + }).then(function (result) { + self.handleStatusUpdate(result); + }); + return this._super(); + }, + + get_subjects: function(type) { + if (this.message_subjects && this.message_subjects[type]) { + return this.message_subjects[type] + } + return []; + }, + + set_error: function(error) { + this.$('.hibou_professional_error').text(error); + }, + + update_message_type: function(el) { + var selected_type = this.$('select.hibou_message_type').val(); + if (selected_type && this.$('.hibou_subject_selection_option.' + selected_type).length > 0) { + this.$('#hibou_subject_selection').show(); + this.$('.hibou_subject_selection_option').hide().attr('disabled', true); + this.$('.hibou_subject_selection_option.' + selected_type).show().attr('disabled', false); + var selected_subject = this.$('.hibou_subject_selection_option.' + selected_type)[0]; + this.$('select.hibou_subject_selection').val(selected_subject.value); + } else if (selected_type) { + this.$('select.hibou_subject_selection').val('0'); + this.$('#hibou_subject_selection').hide(); + } else { + this.$('#hibou_subject_selection').hide(); + this.$('#hibou_message_priority').hide(); + this.$('#hibou_message_subject').hide(); + } + this.update_subject_selection(); + }, + + update_subject_selection: function(el) { + var selected_subject = this.$('select.hibou_subject_selection').val(); + if (selected_subject == '0') { + this.$('#hibou_message_priority').show(); + this.$('#hibou_message_subject').show(); + } else { + this.$('#hibou_message_priority').hide(); + this.$('#hibou_message_subject').hide(); + } + }, + + update_message_subjects: function(subjects_by_type) { + // TODO actually update instead of overriding... + this.message_subjects = subjects_by_type; + this.renderElement(); + }, + + button_update_subscription: function() { + var self = this; + var professional_code = self.$('input.hibou_professional_code').val(); + if (!professional_code) { + alert('Please enter a subscription code first.'); + return; + } + self.$('.update_subscription').prop('disabled', 'disabled'); + self._rpc({ + model: 'publisher_warranty.contract', + method: 'hibou_professional_update', + args: [professional_code], + }).then(function (result) { + self.$('.update_subscription').prop('disabled', false); + self.handleStatusUpdate(result); + }); + }, + + button_update_message_preferences: function() { + var self = this; + var allow_admin_message = self.$('input.hibou_allow_admin_message').prop('checked'); + var allow_message = self.$('input.hibou_allow_message').prop('checked'); + self.$('.update_message_preferences').prop('disabled', 'disabled'); + self._rpc({ + model: 'publisher_warranty.contract', + method: 'hibou_professional_update_message_preferences', + args: [allow_admin_message, allow_message], + }).then(function (result) { + self.$('.update_message_preferences').prop('disabled', false); + self.handleStatusUpdate(result); + }); + }, + + button_quote: function() { + var self = this; + var message_p = self.$('.button-quote-link p'); + message_p.text('Retrieving URL...'); + self._rpc({ + model: 'publisher_warranty.contract', + method: 'hibou_professional_quote', + }).then(function (result) { + if (result && result['url']) { + self.quote_url = result.url + self.$('.button-quote-link').attr('href', self.quote_url); + message_p.text('Quote URL ready. Click again!'); + } else { + message_p.text('Error with quote url. Maybe the database token is incorrect.'); + } + }); + }, + + button_send_message: function() { + var self = this; + var message_type = self.$('select.hibou_message_type').val(); + var message_priority = self.$('select.hibou_message_priority').val(); + var message_subject = self.$('input.hibou_message_subject').val(); + var message_subject_id = self.$('select.hibou_subject_selection').val(); + var current_url = window.location.href; + if (message_subject_id == '0' && (!message_subject || message_subject.length < 3)) { + alert('Please enter a longer subject.'); + return; + } + var message_body = self.$('textarea.hibou_message_body').val(); + self.$('.hibou_send_message').prop('disabled', 'disabled'); + self._rpc({ + model: 'publisher_warranty.contract', + method: 'hibou_professional_send_message', + args: [message_type, message_priority, message_subject, message_body, current_url, message_subject_id], + }).then(function (result) { + // TODO result will have a subject to add to the subjects and re-render. + self.$('.hibou_send_message').prop('disabled', false); + var message_response = self.$('.hibou_message_response'); + var access_link = self.$('.hibou_message_response a'); + var message_form = self.$('.hibou_message_form'); + if (!result) { + access_link.text('An error has occured.') + } else { + if (result.error) { + access_link.text(result.error); + } else { + access_link.text(result.message || 'Your message has been received.') + } + if (result.access_url) { + access_link.attr('href', result.access_url); + } + } + message_response.show(); + message_form.hide(); + }); + }, + + button_get_messages: function() { + var self = this; + var $button = this.$('.hibou_get_messages'); + $button.prop('disabled', 'disabled'); + self._rpc({ + model: 'publisher_warranty.contract', + method: 'hibou_professional_get_messages', + args: [], + }).then(function (result) { + $button.prop('disabled', false); + if (result['message_subjects']) { + self.update_message_subjects(result.message_subjects); + setTimeout(function () { + self.$('.dropdown-toggle').click(); + }, 100); + } else if (result['error']) { + self.set_error(result['error']); + } + }); + }, + + renderElement: function() { + var self = this; + this._super(); + + this.update_message_type(); + this.update_subject_selection(); + + this.$('select.hibou_message_type').on('change', function(el) { + self.update_message_type(el); + }); + this.$('select.hibou_subject_selection').on('change', function(el) { + self.update_subject_selection(el); + }); + + // Update Subscription Button + this.$('.update_subscription').on('click', function(e){ + e.preventDefault(); + e.stopPropagation(); + self.button_update_subscription(); + }); + + this.$('.hibou_get_messages').on('click', function(e){ + e.preventDefault(); + e.stopPropagation(); + self.button_get_messages(); + }); + + // Retrieve quote URL + this.$('.button-quote-link').on('click', function(e){ + if (self.quote_url) { + return; // allow default url click event + } + e.preventDefault(); + e.stopPropagation(); + self.button_quote(); + }); + + // Update Message Preferences Button + this.$('.update_message_preferences').on('click', function(e){ + e.preventDefault(); + e.stopPropagation(); + self.button_update_message_preferences(); + }); + + // Send Message Button + this.$('.hibou_send_message').on('click', function(e){ + e.preventDefault(); + e.stopPropagation(); + self.button_send_message(); + }); + + // Kill the default click event + this.$('.hibou_message_form_container').on('click', function (e) { + //e.preventDefault(); + e.stopPropagation(); + }) + }, + + handleStatusUpdate: function(status) { + this.expiration_date = status.expiration_date; + this.expiration_reason = status.expiration_reason; + this.professional_code = status.professional_code; + this.types = [['lead', 'Sales'], ['ticket', 'Support']]; + if (this.professional_code) { + this.types.push(['task', 'Project Manager/Developer']) + } + this.expiring = status.expiring; + this.expired = status.expired; + this.dbuuid = status.dbuuid; + this.is_admin = status.is_admin; + this.allow_admin_message = status.allow_admin_message; + this.allow_message = status.allow_message; + this.renderElement(); + }, + +}); + +SystrayMenu.Items.push(HibouProfessionalSystrayWidget); + +return { + HibouProfessionalSystrayWidget: HibouProfessionalSystrayWidget, +}; + +}); \ No newline at end of file diff --git a/hibou_professional/static/src/xml/templates.xml b/hibou_professional/static/src/xml/templates.xml new file mode 100644 index 00000000..41b45328 --- /dev/null +++ b/hibou_professional/static/src/xml/templates.xml @@ -0,0 +1,142 @@ + + + +
  • + +