[MERGE] Sync with trunk-website-al

bzr revid: rim@openerp.com-20140130073558-afmllqhuwghv374g
This commit is contained in:
Richard Mathot (OpenERP)
2014-01-30 08:35:58 +01:00
103 changed files with 656 additions and 440 deletions
+8 -5
View File
@@ -3187,11 +3187,14 @@ class wizard_multi_charts_accounts(osv.osv_memory):
def _get_analytic_journal(journal_type):
# Get the analytic journal
data = False
if journal_type in ('sale', 'sale_refund'):
data = obj_data.get_object_reference(cr, uid, 'account', 'analytic_journal_sale')
elif journal_type in ('purchase', 'purchase_refund'):
pass
elif journal_type == 'general':
try:
if journal_type in ('sale', 'sale_refund'):
data = obj_data.get_object_reference(cr, uid, 'account', 'analytic_journal_sale')
elif journal_type in ('purchase', 'purchase_refund'):
data = obj_data.get_object_reference(cr, uid, 'account', 'exp')
elif journal_type == 'general':
pass
except ValueError:
pass
return data and data[1] or False
+6 -1
View File
@@ -23,6 +23,7 @@ import time
from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp.tools.float_utils import float_round
import openerp.addons.decimal_precision as dp
class account_move_line_reconcile(osv.osv_memory):
@@ -64,7 +65,11 @@ class account_move_line_reconcile(osv.osv_memory):
credit += line.credit
debit += line.debit
account_id = line.account_id.id
return {'trans_nbr': count, 'account_id': account_id, 'credit': credit, 'debit': debit, 'writeoff': debit - credit}
precision = self.pool['decimal.precision'].precision_get(cr, uid, 'Account')
writeoff = float_round(debit-credit, precision_digits=precision)
credit = float_round(credit, precision_digits=precision)
debit = float_round(debit, precision_digits=precision)
return {'trans_nbr': count, 'account_id': account_id, 'credit': credit, 'debit': debit, 'writeoff': writeoff}
def trans_rec_addendum_writeoff(self, cr, uid, ids, context=None):
return self.pool.get('account.move.line.reconcile.writeoff').trans_rec_addendum(cr, uid, ids, context)
@@ -17,8 +17,8 @@
</group>
<footer>
<button string="Reconcile" name="trans_rec_reconcile_full" type="object" default_focus="1" attrs="{'invisible':[('writeoff','!=',0)]}" class="oe_highlight"/>
<button string="Reconcile With Write-Off" name="trans_rec_addendum_writeoff" type="object" attrs="{'invisible':[('writeoff','==',0)]}" class="oe_highlight"/>
<button string="Partial Reconcile" name="trans_rec_reconcile_partial_reconcile" type="object" attrs="{'invisible':[('writeoff','==',0)]}" class="oe_highlight"/>
<button string="Reconcile With Write-Off" name="trans_rec_addendum_writeoff" type="object" attrs="{'invisible':[('writeoff','=',0)]}" class="oe_highlight"/>
<button string="Partial Reconcile" name="trans_rec_reconcile_partial_reconcile" type="object" attrs="{'invisible':[('writeoff','=',0)]}" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel"/>
</footer>
@@ -219,7 +219,7 @@
<filter name="open" string="In Progress" domain="[('state','in',('open','draft'))]" help="Contracts in progress (open, draft)"/>
<filter name="pending" string="To Renew" domain="[('state','=','pending')]" help="Pending contracts"/>
<filter name="closed" string="Closed" domain="[('state','=','close')]" help="Closed contracts"/>
<filter name="cancelled" string="Cancelled" domain="[('state','=','cancel')]" help="Cancelled contracts"/>
<filter name="cancelled" string="Cancelled" domain="[('state','=','cancelled')]" help="Cancelled contracts"/>
<separator/>
<filter
string="Expired or consumed"
+3 -3
View File
@@ -35,9 +35,9 @@ class account_asset_category(osv.osv):
'name': fields.char('Name', size=64, required=True, select=1),
'note': fields.text('Note'),
'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
'account_asset_id': fields.many2one('account.account', 'Asset Account', required=True),
'account_depreciation_id': fields.many2one('account.account', 'Depreciation Account', required=True),
'account_expense_depreciation_id': fields.many2one('account.account', 'Depr. Expense Account', required=True),
'account_asset_id': fields.many2one('account.account', 'Asset Account', required=True, domain=[('type','=','other')]),
'account_depreciation_id': fields.many2one('account.account', 'Depreciation Account', required=True, domain=[('type','=','other')]),
'account_expense_depreciation_id': fields.many2one('account.account', 'Depr. Expense Account', required=True, domain=[('type','=','other')]),
'journal_id': fields.many2one('account.journal', 'Journal', required=True),
'company_id': fields.many2one('res.company', 'Company', required=True),
'method': fields.selection([('linear','Linear'),('degressive','Degressive')], 'Computation Method', required=True, help="Choose the method to use to compute the amount of depreciation lines.\n"\
@@ -149,7 +149,7 @@
<para style="terp_default_8">
<font color="white"> </font>
</para>
<para style="terp_default_9_followup_id">[[ format(get_text(o,data['form']['followup_id'])) ]]</para>
<pre style="terp_default_9_followup_id">[[ format(get_text(o,data['form']['followup_id'])) ]]</pre>
<para style="terp_default_9">
<font color="white"> </font>
</para>
+3
View File
@@ -99,6 +99,9 @@ class CompanyLDAP(osv.osv):
filter = filter_format(conf['ldap_filter'], (login,))
try:
results = self.query(conf, filter)
# Get rid of (None, attrs) for searchResultReference replies
results = [i for i in results if i[0]]
if results and len(results) == 1:
dn = results[0][0]
conn = self.connect(conf)
+2 -2
View File
@@ -1,6 +1,6 @@
import logging
import urllib
import werkzeug.urls
import urlparse
import urllib2
import simplejson
@@ -25,7 +25,7 @@ class res_users(osv.Model):
]
def _auth_oauth_rpc(self, cr, uid, endpoint, access_token, context=None):
params = urllib.urlencode({'access_token': access_token})
params = werkzeug.url_encode({'access_token': access_token})
if urlparse.urlparse(endpoint)[4]:
url = endpoint + '&' + params
else:
+6 -6
View File
@@ -1330,13 +1330,13 @@ class calendar_event(osv.Model):
new_id = get_real_ids(arg[2])
new_arg = (arg[0], arg[1], new_id)
new_args.append(new_arg)
#offset, limit and count must be treated separately as we may need to deal with virtual ids
if context.get('virtual_id', True):
res = super(calendar_event, self).search(cr, uid, new_args, offset=0, limit=0, order=None, context=context, count=False)
res = self.get_recurrent_ids(cr, uid, res, args, order=order, context=context)
else:
res = super(calendar_event, self).search(cr, uid, new_args, offset=0, limit=0, order=order, context=context, count=False)
if not context.get('virtual_id', True):
return super(calendar_event, self).search(cr, uid, new_args, offset=offset, limit=limit, order=order, context=context, count=count)
# offset, limit, order and count must be treated separately as we may need to deal with virtual ids
res = super(calendar_event, self).search(cr, uid, new_args, offset=0, limit=0, order=None, context=context, count=False)
res = self.get_recurrent_ids(cr, uid, res, args, order=order, context=context)
if count:
return len(res)
elif limit:
+1 -1
View File
@@ -313,7 +313,7 @@
<field name="name">Meetings</field>
<field name="res_model">calendar.event</field>
<field name="view_mode">form,calendar,tree,gantt</field>
<field name="view_id" ref="action_view_calendar_event_form"/>
<field name="view_id" ref="view_calendar_event_form"/>
</record>
@@ -50,9 +50,9 @@ class decimal_precision(orm.Model):
self.precision_get.clear_cache(self)
for obj in self.pool.obj_list():
for colname, col in self.pool.get(obj)._columns.items():
if isinstance(col, (fields.float, fields.function)):
if hasattr(col, 'digits_change'):
col.digits_change(cr)
RegistryManager.signal_registry_change(cr.dbname)
RegistryManager.signal_caches_change(cr.dbname)
def create(self, cr, uid, data, context=None):
res = super(decimal_precision, self).create(cr, uid, data, context=context)
@@ -262,6 +262,7 @@ class abstracted_fs(object):
if path == '/' and mode in ('list', 'cwd'):
return (None, None, None )
if path == '..': path = self.cwd + '/..'
path = _to_unicode(os.path.normpath(path)) # again, for '/db/../ss'
if path == '.': path = ''
+2 -2
View File
@@ -417,8 +417,8 @@ class email_template(osv.osv):
# create a mail_mail based on values, without attachments
values = self.generate_email(cr, uid, template_id, res_id, context=context)
assert values.get('email_from'), 'email_from is missing or empty after template rendering, send_mail() cannot proceed'
if not values.get('email_from'):
raise osv.except_osv(_('Warning!'),_("Sender email is missing or empty after template rendering. Specify one to deliver your message"))
# process partner_to field that is a comma separated list of partner_ids -> recipient_ids
# NOTE: only usable if force_send is True, because otherwise the value is
# not stored on the mail_mail, and therefore lost -> fixed in v8
+8 -8
View File
@@ -24,7 +24,7 @@ from openerp import SUPERUSER_ID
from openerp.tools.translate import _
from openerp.addons.web.http import request
import urllib
import werkzeug.urls
import urllib2
import simplejson
@@ -43,7 +43,7 @@ class google_service(osv.osv_memory):
#Get the Refresh Token From Google And store it in ir.config_parameter
headers = {"Content-type": "application/x-www-form-urlencoded"}
data = dict(code=authorization_code, client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, grant_type="authorization_code")
data = urllib.urlencode(data)
data = werkzeug.url_encode(data)
try:
req = urllib2.Request("https://accounts.google.com/o/oauth2/token", data, headers)
content = urllib2.urlopen(req).read()
@@ -62,7 +62,7 @@ class google_service(osv.osv_memory):
'response_type': 'code',
'client_id': ir_config.get_param(cr, SUPERUSER_ID, 'google_%s_client_id' % service),
}
uri = 'https://accounts.google.com/o/oauth2/auth?%s' % urllib.urlencode(params)
uri = 'https://accounts.google.com/o/oauth2/auth?%s' % werkzeug.url_encode(params)
return uri
#If no scope is passed, we use service by default to get a default scope
@@ -83,7 +83,7 @@ class google_service(osv.osv_memory):
'access_type':'offline'
}
uri = self.get_uri_oauth(a='auth') + "?%s" % urllib.urlencode(params)
uri = self.get_uri_oauth(a='auth') + "?%s" % werkzeug.url_encode(params)
return uri
def _get_google_token_json(self, cr, uid, authorize_code, service, context=None):
@@ -103,7 +103,7 @@ class google_service(osv.osv_memory):
headers = {"content-type": "application/x-www-form-urlencoded"}
try:
data = urllib.urlencode(params)
data = werkzeug.url_encode(params)
req = urllib2.Request(self.get_uri_oauth(a='token'), data, headers)
content = urllib2.urlopen(req).read()
@@ -128,7 +128,7 @@ class google_service(osv.osv_memory):
headers = {"content-type": "application/x-www-form-urlencoded"}
try:
data = urllib.urlencode(params)
data = werkzeug.url_encode(params)
req = urllib2.Request(self.get_uri_oauth(a='token'), data, headers)
content = urllib2.urlopen(req).read()
res = simplejson.loads(content)
@@ -139,12 +139,12 @@ class google_service(osv.osv_memory):
def _do_request(self,cr,uid,uri,params={},headers={},type='POST', context=None):
_logger.debug("Uri: %s - Type : %s - Headers: %s - Params : %s !" % (uri,type,headers,urllib.urlencode(params) if type =='GET' else params))
_logger.debug("Uri: %s - Type : %s - Headers: %s - Params : %s !" % (uri,type,headers,werkzeug.url_encode(params) if type =='GET' else params))
res = False
try:
if type.upper() == 'GET' or type.upper() == 'DELETE':
data = urllib.urlencode(params)
data = werkzeug.url_encode(params)
req = urllib2.Request(self.get_uri_api() + uri + "?" + data)
elif type.upper() == 'POST' or type.upper() == 'PATCH' or type.upper() == 'PUT':
req = urllib2.Request(self.get_uri_api() + uri, params, headers)
+2 -2
View File
@@ -23,7 +23,7 @@ from openerp import SUPERUSER_ID
from openerp.osv import fields, osv
from openerp.tools.translate import _
import urllib
import werkzeug.urls
import urllib2
import json
import re
@@ -70,7 +70,7 @@ class config(osv.Model):
google_drive_client_secret = ir_config.get_param(cr, SUPERUSER_ID, 'google_drive_client_secret')
#For Getting New Access Token With help of old Refresh Token
data = urllib.urlencode(dict(client_id=google_drive_client_id,
data = werkzeug.url_encode(dict(client_id=google_drive_client_id,
refresh_token=google_drive_refresh_token,
client_secret=google_drive_client_secret,
grant_type="refresh_token",
@@ -23,7 +23,7 @@ import simplejson
import logging
from lxml import etree
import re
import urllib
import werkzeug.urls
import urllib2
from openerp.osv import osv
@@ -89,7 +89,7 @@ class config(osv.osv):
try:
req = urllib2.Request(
'https://spreadsheets.google.com/feeds/cells/%s/od6/private/full/batch?%s' % (spreadsheet_key, urllib.urlencode({'v': 3, 'access_token': access_token})),
'https://spreadsheets.google.com/feeds/cells/%s/od6/private/full/batch?%s' % (spreadsheet_key, werkzeug.url_encode({'v': 3, 'access_token': access_token})),
data=request,
headers={'content-type': 'application/atom+xml', 'If-Match': '*'})
urllib2.urlopen(req)
+6 -10
View File
@@ -21,6 +21,7 @@ except ImportError:
try:
from .. import escpos
from ..escpos import printer
from ..escpos import supported_devices
except ImportError:
escpos = printer = None
@@ -39,21 +40,16 @@ class EscposDriver(Thread):
self.queue = Queue()
self.status = {'status':'connecting', 'messages':[]}
self.supported_printers = [
{ 'vendor' : 0x04b8, 'product' : 0x0e03, 'name' : 'Epson TM-T20' },
{ 'vendor' : 0x04b8, 'product' : 0x0202, 'name' : 'Epson TM-T70' },
]
def connected_usb_devices(self,devices):
def connected_usb_devices(self):
connected = []
for device in devices:
for device in supported_devices.device_list:
if usb.core.find(idVendor=device['vendor'], idProduct=device['product']) != None:
connected.append(device)
return connected
def get_escpos_printer(self):
try:
printers = self.connected_usb_devices(self.supported_printers)
printers = self.connected_usb_devices()
if len(printers) > 0:
self.set_status('connected','Connected to '+printers[0]['name'])
return escpos.printer.Usb(printers[0]['vendor'], printers[0]['product'])
@@ -267,12 +263,12 @@ hw_proxy.drivers['escpos'] = driver
class EscposProxy(hw_proxy.Proxy):
@http.route('/hw_proxy/open_cashbox', type='json', auth='admin')
@http.route('/hw_proxy/open_cashbox', type='json', auth='none')
def open_cashbox(self):
_logger.info('ESC/POS: OPEN CASHBOX')
driver.push_task('cashbox')
@http.route('/hw_proxy/print_receipt', type='json', auth='admin')
@http.route('/hw_proxy/print_receipt', type='json', auth='none')
def print_receipt(self, receipt):
_logger.info('ESC/POS: PRINT RECEIPT')
driver.push_task('receipt',receipt)
+1 -1
View File
@@ -1 +1 @@
__all__ = ["constants","escpos","exceptions","printer"]
__all__ = ["constants","escpos","exceptions","printer","supported_devices"]
@@ -0,0 +1,10 @@
#!/usr/bin/python
# This is a list of esc/pos compatible usb printers. The vendor and product ids can be found by
# typing lsusb in a linux terminal, this will give you the ids in the form ID VENDOR:PRODUCT
device_list = [
{ 'vendor' : 0x04b8, 'product' : 0x0e03, 'name' : 'Epson TM-T20' },
{ 'vendor' : 0x04b8, 'product' : 0x0202, 'name' : 'Epson TM-T70' },
]
+24 -24
View File
@@ -33,7 +33,7 @@ class Proxy(http.Controller):
statuses[driver] = drivers[driver].get_status()
return statuses
@http.route('/hw_proxy/hello', type='http', auth='admin')
@http.route('/hw_proxy/hello', type='http', auth='none')
def hello(self):
return request.make_response('ping', {
'Cache-Control': 'no-cache',
@@ -42,11 +42,11 @@ class Proxy(http.Controller):
'Access-Control-Allow-Methods': 'GET',
})
@http.route('/hw_proxy/handshake', type='json', auth='admin')
@http.route('/hw_proxy/handshake', type='json', auth='none')
def handshake(self):
return True
@http.route('/hw_proxy/status', type='http', auth='admin')
@http.route('/hw_proxy/status', type='http', auth='none')
def status_http(self):
resp = '<html>\n<body>\n<h1>Hardware Proxy Status</h1>\n'
statuses = self.get_status()
@@ -75,39 +75,39 @@ class Proxy(http.Controller):
'Access-Control-Allow-Methods': 'GET',
})
@http.route('/hw_proxy/status_json', type='json', auth='admin')
@http.route('/hw_proxy/status_json', type='json', auth='none')
def status_json(self):
return self.get_status()
@http.route('/hw_proxy/scan_item_success', type='json', auth='admin')
@http.route('/hw_proxy/scan_item_success', type='json', auth='none')
def scan_item_success(self, ean):
"""
A product has been scanned with success
"""
print 'scan_item_success: ' + str(ean)
@http.route('/hw_proxy/scan_item_error_unrecognized', type='json', auth='admin')
@http.route('/hw_proxy/scan_item_error_unrecognized', type='json', auth='none')
def scan_item_error_unrecognized(self, ean):
"""
A product has been scanned without success
"""
print 'scan_item_error_unrecognized: ' + str(ean)
@http.route('/hw_proxy/help_needed', type='json', auth='admin')
@http.route('/hw_proxy/help_needed', type='json', auth='none')
def help_needed(self):
"""
The user wants an help (ex: light is on)
"""
print "help_needed"
@http.route('/hw_proxy/help_canceled', type='json', auth='admin')
@http.route('/hw_proxy/help_canceled', type='json', auth='none')
def help_canceled(self):
"""
The user stops the help request
"""
print "help_canceled"
@http.route('/hw_proxy/weighting_start', type='json', auth='admin')
@http.route('/hw_proxy/weighting_start', type='json', auth='none')
def weighting_start(self):
if self.scale == 'closed':
print "Opening (Fake) Connection to Scale..."
@@ -118,7 +118,7 @@ class Proxy(http.Controller):
else:
print "WARNING: Scale already Connected !!!"
@http.route('/hw_proxy/weighting_read_kg', type='json', auth='admin')
@http.route('/hw_proxy/weighting_read_kg', type='json', auth='none')
def weighting_read_kg(self):
if self.scale == 'open':
print "Reading Scale..."
@@ -130,7 +130,7 @@ class Proxy(http.Controller):
print "WARNING: Reading closed scale !!!"
return 0.0
@http.route('/hw_proxy/weighting_end', type='json', auth='admin')
@http.route('/hw_proxy/weighting_end', type='json', auth='none')
def weighting_end(self):
if self.scale == 'open':
print "Closing Connection to Scale ..."
@@ -141,7 +141,7 @@ class Proxy(http.Controller):
else:
print "WARNING: Scale already Closed !!!"
@http.route('/hw_proxy/payment_request', type='json', auth='admin')
@http.route('/hw_proxy/payment_request', type='json', auth='none')
def payment_request(self, price):
"""
The PoS will activate the method payment
@@ -149,55 +149,55 @@ class Proxy(http.Controller):
print "payment_request: price:"+str(price)
return 'ok'
@http.route('/hw_proxy/payment_status', type='json', auth='admin')
@http.route('/hw_proxy/payment_status', type='json', auth='none')
def payment_status(self):
print "payment_status"
return { 'status':'waiting' }
@http.route('/hw_proxy/payment_cancel', type='json', auth='admin')
@http.route('/hw_proxy/payment_cancel', type='json', auth='none')
def payment_cancel(self):
print "payment_cancel"
@http.route('/hw_proxy/transaction_start', type='json', auth='admin')
@http.route('/hw_proxy/transaction_start', type='json', auth='none')
def transaction_start(self):
print 'transaction_start'
@http.route('/hw_proxy/transaction_end', type='json', auth='admin')
@http.route('/hw_proxy/transaction_end', type='json', auth='none')
def transaction_end(self):
print 'transaction_end'
@http.route('/hw_proxy/cashier_mode_activated', type='json', auth='admin')
@http.route('/hw_proxy/cashier_mode_activated', type='json', auth='none')
def cashier_mode_activated(self):
print 'cashier_mode_activated'
@http.route('/hw_proxy/cashier_mode_deactivated', type='json', auth='admin')
@http.route('/hw_proxy/cashier_mode_deactivated', type='json', auth='none')
def cashier_mode_deactivated(self):
print 'cashier_mode_deactivated'
@http.route('/hw_proxy/open_cashbox', type='json', auth='admin')
@http.route('/hw_proxy/open_cashbox', type='json', auth='none')
def open_cashbox(self):
print 'open_cashbox'
@http.route('/hw_proxy/print_receipt', type='json', auth='admin')
@http.route('/hw_proxy/print_receipt', type='json', auth='none')
def print_receipt(self, receipt):
print 'print_receipt' + str(receipt)
@http.route('/hw_proxy/is_scanner_connected', type='json', auth='admin')
@http.route('/hw_proxy/is_scanner_connected', type='json', auth='none')
def print_receipt(self, receipt):
print 'is_scanner_connected?'
return False
@http.route('/hw_proxy/scanner', type='json', auth='admin')
@http.route('/hw_proxy/scanner', type='json', auth='none')
def print_receipt(self, receipt):
print 'scanner'
time.sleep(10)
return ''
@http.route('/hw_proxy/log', type='json', auth='admin')
@http.route('/hw_proxy/log', type='json', auth='none')
def log(self, arguments):
_logger.info(' '.join(str(v) for v in arguments))
@http.route('/hw_proxy/print_pdf_invoice', type='json', auth='admin')
@http.route('/hw_proxy/print_pdf_invoice', type='json', auth='none')
def print_pdf_invoice(self, pdfinvoice):
print 'print_pdf_invoice' + str(pdfinvoice)
+6 -4
View File
@@ -109,8 +109,8 @@ class Scanner(Thread):
if not evdev:
return None
devices = [ device for device in listdir(self.input_dir)]
keyboards = [ device for device in devices if 'kbd' in device ]
scanners = [ device for device in devices if ('barcode' in device.lower()) or ('scanner' in device.lower()) ]
keyboards = [ device for device in devices if ('kbd' in device) and ('keyboard' not in device.lower())]
scanners = [ device for device in devices if ('barcode' in device.lower()) or ('scanner' in device.lower())]
if len(scanners) > 0:
self.set_status('connected','Connected to '+scanners[0])
return evdev.InputDevice(join(self.input_dir,scanners[0]))
@@ -124,7 +124,7 @@ class Scanner(Thread):
self.set_status('error',str(e))
return None
@http.route('/hw_proxy/Vis_scanner_connected', type='json', auth='admin')
@http.route('/hw_proxy/Vis_scanner_connected', type='json', auth='none')
def is_scanner_connected(self):
return self.get_device() != None
@@ -144,6 +144,8 @@ class Scanner(Thread):
return ''
def get_status(self):
if not s.isAlive():
s.start()
return self.status
def run(self):
@@ -205,7 +207,7 @@ s = Scanner()
hw_proxy.drivers['scanner'] = s
class ScannerDriver(hw_proxy.Proxy):
@http.route('/hw_proxy/scanner', type='json', auth='admin')
@http.route('/hw_proxy/scanner', type='json', auth='none')
def scanner(self):
if not s.isAlive():
s.start()
+24 -5
View File
@@ -185,8 +185,8 @@ class im_message(osv.osv):
def post(self, cr, uid, message, to_session_id, technical=False, uuid=None, context=None):
assert_uuid(uuid)
my_id = self.pool.get('im.user').get_my_id(cr, uid, uuid)
session = self.pool.get('im.session').browse(cr, uid, to_session_id, context)
to_ids = [x.id for x in session.user_ids if x.id != my_id]
session_user_ids = self.pool.get('im.session').get_session_users(cr, uid, to_session_id, context=context).get("user_ids", [])
to_ids = [user_id for user_id in session_user_ids if user_id != my_id]
self.create(cr, openerp.SUPERUSER_ID, {"message": message, 'from_id': my_id,
'to_id': [(6, 0, to_ids)], 'session_id': to_session_id, 'technical': technical}, context=context)
notify_channel(cr, "im_channel", {'type': 'message', 'receivers': [my_id] + to_ids})
@@ -202,7 +202,7 @@ class im_session(osv.osv):
return res
_columns = {
'user_ids': fields.many2many('im.user'),
'user_ids': fields.many2many('im.user', 'im_session_im_user_rel', 'im_session_id', 'im_user_id', 'Users'),
"name": fields.function(_calc_name, string="Name", type='char'),
}
@@ -225,6 +225,9 @@ class im_session(osv.osv):
}, context=context)
return self.read(cr, uid, session_id, context=context)
def get_session_users(self, cr, uid, session_id, context=None):
return self.read(cr, openerp.SUPERUSER_ID, session_id, ['user_ids'], context=context)
def add_to_session(self, cr, uid, session_id, user_id, uuid=None, context=None):
my_id = self.pool.get("im.user").get_my_id(cr, uid, uuid, context=context)
session = self.read(cr, uid, session_id, context=context)
@@ -259,7 +262,7 @@ class im_user(osv.osv):
return ['&', ('im_last_status', '=', True), ('im_last_status_update', '>', (current - delta).strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
else:
return ['|', ('im_last_status', '=', False), ('im_last_status_update', '<=', (current - delta).strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
# TODO: Remove fields arg in trunk. Also in im.js.
def search_users(self, cr, uid, text_search, fields, limit, context=None):
my_id = self.get_my_id(cr, uid, None, context)
group_employee = self.pool['ir.model.data'].get_object_reference(cr, uid, 'base', 'group_user')[1]
@@ -271,7 +274,7 @@ class im_user(osv.osv):
if len(found) < limit:
found += self.search(cr, uid, [["name", "ilike", text_search], ["id", "<>", my_id], ["uuid", "=", False], ["im_status", "=", False], ["id", "not in", found]],
order="name asc", limit=limit-len(found), context=context)
users = self.read(cr, uid, found, fields, context=context)
users = self.read(cr,openerp.SUPERUSER_ID, found, ["name", "user_id", "uuid", "im_status"], context=context)
users.sort(key=lambda obj: found.index(obj['id']))
return users
@@ -319,6 +322,9 @@ class im_user(osv.osv):
continue
return res
def get_users(self, cr, uid, ids, context=None):
return self.read(cr,openerp.SUPERUSER_ID, ids, ["name", "im_status", "uuid"], context=context)
_columns = {
'name': fields.function(_get_name, type='char', size=200, string="Name", store=True, readonly=True),
'assigned_name': fields.char(string="Assigned Name", size=200, required=False),
@@ -341,3 +347,16 @@ class im_user(osv.osv):
('user_uniq', 'unique (user_id)', 'Only one chat user per OpenERP user.'),
('uuid_uniq', 'unique (uuid)', 'Chat identifier already used.'),
]
class res_users(osv.osv):
_inherit = "res.users"
def _get_im_user(self, cr, uid, ids, field_name, arg, context=None):
result = dict.fromkeys(ids, False)
for index, im_user in enumerate(self.pool['im.user'].search_read(cr, uid, domain=[('user_id', 'in', ids)], fields=['name', 'user_id'], context=context)):
result[ids[index]] = im_user.get('user_id') and (im_user['user_id'][0], im_user['name']) or False
return result
_columns = {
'im_user_id' : fields.function(_get_im_user, type='many2one', string="IM User", relation="im.user"),
}
+2 -2
View File
@@ -2,10 +2,10 @@
<openerp>
<data>
<record id="message_rule_1" model="ir.rule">
<field name="name">Can only read messages that you sent or messages sent to you</field>
<field name="name">Can only read messages from a session where user is</field>
<field name="model_id" ref="model_im_message"/>
<field name="groups" eval="[(6,0,[ref('base.group_user')])]"/>
<field name="domain_force">["|", ('to_id.user_id', 'in', [user.id]), ('from_id.user_id', '=', user.id)]</field>
<field name="domain_force">[('session_id.user_ids', 'in', user.im_user_id.id)]</field>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="0"/>
<field name="perm_create" eval="0"/>
+1 -1
View File
@@ -1,4 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_im_message,im.message,model_im_message,base.group_user,1,0,1,0
access_im_message,im.message,model_im_message,,1,0,1,0
access_im_user,im.user,model_im_user,,1,1,1,0
access_im_session,im.session,model_im_session,,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_im_message im.message model_im_message base.group_user 1 0 1 0
3 access_im_user im.user model_im_user 1 1 1 0
4 access_im_session im.session model_im_session 1 0 0 0
+1
View File
@@ -92,6 +92,7 @@
search_changed: function(e) {
var users = new instance.web.Model("im.user");
var self = this;
// TODO: Remove fields arg in trunk. Also in im.js.
return this.user_search_dm.add(users.call("search_users", [this.get("current_search"), ["name", "user_id", "uuid", "im_status"],
USERS_LIMIT], {context:new instance.web.CompoundContext()})).then(function(users) {
var logged_users = _.filter(users, function(u) { return !!u.im_status; });
+30 -7
View File
@@ -145,7 +145,7 @@ function declare($, _, openerp) {
if (_.size(no_cache) === 0)
def = $.when();
else
def = im_common.connection.model("im.user").call("read", [_.values(no_cache), []]).then(function(users) {
def = im_common.connection.model("im.user").call("get_users", [_.values(no_cache)]).then(function(users) {
self.add_to_user_cache(users);
});
return def.then(function() {
@@ -230,7 +230,8 @@ function declare($, _, openerp) {
return self.chat_with_users(users);
});
},
activate_session: function(session_id, focus) {
activate_session: function(session_id, focus, message) {
var self = this;
var conv = _.find(this.conversations, function(conv) {return conv.session_id == session_id;});
var def = $.when();
if (! conv) {
@@ -244,6 +245,9 @@ function declare($, _, openerp) {
this.calc_positions();
this.trigger("new_conversation", conv);
}, this));
def = def.then(function(){
return self.load_history(conv, message);
});
}
if (focus) {
def = def.then(function() {
@@ -252,13 +256,32 @@ function declare($, _, openerp) {
}
return def.then(function() {return conv});
},
received_messages: function(messages) {
load_history: function(conv, message){
var self = this;
var domain = [["session_id", "=", conv.session_id]];
if (!_.isUndefined(message)){
domain.push(["date", "<", message.date]);
}
return im_common.connection.model("im.message").call("search_read", [domain, [], 0, 10]).then(function(messages){
messages.reverse();
var users = _.unique(_.map(messages, function(message){
return message.from_id[0];
}));
return self.ensure_users(users).then(function(){
return self.received_messages(messages, true);
});
});
},
received_messages: function(messages, seen) {
var self = this;
var defs = [];
var received = false;
if (_.isUndefined(seen)){
seen = false;
}
_.each(messages, function(message) {
if (! message.technical) {
defs.push(self.activate_session(message.session_id[0]).then(function(conv) {
defs.push(self.activate_session(message.session_id[0], false, message).then(function(conv) {
received = self.my_id !== message.from_id[0];
return conv.received_message(message);
}));
@@ -269,7 +292,7 @@ function declare($, _, openerp) {
}
});
return $.when.apply($, defs).then(function(){
if (! self.get("window_focus") && received) {
if (! self.get("window_focus") && received && !seen) {
self.set("waiting_messages", self.get("waiting_messages") + messages.length);
self.ting.play();
self.create_ting();
@@ -368,7 +391,7 @@ function declare($, _, openerp) {
refresh_users: function() {
var self = this;
var user_ids;
return im_common.connection.model("im.session").call("read", [self.session_id]).then(function(session) {
return im_common.connection.model("im.session").call("get_session_users", [self.session_id]).then(function(session) {
user_ids = _.without(session.user_ids, self.c_manager.me.get("id"));
return self.c_manager.ensure_users(user_ids);
}).then(function(users) {
@@ -449,7 +472,7 @@ function declare($, _, openerp) {
date = "" + zpad(date.getHours(), 2) + ":" + zpad(date.getMinutes(), 2);
var to_show = _.map(items, im_common.escape_keep_url);
this.last_bubble = $(openerp.qweb.render("im_common.conversation_bubble", {"items": to_show, "user": user, "time": date}));
$(this.$(".oe_im_chatview_content").children()[0]).append(this.last_bubble);
$(this.$(".oe_im_chatview_conversation")).append(this.last_bubble);
this._go_bottom();
},
_go_bottom: function() {
+1 -1
View File
@@ -11,7 +11,7 @@
All users are offline. They will receive your messages on their next connection.
</div>
<div class="oe_im_chatview_content">
<div></div>
<div class="oe_im_chatview_conversation"></div>
</div>
<div class="oe_im_chatview_footer">
<input class="oe_im_chatview_input" t-att-placeholder="widget.inputPlaceholder" />
+2 -2
View File
@@ -171,8 +171,8 @@ class im_livechat_channel(osv.osv):
return users
def get_session(self, cr, uid, channel_id, uuid, context=None):
my_id = self.pool.get("im.user").get_my_id(cr, uid, uuid, context=context)
users = self.get_available_users(cr, uid, channel_id, context=context)
self.pool.get("im.user").get_my_id(cr, uid, uuid, context=context)
users = self.get_available_users(cr, openerp.SUPERUSER_ID, channel_id, context=context)
if len(users) == 0:
return False
user_id = random.choice(users).id
+17
View File
@@ -125,6 +125,7 @@
<field name="res_model">im.message</field>
<field name="view_mode">list</field>
<field name="domain">[('session_id.channel_id', '!=', None)]</field>
<field name="context">{'search_default_group_by_session_id': 1, 'search_default_group_by_date': 1, 'search_default_session_id': 1}</field>
</record>
<menuitem name="History" parent="im_livechat" id="history" action="action_history" groups="group_im_livechat_manager"/>
@@ -141,5 +142,21 @@
</field>
</record>
<record id="im_message_search" model="ir.ui.view">
<field name="name">im.message.search</field>
<field name="model">im.message</field>
<field name="arch" type="xml">
<search string="Search history">
<filter name="session_id" string="My Sessions" domain="[('session_id.user_ids','in', uid)]"/>
<field name="from_id"/>
<field name="to_id"/>
<group expand="0" string="Group By...">
<filter name="group_by_session_id" string="Session" domain="[]" context="{'group_by':'session_id'}"/>
<filter name="group_by_date" string="Date" domain="[]" context="{'group_by':'date'}"/>
</group>
</search>
</field>
</record>
</data>
</openerp>
@@ -2,7 +2,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_ls_chann1,im_livechat.channel,model_im_livechat_channel,,1,0,0,0
access_ls_chann2,im_livechat.channel,model_im_livechat_channel,group_im_livechat,1,1,1,0
access_ls_chann3,im_livechat.channel,model_im_livechat_channel,group_im_livechat_manager,1,1,1,1
access_ls_message_portal,im_livechat.im.message.portal,im.model_im_message,base.group_portal,0,0,0,0
access_im_user_portal,im_livechat.im.user.portal,im.model_im_user,base.group_portal,1,0,0,0
access_ls_message,im_livechat.im.message,im.model_im_message,base.group_public,0,0,0,0
access_im_user,im_livechat.im.user,im.model_im_user,base.group_public,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_ls_chann1 im_livechat.channel model_im_livechat_channel 1 0 0 0
3 access_ls_chann2 im_livechat.channel model_im_livechat_channel group_im_livechat 1 1 1 0
4 access_ls_chann3 im_livechat.channel model_im_livechat_channel group_im_livechat_manager 1 1 1 1
access_ls_message_portal im_livechat.im.message.portal im.model_im_message base.group_portal 0 0 0 0
5 access_im_user_portal im_livechat.im.user.portal im.model_im_user base.group_portal 1 0 0 0
access_ls_message im_livechat.im.message im.model_im_message base.group_public 0 0 0 0
6 access_im_user im_livechat.im.user im.model_im_user base.group_public 1 0 0 0
@@ -107,11 +107,14 @@ define(["openerp", "im_common", "underscore", "require", "jquery",
}
self.manager.activate_session(session_id, true).then(function(conv) {
if (self.options.defaultMessage) {
conv.received_message({
message: self.options.defaultMessage,
date: openerp.datetime_to_str(new Date()),
from_id: [conv.get("users")[0].get("id"), "Unknown"]
});
setTimeout(function(){
conv.received_message({
message: self.options.defaultMessage,
date: openerp.datetime_to_str(new Date()),
from_id: [conv.get("users")[0].get("id"), "Unknown"]
});
},
2500);
}
});
});
+1 -1
View File
@@ -83,7 +83,7 @@ class res_users(osv.Model):
def copy_data(self, *args, **kwargs):
data = super(res_users, self).copy_data(*args, **kwargs)
if data.get('alias_name'):
if data and data.get('alias_name'):
data['alias_name'] = data['login']
return data
+2 -2
View File
@@ -2,7 +2,7 @@
import datetime
import logging
import sys
import urllib
import werkzeug.urls
import urllib2
import openerp
@@ -59,7 +59,7 @@ def get_sys_logs(self, cr, uid):
add_arg = {"timeout":30} if sys.version_info >= (2,6) else {}
arguments = {'arg0': msg, "action": "update",}
arguments_raw = urllib.urlencode(arguments)
arguments_raw = werkzeug.url_encode(arguments)
url = config.get("publisher_warranty_url")
+2 -2
View File
@@ -322,9 +322,9 @@ class mail_compose_message(osv.TransientModel):
mail_values['email_from'] = email_dict.pop('email_from')
# replies redirection: mass mailing only
if not wizard.same_thread:
mail_values['reply_to'] = email_dict.pop('reply_to')
mail_values['reply_to'] = email_dict.pop('reply_to', None)
else:
email_dict.pop('reply_to')
email_dict.pop('reply_to', None)
mail_values.update(email_dict)
# mass mailing without post: mail_mail values
if mass_mail_mode and not wizard.post:
+4 -3
View File
@@ -1,11 +1,12 @@
from openerp import http
from openerp import http, SUPERUSER_ID
from openerp.http import request
class MassMailController(http.Controller):
@http.route('/mail/track/<int:mail_id>/blank.gif', type='http', auth='admin')
@http.route('/mail/track/<int:mail_id>/blank.gif', type='http', auth='none')
def track_mail_open(self, mail_id):
""" Email tracking. """
request.disable_db = False
mail_mail_stats = request.registry.get('mail.mail.statistics')
mail_mail_stats.set_opened(request.cr, request.uid, mail_mail_ids=[mail_id])
mail_mail_stats.set_opened(request.cr, SUPERUSER_ID, mail_mail_ids=[mail_id])
return "data:image/gif;base64,R0lGODlhAQABAIAAANvf7wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
+5 -6
View File
@@ -320,10 +320,9 @@ class mrp_bom(osv.osv):
"""
routing_obj = self.pool.get('mrp.routing')
factor = factor / (bom.product_efficiency or 1.0)
max_rounding = max(bom.product_rounding, bom.product_uom.rounding)
factor = rounding(factor, max_rounding)
if factor < max_rounding:
factor = max_rounding
factor = rounding(factor, bom.product_rounding)
if factor < bom.product_rounding:
factor = bom.product_rounding
result = []
result2 = []
phantom = False
@@ -772,7 +771,7 @@ class mrp_production(osv.osv):
# qty available for consume and produce
qty_avail = scheduled.product_qty - consumed_data.get(scheduled.product_id.id, 0.0)
if qty_avail <= 0.0:
if float_compare(qty_avail, 0, precision_rounding=scheduled.product_id.uom_id.rounding) <= 0:
# there will be nothing to consume for this raw material
continue
@@ -784,7 +783,7 @@ class mrp_production(osv.osv):
# if qtys we have to consume is more than qtys available to consume
prod_name = scheduled.product_id.name_get()[0][1]
raise osv.except_osv(_('Warning!'), _('You are going to consume total %s quantities of "%s".\nBut you can only consume up to total %s quantities.') % (qty, prod_name, qty_avail))
if qty <= 0.0:
if float_compare(qty, 0, precision_rounding=scheduled.product_id.uom_id.rounding) <= 0:
# we already have more qtys consumed than we need
continue
+2 -2
View File
@@ -764,7 +764,7 @@
<field name="workcenter_id"/>
<field name="sequence"/>
<field name="cycle"/>
<field name="hour"/>
<field name="hour" widget="float_time"/>
</group>
</form>
<tree string="Production Work Centers">
@@ -772,7 +772,7 @@
<field name="name"/>
<field name="workcenter_id"/>
<field name="cycle"/>
<field name="hour"/>
<field name="hour" widget="float_time"/>
</tree>
</field>
</page>
@@ -51,7 +51,7 @@
<field name="name"/>
<field name="workcenter_id" widget="selection"/>
<field name="cycle"/>
<field name="hour"/>
<field name="hour" widget="float_time"/>
<field name="state" />
</tree>
</field>
+2 -2
View File
@@ -1,7 +1,7 @@
"""Module to talk to EtherpadLite API."""
import json
import urllib
import werkzeug.urls
import urllib2
@@ -32,7 +32,7 @@ class EtherpadLiteClient:
params = arguments or {}
params.update({'apikey': self.apiKey})
data = urllib.urlencode(params, True)
data = werkzeug.url_encode(params, True)
try:
opener = urllib2.build_opener()
+5 -4
View File
@@ -8,8 +8,8 @@ import logging
import pprint
import werkzeug
from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp import http, SUPERUSER_ID
from openerp.http import request
_logger = logging.getLogger(__name__)
@@ -19,12 +19,13 @@ class AdyenController(http.Controller):
@http.route([
'/payment/adyen/return/',
], type='http', auth='admin')
], type='http', auth='none')
def adyen_return(self, pspReference, **post):
""" Paypal IPN."""
request.disable_db = False
post["pspReference"] = pspReference
_logger.info('Beginning Adyen form_feedback with post data %s', pprint.pformat(post)) # debug
request.registry['payment.transaction'].form_feedback(request.cr, request.uid, post, 'adyen', context=request.context)
request.registry['payment.transaction'].form_feedback(request.cr, SUPERUSER_ID, post, 'adyen', context=request.context)
return_url = post.pop('return_url', '')
if not return_url:
custom = json.loads(post.pop('merchantReturnData', '{}'))
+5 -4
View File
@@ -3,8 +3,8 @@ import logging
import pprint
import werkzeug
from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp import http, SUPERUSER_ID
from openerp.http import request
_logger = logging.getLogger(__name__)
@@ -20,10 +20,11 @@ class OgoneController(http.Controller):
'/payment/ogone/decline', '/payment/ogone/test/decline',
'/payment/ogone/exception', '/payment/ogone/test/exception',
'/payment/ogone/cancel', '/payment/ogone/test/cancel',
], type='http', auth='admin')
], type='http', auth='none')
def ogone_form_feedback(self, **post):
""" Ogone contacts using GET, at least for accept """
request.disable_db = False
_logger.info('Ogone: entering form_feedback with post data %s', pprint.pformat(post)) # debug
cr, uid, context = request.cr, request.uid, request.context
cr, uid, context = request.cr, SUPERUSER_ID, request.context
request.registry['payment.transaction'].form_feedback(cr, uid, post, 'ogone', context=context)
return werkzeug.utils.redirect(post.pop('return_url', '/'))
+11 -9
View File
@@ -6,12 +6,11 @@ except ImportError:
import json
import logging
import pprint
import urllib
import urllib2
import werkzeug
from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp import http, SUPERUSER_ID
from openerp.http import request
_logger = logging.getLogger(__name__)
@@ -41,12 +40,12 @@ class PaypalController(http.Controller):
Once data is validated, process it. """
res = False
new_post = dict(post, cmd='_notify-validate')
urequest = urllib2.Request("https://www.sandbox.paypal.com/cgi-bin/webscr", urllib.urlencode(new_post))
urequest = urllib2.Request("https://www.sandbox.paypal.com/cgi-bin/webscr", werkzeug.url_encode(new_post))
uopen = urllib2.urlopen(urequest)
resp = uopen.read()
if resp == 'VERIFIED':
_logger.info('Paypal: validated data')
cr, uid, context = request.cr, request.uid, request.context
cr, uid, context = request.cr, SUPERUSER_ID, request.context
res = request.registry['payment.transaction'].form_feedback(cr, uid, post, 'paypal', context=context)
elif resp == 'INVALID':
_logger.warning('Paypal: answered INVALID on data verification')
@@ -54,25 +53,28 @@ class PaypalController(http.Controller):
_logger.warning('Paypal: unrecognized paypal answer, received %s instead of VERIFIED or INVALID' % resp.text)
return res
@http.route('/payment/paypal/ipn/', type='http', auth='admin', methods=['POST'])
@http.route('/payment/paypal/ipn/', type='http', auth='none', methods=['POST'])
def paypal_ipn(self, **post):
""" Paypal IPN. """
request.disable_db = False
_logger.info('Beginning Paypal IPN form_feedback with post data %s', pprint.pformat(post)) # debug
self.paypal_validate_data(**post)
return ''
@http.route('/payment/paypal/dpn', type='http', auth="admin", methods=['POST'])
@http.route('/payment/paypal/dpn', type='http', auth="none", methods=['POST'])
def paypal_dpn(self, **post):
""" Paypal DPN """
request.disable_db = False
_logger.info('Beginning Paypal DPN form_feedback with post data %s', pprint.pformat(post)) # debug
return_url = self._get_return_url(**post)
self.paypal_validate_data(**post)
return werkzeug.utils.redirect(return_url)
@http.route('/payment/paypal/cancel', type='http', auth="admin")
@http.route('/payment/paypal/cancel', type='http', auth="none")
def paypal_cancel(self, **post):
""" When the user cancels its Paypal payment: GET on this route """
cr, uid, context = request.cr, request.uid, request.context
request.disable_db = False
cr, uid, context = request.cr, SUPERUSER_ID, request.context
_logger.info('Beginning Paypal cancel with post data %s', pprint.pformat(post)) # debug
return_url = self._get_return_url(**post)
return werkzeug.utils.redirect(return_url)
+2 -2
View File
@@ -7,7 +7,7 @@ except ImportError:
import json
import logging
import urlparse
import urllib
import werkzeug.urls
import urllib2
from openerp.addons.payment.models.payment_acquirer import ValidationError
@@ -134,7 +134,7 @@ class AcquirerPaypal(osv.Model):
password manager
"""
res = dict.fromkeys(ids, False)
parameters = urllib.urlencode({'grant_type': 'client_credentials'})
parameters = werkzeug.url_encode({'grant_type': 'client_credentials'})
for acquirer in self.browse(cr, uid, ids, context=context):
tx_url = self._get_paypal_urls(cr, uid, acquirer.env)['paypal_rest_url']
+5 -4
View File
@@ -3,8 +3,8 @@ import logging
import pprint
import werkzeug
from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp import http, SUPERUSER_ID
from openerp.http import request
_logger = logging.getLogger(__name__)
@@ -14,9 +14,10 @@ class OgoneController(http.Controller):
@http.route([
'/payment/transfer/feedback',
], type='http', auth='admin')
], type='http', auth='none')
def transfer_form_feedback(self, **post):
cr, uid, context = request.cr, request.uid, request.context
request.disable_db = False
cr, uid, context = request.cr, SUPERUSER_ID, request.context
_logger.info('Beginning form_feedback with post data %s', pprint.pformat(post)) # debug
request.registry['payment.transaction'].form_feedback(cr, uid, post, 'transfer', context)
return werkzeug.utils.redirect(post.pop('return_url', '/'))
+16 -5
View File
@@ -93,8 +93,9 @@ class plugin_handler(osv.osv_memory):
"""
mail_message = self.pool.get('mail.message')
model_obj = self.pool[model]
msg = self.pool.get('mail.thread').message_parse(cr, uid, email)
message_id = msg.get('message-id')
mail_thread_obj = self.pool.get('mail.thread')
msg = mail_thread_obj.message_parse(cr, uid, email)
message_id = msg.get('message_id')
mail_ids = mail_message.search(cr, uid, [('message_id', '=', message_id), ('res_id', '=', res_id), ('model', '=', model)])
if message_id and mail_ids:
mail_record = mail_message.browse(cr, uid, mail_ids)[0]
@@ -107,12 +108,22 @@ class plugin_handler(osv.osv_memory):
res_id = model_obj.message_process(cr, uid, model, email)
notify = _("Mail successfully pushed, a new %s has been created.") % model
else:
email_from = msg.get('email_from')
if not email_from:
author_id = False
else:
authors = mail_thread_obj.message_find_partner_from_emails(cr, uid, [res_id], [email_from])
author_id = authors and authors[0].get('partner_id') or False
model_obj.message_post(cr, uid, [res_id],
body=msg.get('body'),
subject=msg.get('subject'),
type='comment' if model == 'res.partner' else 'email',
parent_id=msg.get('parent_id'),
attachments=msg.get('attachments'))
attachments=msg.get('attachments'),
message_id=message_id,
email_from=email_from,
author_id=author_id)
notify = _("Mail successfully pushed")
url = self._make_url(cr, uid, res_id, model)
return (model, res_id, url, notify)
@@ -151,7 +162,7 @@ class plugin_handler(osv.osv_memory):
ir_attachment_obj = self.pool.get('ir.attachment')
attach_ids = []
msg = self.pool.get('mail.thread').message_parse(cr, uid, headers)
message_id = msg.get('message-id')
message_id = msg.get('message_id')
push_mail = self.push_message(cr, uid, model, headers, res_id)
res_id = push_mail[1]
model = push_mail[0]
@@ -165,6 +176,6 @@ class plugin_handler(osv.osv_memory):
attach_ids.append(ir_attachment_obj.create(cr, uid, vals))
mail_ids = mail_message.search(cr, uid, [('message_id', '=', message_id), ('res_id', '=', res_id), ('model', '=', model)])
if mail_ids:
mail_message.write(cr, uid, mail_ids[0], {'attachment_ids': [(6, 0, attach_ids)], 'body': body, 'body_html': body_html})
mail_message.write(cr, uid, mail_ids[0], {'attachment_ids': [(6, 0, attach_ids)], 'body': body_html})
url = self._make_url(cr, uid, res_id, model)
return (model, res_id, url, notify)
@@ -92,6 +92,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
},
connect_to_proxy: function(){
var self = this;
var done = new $.Deferred();
this.barcode_reader.disconnect_from_proxy();
this.pos_widget.loading_message(_t('Connecting to the PosBox'),0);
this.pos_widget.loading_skip(function(){
@@ -106,7 +107,10 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
if(self.config.iface_scan_via_proxy){
self.barcode_reader.connect_to_proxy();
}
}).always(function(){
done.resolve();
});
return done;
},
// helper function to load data from the server
@@ -52,7 +52,8 @@ class project_compute_phases(osv.osv_memory):
else:
project_ids = project_pool.search(cr, uid, [('user_id','=',uid)], context=context)
project_pool.schedule_phases(cr, uid, project_ids, context=context)
if project_ids:
project_pool.schedule_phases(cr, uid, project_ids, context=context)
return self._open_phases_list(cr, uid, data, context=context)
def _open_phases_list(self, cr, uid, data, context=None):
+10 -1
View File
@@ -695,7 +695,7 @@ class purchase_order(osv.osv):
continue
if order_line.product_id.type in ('product', 'consu'):
move = stock_move.create(cr, uid, self._prepare_order_line_move(cr, uid, order, order_line, picking_id, context=context))
if order_line.move_dest_id:
if order_line.move_dest_id and order_line.move_dest_id.state != 'done':
order_line.move_dest_id.write({'location_id': order.location_id.id})
todo_moves.append(move)
stock_move.action_confirm(cr, uid, todo_moves)
@@ -909,6 +909,15 @@ class purchase_order_line(osv.osv):
default.update({'state':'draft', 'move_ids':[],'invoiced':0,'invoice_lines':[]})
return super(purchase_order_line, self).copy_data(cr, uid, id, default, context)
def unlink(self, cr, uid, ids, context=None):
procurement_ids_to_cancel = []
for line in self.browse(cr, uid, ids, context=context):
if line.move_dest_id:
procurement_ids_to_cancel.extend(procurement.id for procurement in line.move_dest_id.procurements)
if procurement_ids_to_cancel:
self.pool['procurement.order'].action_cancel(cr, uid, procurement_ids_to_cancel)
return super(purchase_order_line, self).unlink(cr, uid, ids, context=context)
def onchange_product_uom(self, cr, uid, ids, pricelist_id, product_id, qty, uom_id,
partner_id, date_order=False, fiscal_position_id=False, date_planned=False,
name=False, price_unit=False, context=None):
+2 -2
View File
@@ -208,7 +208,7 @@
<page string="Purchase Order">
<field name="order_line">
<tree string="Purchase Order Lines" editable="bottom">
<field name="product_id" on_change="onchange_product_id(parent.pricelist_id,product_id,0,product_uom,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)"/>
<field name="product_id" on_change="onchange_product_id(parent.pricelist_id,product_id,0,False,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)"/>
<field name="name"/>
<field name="date_planned"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
@@ -387,7 +387,7 @@
<sheet>
<group>
<group>
<field name="product_id" on_change="onchange_product_id(parent.pricelist_id,product_id,0,product_uom,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)"/>
<field name="product_id" on_change="onchange_product_id(parent.pricelist_id,product_id,0,False,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)"/>
<label for="product_qty"/>
<div>
<field name="product_qty" on_change="onchange_product_id(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id,parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)" class="oe_inline"/>
@@ -24,3 +24,13 @@
!python {model: procurement.order}: |
procurement = self.browse(cr, uid, ref('procurement_order_testcase0'))
assert procurement.purchase_id, 'RFQ should be generated!'
-
I delete the line from the purchase order and check that the move and the procurement are cancelled
-
!python {model: procurement.order}: |
procurement = self.browse(cr, uid, ref('procurement_order_testcase0'))
move = procurement.purchase_id.order_line[0].move_dest_id
procurement.purchase_id.order_line[0].unlink()
assert move.state == 'cancel', 'Move should be cancelled'
procurement.refresh()
assert procurement.state == 'cancel', 'Procurement should be cancelled'
+2 -2
View File
@@ -125,7 +125,7 @@
<field name="product_id"
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
groups="base.group_user"
on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_uos_qty, product_uos, name, parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position, False, context)"/>
on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, False, product_uos_qty, False, name, parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position, False, context)"/>
<label for="product_uom_qty"/>
<div>
<field
@@ -169,7 +169,7 @@
<field name="product_id"
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
groups="base.group_user"
on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_uos_qty, product_uos, name, parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position, False, context)"/>
on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, False, product_uos_qty, False, name, parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position, False, context)"/>
<field name="name"/>
<field name="product_uom_qty"
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
+1 -1
View File
@@ -33,7 +33,7 @@
<field name="product_id"
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
groups="base.group_user"
on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, False, True, parent.date_order, product_packaging, parent.fiscal_position, False, context)"/>
on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,False,product_uos_qty,False,name,parent.partner_id, False, True, parent.date_order, product_packaging, parent.fiscal_position, False, context)"/>
</field>
<field name="product_uom_qty" position="replace">
<field context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
+7 -8
View File
@@ -90,13 +90,14 @@ class product_product(osv.osv):
stock_output_acc = datas.get('stock_output_account', False)
stock_input_acc = datas.get('stock_input_account', False)
journal_id = datas.get('stock_journal', False)
product_obj=self.browse(cr, uid, ids, context=context)[0]
account_valuation = product_obj.categ_id.property_stock_valuation_account_id
account_valuation_id = account_valuation and account_valuation.id or False
if not account_valuation_id: raise osv.except_osv(_('Error!'), _('Specify valuation Account for Product Category: %s.') % (product_obj.categ_id.name))
move_ids = []
loc_ids = location_obj.search(cr, uid,[('usage','=','internal')])
for rec_id in ids:
for product in self.browse(cr, uid, ids, context=context):
if product.valuation != 'real_time':
continue
account_valuation = product.categ_id.property_stock_valuation_account_id
account_valuation_id = account_valuation and account_valuation.id or False
if not account_valuation_id: raise osv.except_osv(_('Error!'), _('Specify valuation Account for Product Category: %s.') % (product.categ_id.name))
for location in location_obj.browse(cr, uid, loc_ids, context=context):
c = context.copy()
c.update({
@@ -104,7 +105,6 @@ class product_product(osv.osv):
'compute_child': False
})
product = self.browse(cr, uid, rec_id, context=c)
qty = product.qty_available
diff = product.standard_price - new_price
if not diff: raise osv.except_osv(_('Error!'), _("No difference between standard price and new price!"))
@@ -182,8 +182,7 @@ class product_product(osv.osv):
'debit': amount_diff,
'move_id': move_id
})
self.write(cr, uid, rec_id, {'standard_price': new_price})
self.write(cr, uid, ids, {'standard_price': new_price})
return move_ids
+1 -1
View File
@@ -2625,7 +2625,7 @@ class stock_move(osv.osv):
quantity = move.product_qty
uos_qty = quantity / move_qty * move.product_uos_qty
if quantity_rest > 0:
if float_compare(quantity_rest, 0, precision_rounding=move.product_id.uom_id.rounding):
default_val = {
'product_qty': quantity,
'product_uos_qty': uos_qty,
+63 -52
View File
@@ -28,11 +28,15 @@ logger = logging.getLogger(__name__)
MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT = IMAGE_LIMITS = (1024, 768)
class Website(openerp.addons.web.controllers.main.Home):
#------------------------------------------------------
# View
#------------------------------------------------------
@http.route('/', type='http', auth="public", website=True, multilang=True)
def index(self, **kw):
try:
main_menu = request.registry['ir.model.data'].get_object(request.cr, request.uid, 'website', 'main_menu')
first_menu = main_menu.child_id and main_menu.child_id[0]
# Dont 302 loop on /
if first_menu and not ((first_menu.url == '/') or first_menu.url.startswith('/#') or first_menu.url.startswith('/?')):
return request.redirect(first_menu.url)
except:
@@ -47,7 +51,50 @@ class Website(openerp.addons.web.controllers.main.Home):
response = request.website.render(response.params['template'], values)
return response
@http.route('/pagenew/<path:path>', type='http', auth="user", website=True)
@http.route('/page/<page:page>', type='http', auth="public", website=True, multilang=True)
def page(self, page, **opt):
values = {
'path': page,
}
# allow shortcut for /page/<website_xml_id>
if '.' not in page:
page = 'website.%s' % page
try:
request.website.get_template(page)
except ValueError, e:
# page not found
if request.context['editable']:
page = 'website.page_404'
else:
return request.registry['ir.http']._handle_exception(e, 404)
return request.website.render(page, values)
@http.route(['/robots.txt'], type='http', auth="public", website=True)
def robots(self):
response = request.website.render('website.robots', {'url_root': request.httprequest.url_root})
response.mimetype = 'text/plain'
return response
@http.route('/sitemap', type='http', auth='public', website=True, multilang=True)
def sitemap(self):
return request.website.render('website.sitemap', {
'pages': request.website.enumerate_pages()
})
@http.route('/sitemap.xml', type='http', auth="public", website=True)
def sitemap_xml(self):
response = request.website.render('website.sitemap_xml', {
'pages': request.website.enumerate_pages()
})
response.headers['Content-Type'] = 'application/xml;charset=utf-8'
return response
#------------------------------------------------------
# Edit
#------------------------------------------------------
@http.route('/website/add/<path:path>', type='http', auth="user", website=True)
def pagenew(self, path, noredirect=False):
xml_id = request.registry['website'].new_page(request.cr, request.uid, path, context=request.context)
url = "/page/" + xml_id
@@ -55,7 +102,7 @@ class Website(openerp.addons.web.controllers.main.Home):
return werkzeug.wrappers.Response(url, mimetype='text/plain')
return werkzeug.utils.redirect(url)
@http.route('/website/theme_change', type='http', auth="admin", website=True)
@http.route('/website/theme_change', type='http', auth="user", website=True)
def theme_change(self, theme_id=False, **kwargs):
imd = request.registry['ir.model.data']
view = request.registry['ir.ui.view']
@@ -81,26 +128,6 @@ class Website(openerp.addons.web.controllers.main.Home):
def snippets(self):
return request.website._render('website.snippets')
@http.route('/page/<page:page>', type='http', auth="public", website=True, multilang=True)
def page(self, page, **opt):
values = {
'path': page,
}
# allow shortcut for /page/<website_xml_id>
if '.' not in page:
page = 'website.%s' % page
try:
request.website.get_template(page)
except ValueError, e:
# page not found
if request.context['editable']:
page = 'website.page_404'
else:
return request.registry['ir.http']._handle_exception(e, 404)
return request.website.render(page, values)
@http.route('/website/reset_templates', type='http', auth='user', methods=['POST'], website=True)
def reset_template(self, templates, redirect='/'):
templates = request.httprequest.form.getlist('templates')
@@ -166,7 +193,7 @@ class Website(openerp.addons.web.controllers.main.Home):
})
return result
@http.route('/website/get_view_translations', type='json', auth='admin', website=True)
@http.route('/website/get_view_translations', type='json', auth='public', website=True)
def get_view_translations(self, xml_id, lang=None):
lang = lang or request.context.get('lang')
views = self.customize_template_get(xml_id, optional=False)
@@ -175,7 +202,7 @@ class Website(openerp.addons.web.controllers.main.Home):
irt = request.registry.get('ir.translation')
return irt.search_read(request.cr, request.uid, domain, ['id', 'res_id', 'value'], context=request.context)
@http.route('/website/set_translations', type='json', auth='admin', website=True)
@http.route('/website/set_translations', type='json', auth='public', website=True)
def set_translations(self, data, lang):
irt = request.registry.get('ir.translation')
for view_id, trans in data.items():
@@ -210,11 +237,9 @@ class Website(openerp.addons.web.controllers.main.Home):
irt.create(request.cr, request.uid, new_trans)
return True
@http.route('/website/attach', type='http', auth='user', website=True)
@http.route('/website/attach', type='http', auth='user', methods=['POST'], website=True)
def attach(self, func, upload):
req = request.httprequest
if req.method != 'POST':
return werkzeug.exceptions.MethodNotAllowed(valid_methods=['POST'])
url = message = None
try:
@@ -257,42 +282,27 @@ class Website(openerp.addons.web.controllers.main.Home):
obj = _object.browse(request.cr, request.uid, _id)
return bool(obj.website_published)
#------------------------------------------------------
# Helpers
#------------------------------------------------------
@http.route(['/website/kanban/'], type='http', auth="public", methods=['POST'], website=True)
def kanban(self, **post):
return request.website.kanban_col(**post)
@http.route(['/robots.txt'], type='http', auth="public", website=True)
def robots(self):
response = request.website.render('website.robots', {'url_root': request.httprequest.url_root})
response.mimetype = 'text/plain'
return response
@http.route('/sitemap', type='http', auth='public', website=True, multilang=True)
def sitemap(self):
return request.website.render('website.sitemap', {
'pages': request.website.enumerate_pages()
})
@http.route('/sitemap.xml', type='http', auth="public", website=True)
def sitemap_xml(self):
response = request.website.render('website.sitemap_xml', {
'pages': request.website.enumerate_pages()
})
response.headers['Content-Type'] = 'application/xml;charset=utf-8'
return response
class Images(http.Controller):
def placeholder(self, response):
# file_open may return a StringIO. StringIO can be closed but are
# not context managers in Python 2 though that is fixed in 3
with contextlib.closing(openerp.tools.misc.file_open(
os.path.join('web', 'static', 'src', 'img', 'placeholder.png'),
mode='rb')) as f:
response.set_data(f.read())
response.data = f.read()
return response.make_conditional(request.httprequest)
@http.route('/website/image', auth="public", website=True)
def image(self, model, id, field, max_width=maxint, max_height=maxint):
@http.route([
'/website/image',
'/website/image/<model>/<id>/<field>'
], auth="public", website=True)
def website_image(self, model, id, field, max_width=maxint, max_height=maxint):
Model = request.registry[model]
response = werkzeug.wrappers.Response()
@@ -346,7 +356,7 @@ class Images(http.Controller):
max_w, max_h = fit
if w < max_w and h < max_h:
response.set_data(data)
response.data = data
else:
image.thumbnail(fit, Image.ANTIALIAS)
image.save(response.stream, image.format)
@@ -356,4 +366,5 @@ class Images(http.Controller):
return response
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
+1 -1
View File
@@ -95,7 +95,7 @@ class ir_http(orm.AbstractModel):
werkzeug.exceptions.abort(werkzeug.utils.redirect(url))
def _handle_exception(self, exception=None, code=500):
if isinstance(exception, werkzeug.exceptions.HTTPException) and exception.response:
if isinstance(exception, werkzeug.exceptions.HTTPException) and hasattr(exception, 'response') and exception.response:
return exception.response
if getattr(request, 'website_enabled', False) and request.website:
values = dict(
+3 -3
View File
@@ -39,7 +39,7 @@ def keep_query(*args, **kw):
def url_for(path_or_uri, lang=None):
location = path_or_uri.strip()
url = urlparse.urlparse(location)
if request and not url.netloc and not url.scheme:
if request and url.path and not url.netloc and not url.scheme:
location = urlparse.urljoin(request.httprequest.path, location)
force_lang = lang is not None
lang = lang or request.context.get('lang')
@@ -279,7 +279,7 @@ class website(osv.osv):
def get_url(page):
_url = "%spage/%s/" % (url, page)
if url_args:
_url = "%s?%s" % (_url, urllib.urlencode(url_args))
_url = "%s?%s" % (_url, werkzeug.url_encode(url_args))
return _url
return {
@@ -446,7 +446,7 @@ class website(osv.osv):
get_args.setdefault('kanban', "")
kanban = get_args.pop('kanban')
kanban_url = "?%s&kanban=" % urllib.urlencode(get_args)
kanban_url = "?%s&kanban=" % werkzeug.url_encode(get_args)
pages = {}
for col in kanban.split(","):
@@ -20,7 +20,7 @@
input: "Page Title",
}).then(function (val) {
if (val) {
document.location = '/pagenew/' + encodeURI(val);
document.location = '/website/add/' + encodeURI(val);
}
});
}
@@ -96,7 +96,7 @@
title: "Save your modifications",
content: "Publish your page by clicking on the <em>'Save'</em> button.",
template: self.popover({ fixed: true }),
trigger: 'click',
trigger: 'reload',
},
{
stepId: 'part-2',
+106 -58
View File
@@ -21,6 +21,7 @@
},
registerStep: function (step) {
var self = this;
step.tour = self;
step.title = openerp.qweb.render('website.tour_popover_title', { title: step.title });
if (!step.element) {
step.orphan = true;
@@ -37,15 +38,21 @@
};
} else if (step.trigger === 'reload') {
step.triggers = function (callback) {
var stack = JSON.parse(localStorage.getItem("website-reloads")) || [];
var stack = JSON.parse(step.tour.tourStorage.getItem("website-reloads")) || [];
var index = stack.indexOf(step.stepId);
if (index !== -1) {
stack.splice(index,1);
(callback || self.moveToNextStep).apply(self);
setTimeout(function () {
$(step.element).popover("destroy");
setTimeout(function () {
stack.splice(index,1);
(callback || self.moveToNextStep).apply(self);
step.tour.tourStorage.setItem("website-reloads", JSON.stringify(stack));
},10);
},0);
} else {
stack.push(step.stepId);
step.tour.tourStorage.setItem("website-reloads", JSON.stringify(stack));
}
localStorage.setItem("website-reloads", JSON.stringify(stack));
};
} else if (step.trigger === 'drag') {
step.triggers = function (callback) {
@@ -70,7 +77,7 @@
}
} else if (step.trigger.url) {
step.triggers = function (callback) {
var stack = JSON.parse(localStorage.getItem("website-geturls")) || [];
var stack = JSON.parse(step.tour.tourStorage.getItem("website-geturls")) || [];
var id = step.trigger.url.toString();
var index = stack.indexOf(id);
if (index !== -1) {
@@ -79,12 +86,18 @@
step.trigger.url == url.pathname+url.search :
step.trigger.url.test(url.pathname+url.search);
if (!test) return;
stack.splice(index,1);
(callback || self.moveToNextStep).apply(self);
setTimeout(function () {
$(step.element).popover("destroy");
setTimeout(function () {
stack.splice(index,1);
(callback || self.moveToNextStep).apply(self);
step.tour.tourStorage.setItem("website-geturls", JSON.stringify(stack));
},10);
},0);
} else {
stack.push(id);
step.tour.tourStorage.setItem("website-geturls", JSON.stringify(stack));
}
localStorage.setItem("website-geturls", JSON.stringify(stack));
return index !== -1;
};
} else if (step.trigger.modal) {
@@ -99,7 +112,6 @@
$doc.one('shown.bs.modal', function () {
$('.modal button.btn-primary').one('click', function () {
$doc.off('hide.bs.modal', onStop);
console.log(callback);
if (!callback) {
self.moveToStep(step.trigger.modal.afterSubmit);
}
@@ -107,6 +119,30 @@
(callback || self.moveToNextStep).apply(self);
});
};
} else if (step.trigger === 'ajax') {
step.triggers = function (callback) {
$( document ).ajaxSuccess(function(event, xhr, settings) {
$( document ).unbind('ajaxSuccess');
xhr.then(function () {
setTimeout(function () {
$(step.element).popover("destroy");
setTimeout(function () {
(callback || self.moveToNextStep).apply(self);
},10);
},0);
});
});
};
} else {
step.triggers = function (callback) {
var emitter = $(step.element);
if (!emitter.size()) throw "Emitter is undefined";
var trigger = function () {
emitter.off(step.trigger, trigger);
(callback || self.moveToNextStep).apply(self, arguments);
};
emitter.on(step.trigger, trigger);
};
}
}
step.onShow = (function () {
@@ -130,10 +166,15 @@
reset: function () {
this.tourStorage.removeItem(this.id+'_current_step');
this.tourStorage.removeItem(this.id+'_end');
this.tourStorage.removeItem("website-reloads");
this.tourStorage.removeItem("website-geturls");
this.tour._current = 0;
$('.popover.tour').remove();
},
start: function () {
window.Tour.prototype._isOrphan = function(step) {
return (step.element == null) || !$(step.element).length;
};
if (this.resume() || ((this.currentStepIndex() === 0) && !this.tour.ended())) {
this.tour.start();
}
@@ -160,12 +201,8 @@
this.stop();
} else if (index >= 0) {
var self = this;
setTimeout(function () {
$('.popover.tour').remove();
setTimeout(function () {
self.tour.goto(index);
}, 0);
}, 0);
$('.popover.tour').remove();
self.tour.goto(index);
}
},
moveToNextStep: function () {
@@ -319,13 +356,16 @@
_.each(this.tours, function (tour) {
var $menuItem = $($.parseHTML('<li><a href="#">'+tour.name+'</a></li>'));
$menuItem.click(function () {
tour.redirect(new website.UrlParser(window.location.href));
tour.reset();
tour.start();
tour.redirect(new website.UrlParser(window.location.href));
});
menu.append($menuItem);
if (tour.trigger()) {
tour.start();
setTimeout(function () {
setTimeout(function () {
tour.start();
},100);
},0);
}
});
return this._super();
@@ -335,63 +375,63 @@
var testId = 'test_'+tour.id+'_tour';
this.tours.push(tour);
var defaultDelay = 250; //ms
var defaultDelayReload = 1500; //ms
var overlapsCrash;
var test = {
id: tour.id,
run: function (force) {
if (force === true) {
this.reset();
tour.reset();
}
var actionSteps = _.filter(tour.steps, function (step) {
return step.trigger || step.sampleText;
return step.trigger || step.triggers || step.sampleText;
});
window.onbeforeunload = function () {
clearTimeout(overlapsCrash);
};
function throwError (message) {
console.log(window.localStorage.getItem("test-report"));
console.log(tour.tourStorage.getItem("test-report"));
test.reset();
tour.reset();
throw message;
}
function initReport () {
// set last time for report
if (!window.localStorage.getItem("test-last-time")) {
window.localStorage.setItem("test-last-time", new Date().getTime());
if (!tour.tourStorage.getItem("test-last-time")) {
tour.tourStorage.setItem("test-last-time", new Date().getTime());
}
}
function setReport (step) {
var report = JSON.parse(window.localStorage.getItem("test-report")) || {};
report[step.stepId] = (new Date().getTime() - window.localStorage.getItem("test-last-time")) + " ms";
window.localStorage.setItem("test-report", JSON.stringify(report));
var report = JSON.parse(tour.tourStorage.getItem("test-report")) || {};
report[step.stepId] = (new Date().getTime() - tour.tourStorage.getItem("test-last-time")) + " ms";
tour.tourStorage.setItem("test-report", JSON.stringify(report));
}
function testCycling (step) {
var lastStep = window.localStorage.getItem(testId);
var tryStep = lastStep != step.stepId ? 0 : (+(window.localStorage.getItem("test-last-"+testId) || 0) + 1);
window.localStorage.setItem("test-last-"+testId, tryStep);
var lastStep = tour.tourStorage.getItem(testId);
var tryStep = lastStep != step.stepId ? 0 : (+(tour.tourStorage.getItem("test-last-"+testId) || 0) + 1);
tour.tourStorage.setItem("test-last-"+testId, tryStep);
if (tryStep > 2) {
throwError("Test: '" + testId + "' cycling step: '" + step.stepId + "'");
}
return tryStep;
}
function getDelay (step) {
return step.delay ||
((step.trigger === 'reload' || (step.trigger && step.trigger.url))
? defaultDelayReload
: defaultDelay);
return step.delay || defaultDelay;
}
function executeStep (step) {
if (testCycling(step) === 0) initReport();
tour.tourStorage.setItem(testId, step.stepId);
var delay = getDelay (step);
overlapsCrash = setTimeout(function () {
throwError("Test: '" + testId + "' can't resolve step: '" + step.stepId + "'");
}, delay + 1000);
}, delay + 3000);
var _next = false;
window.localStorage.setItem(testId, step.stepId);
tour.tourStorage.setItem(testId, step.stepId);
function next () {
_next = true;
clearTimeout(overlapsCrash);
@@ -401,16 +441,15 @@
var nextStep = actionSteps.shift();
if (nextStep) {
setTimeout(function () {
executeStep(nextStep);
}, delay);
executeStep(nextStep);
} else {
window.localStorage.removeItem(testId);
tour.tourStorage.removeItem(testId);
}
}
setTimeout(function () {
var $element = $(step.element);
var flag = step.triggers && (!step.trigger || !step.trigger.modal);
if (flag) {
try {
@@ -420,34 +459,41 @@
}
}
if ((step.trigger === 'reload' || (step.trigger && step.trigger.url)) && _next) return;
if (step.snippet && step.trigger === 'drag') {
website.TestConsole.dragAndDropSnippet(step.snippet);
} else if (step.trigger && step.trigger.id === 'change') {
$element.trigger($.Event("change", { srcElement: $element }));
} else if (step.sampleText) {
$element.val(step.sampleText);
$element.trigger($.Event("change", { srcElement: $element }));
} else if ($element.is(":visible")) { // Click by default
if (step.trigger.id === 'mousedown') {
$element.trigger($.Event("mousedown", { srcElement: $element }));
$element.trigger($.Event("keydown", { srcElement: $element }));
if ($element.is("select") || $element.is("input") ) {
$element.val(step.sampleText);
} else {
$element.html(step.sampleText);
}
$element.trigger($.Event("change", { srcElement: $element }));
$element.trigger($.Event("keyup", { srcElement: $element }));
} else if ($element.is(":visible")) { // Click by default
$element.trigger($.Event("mouseenter", { srcElement: $element }));
$element.trigger($.Event("mousedown", { srcElement: $element }));
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
$element[0].dispatchEvent(evt);
if (step.trigger.id === 'mouseup') {
$element.trigger($.Event("mouseup", { srcElement: $element }));
}
$element.trigger($.Event("mouseup", { srcElement: $element }));
$element.trigger($.Event("mouseleave", { srcElement: $element }));
// trigger after for step like: mouseenter, next step click on button display with mouseenter
$element.trigger($.Event("mouseenter", { srcElement: $element }));
}
if (!flag) next();
},0);
},delay);
}
var url = new website.UrlParser(window.location.href);
if (tour.path && url.pathname !== tour.path && !window.localStorage.getItem(testId)) {
window.localStorage.setItem(testId, actionSteps[0].stepId);
if (tour.path && url.pathname !== tour.path && !tour.tourStorage.getItem(testId)) {
tour.tourStorage.setItem(testId, actionSteps[0].stepId);
window.location.href = tour.path;
} else {
var lastStepId = window.localStorage.getItem(testId);
var lastStepId = tour.tourStorage.getItem(testId);
var currentStep = actionSteps.shift();
if (lastStepId) {
while (currentStep && lastStepId !== currentStep.stepId) {
@@ -459,16 +505,18 @@
executeStep(currentStep);
});
} else {
executeStep(currentStep);
setTimeout(function () {
executeStep(currentStep);
}, 500);
}
}
},
reset: function () {
window.localStorage.removeItem(testId);
window.localStorage.removeItem("test-report");
for (var k in window.localStorage) {
if (window.localStorage[k].indexOf("test-last")) {
window.localStorage.removeItem(k);
tour.tourStorage.removeItem(testId);
tour.tourStorage.removeItem("test-report");
for (var k in tour.tourStorage) {
if (tour.tourStorage[k].indexOf("test-last")) {
tour.tourStorage.removeItem(k);
}
}
},
+2 -1
View File
@@ -4,6 +4,7 @@ import urllib
import urlparse
import unittest2
import urllib2
import werkzeug.urls
import lxml.html
@@ -70,7 +71,7 @@ class CrawlSuite(unittest2.TestSuite):
if user is not None:
url = 'http://localhost:{port}/login?{query}'.format(
port=tools.config['xmlrpc_port'],
query=urllib.urlencode({
query=werkzeug.url_encode({
'db': tools.config['db_name'],
'login': user,
'key': password,
+16 -16
View File
@@ -372,22 +372,6 @@
</div>
</div>
<div data-snippet-id='colmd' data-selector-vertical-children='.row'>
<div class="oe_snippet_thumbnail">
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_column.png"/>
<span class="oe_snippet_thumbnail_title">Add Column</span>
</div>
<div class="oe_snippet_body col-md-4">
<img class="img img-rounded img-responsive" src="/website/static/src/img/china_thumb.jpg"/>
<h4 class="mt16">Feature</h4>
<p>
Delete the above image or replace it with a picture
that illustrates your message. Click on the picture to
change it's <em>rounded corner</em> style.
</p>
</div>
</div>
</div>
<div id="snippet_feature" class="tab-pane fade">
@@ -878,6 +862,22 @@
<!-- use to declare snippet for snippet editor -->
<div id="snippet_hidden" class="hidden">
<div data-snippet-id='colmd' data-selector-vertical-children='.row'>
<div class="oe_snippet_thumbnail">
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_column.png"/>
<span class="oe_snippet_thumbnail_title">Add Column</span>
</div>
<div class="oe_snippet_body col-md-4">
<img class="img img-rounded img-responsive" src="/website/static/src/img/china_thumb.jpg"/>
<h4 class="mt16">Feature</h4>
<p>
Delete the above image or replace it with a picture
that illustrates your message. Click on the picture to
change it's <em>rounded corner</em> style.
</p>
</div>
</div>
</div>
<div id="snippet_styles" class="hidden">
+1 -1
View File
@@ -416,7 +416,7 @@
<div class="container">
<div class="well mt32">
<p>This page does not exists, but you can create it as you are administrator of this site.</p>
<a class="btn btn-primary" t-attf-href="/pagenew/#{ path }">Create Page</a>
<a class="btn btn-primary" t-attf-href="/website/add/#{ path }">Create Page</a>
<span class="text-muted">or</span> <a href="/sitemap">Search a Page</a>
</div>
<div class="text-center text-muted">Edit the content below this line to adapt the default "page not found" page.</div>
@@ -75,7 +75,7 @@
</div>
<div class="text-muted" t-if="len(blog.message_ids) &gt; 0">
<span class="fa fa-comment-o">
<a t-attf-href="/blogpost/#{ slug(blog) }/?#{ tag and 'tag=%s' % tag.id or '' }#{tag and date and '&amp;' or ''}#{ date and 'date=%s' % date or ''}#comment">
<a t-attf-href="/blogpost/#{ slug(blog) }/?#{ tag and 'tag=%s' % tag.id or '' }#{tag and date and '&amp;' or ''}#{ date and 'date=%s' % date or ''}#comments">
<t t-if="len(blog.message_ids) &lt;= 1" ><t t-esc="len(blog.message_ids)"/> comment</t>
<t t-if="len(blog.message_ids) > 1"><t t-esc="len(blog.message_ids)"/> comments</t>
</a>
@@ -117,7 +117,7 @@
<p class="post-meta text-muted text-center" name='blog_post_data'>
<span class="fa fa-calendar oe_date"> <span t-field="blog_post.create_date"/> &amp;nbsp;</span>
<span t-if="len(blog_post.message_ids) &gt; 0" class="fa fa-comment-o">
<a t-attf-href="/blogpost/#{ slug(blog_post) }/?#{ tag and 'tag=%s' % tag.id or '' }#{tag and date and '&amp;' or ''}#{ date and 'date=%s' % date or ''}#comment">
<a t-attf-href="/blogpost/#{ slug(blog_post) }/?#{ tag and 'tag=%s' % tag.id or '' }#{tag and date and '&amp;' or ''}#{ date and 'date=%s' % date or ''}#comments">
<t t-if="len(blog_post.message_ids) &lt;= 1" ><t t-esc="len(blog_post.message_ids)"/> comment</t>
<t t-if="len(blog_post.message_ids) > 1"><t t-esc="len(blog_post.message_ids)"/> comments</t>
</a>
+1 -1
View File
@@ -1,7 +1,7 @@
{
'name': 'Contact Form',
'category': 'Website',
'summary': 'Generate Leads From Contact Form',
'summary': 'Create Leads From Contact Form',
'version': '1.0',
'description': """
OpenERP Contact Form
+5 -5
View File
@@ -40,14 +40,14 @@ class contactus(http.Controller):
# if not given: subject is contact name
if not post.get('name'):
post['name'] = post.get('contact_name')
section_ids = request.registry["crm.case.section"].search(
request.cr, SUPERUSER_ID, [("code", "=", "Website")], context=request.context)
if section_ids:
post['section_id'] = section_ids[0]
post['user_id'] = False
try:
post['channel_id'] = request.registry['ir.model.data'].get_object_reference(request.cr, SUPERUSER_ID, 'crm', 'crm_case_channel_website')[1]
except ValueError:
pass
request.registry['crm.lead'].create(request.cr, SUPERUSER_ID, post, request.context)
company = request.website.company_id
values = {
@@ -1,7 +1,7 @@
{
'name': 'Publish Partner Assignment',
'name': 'Resellers',
'category': 'Website',
'summary': 'Publish and Assign Partner',
'summary': 'Publish Your Channel of Resellers',
'version': '1.0',
'description': """
Publish and Assign Partner
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
import urllib
import werkzeug.urls
import openerp
from openerp import SUPERUSER_ID
@@ -89,7 +89,7 @@ class WebsiteCrmPartnerAssign(http.Controller):
'google_map_partner_ids': google_map_partner_ids,
'pager': pager,
'searches': post,
'search_path': "?%s" % urllib.urlencode(post),
'search_path': "?%s" % werkzeug.url_encode(post),
}
return request.website.render("website_crm_partner_assign.index", values)
+2 -2
View File
@@ -20,9 +20,9 @@
##############################################################################
{
'name': 'Public Customer References',
'name': 'Customer References',
'category': 'Website',
'summary': 'Publish Customer References on your Website',
'summary': 'Publish Your Customer References',
'version': '1.0',
'description': """
OpenERP Customer References
+5 -5
View File
@@ -6,7 +6,7 @@ from openerp.addons.web import http
from openerp.tools.translate import _
from openerp.addons.web.http import request
from openerp.addons.website_partner.controllers import main as website_partner
import urllib
import werkzeug.urls
class WebsiteCustomer(http.Controller):
@@ -30,10 +30,10 @@ class WebsiteCustomer(http.Controller):
if partner_name:
domain += [
'|',
('name', 'ilike', "%%%s%%" % post.get("search")),
('website_description', 'ilike', "%%%s%%" % post.get("search"))
('name', 'ilike', post.get("search")),
('website_description', 'ilike', post.get("search"))
]
country_id = None
if country_id:
domain += [('country_id', '=', country_id)]
@@ -71,7 +71,7 @@ class WebsiteCustomer(http.Controller):
'google_map_partner_ids': google_map_partner_ids,
'pager': pager,
'post': post,
'search_path': "?%s" % urllib.urlencode(post),
'search_path': "?%s" % werkzeug.url_encode(post),
}
return request.website.render("website_customer.index", values)
@@ -48,7 +48,7 @@
<h4>
<a t-attf-href="/customers/#{ slug([partner_data.get('id'), partner_data.get('name')]) }/" t-esc="partner_data.get('name')"/>
</h4>
<div t-raw="partner_data.get('website_short_description')"/>
<div t-raw="partner_data.get('website_short_description') or ''"/>
</div>
<div class="clearfix mb8"/>
</t>
@@ -117,36 +117,6 @@
</t>
</template>
<template id="customer_contact" inherit_id="website_customer.details" inherit_option_id="website_customer.details" name="Customer Contacts">
<xpath expr="//div[@id='left_column']" position="inside">
<div class="panel panel-default">
<div class="panel-heading">
<h4>Customer Reference</h4>
</div>
<div class="panel-body">
<div class="text-center">
<img class="img img-shadow" t-attf-src="data:image/png;base64,#{partner_data.get('image_medium')}"/>
</div>
<address class="mt16 mb8">
<strong t-esc="partner_data.get('name')"/>
<t t-if="partner_data.get('commercial_partner_id')">
<p t-raw="'&lt;br/&gt;'.join(partner_data.get('commercial_partner_id')[1].split('\n')[1:])"/>
</t>
<div t-if="partner_data.get('phone')">
<span class="fa fa-phone"/> <span t-esc="partner_data.get('phone')"/>
</div>
<div t-if="partner_data.get('email')">
<span class="fa fa-envelope"/>
<a t-att-href="'mailto:'+partner_data.get('email')">
<span t-esc="partner_data.get('email')"/>
</a>
</div>
</address>
</div>
</div>
</xpath>
</template>
<template id="partner_assign" inherit_option_id="website_customer.details" inherit_id="website_customer.details" name="Implemented By">
<xpath expr="//div[@id='left_column']" position="inside">
<t t-if="assigned_partner_data">
+2 -2
View File
@@ -30,7 +30,7 @@ controllers = controllers()
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from openerp import tools
import urllib
import werkzeug.urls
class website_event(http.Controller):
@http.route(['/event/', '/event/page/<int:page>'], type='http', auth="public", website=True, multilang=True)
@@ -155,7 +155,7 @@ class website_event(http.Controller):
'countries': countries,
'pager': pager,
'searches': searches,
'search_path': "?%s" % urllib.urlencode(searches),
'search_path': "?%s" % werkzeug.url_encode(searches),
}
return request.website.render("website_event.index", values)
@@ -42,7 +42,6 @@
trigger: {
modal: {
stopOnClose: true,
afterSubmit: 'event-page',
},
},
},
@@ -53,6 +52,7 @@
placement: 'right',
title: "Create an Event Name",
content: "Create a name for your new event and click <em>'Continue'</em>. e.g: Technical Training",
trigger: 'keyup',
},
{
stepId: 'continue-name',
@@ -60,9 +60,7 @@
placement: 'right',
title: "Create Event",
content: "Click <em>Continue</em> to create the event.",
trigger: {
url: /event\/[0-9]+\/register/
},
trigger: 'reload',
},
{
stepId: 'event-page',
@@ -139,7 +137,7 @@
placement: 'top',
title: "Publish your event",
content: "Click to publish your event.",
trigger: 'click',
trigger: 'ajax'
},
{
stepId: 'customize-event',
@@ -17,7 +17,7 @@ testRunner.run(function eventTest (page, timeout) {
waitFor(function testExecuted () {
var after = page.evaluate(function () {
return window.$ && $('button[data-action=edit]').is(":visible") && {
banner: $('#wrap [data-snippet-id=carousel]').length,
banner: $('#wrap [data-snippet-id=image-text]').length,
text: $('#wrap [data-snippet-id=text-block]').length,
image: $('#wrap [data-snippet-id=three-columns]').length,
};
@@ -29,4 +29,4 @@ testRunner.run(function eventTest (page, timeout) {
phantom.exit();
}, 4*timeout/5);
}, timeout/5);
});
});
@@ -13,6 +13,12 @@
</xpath>
</template>
<template id="mycart" inherit_id="website_sale.mycart" name="My Cart Event's Price">
<xpath expr="//td[@name='price']/t" position="attributes">
<attribute name="t-if">abs(line.product_id.lst_price - line.price_unit) &gt; 0.2 and not line.product_id.event_ok</attribute>
</xpath>
</template>
<template id="event_description_full" inherit_id="website_event.event_description_full" inherit_option_id="website_event.event_description_full" name="Event's Ticket form">
<xpath expr="//div[@t-field='event.description']" position="before">
<form t-attf-action="/event/add_cart?event_id=#{ event.id }" method="post" t-if="event.event_ticket_ids">
+2 -2
View File
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
{
'name': 'Tracks and Agenda of Events',
'name': 'Advanced Events',
'category': 'Website',
'summary': 'Organize Your Events',
'summary': 'Sponsors, Tracks, Agenda, Event News',
'version': '1.0',
'description': """
Online Events
@@ -84,6 +84,7 @@ class event_track(osv.osv):
'priority': fields.selection([('3','Low'),('2','Medium (*)'),('1','High (**)'),('0','Highest (***)')], 'Priority', required=True),
'website_published': fields.boolean('Available in the website'),
'website_url': fields.function(_website_url, string="Website url", type="char"),
'image': fields.related('speaker_ids', 'image', type='binary', readonly=True)
}
def set_priority(self, cr, uid, ids, priority, context={}):
return self.write(cr, uid, ids, {'priority' : priority})
@@ -136,8 +136,8 @@
</div>
<div class="row mb32" t-foreach="tracks" t-as="track">
<div class="col-sm-2">
<t t-if="len(track.speaker_ids)">
<span t-field="track.speaker_ids[0].image"
<t t-if="track.image">
<span t-field="track.image"
t-field-options='{"widget": "image", "class": "img-circle"}'/>
</t>
</div>
@@ -7,7 +7,7 @@ from datetime import datetime
class google_map(http.Controller):
@http.route(['/google_map/'], type='http', auth="admin", website=True)
@http.route(['/google_map/'], type='http', auth="public", website=True)
def google_map(self, *arg, **post):
values = {
'partner_ids': post.get('partner_ids', ""),
@@ -17,7 +17,7 @@ class google_map(http.Controller):
}
return request.website.render("website_google_map.google_map", values)
@http.route(['/google_map/partners.json'], type='http', auth="admin", website=True)
@http.route(['/google_map/partners.json'], type='http', auth="public", website=True)
def google_map_data(self, *arg, **post):
partner_obj = request.registry['res.partner']
@@ -28,7 +28,7 @@ class google_map(http.Controller):
return partner_obj.google_map_json(request.cr, openerp.SUPERUSER_ID,
partner_ids, request.context)
@http.route(['/google_map/set_partner_position/'], type='http', auth="admin", website=True)
@http.route(['/google_map/set_partner_position/'], type='http', auth="public", website=True)
def google_map_set_partner_position(self, *arg, **post):
partner_obj = request.registry['res.partner']
@@ -41,7 +41,7 @@ class google_map(http.Controller):
'partner_longitude': longitude,
'date_localization': datetime.now().strftime('%Y-%m-%d'),
}
partner_obj.write(request.cr, request.uid, [partner_id], values,
partner_obj.write(request.cr, openerp.SUPERUSER_ID, [partner_id], values,
request.context)
+2 -2
View File
@@ -1,8 +1,8 @@
{
'name': 'Career Form',
'name': 'Jobs',
'category': 'Website',
'version': '1.0',
'summary': 'Job Position',
'summary': 'Job Descriptions And Application Forms',
'description': """
OpenERP Contact Form
====================
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import base64
from openerp import SUPERUSER_ID
from openerp.addons.web import http
from openerp.tools.translate import _
from openerp.addons.web.http import request
@@ -62,19 +63,19 @@ class website_hr_recruitment(http.Controller):
def jobs_apply(self, job):
return request.website.render("website_hr_recruitment.apply", { 'job': job })
@http.route(['/jobs/thankyou'], methods=['POST'], type='http', auth="admin", website=True, multilang=True)
@http.route(['/jobs/thankyou'], methods=['POST'], type='http', auth="public", website=True, multilang=True)
def jobs_thankyou(self, **post):
cr, uid, context = request.cr, request.uid, request.context
imd = request.registry['ir.model.data']
value = {
'name': _('Online Form'),
'user_id': False,
'source_id' : imd.xmlid_to_res_id(cr, uid, 'hr_recruitment.source_website_company'),
'source_id' : imd.xmlid_to_res_id(cr, SUPERUSER_ID, 'hr_recruitment.source_website_company'),
}
for f in ['phone', 'email_from', 'partner_name', 'description', 'department_id', 'job_id']:
value[f] = post.get(f)
job_id = request.registry['hr.applicant'].create(cr, uid, value, context=context)
job_id = request.registry['hr.applicant'].create(cr, SUPERUSER_ID, value, context=context)
if post['ufile']:
attachment_value = {
'name': post['ufile'].filename,
@@ -84,7 +85,7 @@ class website_hr_recruitment(http.Controller):
'datas': base64.encodestring(post['ufile'].read()),
'datas_fname': post['ufile'].filename,
}
request.registry['ir.attachment'].create(cr, uid, attachment_value, context=context)
request.registry['ir.attachment'].create(cr, SUPERUSER_ID, attachment_value, context=context)
return request.website.render("website_hr_recruitment.thankyou", {})
# vim :et:
+2 -2
View File
@@ -1,7 +1,7 @@
{
'name': 'Website LiveChat',
'name': 'LiveChat',
'category': 'Website',
'summary': 'Instant Messaging for your Website',
'summary': 'Chat With Your Website Visitors',
'version': '1.0',
'description': """
OpenERP Website LiveChat
+1 -1
View File
@@ -56,7 +56,7 @@ class MailMessage(osv.Model):
group_ids = self.pool.get('res.users').browse(cr, uid, uid, context=context).groups_id
group_user_id = self.pool.get("ir.model.data").get_object_reference(cr, uid, 'base', 'group_public')[1]
if group_user_id in [group.id for group in group_ids]:
cr.execute('SELECT DISTINCT id FROM "%s" WHERE website_published IS FALSE AND id = ANY (%%s)' % (self._table), (ids,))
cr.execute('SELECT id FROM "%s" WHERE website_published IS FALSE AND id = ANY (%%s)' % (self._table), (ids,))
if cr.fetchall():
raise osv.except_osv(
_('Access Denied'),
@@ -6,6 +6,11 @@ $(document).ready(function () {
var self = this;
var $data = $(this).parents("div.js_follow");
var $email = $data.find(".js_follow_email");
if ($email.length && !$email.val().match(/.+@.+/)) {
return false;
}
var message_is_follower = $data.attr("data-follow") || "off";
$data.attr("data-follow", message_is_follower == 'off' ? 'on' : 'off');
@@ -13,7 +18,7 @@ $(document).ready(function () {
'id': $data.data('id'),
'object': $data.data('object'),
'message_is_follower': message_is_follower,
'email': $email && $email.val() || false,
'email': $email.length ? $email.val() : false,
}).then(function (result) {
if (result) {
$data.find(" > *").toggleClass("hidden");
+2 -1
View File
@@ -10,7 +10,8 @@
type="email" name="email"
class="js_follow_email form-control"
t-att-value="email or ''"
placeholder="your email..."/>
placeholder="your email..."
groups="base.group_public"/>
<span class="input-group-btn">
<button href="#" t-attf-class="btn btn-default js_unfollow_btn">Unsubscribe</button>
<button href="#" t-attf-class="btn btn-primary js_follow_btn">Subscribe</button>
+2 -2
View File
@@ -1,6 +1,6 @@
{
'name': 'Public Members',
'name': 'Website for Associations',
'name': 'Associations: Members',
'summary': 'Online Directory of Members',
'category': 'Website',
'summary': 'Publish Associations, Groups and Memberships',
'version': '1.0',
@@ -7,7 +7,7 @@ from openerp.addons.web.http import request
from openerp.addons.website_partner.controllers import main as website_partner
from openerp.tools.translate import _
import urllib
import werkzeug.urls
class WebsiteMembership(http.Controller):
@@ -37,14 +37,15 @@ class WebsiteMembership(http.Controller):
post_name = post.get('name', '')
# base domain for groupby / searches
base_line_domain = []
base_line_domain = [('state', 'in', ['free', 'paid'])]
if membership_id:
base_line_domain.append(('membership_id', '=', membership_id))
membership = product_obj.browse(cr, uid, membership_id, context=context)
else:
membership = None
if post_name:
base_line_domain += ['|', ('partner.name', 'ilike', "%%%s%%" % post_name), ('partner.website_description', 'ilike', "%%%s%%" % post_name)]
base_line_domain += ['|', ('partner.name', 'ilike', post_name),
('partner.website_description', 'ilike', post_name)]
# group by country, based on all customers (base domain)
membership_line_ids = membership_line_obj.search(cr, uid, base_line_domain, context=context)
@@ -64,6 +65,7 @@ class WebsiteMembership(http.Controller):
membership_line_ids = membership_line_obj.search(cr, uid, line_domain, context=context)
membership_lines = membership_line_obj.browse(cr, uid, membership_line_ids, context=context)
membership_lines.sort(key=lambda x: x.membership_id.website_sequence)
partner_ids = [m.partner and m.partner.id for m in membership_lines]
google_map_partner_ids = ",".join(map(str, partner_ids))
@@ -72,7 +74,7 @@ class WebsiteMembership(http.Controller):
partners_data[partner.get("id")] = partner
# format domain for group_by and memberships
membership_ids = product_obj.search(cr, uid, [('membership', '=', True)], context=context)
membership_ids = product_obj.search(cr, uid, [('membership', '=', True)], order="website_sequence", context=context)
memberships = product_obj.browse(cr, uid, membership_ids, context=context)
# request pager for lines
@@ -87,7 +89,7 @@ class WebsiteMembership(http.Controller):
'google_map_partner_ids': google_map_partner_ids,
'pager': pager,
'post': post,
'search': "?%s" % urllib.urlencode(post),
'search': "?%s" % werkzeug.url_encode(post),
}
return request.website.render("website_membership.index", values)
+4 -2
View File
@@ -31,9 +31,11 @@ class Website(osv.Model):
project_obj = request.registry['project.project']
project_ids = project_obj.search(cr, uid, [('privacy_visibility', "=", "public")], context=request.context)
request.context['website_project_ids'] = project_obj.browse(cr, uid, project_ids, request.context)
request.context.update({
'website_project_ids': project_obj.browse(cr, uid, project_ids, context=request.context)
})
return super(Website, self).preprocess_request(cr, uid, ids, request, context)
return super(Website, self).preprocess_request(cr, uid, ids, request, context=None)
class website_project(http.Controller):
@@ -5,7 +5,9 @@
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_id="website.layout" name="Footer Project's Links">
<xpath expr="//footer//ul[@name='products']" position="inside">
<li t-foreach="website_project_ids" t-as="project"><a t-attf-href="/project/#{ project.id }/" t-field="project.name"/></li>
<li t-foreach="website_project_ids" t-as="project">
<a t-attf-href="/project/#{ project.id }/"><span t-field="project.name"/></a>
</li>
</xpath>
</template>
+2 -2
View File
@@ -1,7 +1,7 @@
{
'name': 'Online Quotation',
'name': 'Online Proposals',
'category': 'Website',
'summary': 'Send Live Quotation',
'summary': 'Send Professional Quotations',
'version': '1.0',
'description': """
OpenERP Sale Quote Roller
+1 -1
View File
@@ -1,5 +1,5 @@
{
'name': 'E-Commerce',
'name': 'eCommerce',
'category': 'Website',
'summary': 'Sell Your Products Online',
'version': '1.0',
+3 -4
View File
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
import random
import simplejson
import urllib
import werkzeug
from openerp import SUPERUSER_ID
@@ -206,8 +205,8 @@ class Ecommerce(http.Controller):
domain = request.registry.get('website').ecommerce_get_product_domain()
if search:
domain += ['|',
('name', 'ilike', "%%%s%%" % search),
('description', 'ilike', "%%%s%%" % search)]
('name', 'ilike', search),
('description', 'ilike', search)]
if category:
domain.append(('product_variant_ids.public_categ_id', 'child_of', category.id))
if filters:
@@ -634,7 +633,7 @@ class Ecommerce(http.Controller):
})
acquirer_form_post_url = payment_obj.get_form_action_url(cr, uid, acquirer_id, context=context)
acquirer_total_url = '%s?%s' % (acquirer_form_post_url, urllib.urlencode(post))
acquirer_total_url = '%s?%s' % (acquirer_form_post_url, werkzeug.url_encode(post))
return request.redirect(acquirer_total_url)
@http.route('/shop/payment/get_status/<int:sale_order_id>', type='json', auth="public", website=True, multilang=True)
+5 -2
View File
@@ -5,8 +5,11 @@ class sale_configuration(osv.osv_memory):
_inherit = 'sale.config.settings'
_columns = {
'group_product_characteristics': fields.boolean("Support multiple characteristics per products ",
'group_product_characteristics': fields.boolean("Support custom product attributes",
group='base.group_user,base.group_portal,base.group_public',
implied_group='product.group_product_characteristics',
help="""Allow to manage several characteristics per product. This characteristics are used for filter and compare your products. As an example, if you sell all in one computers, you may have characteristics like RAM, Processor Speed, Manufacturing, to compare products"""),
help="Lets you add multiple custom attributes on products, "
"usable to filter and compare them. "
"For example if you sell computers, you could add custom attributes such as RAM size "
"or CPU speed to compare your products"""),
}
+3
View File
@@ -112,6 +112,9 @@ class Website(orm.Model):
values['product_id'] = product_id
values['order_id'] = order.id
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
values['name'] = "%s: %s" % (product.name, product.variants) if product.variants else product.name
if order_line_id:
order_line_obj.write(cr, SUPERUSER_ID, order_line_ids, values, context=context)
else:
@@ -44,27 +44,36 @@
trigger: {
modal: {
stopOnClose: true,
afterSubmit: 'product-page',
},
},
},
{
stepId: 'enter-name',
element: '.modal input[type=text]',
sampleText: 'New Product',
placement: 'right',
title: "Choose name",
content: "Enter a name for your new product then click 'Continue'.",
trigger: 'keyup',
},
{
stepId: 'continue-name',
element: '.modal button.btn-primary',
placement: 'right',
title: "Create Product",
content: "Click <em>Continue</em> to create the product.",
trigger: 'reload',
},
{
stepId: 'product-page',
title: "New product created",
content: "This page contains all the information related to the new product.",
template: self.popover({ next: "OK" }),
backdrop: true,
},
{
stepId: 'edit-price',
element: '.product_price',
stepId: 'edit-price-cke',
element: '.product_price .oe_currency_value',
sampleText: '20.50',
placement: 'left',
title: "Change the price",
content: "Edit the price of this product by clicking on the amount.",
@@ -76,17 +85,24 @@
placement: 'top',
title: "Update image",
content: "Click here to set an image describing your product.",
triggers: function () {
function registerClick () {
$('button.hover-edition-button').one('click', function () {
$('#wrap img.img:first').off('hover', registerClick);
self.moveToNextStep();
});
}
$('#wrap img.img:first').on('hover', registerClick);
triggers: function (callback) {
var self = this;
$(self.element).on('mouseenter', function () {
$(this).off('mouseenter');
setTimeout(function () {
(callback || self.tour.moveToNextStep).apply(self.tour);
},0);
});
},
},
{
stepId: 'update-image-button',
element: 'button.hover-edition-button:visible',
placement: 'top',
title: "Update image",
content: "Click here to set an image describing your product.",
trigger: 'click',
},
{
stepId: 'upload-image',
element: '.well a.pull-right',
@@ -94,7 +110,7 @@
title: "Select an Image",
content: "Let's select an existing image.",
template: self.popover({ fixed: true }),
trigger: 'click',
trigger: 'ajax'
},
{
stepId: 'select-image',
@@ -103,13 +119,22 @@
title: "Select an Image",
content: "Let's select an imac image.",
template: self.popover({ fixed: true }),
trigger: 'click',
triggers: function (callback) {
var self = this;
var click = function () {
$('.modal-dialog.select-image img').off('click', click);
setTimeout(function () {
(callback || self.tour.moveToNextStep).apply(self.tour);
},0);
};
$('.modal-dialog.select-image img').on('click', click);
},
},
{
stepId: 'save-image',
element: 'button.save',
placement: 'bottom',
title: "Save the Image",
title: "Select this Image",
content: "Click to add the image to the product decsription.",
template: self.popover({ fixed: true }),
trigger: 'click',
@@ -139,7 +164,7 @@
title: "Save your modifications",
content: "Once you click on save, your product is updated.",
template: self.popover({ fixed: true }),
trigger: 'click',
trigger: 'reload',
},
{
@@ -148,7 +173,7 @@
placement: 'top',
title: "Publish your product",
content: "Click to publish your product so your customers can see it.",
trigger: 'click',
trigger: 'ajax'
},
{
stepId: 'congratulations',
@@ -190,8 +215,8 @@
website.EditorShopTest = website.Test.extend({
id: 'shoptest',
name: "Try to by products",
id: 'shop_buy_product',
name: "Try to buy products",
path: '/shop',
init: function (editor) {
var self = this;
@@ -212,9 +237,7 @@
{
stepId: 'choose-ipod',
element: 'input[name="product_id"]:not([checked])',
trigger: {
id: 'mouseup',
},
trigger: 'mouseup',
},
{
stepId: 'add-ipod',
@@ -231,7 +254,7 @@
{
stepId: 'more-product',
element: '.oe_mycart a.js_add_cart_json:eq(1)',
trigger: 'click',
trigger: 'ajax',
},
{
stepId: 'less-product',
@@ -241,17 +264,8 @@
{
stepId: 'number-product',
element: '.oe_mycart input.js_quantity',
sampleText: '1',
trigger: 'reload',
beforeTrigger: function (tour) {
if (parseInt($(".oe_mycart input.js_quantity").val(),10) !== 1)
$(".oe_mycart input.js_quantity").val("1").change();
},
afterTrigger: function (tour) {
if ($(".oe_mycart input.js_quantity").size() !== 1)
throw "Can't remove suggested item from my cart";
if (parseInt($(".oe_mycart input.js_quantity").val(),10) !== 1)
throw "Can't defined number of items in my cart";
},
},
{
stepId: 'go-checkout-product',
+1 -2
View File
@@ -1,2 +1 @@
#TODO : fix test runing
#import test_ui
import test_ui
+2
View File
@@ -3,4 +3,6 @@ import openerp.addons.website.tests.test_ui as test_ui
def load_tests(loader, base, _):
base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'website_sale-sale_process-test.js'),
{ 'action': 'website.action_website_homepage' }, 120.0))
base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'website_sale-sale_process-test-2.js'),
{ 'action': 'website.action_website_homepage' }, 120.0))
return base
@@ -0,0 +1,28 @@
var testRunner = require('../../../website/tests/ui_suite/ui_test_runner.js');
var waitFor = testRunner.waitFor;
testRunner.run(function websiteSaleTest (page, timeout) {
page.evaluate(function () { localStorage.clear(); });
waitFor(function clientReady () {
return page.evaluate(function () {
return window.$ && window.openerp && window.openerp.website
&& window.openerp.website.TestConsole
&& window.openerp.website.TestConsole.test('shop');
});
}, function executeTest () {
page.evaluate(function () {
window.openerp.website.TestConsole.test('shop').run(true);
});
waitFor(function testExecuted () {
var after = page.evaluate(function () {
return window.$ && $('button[data-action=edit]').is(":visible") &&
$('data-snippet-id="big-picture"').length;
});
return after;
}, function finish () {
console.log('{ "event": "success" }');
phantom.exit();
}, 4*timeout/5);
}, timeout/5);
});
@@ -8,11 +8,11 @@ testRunner.run(function websiteSaleTest (page, timeout) {
return page.evaluate(function () {
return window.$ && window.openerp && window.openerp.website
&& window.openerp.website.TestConsole
&& window.openerp.website.TestConsole.test('shoptest');
&& window.openerp.website.TestConsole.test('shop_buy_product');
});
}, function executeTest () {
page.evaluate(function () {
window.openerp.website.TestConsole.test('shoptest').run(true);
window.openerp.website.TestConsole.test('shop_buy_product').run(true);
});
}, timeout);
});
+2 -3
View File
@@ -487,13 +487,12 @@
<td t-if="line.product_id.product_tmpl_id">
<div>
<a t-attf-href="/shop/product/#{ slug(line.product_id.product_tmpl_id) }/">
<strong t-field="line.product_id.name"/>
<t t-if="line.product_id.variants">(<t t-esc="line.product_id.variants"/>)</t>
<strong t-field="line.name"/>
</a>
</div>
<div class="text-muted" t-field="line.product_id.description_sale"/>
</td>
<td class="text-center">
<td class="text-center" name="price">
<t t-if="abs(line.product_id.lst_price - line.price_unit) &gt; 0.2">
<del class="text-danger"
t-field="line.product_id.lst_price" t-field-options='{

Some files were not shown because too many files have changed in this diff Show More