[MERGE] from saas-3

bzr revid: chm@openerp.com-20140212095425-ew77la0i2wzfzlno
This commit is contained in:
Christophe Matthieu
2014-02-12 10:54:25 +01:00
18 changed files with 270 additions and 140 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ def fragment_to_query_string(func):
return """<html><head><script>
var l = window.location;
var q = l.hash.substring(1);
var r = '/' + l.search;
var r = l.pathname + l.search;
if(q.length !== 0) {
var s = l.search ? (l.search === '?' ? '' : '&') : '?';
r = l.pathname + l.search + s + q;
+9 -4
View File
@@ -62,7 +62,7 @@ class AuthSignup(openerp.addons.web.controllers.main.Home):
}
qcontext.update(config)
if 'error' in qcontext or mode not in ('reset', 'signup') or (not token and not config[mode]):
if 'error' in request.params or mode not in ('reset', 'signup') or (not token and not config[mode]):
if isinstance(super_response, LazyResponse):
super_response.params['values'].update(config)
return super_response
@@ -85,15 +85,20 @@ class AuthSignup(openerp.addons.web.controllers.main.Home):
values = dict((key, qcontext.get(key)) for key in ('login', 'name', 'password'))
try:
self._signup_with_values(token, values)
request.cr.commit()
redirect = request.params.get('redirect')
if not redirect:
redirect = '/web?' + request.httprequest.query_string
return http.redirect_with_hash(redirect)
except SignupError, e:
qcontext['error'] = exception_to_unicode(e)
return super(AuthSignup, self).web_login(*args, **kw)
return response
def _signup_with_values(self, token, values):
request.registry['res.users'].signup(request.cr, openerp.SUPERUSER_ID, values, token)
db, login, password = request.registry['res.users'].signup(request.cr, openerp.SUPERUSER_ID, values, token)
uid = request.session.authenticate(db, login, password)
if uid is not False:
raise SignupError(_('Authentification Failed.'))
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -40,7 +40,7 @@
<div class="form-group field-login">
<label for="login" class="control-label">Your Email</label>
<input type="text" name="login" t-att-value="login" id="login" class="form-control"
<input type="email" name="login" t-att-value="login" id="login" class="form-control"
t-att-autofocus="'autofocus' if reset_without_token else None"
required="required" t-att-disabled="'disabled' if mode == 'reset' and token else None"/>
<input type="hidden" name="login" t-att-value="login" t-if="mode == 'reset' and token"/>
+11 -6
View File
@@ -120,14 +120,14 @@ class crm_case_section(osv.osv):
month_begin = date.today().replace(day=1)
section_result = [{
'value': 0,
'tooltip': (month_begin + relativedelta.relativedelta(months=-i)).strftime('%B'),
'tooltip': (month_begin + relativedelta.relativedelta(months=-i)).strftime('%B %Y'),
} for i in range(self._period_number - 1, -1, -1)]
group_obj = obj.read_group(cr, uid, domain, read_fields, groupby_field, context=context)
pattern = tools.DEFAULT_SERVER_DATE_FORMAT if obj.fields_get(cr, uid, groupby_field)[groupby_field]['type'] == 'date' else tools.DEFAULT_SERVER_DATETIME_FORMAT
for group in group_obj:
group_begin_date = datetime.strptime(group['__domain'][0][2], pattern)
month_delta = relativedelta.relativedelta(month_begin, group_begin_date)
section_result[self._period_number - (month_delta.months + 1)] = {'value': group.get(value_field, 0), 'tooltip': group_begin_date.strftime('%B')}
section_result[self._period_number - (month_delta.months + 1)] = {'value': group.get(value_field, 0), 'tooltip': group.get(groupby_field, 0)}
return section_result
def _get_opportunities_data(self, cr, uid, ids, field_name, arg, context=None):
@@ -140,13 +140,18 @@ class crm_case_section(osv.osv):
month_begin = date.today().replace(day=1)
date_begin = month_begin - relativedelta.relativedelta(months=self._period_number - 1)
date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1])
date_domain = [('create_date', '>=', date_begin.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)), ('create_date', '<=', date_end.strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
lead_pre_domain = [('create_date', '>=', date_begin.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)),
('create_date', '<=', date_end.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)),
('type', '=', 'lead')]
opp_pre_domain = [('date_deadline', '>=', date_begin.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)),
('date_deadline', '<=', date_end.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)),
('type', '=', 'opportunity')]
for id in ids:
res[id] = dict()
lead_domain = date_domain + [('type', '=', 'lead'), ('section_id', '=', id)]
lead_domain = lead_pre_domain + [('section_id', '=', id)]
opp_domain = opp_pre_domain + [('section_id', '=', id)]
res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, obj, lead_domain, ['create_date'], 'create_date_count', 'create_date', context=context)
opp_domain = date_domain + [('type', '=', 'opportunity'), ('section_id', '=', id)]
res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'create_date'], 'planned_revenue', 'create_date', context=context)
res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'date_deadline'], 'planned_revenue', 'date_deadline', context=context)
return res
_columns = {
+6 -7
View File
@@ -65,9 +65,9 @@
<record id="action_report_crm_lead_salesteam" model="ir.actions.act_window">
<field name="name">Leads Analysis</field>
<field name="res_model">crm.lead.report</field>
<field name="view_type">form</field>
<field name="context">{"search_default_month":1}</field>
<field name="view_mode">tree,graph</field>
<field name="view_mode">graph</field>
<field name="view_id" ref="crm.view_report_crm_lead_graph_two"/>
<field name="domain">[('type','=', 'lead'),('section_id', '=', active_id)]</field>
<field name="help">Leads Analysis allows you to check different CRM related information like the treatment delays or number of leads per state. You can sort out your leads analysis by different groups to get accurate grained analysis.</field>
</record>
@@ -75,9 +75,8 @@
<record id="action_report_crm_opportunity_salesteam" model="ir.actions.act_window">
<field name="name">Opportunities Analysis</field>
<field name="res_model">crm.lead.report</field>
<field name="view_type">form</field>
<field name="context">{"search_default_month":1}</field>
<field name="view_mode">tree,graph</field>
<field name="view_mode">graph</field>
<field name="view_id" ref="crm.view_report_crm_opportunity_graph"/>
<field name="domain">[('type','=', 'opportunity'), ('section_id', '=', active_id)]</field>
<field name="help">Opportunities Analysis gives you an instant access to your opportunities with information such as the expected revenue, planned cost, missed deadlines or the number of interactions per opportunity. This report is mainly used by the sales manager in order to do the periodic review with the teams of the sales pipeline.</field>
</record>
@@ -119,14 +118,14 @@
<a name="%(crm_case_form_view_salesteams_lead)d" type="action">Leads</a>
<a name="%(action_report_crm_lead_salesteam)d" type="action" class="oe_sparkline_bar_link">
<field name="monthly_open_leads" widget="sparkline_bar"
options="{'height': '20px', 'barWidth': 4, 'barSpacing': 1, 'delayIn': '3000', 'tooltip_suffix': 'Leads'}">Open Leads per Month<br/>Click to see a detailed analysis of leads.</field>
options="{'height': '20px', 'barWidth': 4, 'barSpacing': 1, 'delayIn': '3000', 'tooltip_suffix': ' Leads'}">Open Leads per Month<br/>Click to see a detailed analysis of leads.</field>
</a>
</div>
<div class="oe_salesteams_opportunities">
<a name="%(crm_case_form_view_salesteams_opportunity)d" type="action">Opportunities</a>
<a name="%(action_report_crm_opportunity_salesteam)d" type="action">
<field name="monthly_planned_revenue" widget="sparkline_bar"
options="{'height': '20px', 'barWidth': '4', 'barSpacing': '1', 'delayIn': '3000', 'tooltip_suffix': 'Opportunities'}">Planned Revenue per Month<br/>Click to see a detailed analysis of opportunities.</field>
options="{'height': '20px', 'barWidth': '4', 'barSpacing': '1', 'delayIn': '3000', 'tooltip_suffix': ' (Planned Revenue)'}">Planned Revenue per Month<br/>Click to see a detailed analysis of opportunities.</field>
</a>
</div>
</div>
-4
View File
@@ -89,10 +89,6 @@ class crm_lead_report(osv.osv):
id,
c.date_deadline,
to_char(c.create_date, 'YYYY') as creation_year,
to_char(c.create_date, 'MM') as creation_month,
to_char(c.create_date, 'YYYY-MM-DD') as creation_day,
to_char(c.date_open, 'YYYY-MM-DD') as opening_date,
to_char(c.date_closed, 'YYYY-mm-dd') as date_closed,
+13 -2
View File
@@ -15,6 +15,17 @@
</field>
</record>
<record id="view_report_crm_lead_graph_two" model="ir.ui.view">
<field name="name">crm.lead.report.graph.two</field>
<field name="model">crm.lead.report</field>
<field name="arch" type="xml">
<graph string="Leads Analysis" type="pivot" stacked="True">
<field name="create_date" type="row"/>
<field name="user_id" type="col"/>
</graph>
</field>
</record>
<record id="view_report_crm_opportunity_graph" model="ir.ui.view">
<field name="name">crm.opportunity.report.graph</field>
<field name="model">crm.lead.report</field>
@@ -23,7 +34,7 @@
<field name="date_deadline" type="row"/>
<field name="user_id" type="col"/>
<field name="stage_id" type="col"/>
<field name="probable_revenue" type="measure"/>
<field name="planned_revenue" type="measure"/>
</graph>
</field>
</record>
@@ -84,7 +95,7 @@
<filter string="Creation date (week)" icon="terp-go-year"
domain="[]" context="{'group_by':'create_date:week'}"/>
<filter string="Creation date (month)" icon="terp-go-year"
domain="[]" context="{'group_by':'create_date:month'}"/>
domain="[]" context="{'group_by':'create_date:month'}" name="month"/>
<filter string="Creation date (year)" icon="terp-go-year"
domain="[]" context="{'group_by':'create_date:year'}"/>
<separator orientation="vertical" />
+2 -2
View File
@@ -74,8 +74,8 @@
<label for="number_of_days_temp" string="Duration"/>
<div>
<group col="3" attrs="{'invisible': [('type', '=', 'add')]}">
<field name="date_from" nolabel="1" on_change="onchange_date_from(date_to, date_from)" required="1" class="oe_inline"/><label string="-" class="oe_inline"/>
<field name="date_to" nolabel="1" on_change="onchange_date_to(date_to, date_from)" required="1" class="oe_inline"/>
<field name="date_from" nolabel="1" on_change="onchange_date_from(date_to, date_from)" attrs="{'required':[('type', '=', 'remove')]}" class="oe_inline"/><label string="-" class="oe_inline"/>
<field name="date_to" nolabel="1" on_change="onchange_date_to(date_to, date_from)" attrs="{'required':[('type', '=', 'remove')]}" class="oe_inline"/>
</group>
<div>
<field name="number_of_days_temp" class="oe_inline"/> days
+38 -4
View File
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
import commands
import logging
import simplejson
import os
@@ -11,7 +12,7 @@ import math
import md5
import openerp.addons.hw_proxy.controllers.main as hw_proxy
import subprocess
from threading import Thread
from threading import Thread, Lock
from Queue import Queue, Empty
try:
@@ -39,6 +40,7 @@ class EscposDriver(Thread):
def __init__(self):
Thread.__init__(self)
self.queue = Queue()
self.lock = Lock()
self.status = {'status':'connecting', 'messages':[]}
def connected_usb_devices(self):
@@ -47,6 +49,13 @@ class EscposDriver(Thread):
if usb.core.find(idVendor=device['vendor'], idProduct=device['product']) != None:
connected.append(device)
return connected
def lockedstart(self):
self.lock.acquire()
if not self.isAlive():
self.daemon = True
self.start()
self.lock.release()
def get_escpos_printer(self):
try:
@@ -105,6 +114,8 @@ class EscposDriver(Thread):
elif task == 'cashbox':
if timestamp >= time.time() - 12:
self.open_cashbox(printer)
elif task == 'printstatus':
self.print_status(printer)
elif task == 'status':
pass
@@ -113,10 +124,31 @@ class EscposDriver(Thread):
_logger.error(e);
def push_task(self,task, data = None):
if not self.isAlive():
self.start()
self.lockedstart()
self.queue.put((time.time(),task,data))
def print_status(self,eprint):
localips = ['0.0.0.0','127.0.0.1','127.0.1.1']
ips = [ c.split(':')[1].split(' ')[0] for c in commands.getoutput("/sbin/ifconfig").split('\n') if 'inet addr' in c ]
ips = [ ip for ip in ips if ip not in localips ]
eprint.text('\n\n')
eprint.set(align='center',type='b',height=2,width=2)
eprint.text('PosBox Status\n')
eprint.text('\n')
eprint.set(align='center')
if len(ips) == 0:
eprint.text('ERROR: Could not connect to LAN\n\nPlease check that the PosBox is correc-\ntly connected with a network cable,\n that the LAN is setup with DHCP, and\nthat network addresses are available')
elif len(ips) == 1:
eprint.text('IP Address\n'+ips[0]+'\n')
else:
eprint.text('IP Addresses\n')
for ip in ips:
eprint.text(ip+'\n')
eprint.text('\n\n')
eprint.cut()
def print_receipt_body(self,eprint,receipt):
def check(string):
@@ -134,7 +166,6 @@ class EscposDriver(Thread):
else:
return str(amount)
def printline(left, right='', width=40, ratio=0.5, indent=0):
lwidth = int(width * ratio)
rwidth = width - lwidth
@@ -157,6 +188,7 @@ class EscposDriver(Thread):
# Receipt Header
if receipt['company']['logo']:
eprint.set(align='center')
eprint.print_base64_image(receipt['company']['logo'])
eprint.text('\n')
else:
@@ -245,6 +277,8 @@ class EscposDriver(Thread):
driver = EscposDriver()
hw_proxy.drivers['escpos'] = driver
driver.push_task('printstatus')
class EscposProxy(hw_proxy.Proxy):
+12 -7
View File
@@ -4,7 +4,7 @@ import os
import time
from os import listdir
from os.path import join
from threading import Thread
from threading import Thread, Lock
from select import select
from Queue import Queue, Empty
@@ -26,6 +26,7 @@ except ImportError:
class Scanner(Thread):
def __init__(self):
Thread.__init__(self)
self.lock = Lock()
self.status = {'status':'connecting', 'messages':[]}
self.input_dir = '/dev/input/by-id/'
self.barcodes = Queue()
@@ -86,6 +87,12 @@ class Scanner(Thread):
57:(" "," "),
}
def lockedstart(self):
self.lock.acquire()
if not self.isAlive():
self.start()
self.lock.release()
def set_status(self, status, message = None):
if status == self.status['status']:
if message != None and message != self.status['messages'][-1]:
@@ -102,8 +109,6 @@ class Scanner(Thread):
elif status == 'disconnected' and message:
_logger.warning('Disconnected Barcode Scanner: '+message)
def get_device(self):
try:
if not evdev:
@@ -135,6 +140,8 @@ class Scanner(Thread):
busy reading another barcode
"""
self.lockedstart()
while True:
try:
timestamp, barcode = self.barcodes.get(True, 5)
@@ -144,8 +151,7 @@ class Scanner(Thread):
return ''
def get_status(self):
if not s.isAlive():
s.start()
self.lockedstart()
return self.status
def run(self):
@@ -209,7 +215,6 @@ hw_proxy.drivers['scanner'] = s
class ScannerDriver(hw_proxy.Proxy):
@http.route('/hw_proxy/scanner', type='json', auth='none', cors='*')
def scanner(self):
if not s.isAlive():
s.start()
return s.get_barcode()
+88 -56
View File
@@ -162,25 +162,37 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
},
// find a proxy and connects to it. for options see find_proxy
// - force_ip : only try to connect to the specified ip.
// - port: what port to listen to (default 8069)
// - progress(fac) : callback for search progress ( fac in [0,1] )
autoconnect: function(options){
var self = this;
this.set_connection_status('connecting',{});
var found_url = new $.Deferred();
var success = new $.Deferred();
this.find_proxy(options)
.then(function(proxies){
if(proxies.length > 0){
self.connect(proxies[0])
.then(function(){
success.resolve();
},function(){
self.set_connection_status('disconnected');
success.reject();
});
}else{
self.set_connection_status('disconnected');
success.reject();
}
if ( options.force_ip ){
// if the ip is forced by server config, bailout on fail
found_url = this.try_hard_to_connect(options.force_ip, options)
}else if( localStorage['hw_proxy_url'] ){
// try harder when we remember a good proxy url
found_url = this.try_hard_to_connect(localStorage['hw_proxy_url'], options)
.then(null,function(){
return self.find_proxy(options);
});
}else{
// just find something quick
found_url = this.find_proxy(options);
}
success = found_url.then(function(url){
return self.connect(url);
});
success.fail(function(){
self.set_connection_status('disconnected');
});
return success;
},
@@ -217,10 +229,50 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
}
},
// returns as a deferred a list of valid hosts urls that can be used as proxy.
// try several time to connect to a known proxy url
try_hard_to_connect: function(url,options){
options = options || {};
var port = ':' + (options.port || '8069');
this.set_connection_status('connecting');
if(url.indexOf('//') < 0){
url = 'http://'+url;
}
if(url.indexOf(':',5) < 0){
url = url+port;
}
// try real hard to connect to url, with a 1sec timeout and up to 'retries' retries
function try_real_hard_to_connect(url, retries, done){
done = done || new $.Deferred();
var c = $.ajax({
url: url + '/hw_proxy/hello',
method: 'GET',
timeout: 1000,
})
.done(function(){
done.resolve(url);
})
.fail(function(){
if(retries > 0){
try_real_hard_to_connect(url,retries-1,done);
}else{
done.reject();
}
});
return done;
}
return try_real_hard_to_connect(url,3);
},
// returns as a deferred a valid host url that can be used as proxy.
// options:
// - port: what port to listen to (default 8069)
// - force_ip : limit the search to the specified ip
// - progress(fac) : callback for search progress ( fac in [0,1] )
find_proxy: function(options){
options = options || {};
@@ -228,36 +280,17 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
var port = ':' + (options.port || '8069');
var urls = [];
var found = false;
var proxies = [];
var done = new $.Deferred();
var parallel = 8;
var done = new $.Deferred(); // will be resolved with the proxies valid urls
var threads = [];
var progress = 0;
this.set_connection_status('connecting');
if(options.force_ip){
var url = options.force_ip;
if(url.indexOf('//') < 0){
url = 'http://'+url;
}
if(url.indexOf(':',5) < 0){
url = url+port;
}
urls.push(url);
}else{
if(localStorage['hw_proxy_url']){
urls.push(localStorage['hw_proxy_url']);
}
urls.push('http://localhost'+port);
for(var i = 0; i < 256; i++){
urls.push('http://192.168.0.'+i+port);
urls.push('http://192.168.1.'+i+port);
urls.push('http://192.168.2.'+i+port);
urls.push('http://10.0.0.'+i+port);
}
urls.push('http://localhost'+port);
for(var i = 0; i < 256; i++){
urls.push('http://192.168.0.'+i+port);
urls.push('http://192.168.1.'+i+port);
urls.push('http://10.0.0.'+i+port);
}
var prog_inc = 1/urls.length;
@@ -269,40 +302,39 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
}
}
function thread(url,done){
if(!url){
function thread(done){
var url = urls.shift();
done = done || new $.Deferred();
if( !url || found || !self.searching_for_proxy ){
done.resolve();
return done;
}
var c = $.ajax({
url: url + '/hw_proxy/hello',
method: 'GET',
timeout: 300,
timeout: 400,
}).done(function(){
found = true;
update_progress();
proxies.push(url);
done.resolve(url);
})
.fail(function(){
update_progress();
var next_url = urls.shift();
if(found ||! self.searching_for_proxy || !next_url){
done.resolve();
}else{
thread(next_url,done);
}
thread(done);
});
return done;
}
this.searching_for_proxy = true;
for(var i = 0; i < Math.min(parallel,urls.length); i++){
threads.push(thread(urls.shift(),new $.Deferred()));
for(var i = 0, len = Math.min(parallel,urls.length); i < len; i++){
threads.push(thread());
}
var done = new $.Deferred();
$.when.apply($,threads).then(function(){
var urls = [];
for(var i = 0; i < arguments.length; i++){
@@ -310,7 +342,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
urls.push(arguments[i]);
}
}
done.resolve(urls);
done.resolve(urls[0]);
});
return done;
@@ -258,8 +258,8 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
var total = order ? order.getTotalTaxIncluded() : 0;
var taxes = order ? total - order.getTotalTaxExcluded() : 0;
this.el.querySelector('.summary .total > .value').innerText = this.format_currency(total);
this.el.querySelector('.summary .total .subentry .value').innerText = this.format_currency(taxes);
this.el.querySelector('.summary .total > .value').textContent = this.format_currency(total);
this.el.querySelector('.summary .total .subentry .value').textContent = this.format_currency(taxes);
},
});
+4 -4
View File
@@ -635,9 +635,9 @@
Shop: <t t-esc="widget.pos.shop.name"/><br />
<br />
<t t-if="widget.pos.config.receipt_header">
<pre>
<div style='text-align:center'>
<t t-esc="widget.pos.config.receipt_header" />
</pre>
</div>
<br />
</t>
<table>
@@ -711,9 +711,9 @@
</table>
<t t-if="widget.pos.config.receipt_footer">
<br />
<pre>
<div style='text-align:center'>
<t t-esc="widget.pos.config.receipt_footer" />
</pre>
</div>
</t>
</div>
</t>
+3 -6
View File
@@ -214,8 +214,7 @@
<record id="action_order_report_quotation_salesteam" model="ir.actions.act_window">
<field name="name">Quotations Analysis</field>
<field name="res_model">sale.report</field>
<field name="view_type">form</field>
<field name="view_mode">tree,graph</field>
<field name="view_mode">graph</field>
<field name="domain">[('state','=','draft'),('section_id', '=', active_id)]</field>
<field name="context">{'search_default_order_month':1}</field>
<field name="help">This report performs analysis on your quotations. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application.</field>
@@ -224,8 +223,7 @@
<record id="action_order_report_so_salesteam" model="ir.actions.act_window">
<field name="name">Sales Analysis</field>
<field name="res_model">sale.report</field>
<field name="view_type">form</field>
<field name="view_mode">tree,graph</field>
<field name="view_mode">graph</field>
<field name="domain">[('state','not in',('draft','sent','cancel')),('section_id', '=', active_id)]</field>
<field name="context">{'search_default_order_month':1}</field>
<field name="help">This report performs analysis on your sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application.</field>
@@ -234,8 +232,7 @@
<record id="action_account_invoice_report_salesteam" model="ir.actions.act_window">
<field name="name">Invoices Analysis</field>
<field name="res_model">account.invoice.report</field>
<field name="view_type">form</field>
<field name="view_mode">tree,graph</field>
<field name="view_mode">graph</field>
<field name="domain">[('section_id', '=', active_id),('state', 'not in', ['draft', 'cancel'])]</field>
<field name="context">{'search_default_month':1}</field>
<field name="help">From this report, you can have an overview of the amount invoiced to your customer. The tool search can also be used to personalise your Invoices reports and so, match this analysis to your needs.</field>
+30 -5
View File
@@ -304,20 +304,23 @@ ul.nav-stacked > li > a {
}
.carousel .carousel-caption {
left: auto;
right: auto;
bottom: 10%;
position: absolute;
right: 50%;
left: 50%;
bottom: 20px;
}
.carousel .carousel-caption > div {
position: absolute;
text-align: left;
padding: 20px;
background: rgba(0, 0, 0, 0.4);
bottom: 20px;
}
.carousel .carousel-caption h1, .carousel .carousel-caption h2, .carousel .carousel-caption h3 {
margin-top: 10px;
margin-bottom: 10px;
}
.carousel .carousel-image {
right: 50%;
left: 50%;
top: 5%;
bottom: 5%;
position: absolute;
@@ -332,13 +335,35 @@ ul.nav-stacked > li > a {
right: 10%;
top: 10%;
bottom: auto;
}
.carousel .item.text_only .carousel-caption > div {
text-align: center;
background: transparent;
bottom: auto;
width: 100%;
}
.carousel .item.text_image .carousel-caption {
left: 10%;
}
.carousel .item.text_image .carousel-caption > div {
right: 50%;
margin-right: -20%;
max-width: 550px;
}
.carousel .item.text_image .carousel-image {
right: 10%;
left: 50%;
}
.carousel .item.image_text .carousel-caption {
right: 10%;
}
.carousel .item.image_text .carousel-caption > div {
left: 50%;
margin-left: -20%;
max-width: 550px;
}
.carousel .item.image_text .carousel-image {
right: 50%;
left: 10%;
}
.carousel .carousel-control {
+30 -10
View File
@@ -248,18 +248,20 @@ ul.nav-stacked > li > a
background-size: cover
.carousel
.carousel-caption
left: auto
right: auto
bottom: 10%
text-align: left
padding: 20px
background: rgba(0, 0, 0, 0.4)
position: absolute
right: 50%
left: 50%
bottom: 20px
> div
position: absolute
text-align: left
padding: 20px
background: rgba(0, 0, 0, 0.4)
bottom: 20px
h1, h2, h3
margin-top: 10px
margin-bottom: 10px
.carousel-image
right: 50%
left: 50%
top: 5%
bottom: 5%
position: absolute
@@ -273,12 +275,30 @@ ul.nav-stacked > li > a
right: 10%
top: 10%
bottom: auto
text-align: center
background: transparent
> div
text-align: center
background: transparent
bottom: auto
width: 100%
.item.text_image
.carousel-caption
left: 10%
> div
right: 50%
margin-right: -20%
max-width: 550px
.carousel-image
right: 10%
left: 50%
.item.image_text
.carousel-caption
right: 10%
> div
left: 50%
margin-left: -20%
max-width: 550px
.carousel-image
right: 50%
left: 10%
.carousel-control
cursor: pointer
+16 -14
View File
@@ -34,11 +34,13 @@
<div class="item image_text active" style="background-image: url('/website/static/src/img/banner/color_splash.jpg')">
<div class="container">
<div class="carousel-caption content">
<h2>Your Banner Title</h2>
<h3>Click to customize this text</h3>
<p>
<a href="/page/website.contactus" class="btn btn-success btn-large">Contact us</a>
</p>
<div>
<h2>Your Banner Title</h2>
<h3>Click to customize this text</h3>
<p>
<a href="/page/website.contactus" class="btn btn-success btn-large">Contact us</a>
</p>
</div>
</div>
<img class="carousel-image hidden-xs" src="/website/static/src/img/banner/banner_picture.png" alt="Banner OpenERP Image"/>
</div>
@@ -182,15 +184,6 @@
</div>
<div data-snippet-id="hr" data-selector-children=".oe_structure, [data-oe-type=html]">
<div class="oe_snippet_thumbnail">
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_separator.png"/>
<span class="oe_snippet_thumbnail_title">Separator</span>
</div>
<hr class="oe_snippet_body"/>
</div>
<div data-snippet-id="big-picture" data-selector-children=".oe_structure, [data-oe-type=html]">
<div class="oe_snippet_thumbnail">
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_big_picture.png"/>
@@ -372,6 +365,15 @@
</div>
</div>
<div data-snippet-id="hr" data-selector-children=".oe_structure, [data-oe-type=html]">
<div class="oe_snippet_thumbnail">
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_separator.png"/>
<span class="oe_snippet_thumbnail_title">Separator</span>
</div>
<hr class="oe_snippet_body"/>
</div>
</div>
<div id="snippet_feature" class="tab-pane fade">
+4 -5
View File
@@ -673,10 +673,9 @@ Sitemap: <t t-esc="url_root"/>sitemap.xml
<template id="company_description" name="Company Description">
<address>
<div t-field="res_company.name">Name</div>
<br />
<div><i class="fa fa-phone"/> <span t-field="res_company.phone"/></div>
<div><i class="fa fa-envelope"/> <span t-field="res_company.email"/></div>
<div t-field="res_company.partner_id" t-field-options='{
"widget": "contact",
"fields": ["name", "address", "phone", "mobile", "fax", "email"]}'/>
</address>
<a t-att-href="res_company.google_map_link()" target="_BLANK">
<img class="thumbnail img-responsive" t-att-src="res_company.google_map_img()" />
@@ -703,7 +702,7 @@ Sitemap: <t t-esc="url_root"/>sitemap.xml
</div>
<div class="col-md-4 mb32">
<div groups="base.group_website_publisher" t-ignore="true" class="pull-right css_editable_mode_hidden" t-att-style="style or ''">
<a class="btn btn-primary" t-att-href="'/web#return_label=Website&amp;model=%s&amp;id=%s' % (res_company._name, res_company.id)" title='Edit in backend'>Edit</a>
<a class="btn btm-sm btn-default" t-att-href="'/web#return_label=Website&amp;model=%s&amp;id=%s' % (res_company._name, res_company.id)">Change address</a>
</div>
<t t-call="website.company_description"/>
</div>