[ADD] planner, planner_crm : add the planner engine, and the planner for CRM module. A planner is a tool helping people to set up quickly. For CRM, it helps to define sales strategy, objectives, leads, KPIs and much more !
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Odoo, Open Source Management Solution
|
||||
# Copyright (C) 2014-Today Odoo SA (<http://www.odoo.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import models
|
||||
@@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
{
|
||||
'name': 'Planner',
|
||||
'category': 'Planner',
|
||||
'summary': 'Help to configure application',
|
||||
'version': '1.0',
|
||||
'description': """Application Planner""",
|
||||
'author': 'Odoo SA',
|
||||
'depends': ['web'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/planner.xml',
|
||||
],
|
||||
'qweb': ['static/src/xml/planner.xml'],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import planner
|
||||
@@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from openerp import api, models, fields
|
||||
|
||||
|
||||
class Planner(models.Model):
|
||||
"""Planner Model.
|
||||
Each Planner has link to an ir.ui.view record that is a template used
|
||||
to display the planner pages.
|
||||
Each Planner has link to ir.ui.menu record that is a top menu used to display the
|
||||
planner launcher(progressbar)
|
||||
|
||||
Method _prepare_<planner_application>_data(self, cr, uid, context) that
|
||||
generate the values used to display in specific planner pages
|
||||
"""
|
||||
|
||||
_name = 'planner.planner'
|
||||
_description = 'Planner'
|
||||
|
||||
@api.model
|
||||
def _get_planner_application(self):
|
||||
return []
|
||||
|
||||
name = fields.Char(string='Name', required=True)
|
||||
menu_id = fields.Many2one('ir.ui.menu', string='Menu', required=True)
|
||||
view_id = fields.Many2one('ir.ui.view', string='Template', required=True)
|
||||
progress = fields.Integer(string="Progress Percentage")
|
||||
# used to store the data filled by user in planner(JSON Data)
|
||||
data = fields.Text(string='Data')
|
||||
tooltip_planner = fields.Html(string='Planner Tooltips', translate=True)
|
||||
planner_application = fields.Selection('_get_planner_application', string='Planner Application', required=True)
|
||||
|
||||
@api.model
|
||||
def render(self, template_id, planner_app):
|
||||
# prepare the planner data as per the planner application
|
||||
values = {
|
||||
'prepare_backend_url': self.prepare_backend_url,
|
||||
}
|
||||
planner_find_method_name = '_prepare_%s_data' % planner_app
|
||||
if hasattr(self, planner_find_method_name):
|
||||
values.update(getattr(self, planner_find_method_name)()) # update the default value
|
||||
return self.env['ir.ui.view'].browse(template_id).render(values=values)
|
||||
|
||||
@api.model
|
||||
def prepare_backend_url(self, action_xml_id, view_type='list', module_name=None):
|
||||
""" prepare the backend url to the given action, or to the given module view. """
|
||||
action = self.env.ref(action_xml_id, False)
|
||||
url = "/web#view_type=%s&action=%s" % (view_type, action and action.id or '')
|
||||
if module_name:
|
||||
module = self.env['ir.module.module'].search([('name', '=', module_name)], limit=1)
|
||||
if module:
|
||||
url = "&id=%s" % (module.id,)
|
||||
return url
|
||||
@@ -0,0 +1,3 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_planner_emp,planner.planner.emp,model_planner_planner,base.group_user,1,1,0,0
|
||||
access_planner_manager,planner.planner.manager,model_planner_planner,base.group_erp_manager,1,1,1,1
|
||||
|
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 212 B |
@@ -0,0 +1,306 @@
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
||||
var instance = openerp;
|
||||
var QWeb = instance.web.qweb;
|
||||
instance.planner = {};
|
||||
|
||||
instance.web.WebClient.include({
|
||||
start: function() {
|
||||
this.planner_manager = new instance.planner.PlannerManager(this);
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
show_application: function() {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
self.menu.on("top_menu_change", self.planner_manager, self.planner_manager.on_menu_clicked);
|
||||
}
|
||||
});
|
||||
|
||||
instance.web.Menu.include({
|
||||
open_menu: function (id) {
|
||||
this._super.apply(this, arguments);
|
||||
// find the active menu with class '.active', instead of with click event to display planner in case of refresh
|
||||
var active_top_menu = this.$el.find('.active a').data('menu');
|
||||
if((active_top_menu != this.active_top_menu)){
|
||||
this.active_top_menu = active_top_menu;
|
||||
this.trigger('top_menu_change', active_top_menu);
|
||||
this.reflow(); // re-arrange the top menu (hide overflow in 'more' menu)
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
instance.planner.PlannerManager = instance.web.Widget.extend({
|
||||
init: function(parent) {
|
||||
var self = this;
|
||||
this._super(parent);
|
||||
this.planner_by_menu = {};
|
||||
this.fetch_application_planner().done(function(apps) {
|
||||
self.planner_apps = apps;
|
||||
});
|
||||
},
|
||||
on_menu_clicked: function(menu_id) {
|
||||
var self = this;
|
||||
self.planner_launcher && self.planner_launcher.destroy();
|
||||
if (_.contains(_.keys(self.planner_apps), menu_id.toString())) {
|
||||
self.planner_launcher = new instance.planner.PlannerLauncher(self, self.planner_apps[menu_id]);
|
||||
self.planner_launcher.prependTo(window.$('.oe_systray'));
|
||||
}
|
||||
},
|
||||
fetch_application_planner: function() {
|
||||
var self = this;
|
||||
var def = $.Deferred();
|
||||
if (!_.isEmpty(this.planner_by_menu)) {
|
||||
def.resolve(self.planner_by_menu);
|
||||
}else{
|
||||
(new instance.web.Model('planner.planner')).query().all().then(function(res) {
|
||||
_.each(res, function(planner){
|
||||
self.planner_by_menu[planner.menu_id[0]] = planner;
|
||||
self.planner_by_menu[planner.menu_id[0]].data = jQuery.parseJSON(self.planner_by_menu[planner.menu_id[0]].data) || {};
|
||||
});
|
||||
def.resolve(self.planner_by_menu);
|
||||
}).fail(function() {def.reject();});
|
||||
}
|
||||
return def;
|
||||
}
|
||||
});
|
||||
|
||||
instance.planner.PlannerLauncher = instance.web.Widget.extend({
|
||||
template: "PlannerLauncher",
|
||||
events: {
|
||||
'click .oe_planner_progress': 'toggle_dialog'
|
||||
},
|
||||
init: function(parent, planner) {
|
||||
this._super(parent);
|
||||
this.planner = planner;
|
||||
this.dialog = new instance.planner.PlannerDialog(this, planner);
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
this.$(".oe_planner_progress").tooltip({html: true, title: this.planner.tooltip_planner, placement: 'bottom', delay: {'show': 500}});
|
||||
this.dialog.on("planner_progress_changed", this, function(percent){
|
||||
self.update_parent_progress_bar(percent);
|
||||
});
|
||||
this.dialog.appendTo(document.body);
|
||||
},
|
||||
update_parent_progress_bar: function(percent) {
|
||||
this.$(".progress-bar").css('width', percent+"%");
|
||||
},
|
||||
toggle_dialog: function() {
|
||||
this.dialog.$('#PlannerModal').modal('toggle');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
Widget implementing the Modal of the planner (with all form, menu items, mark buttons, pages, ...). The content (the view_id) is fetched by
|
||||
calling the 'render' method (server side), then footer are appended to every pages (client side). Data are saved when a 'mark button' is clicked.
|
||||
Some element of the template MUST respect naming convention :
|
||||
* Input elements (select, textarea, radio, ...) MUST HAVE an 'id' as 'input_element_#####', where ##### is a string.
|
||||
* Page div MUST HAVE the class "panel-collapse collapse" and an 'id' as 'planner_page#', where # is ideally a
|
||||
number. !!! The order of the page are important. The 'id' number is NOT the sequence order.
|
||||
* Menu item MUST HAVE an href attribute containing the id of the page they are referencing.
|
||||
*/
|
||||
instance.planner.PlannerDialog = instance.web.Widget.extend({
|
||||
template: "PlannerDialog",
|
||||
events: {
|
||||
'click .oe_planner div[id^="planner_page"] a[href^="#planner_page"]': 'next_page',
|
||||
'click .oe_planner li a[href^="#planner_page"]': 'onclick_menu',
|
||||
'click .oe_planner div[id^="planner_page"] button[data-pageid^="planner_page"]': 'mark_as_done',
|
||||
},
|
||||
init: function(parent, planner) {
|
||||
this._super(parent);
|
||||
this.planner_launcher = parent;
|
||||
this.planner = planner;
|
||||
this.cookie_name = this.planner['planner_application'] + '_last_page';
|
||||
this.set('progress', 0);
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
this._setup_view().then(function(){
|
||||
self.prepare_planner_event();
|
||||
});
|
||||
},
|
||||
// This method should be overridden in other planners to bind their custom events, once the view is loaded.
|
||||
prepare_planner_event: function() {
|
||||
var self = this;
|
||||
self.on('change:progress', self, function(){
|
||||
self.trigger('planner_progress_changed', self.get('progress'));
|
||||
});
|
||||
this.on('planner_progress_changed', this, this.update_ui_progress_bar);
|
||||
this.set('progress', this.planner.progress); // init progress to trigger ui update
|
||||
|
||||
$(window).on('resize', function() {
|
||||
self.resize_dialog();
|
||||
});
|
||||
},
|
||||
// ui
|
||||
_setup_view: function(){
|
||||
var self = this;
|
||||
return (new instance.web.Model('planner.planner')).call('render', [self.planner.view_id[0], self.planner.planner_application]).then(function(res) {
|
||||
self.$('.content_page').html(res);
|
||||
// add footer to each page
|
||||
self.add_pages_footer();
|
||||
// update the planner_data with the new inputs of the view
|
||||
var actual_vals = self._get_values();
|
||||
self.planner.data = _.defaults(self.planner.data, actual_vals);
|
||||
// set the default value
|
||||
self._set_values(self.planner.data);
|
||||
// show last opened page
|
||||
var last_open_page = (openerp.session.get_cookie(self.cookie_name)) ? openerp.session.get_cookie(self.cookie_name) : self.planner.data['last_open_page'] || false;
|
||||
if (last_open_page) {
|
||||
$(".oe_planner li a[href='#"+last_open_page+"']").trigger('click');
|
||||
}
|
||||
// Call resize function at the beginning
|
||||
self.resize_dialog();
|
||||
self.$el.on('keyup', "textarea", function() {
|
||||
if (this.scrollHeight != this.clientHeight) {
|
||||
this.style.height = this.scrollHeight + "px";
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
update_ui_progress_bar: function(percent) {
|
||||
this.$(".progress-bar").css('width', percent+"%");
|
||||
this.$(".progress_col").find('span.counter').text(percent+"%");
|
||||
},
|
||||
add_pages_footer: function() {
|
||||
var self = this;
|
||||
//find all the pages and append footer to each pages
|
||||
_.each(self.$('.oe_planner div[id^="planner_page"]'), function(element) {
|
||||
var $el = $(element);
|
||||
var next_page_name = self.$(".oe_planner .side li a[href='#"+$el.next().attr('id')+"']").text() || ' Finished!';
|
||||
var footer_template = QWeb.render("PlannerFooter", {
|
||||
'next_page_name': next_page_name,
|
||||
'next_page_id': $el.next().attr('id'),
|
||||
'current_page_id': $el.attr('id'),
|
||||
'start': $el.prev().length ? false: true,
|
||||
'end': $el.next().length ? false: true
|
||||
});
|
||||
$el.append(footer_template);
|
||||
});
|
||||
},
|
||||
resize_dialog: function() {
|
||||
var winH = $(window).height();
|
||||
var $modal = this.$('.planner-dialog');
|
||||
$modal.height(winH/1.1);
|
||||
this.$('.pages').height($modal.height() - 60);
|
||||
this.$('.side').height($modal.height() - 75);
|
||||
},
|
||||
// page switching
|
||||
onclick_menu: function(ev) {
|
||||
ev.stopPropagation();
|
||||
var page_id = $(ev.currentTarget).attr('href').replace('#', '');
|
||||
this._switch_page(page_id);
|
||||
},
|
||||
next_page: function(ev) {
|
||||
ev.stopPropagation();
|
||||
var next_page_id = $(ev.currentTarget).attr('href').replace('#', '');
|
||||
this._switch_page(next_page_id);
|
||||
},
|
||||
_switch_page: function(page_id) {
|
||||
this.$(".oe_planner li a[href^='#planner_page']").parent().removeClass('active');
|
||||
this.$(".oe_planner li a[href=#"+page_id+"]").parent().addClass('active');
|
||||
this.$(".oe_planner div[id^='planner_page']").removeClass('in');
|
||||
this.$(".oe_planner div[id="+page_id+"]").addClass('in');
|
||||
this.planner.data['last_open_page'] = page_id;
|
||||
openerp.session.set_cookie(this.cookie_name, page_id, 8*60*60); // create cookie for 8h
|
||||
},
|
||||
// planner data functions
|
||||
_get_values: function(page_id){
|
||||
// if no page_id, take the complete planner
|
||||
var base_elem = page_id ? this.$el : this.$(".oe_planner div[id='planner_page"+page_id+"']");
|
||||
var values = {};
|
||||
// get the selector for all the input and mark_button
|
||||
// only INPUT (select, textearea, input, checkbox and radio), and BUTTON (.mark_button#) are observed
|
||||
var inputs = base_elem.find("textarea[id^='input_element'], input[id^='input_element'], select[id^='input_element'], button[id^='mark_button']");
|
||||
_.each(inputs, function(elem){
|
||||
elem = $(elem);
|
||||
var tid = elem.attr('id');
|
||||
if (elem.prop("tagName") == 'BUTTON'){
|
||||
if(elem.hasClass('fa-check-square-o')){
|
||||
values[tid] = 'marked';
|
||||
}else{
|
||||
values[tid] = '';
|
||||
}
|
||||
}
|
||||
if (elem.prop("tagName") == 'INPUT' || elem.prop("tagName") == 'TEXTAREA'){
|
||||
var ttype = elem.attr('type');
|
||||
if (ttype == 'checkbox' || ttype == 'radio'){
|
||||
values[tid] = '';
|
||||
if (elem.is(':checked')){
|
||||
values[tid] = 'checked';
|
||||
}
|
||||
}else{
|
||||
values[tid] = elem.val();
|
||||
}
|
||||
}
|
||||
});
|
||||
return values;
|
||||
},
|
||||
_set_values: function(values){
|
||||
var self = this;
|
||||
_.each(values, function(val, id){
|
||||
var elem = self.$('#'+id);
|
||||
if (elem.prop("tagName") == 'BUTTON'){
|
||||
if(val == 'marked'){
|
||||
elem.addClass('fa-check-square-o btn-default').removeClass('fa-square-o btn-primary');
|
||||
self.$(".oe_planner li a[href=#"+elem.data('pageid')+"] span").addClass('fa-check');
|
||||
}
|
||||
}
|
||||
if (elem.prop("tagName") == 'INPUT' || elem.prop("tagName") == 'TEXTAREA'){
|
||||
var ttype = elem.attr("type");
|
||||
if (ttype == 'checkbox' || ttype == 'radio'){
|
||||
if (val == 'checked') {
|
||||
elem.attr('checked', 'checked');
|
||||
}
|
||||
}else{
|
||||
elem.val(val);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
update_planner: function(page_id){
|
||||
// update the planner.data with the inputs
|
||||
var vals = this._get_values(page_id);
|
||||
this.planner.data = _.extend(this.planner.data, vals);
|
||||
// re compute the progress percentage
|
||||
var mark_btn = this.$(".oe_planner button[id^='mark_button']");
|
||||
var marked_btn = this.$(".oe_planner button[id^='mark_button'].fa-check-square-o");
|
||||
var percent = parseInt((marked_btn.length / mark_btn.length * 100));
|
||||
this.set('progress', percent);
|
||||
this.planner.progress = percent;
|
||||
// save data and progress in database
|
||||
this._save_planner_data();
|
||||
},
|
||||
_save_planner_data: function() {
|
||||
return (new instance.web.Model('planner.planner')).call('write', [this.planner.id, {'data': JSON.stringify(this.planner.data), 'progress': this.planner.progress}]);
|
||||
},
|
||||
// user actions
|
||||
mark_as_done: function(ev) {
|
||||
var self = this;
|
||||
var btn = $(ev.currentTarget);
|
||||
var page_id = btn.attr('data-pageid');
|
||||
var active_menu = self.$(".oe_planner li a[href=#"+page_id+"] span");
|
||||
var active_page = self.$(".oe_planner div[id^='planner_page'].panel-collapse.collapse.in");
|
||||
|
||||
var next_button = self.$(".oe_planner a[data-parent="+page_id+"]");
|
||||
if (!btn.hasClass('fa-check-square-o')) {
|
||||
active_menu.addClass('fa-check');
|
||||
btn.addClass('fa-check-square-o btn-default').removeClass('fa-square-o btn-primary');
|
||||
next_button.addClass('btn-primary').removeClass('btn-default');
|
||||
// page checked animation
|
||||
active_page.addClass('marked');
|
||||
setTimeout(function() { active_page.removeClass('marked'); }, 1000);
|
||||
} else {
|
||||
btn.removeClass('fa-check-square-o btn-default').addClass('fa-square-o btn-primary');
|
||||
next_button.addClass('btn-default').removeClass('btn-primary');
|
||||
active_menu.removeClass('fa-check');
|
||||
}
|
||||
self.update_planner(page_id);
|
||||
},
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,590 @@
|
||||
// FONTS
|
||||
|
||||
@font-face {
|
||||
font-family: 'LatoLatin';
|
||||
src: url('/planner/static/src/fonts/LatoLatin/lato-thin-webfont.eot');
|
||||
src: url('/planner/static/src/fonts/LatoLatin/lato-thin-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-thin-webfont.woff2') format('woff2'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-thin-webfont.woff') format('woff'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-thin-webfont.ttf') format('truetype'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-thin-webfont.svg#latolight') format('svg');
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'LatoLatin';
|
||||
src: url('/planner/static/src/fonts/LatoLatin/lato-light-webfont.eot');
|
||||
src: url('/planner/static/src/fonts/LatoLatin/lato-light-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-light-webfont.woff2') format('woff2'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-light-webfont.woff') format('woff'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-light-webfont.ttf') format('truetype'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-light-webfont.svg#latolight') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'LatoLatin';
|
||||
src: url('/planner/static/src/fonts/LatoLatin/lato-semibold-webfont.eot');
|
||||
src: url('/planner/static/src/fonts/LatoLatin/lato-semibold-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-semibold-webfont.woff2') format('woff2'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-semibold-webfont.woff') format('woff'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-semibold-webfont.ttf') format('truetype'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-semibold-webfont.svg#latosemibold') format('svg');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'LatoLatin';
|
||||
src: url('/planner/static/src/fonts/LatoLatin/lato-regular-webfont.eot');
|
||||
src: url('/planner/static/src/fonts/LatoLatin/lato-regular-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-regular-webfont.woff2') format('woff2'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-regular-webfont.woff') format('woff'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-regular-webfont.ttf') format('truetype'),
|
||||
url('/planner/static/src/fonts/LatoLatin/lato-regular-webfont.svg#latoregular') format('svg');
|
||||
font-weight: medium;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Simple-Line-Icons';
|
||||
src:url('/planner/static/src/fonts/Simple-Line-Icons.eot');
|
||||
src:url('/planner/static/src/fonts/Simple-Line-Icons.eot?#iefix') format('embedded-opentype'),
|
||||
url('/planner/static/src/fonts/Simple-Line-Icons.woff') format('woff'),
|
||||
url('/planner/static/src/fonts/Simple-Line-Icons.ttf') format('truetype'),
|
||||
url('/planner/static/src/fonts/Simple-Line-Icons.svg#Simple-Line-Icons') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
// use data attributes for inserting icons
|
||||
// http://www.issme.net/india/assets/plugins/line-icons/
|
||||
|
||||
[data-icon]:before {
|
||||
font-family: 'Simple-Line-Icons';
|
||||
content: attr(data-icon);
|
||||
speak: none;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
//Animations
|
||||
@keyframes markAnim {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scaleX(0.50) scaleY(0.50);
|
||||
}
|
||||
30% {
|
||||
opacity: 1;
|
||||
transform: scaleX(1.00) scaleY(1.00);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: scaleX(1.00) scaleY(1.00);
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes markAnim {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-moz-transform: scaleX(0.50) scaleY(0.50);
|
||||
}
|
||||
30% {
|
||||
opacity: 1;
|
||||
-moz-transform: scaleX(1.00) scaleY(1.00);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
-moz-transform: scaleX(1.00) scaleY(1.00);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes markAnim {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scaleX(0.50) scaleY(0.50);
|
||||
}
|
||||
30% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scaleX(1.00) scaleY(1.00);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scaleX(1.00) scaleY(1.00);
|
||||
}
|
||||
}
|
||||
|
||||
@-o-keyframes markAnim {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-o-transform: scaleX(0.50) scaleY(0.50);
|
||||
}
|
||||
30% {
|
||||
opacity: 1;
|
||||
-o-transform: scaleX(1.00) scaleY(1.00);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
-o-transform: scaleX(1.00) scaleY(1.00);
|
||||
}
|
||||
}
|
||||
|
||||
@-ms-keyframes markAnim {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-ms-transform: scaleX(0.50) scaleY(0.50);
|
||||
}
|
||||
30% {
|
||||
opacity: 1;
|
||||
-ms-transform: scaleX(1.00) scaleY(1.00);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
-ms-transform: scaleX(1.00) scaleY(1.00);
|
||||
}
|
||||
}
|
||||
|
||||
//bounce effect
|
||||
@-webkit-keyframes bounceIn {
|
||||
0%, 20%, 40%, 60%, 80%, 100% {
|
||||
-webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
}
|
||||
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale3d(.3, .3, .3);
|
||||
transform: scale3d(.3, .3, .3);
|
||||
}
|
||||
20% {
|
||||
-webkit-transform: scale3d(1.1, 1.1, 1.1);
|
||||
transform: scale3d(1.1, 1.1, 1.1);
|
||||
}
|
||||
40% {
|
||||
-webkit-transform: scale3d(.9, .9, .9);
|
||||
transform: scale3d(.9, .9, .9);
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale3d(1.03, 1.03, 1.03);
|
||||
transform: scale3d(1.03, 1.03, 1.03);
|
||||
}
|
||||
80% {
|
||||
-webkit-transform: scale3d(.97, .97, .97);
|
||||
transform: scale3d(.97, .97, .97);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale3d(1, 1, 1);
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounceIn {
|
||||
0%, 20%, 40%, 60%, 80%, 100% {
|
||||
-webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
}
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale3d(.3, .3, .3);
|
||||
transform: scale3d(.3, .3, .3);
|
||||
}
|
||||
20% {
|
||||
-webkit-transform: scale3d(1.1, 1.1, 1.1);
|
||||
transform: scale3d(1.1, 1.1, 1.1);
|
||||
}
|
||||
40% {
|
||||
-webkit-transform: scale3d(.9, .9, .9);
|
||||
transform: scale3d(.9, .9, .9);
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale3d(1.03, 1.03, 1.03);
|
||||
transform: scale3d(1.03, 1.03, 1.03);
|
||||
}
|
||||
80% {
|
||||
-webkit-transform: scale3d(.97, .97, .97);
|
||||
transform: scale3d(.97, .97, .97);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale3d(1, 1, 1);
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#oe_main_menu_navbar.navbar {
|
||||
.oe_planner_systray {
|
||||
width: 100px;
|
||||
height: 13px;
|
||||
margin: 10px 20px 0 0;
|
||||
div.oe_planner_progress {
|
||||
cursor: pointer;
|
||||
}
|
||||
.oe_planner_progress {
|
||||
div.progress {
|
||||
height: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#PlannerModal {
|
||||
.planner-dialog {
|
||||
width: 85%;
|
||||
font-family: "LatoLatin", serif;
|
||||
max-width: 1024px;
|
||||
overflow: hidden;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
//button close
|
||||
button.close {
|
||||
width: 13px;
|
||||
height: 30px;
|
||||
opacity: 1;
|
||||
line-height: 30px;
|
||||
font-size: 16px;
|
||||
color: rgba(51, 51, 51, 0.54);
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
z-index: 9999;
|
||||
}
|
||||
button.close:hover {color:#222;}
|
||||
.modal-content {
|
||||
border-radius: 2px;
|
||||
background: #f3f3f3;
|
||||
}
|
||||
.modal-body {
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
max-height: inherit;
|
||||
}
|
||||
.row.oe_planner {padding: 0 15px;}
|
||||
.mb1{
|
||||
margin-bottom: 1em!important;
|
||||
}
|
||||
.mb2{
|
||||
margin-bottom: 2em!important;
|
||||
}
|
||||
.mb3{
|
||||
margin-bottom: 3em!important;
|
||||
}
|
||||
.mb5{
|
||||
margin-bottom: 5em!important;
|
||||
}
|
||||
.odoo_purple, a.btn.odoo_purple {
|
||||
background-color: #A24689;
|
||||
border:none;
|
||||
text-shadow:none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.odoo_purple.btn:hover{
|
||||
background: #a24689;
|
||||
color: #fff;
|
||||
}
|
||||
.planner_footer {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
overflow-y: visible;
|
||||
overflow-x: initial;
|
||||
display: block;
|
||||
background: transparent;
|
||||
position: fixed;
|
||||
z-index: 99;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
.shadow {
|
||||
background: url("/planner/static/src/img/shadow.png") center center repeat-x;
|
||||
height: 12px;
|
||||
width: 100%;
|
||||
display: block;
|
||||
border-bottom: 1px solid #E5E5E5;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
> div {
|
||||
padding: 1em 15px;
|
||||
background: #f7f7f7;
|
||||
min-height: 60px;
|
||||
}
|
||||
button.fa-check-square-o {
|
||||
background-color: transparent;
|
||||
line-height: 0.8em;
|
||||
font-size: 2em;
|
||||
border: none;
|
||||
|
||||
&:focus {outline:none}
|
||||
&:hover:before{
|
||||
content: "\f096";
|
||||
color: #888;
|
||||
position: absolute;
|
||||
margin-left: 2px;
|
||||
}
|
||||
&:hover i:before{
|
||||
content: "\f00c";
|
||||
color: #CACACA;
|
||||
}
|
||||
span {display: none;}
|
||||
}
|
||||
button.fa-check-square-o:after {
|
||||
color: #7C7C7C;
|
||||
content: "Unckeck";
|
||||
position: absolute;
|
||||
left: 5.5em;
|
||||
font-size: .5em;
|
||||
opacity: 0;
|
||||
-webkit-transition: all 0.3s ease-in-out;
|
||||
-moz-transition: all 0.3s ease-in-out;
|
||||
-ms-transition: all 0.3s ease-in-out;
|
||||
-o-transition: all 0.3s ease-in-out;
|
||||
}
|
||||
button.fa-check-square-o:hover:after {
|
||||
left: 5em;
|
||||
opacity: 1;
|
||||
-webkit-transition: all 0.3s ease-in-out;
|
||||
-moz-transition: all 0.3s ease-in-out;
|
||||
-ms-transition: all 0.3s ease-in-out;
|
||||
-o-transition: all 0.3s ease-in-out;
|
||||
}
|
||||
.btn-next i {
|
||||
transform: translateY(1px);
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-size: 1.3em;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.span-next {
|
||||
position: absolute;
|
||||
right: 9em;
|
||||
top: 0.8em;
|
||||
opacity: 0;
|
||||
line-height: 2.2em;
|
||||
height: 1.3em;
|
||||
font-size: 1em;
|
||||
display: block;
|
||||
-webkit-transition: all 0.3s ease-in-out;
|
||||
-moz-transition: all 0.3s ease-in-out;
|
||||
-ms-transition: all 0.3s ease-in-out;
|
||||
-o-transition: all 0.3s ease-in-out;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
.next-block:hover .span-next {
|
||||
right: 9.6em;
|
||||
opacity: 1;
|
||||
}
|
||||
.mark_as_done span {
|
||||
font-family: "LatoLatin", serif;
|
||||
}
|
||||
}
|
||||
// MARKED ANIMATION
|
||||
.panel-collapse.marked:after {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
font-size: 20em;
|
||||
font-family: "FontAwesome";
|
||||
content: "\f00c";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999;
|
||||
color: #21b799;
|
||||
vertical-align: middle;
|
||||
padding: 0.3em 0.7em;
|
||||
animation: markAnim ease-in-out 1s;
|
||||
animation-iteration-count: 1;
|
||||
transform-origin: 50% 50%;
|
||||
animation-fill-mode:forwards; /*when the spec is finished*/
|
||||
-webkit-animation: markAnim ease-in-out 1s;
|
||||
-webkit-animation-iteration-count: 1;
|
||||
-webkit-transform-origin: 50% 50%;
|
||||
-webkit-animation-fill-mode:forwards; /*Chrome 16+, Safari 4+*/
|
||||
-moz-animation: markAnim ease-in-out 1s;
|
||||
-moz-animation-iteration-count: 1;
|
||||
-moz-transform-origin: 50% 50%;
|
||||
-moz-animation-fill-mode:forwards; /*FF 5+*/
|
||||
-o-animation: markAnim ease-in-out 1s;
|
||||
-o-animation-iteration-count: 1;
|
||||
-o-transform-origin: 50% 50%;
|
||||
-o-animation-fill-mode:forwards; /*Not implemented yet*/
|
||||
-ms-animation: markAnim ease-in-out 1s;
|
||||
-ms-animation-iteration-count: 1;
|
||||
-ms-transform-origin: 50% 50%;
|
||||
-ms-animation-fill-mode:forwards; /*IE 10+*/
|
||||
}
|
||||
// PROGRESS
|
||||
.progress_container {
|
||||
padding: 0;
|
||||
|
||||
.progress_col {
|
||||
border-right: 1px solid #E5E5E5;
|
||||
background: #f7f7f7;
|
||||
padding: 10px 12px 0 12px;
|
||||
|
||||
i.fa{
|
||||
font-size: 1.7em;
|
||||
padding-left: 0;
|
||||
margin-right: 0.2em;
|
||||
margin-top: 0.1em;
|
||||
color: #929292;
|
||||
}
|
||||
|
||||
.counter {
|
||||
padding: 6px 0 0 8px;
|
||||
font-weight: bold;
|
||||
color: #727272;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: #C4C4C4;
|
||||
height: 30px;
|
||||
padding: 3px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.progress-bar {
|
||||
background: #A24689;
|
||||
background: -moz-linear-gradient(top, #cc59ab 0%, #a24689 8%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#cc59ab), color-stop(8%,#a24689));
|
||||
background: -webkit-linear-gradient(top, #cc59ab 0%,#a24689 8%);
|
||||
background: -o-linear-gradient(top, #cc59ab 0%,#a24689 8%);
|
||||
background: -ms-linear-gradient(top, #cc59ab 0%,#a24689 8%);
|
||||
background: linear-gradient(to bottom, #cc59ab 0%,#a24689 8%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#cc59ab', endColorstr='#a24689',GradientType=0 );
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 1px 0 #4E2443;
|
||||
line-height: 25px;
|
||||
height: 95%;
|
||||
box-shadow: 0 1px 0 #7A3067;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dropdown.mobile {
|
||||
padding: 10px 12px 0 0px;
|
||||
.dropdown-menu{
|
||||
width: 100%;
|
||||
height: 250px;
|
||||
overflow: auto;
|
||||
}
|
||||
.btn {
|
||||
height: 30px;
|
||||
line-height: 15px;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.dropdown-menu a {
|
||||
color: #333;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus {
|
||||
background-color: #F3F3F3;
|
||||
}
|
||||
|
||||
.dropdown-menu h4 {
|
||||
padding: 10px 15px 0;
|
||||
border-top: 1px solid #EAEAEA;
|
||||
}
|
||||
.fa {
|
||||
color: #21b799;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.side {
|
||||
padding: 5px 5px 20px 10px;
|
||||
border-right: 1px solid #E5E5E5;
|
||||
background: #f7f7f7;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
.nav-stacked h4 {
|
||||
font-size: 15px;
|
||||
color: #444;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.nav-stacked h4 i {
|
||||
margin-right: 8px;
|
||||
color: #A24689;
|
||||
}
|
||||
.nav-stacked li a {
|
||||
padding: 3px 0 3px 38px;
|
||||
margin: auto -10px;
|
||||
color: #444;
|
||||
font-size: 13px;
|
||||
border-radius: 0;
|
||||
}
|
||||
.nav-stacked li a:hover, .nav-stacked li a:focus {
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.nav-stacked li.active a{
|
||||
background-color: #EAEAEA;
|
||||
}
|
||||
.fa.fa-fw.fa-check {
|
||||
color: #21b799;
|
||||
margin-left: -5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.pages {
|
||||
color: #444;
|
||||
margin-top:-60px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
> div {
|
||||
padding-top: 25px;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
> h1, > h2, > h3, > h4, > h5, > h6, > p, > strong, > i {
|
||||
text-shadow:none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
font-size: 3.5em;
|
||||
font-weight: 100;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.odoo_colors {
|
||||
a {color: #2E917D}
|
||||
a:hover, a:focus {color: #21b799; text-decoration: none;}
|
||||
textarea:focus, input:focus, select:focus, option:focus, .btn:focus, a:focus {outline-color: #26A38A;}
|
||||
.btn-default {
|
||||
background-color: transparent;
|
||||
color: #7C7C7C;
|
||||
line-height: 19px;
|
||||
}
|
||||
.btn-default:hover {
|
||||
color: #333;
|
||||
}
|
||||
.btn-primary {
|
||||
background: #21b799;
|
||||
border-color: #26A38A;
|
||||
text-shadow: 0 1px 0 #2E917D;
|
||||
font-weight: normal;
|
||||
line-height: 19px;
|
||||
color: white;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: #2E917D;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<templates id="template">
|
||||
<t t-name="PlannerLauncher">
|
||||
<li class="oe_topbar_item oe_planner_systray">
|
||||
<div class="oe_planner_progress">
|
||||
<span class="fa fa-question-circle pull-left" style="color:white;"/>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" t-attf-style="width:0%;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
<t t-name="PlannerFooter">
|
||||
<div class="planner_footer col-sm-8 col-md-9 col-xs-12 hidden-print">
|
||||
<span class="shadow" />
|
||||
|
||||
<div class="col-xs-6 text-left mark-block">
|
||||
<t t-if="!start and !end">
|
||||
<button t-attf-id='mark_button#{current_page_id}'
|
||||
t-attf-data-pageid='#{current_page_id}'
|
||||
class='fa fa-square-o btn btn-primary mark_as_done'><i /> <span>Mark As Done</span>
|
||||
</button>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-xs-6 next-block">
|
||||
<t t-if="!end">
|
||||
<a data-toggle='collapse'
|
||||
t-attf-class="pull-right btn btn-#{start and 'primary' or 'default'} btn-next"
|
||||
t-attf-data-parent='#{current_page_id}'
|
||||
t-attf-href='##{next_page_id}'>Next step<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
<span class="help-block span-next"> <t t-esc="next_page_name"/></span>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="PlannerDialog">
|
||||
<div>
|
||||
<div class="modal fade odoo_colors" id="PlannerModal" tabindex="-1" role="dialog" aria-labelledby="PlannerModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog planner-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<button type="button" class="close" data-dismiss="modal"><i class="fa fa-times"></i></button>
|
||||
<div>
|
||||
<div class="col-sm-12 col-xs-7 progress_container">
|
||||
<div class="col-sm-4 col-md-3 col-xs-12 progress_col">
|
||||
<i class="fa fa-question-circle col-xs-1"></i>
|
||||
<div class="progress col-xs-9">
|
||||
<div class="progress-bar" t-attf-style="width:0%;"></div>
|
||||
</div>
|
||||
<span class="counter col-xs-1"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content_page"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<odoo>
|
||||
<template id="assets_backend" name="planner assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/planner/static/src/less/planner.less"/>
|
||||
<script type="text/javascript" src="/planner/static/src/js/planner.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Odoo, Open Source Management Solution
|
||||
# Copyright (C) 2014-Today Odoo SA (<http://www.odoo.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import models
|
||||
@@ -0,0 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': 'CRM Planner',
|
||||
'summary': 'Help to configure application',
|
||||
'version': '1.0',
|
||||
'category': 'Planner',
|
||||
'description': """Plan your sales strategy: objectives, leads, KPIs, and much more!""",
|
||||
'author': 'Odoo SA',
|
||||
'depends': ['planner', 'crm'],
|
||||
'data': [
|
||||
'data/planner_data.xml',
|
||||
'views/planner_crm.xml'
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import planner_crm
|
||||
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from openerp import api, models
|
||||
|
||||
|
||||
class PlannerCrm(models.Model):
|
||||
|
||||
_inherit = 'planner.planner'
|
||||
|
||||
@api.model
|
||||
def _get_planner_application(self):
|
||||
planner = super(PlannerCrm, self)._get_planner_application()
|
||||
planner.append(['planner_crm', 'CRM Planner'])
|
||||
return planner
|
||||
|
||||
@api.model
|
||||
def _prepare_planner_crm_data(self):
|
||||
sales_team = self.env.ref('sales_team.team_sales_department')
|
||||
company_data = self.env['res.users'].browse(self._uid).company_id
|
||||
values = {
|
||||
'prepare_backend_url': self.prepare_backend_url,
|
||||
'alias_domain': sales_team.alias_domain,
|
||||
'alias_name': sales_team.alias_name,
|
||||
'company_data': company_data,
|
||||
}
|
||||
return values
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,45 @@
|
||||
(function(){
|
||||
"use strict";
|
||||
|
||||
openerp.planner.PlannerDialog.include({
|
||||
prepare_planner_event: function() {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
if(self.planner['planner_application'] == 'planner_crm') {
|
||||
var stages = {
|
||||
'solution_selling': [
|
||||
'Territory', 'Qualified', 'Qualified Sponsor',
|
||||
'Proposal', 'Negotiation', 'Won', '',
|
||||
'New propspect assigned to the right salesperson',
|
||||
'Set fields: Expected Revenue, Expected Closing Date, Next Action',
|
||||
'You are in discussion with the decision maker and HE agreed on his pain points',
|
||||
'Quotation sent to customer', 'The customer came back to you to discuss your quotation',
|
||||
'Quotation signed by the customer', ''],
|
||||
'b2c': [
|
||||
'New', 'Initial Contact', 'Product Demonstration', 'Proposal', 'Won', '', '',
|
||||
'', 'Phone call with following questions: ...',
|
||||
'Meeting with a demo. Set Fields: expected revenue, closing date',
|
||||
'Quotation sent', '', '', ''],
|
||||
'b2b': [
|
||||
'New', 'Qualified', 'Needs assessment', 'POC Sold', 'Demonstration', 'Proposal', 'Won',
|
||||
'Set fields: Expected Revenue, Expected Closing Date, Next Action',
|
||||
'Close opportunity if: "pre-sales days * $500" < "expected revenue" * probability',
|
||||
'GAP analysis with customer', 'Create a Proof of Concept with consultants',
|
||||
'POC demonstration to the customer', 'Final Proposal sent', ''],
|
||||
'odoo_default': [
|
||||
'New', 'Qualified', 'Proposition', 'Negotiation', 'Won', 'Lost', '',
|
||||
'', '', '', '', '', '', '']
|
||||
}
|
||||
self.$el.on('change', '#input_element_pipeline', function(ev) {
|
||||
var option = $(ev.target).find(":selected").val();
|
||||
if (_.has(stages, option)) {
|
||||
var values = stages[option];
|
||||
for(var i=0; i<values.length; i++) {
|
||||
$('#input_element_stage_'+i).val(values[i]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,320 @@
|
||||
|
||||
#PlannerModal {
|
||||
|
||||
font-family: "LatoLatin", serif;
|
||||
|
||||
.flip {
|
||||
-moz-transform: scaleX(-1);
|
||||
-o-transform: scaleX(-1);
|
||||
-webkit-transform: scaleX(-1);
|
||||
transform: scaleX(-1);
|
||||
filter: FlipH;
|
||||
-ms-filter: "FlipH";
|
||||
}
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
// PARAGRAPHS
|
||||
.paragraphs .icon {
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
margin: 1em auto .5em;
|
||||
color: #A24689;
|
||||
}
|
||||
|
||||
//NOTEBOOK
|
||||
.notebook-container {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.notebook {
|
||||
color: #555;
|
||||
font-size: 22px;
|
||||
padding: 0 !important;
|
||||
width: 100%;
|
||||
font-family: courier, monospace;
|
||||
border: 1px solid #faebcc;
|
||||
margin: 0;
|
||||
color: #8a6d3b;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: #fcf8e3;
|
||||
|
||||
textarea,input {
|
||||
background: #fcf8e3;
|
||||
border: none;
|
||||
margin-bottom: 1em;
|
||||
/* min-width: 90%;
|
||||
max-width: 90%; */
|
||||
font-size: 15px;
|
||||
line-height: 17px;
|
||||
}
|
||||
> li {
|
||||
list-style: none;
|
||||
border-bottom: 1px dashed #C1D6DA;
|
||||
height: auto;
|
||||
padding: 10px 10px 5px 55px;
|
||||
text-transform: capitalize;
|
||||
margin: 0;
|
||||
span {
|
||||
position: absolute;
|
||||
left: 9px;
|
||||
color: rgb(199, 180, 146);
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
> li:last-child { border-bottom: 0;}
|
||||
|
||||
li.header {
|
||||
h5{padding-left: 0px}
|
||||
div.dropdown { display: inline;float:right; }
|
||||
span {position: relative; left: 0;}
|
||||
a {
|
||||
color: #C4A87A;
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
right: 13px;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: #9C855D;
|
||||
}
|
||||
}
|
||||
select {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
max-width: 80%;
|
||||
font-family: sans-serif;
|
||||
font-size: 17px;
|
||||
color: #8A6D3B;
|
||||
border: none;
|
||||
background: #F5E7CF;
|
||||
}
|
||||
|
||||
.help{
|
||||
font-size: 14px;
|
||||
color: rgb(126, 95, 41);
|
||||
background: #fcf8e3;
|
||||
background: -moz-linear-gradient(top, #fcf8e3 0%, #FFF8D7 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#fcf8e3), color-stop(100%,#FFF8D7));
|
||||
background: -webkit-linear-gradient(top, #fcf8e3 0%,#FFF8D7 100%);
|
||||
background: -o-linear-gradient(top, #fcf8e3 0%,#FFF8D7 100%);
|
||||
background: -ms-linear-gradient(top, #fcf8e3 0%,#FFF8D7 100%);
|
||||
background: linear-gradient(to bottom, #fcf8e3 0%,#FFF8D7 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fcf8e3', endColorstr='#FFF8D7',GradientType=0 );
|
||||
padding: 0 1em;
|
||||
margin: 0;
|
||||
border: none;
|
||||
z-index: 10;
|
||||
position: relative;
|
||||
box-shadow: inset 0 10px 10px -9px #A89E64;
|
||||
margin: 0px -10px 0px -55px;
|
||||
|
||||
h5 {padding: 14px 0 0; text-indent:0; display: inline-block;}
|
||||
ul { padding: 0 20px 20px 50px;}
|
||||
li {text-indent: 0; font-style: italic;}
|
||||
}
|
||||
}
|
||||
> li:not(.header):hover {
|
||||
background-color: #F5EFD0;
|
||||
-webkit-transition: all 0.2s;
|
||||
-moz-transition: all 0.2s;
|
||||
-ms-transition: all 0.2s;
|
||||
-o-transition: all 0.2s;
|
||||
}
|
||||
.lines {
|
||||
border-left: 1px solid #ffaa9f;
|
||||
border-right: 1px solid #ffaa9f;
|
||||
background: transparent;
|
||||
position: absolute;
|
||||
width: 3px;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 42px;
|
||||
border-bottom: 0;
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.notebook.columns {
|
||||
li.header{
|
||||
padding: 10px 10px 5px 55px;
|
||||
h5{padding: 0}
|
||||
}
|
||||
textarea,input {
|
||||
vertical-align: top;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.notebook-container:after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 94%;
|
||||
left: 20px;
|
||||
background: transparent;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
z-index: 0;
|
||||
background-color: #fcf8e3;
|
||||
border: 1px solid #faebcc;
|
||||
}
|
||||
|
||||
.info_icon{
|
||||
position: relative;
|
||||
padding-left: 5em;
|
||||
overflow: hidden;
|
||||
min-height: 6em;
|
||||
span.fa{
|
||||
position: absolute;
|
||||
font-size: 5em;
|
||||
top: 0.2em;
|
||||
left: 0.2em;
|
||||
opacity: 0.6;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-info.info_icon span.fa { color: #8ABCD4; }
|
||||
|
||||
.arrow-down {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 30px solid transparent;
|
||||
border-right: 30px solid transparent;
|
||||
border-top: 15px solid #D6D6D6;
|
||||
position: absolute;
|
||||
margin-left: -30px;
|
||||
bottom: -15px;
|
||||
}
|
||||
|
||||
#planner_page1 {
|
||||
h1 {margin-bottom: 20px;}
|
||||
p.last{
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.fabien {
|
||||
margin-bottom: 30px;
|
||||
pointer-events: none;
|
||||
.item{
|
||||
float: left;
|
||||
display: inline;
|
||||
&:last-child{
|
||||
margin-left:10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
img.signature{
|
||||
margin-top: 3em;
|
||||
}
|
||||
address {
|
||||
margin-bottom: 0px;
|
||||
font-style: normal;
|
||||
line-height: 1.42857143;
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
#planner_page2,#planner_page3 {
|
||||
textarea{
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
#planner_page4 {
|
||||
textarea {
|
||||
width: 58%;
|
||||
max-width: 58%;
|
||||
}
|
||||
}
|
||||
#planner_page5, #planner_page7, #planner_page8, #planner_page10, #planner_page11 {
|
||||
.container-fluid {
|
||||
.col-xs-12 {
|
||||
padding: 5px;
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#planner_page11 {
|
||||
.container-fluid {
|
||||
.col-xs-12 .panel-heading {
|
||||
min-height: 200px;
|
||||
padding: 0;
|
||||
font-size: 1.1em;
|
||||
color: #407241;
|
||||
a {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
padding-top: 40px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#planner_page16 {
|
||||
.trophy{
|
||||
font-size: 11em;
|
||||
color: #A24689;
|
||||
margin-top: .5em;
|
||||
-webkit-animation-name: bounceIn;
|
||||
animation-name: bounceIn;
|
||||
-webkit-animation-duration: 1.75s;
|
||||
animation-duration: 1.75s;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// XS size
|
||||
@media screen and (max-width: 768px) {
|
||||
#PlannerModal .pages {
|
||||
margin-top: 0px;
|
||||
}
|
||||
#PlannerModal .counter {
|
||||
padding: 6px 0 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
// SM size
|
||||
@media screen and (min-width: 769px) {
|
||||
#PlannerModal .paragraphs {
|
||||
div.col-md-10 {
|
||||
margin-top:1em;
|
||||
}
|
||||
.icon {
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
margin: 1em auto .5em;
|
||||
}
|
||||
span {
|
||||
transform: translateX(-8px);
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MD size
|
||||
@media screen and (min-width: 992px) {
|
||||
#PlannerModal .paragraphs .icon {
|
||||
text-align: center;
|
||||
font-size: 4em;
|
||||
margin: 0.3em auto .5em;
|
||||
span{
|
||||
transform: translateX(0px);
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<odoo>
|
||||
<template id="assets_backend" name="planner crm assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link href="/planner_crm/static/src/less/planner_crm.less" rel="stylesheet" type="text/less"/>
|
||||
<script type="text/javascript" src="/planner_crm/static/src/js/planner_crm.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||