[MERGE] forward port branch 9.0 up to cb1ae53
This commit is contained in:
@@ -101,7 +101,7 @@ class AccountBankStatement(models.Model):
|
|||||||
journals = self.env['account.journal'].search([('type', '=', journal_type), ('company_id', '=', company_id)])
|
journals = self.env['account.journal'].search([('type', '=', journal_type), ('company_id', '=', company_id)])
|
||||||
if journals:
|
if journals:
|
||||||
return journals[0]
|
return journals[0]
|
||||||
return False
|
return self.env['account.journal']
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _get_opening_balance(self, journal_id):
|
def _get_opening_balance(self, journal_id):
|
||||||
|
|||||||
@@ -508,7 +508,7 @@ class AccountInvoice(models.Model):
|
|||||||
report_invoice = self.env['report']._get_report_from_name('account.report_invoice')
|
report_invoice = self.env['report']._get_report_from_name('account.report_invoice')
|
||||||
except IndexError:
|
except IndexError:
|
||||||
report_invoice = False
|
report_invoice = False
|
||||||
if report_invoice:
|
if report_invoice and report_invoice.attachment:
|
||||||
for invoice in self:
|
for invoice in self:
|
||||||
with invoice.env.do_in_draft():
|
with invoice.env.do_in_draft():
|
||||||
invoice.number, invoice.state = invoice.move_name, 'open'
|
invoice.number, invoice.state = invoice.move_name, 'open'
|
||||||
|
|||||||
@@ -316,8 +316,10 @@ class xml_decl(osv.TransientModel):
|
|||||||
|
|
||||||
numlgn = 0
|
numlgn = 0
|
||||||
for linekey in entries:
|
for linekey in entries:
|
||||||
numlgn += 1
|
|
||||||
amounts = entries[linekey]
|
amounts = entries[linekey]
|
||||||
|
if round(amounts[0], 0) == 0:
|
||||||
|
continue
|
||||||
|
numlgn += 1
|
||||||
item = ET.SubElement(datas, 'Item')
|
item = ET.SubElement(datas, 'Item')
|
||||||
self._set_Dim(item, 'EXSEQCODE', unicode(numlgn))
|
self._set_Dim(item, 'EXSEQCODE', unicode(numlgn))
|
||||||
self._set_Dim(item, 'EXTRF', unicode(linekey.EXTRF))
|
self._set_Dim(item, 'EXTRF', unicode(linekey.EXTRF))
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ class ResCompany(osv.Model):
|
|||||||
Acquirer = self.pool['payment.acquirer']
|
Acquirer = self.pool['payment.acquirer']
|
||||||
company_id = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.id
|
company_id = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.id
|
||||||
paypal_ids = Acquirer.search(cr, uid, [
|
paypal_ids = Acquirer.search(cr, uid, [
|
||||||
('website_published', '=', True),
|
|
||||||
('name', 'ilike', 'paypal'),
|
('name', 'ilike', 'paypal'),
|
||||||
('company_id', '=', company_id),
|
('company_id', '=', company_id),
|
||||||
], limit=1, context=context)
|
], limit=1, context=context)
|
||||||
@@ -24,7 +23,6 @@ class ResCompany(osv.Model):
|
|||||||
company_id = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.id
|
company_id = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.id
|
||||||
paypal_account = self.browse(cr, uid, id, context=context).paypal_account
|
paypal_account = self.browse(cr, uid, id, context=context).paypal_account
|
||||||
paypal_ids = Acquirer.search(cr, uid, [
|
paypal_ids = Acquirer.search(cr, uid, [
|
||||||
('website_published', '=', True),
|
|
||||||
('paypal_email_account', '=', paypal_account),
|
('paypal_email_account', '=', paypal_account),
|
||||||
('company_id', '=', company_id),
|
('company_id', '=', company_id),
|
||||||
], context=context)
|
], context=context)
|
||||||
|
|||||||
@@ -379,7 +379,10 @@ class task(osv.osv):
|
|||||||
""" Gives default stage_id """
|
""" Gives default stage_id """
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
return self.stage_find(cr, uid, [], context.get('default_project_id'), [('fold', '=', False)], context=context)
|
default_project_id = context.get('default_project_id')
|
||||||
|
if not default_project_id:
|
||||||
|
return False
|
||||||
|
return self.stage_find(cr, uid, [], default_project_id, [('fold', '=', False)], context=context)
|
||||||
|
|
||||||
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
|
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
|
||||||
if context is None:
|
if context is None:
|
||||||
@@ -417,11 +420,16 @@ class task(osv.osv):
|
|||||||
|
|
||||||
@api.cr_uid_ids_context
|
@api.cr_uid_ids_context
|
||||||
def onchange_project(self, cr, uid, id, project_id, context=None):
|
def onchange_project(self, cr, uid, id, project_id, context=None):
|
||||||
|
values = {}
|
||||||
if project_id:
|
if project_id:
|
||||||
project = self.pool.get('project.project').browse(cr, uid, project_id, context=context)
|
project = self.pool.get('project.project').browse(cr, uid, project_id, context=context)
|
||||||
if project and project.partner_id:
|
if project and project.partner_id:
|
||||||
return {'value': {'partner_id': project.partner_id.id}}
|
values['partner_id'] = project.partner_id.id
|
||||||
return {}
|
values['stage_id'] = self.stage_find(cr, uid, [], project_id, [('fold', '=', False)], context=context)
|
||||||
|
else:
|
||||||
|
values['stage_id'] = False
|
||||||
|
values['partner_id'] = False
|
||||||
|
return {'value': values}
|
||||||
|
|
||||||
def onchange_user_id(self, cr, uid, ids, user_id, context=None):
|
def onchange_user_id(self, cr, uid, ids, user_id, context=None):
|
||||||
vals = {}
|
vals = {}
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ class project_issue(osv.Model):
|
|||||||
""" Gives default stage_id """
|
""" Gives default stage_id """
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
return self.stage_find(cr, uid, [], context.get('default_project_id'), [('fold', '=', False)], context=context)
|
default_project_id = context.get('default_project_id')
|
||||||
|
if not default_project_id:
|
||||||
|
return False
|
||||||
|
return self.stage_find(cr, uid, [], default_project_id, [('fold', '=', False)], context=context)
|
||||||
|
|
||||||
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
|
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
|
||||||
if context is None:
|
if context is None:
|
||||||
@@ -117,11 +120,18 @@ class project_issue(osv.Model):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
def on_change_project(self, cr, uid, ids, project_id, context=None):
|
def on_change_project(self, cr, uid, ids, project_id, context=None):
|
||||||
|
values = {}
|
||||||
if project_id:
|
if project_id:
|
||||||
project = self.pool.get('project.project').browse(cr, uid, project_id, context=context)
|
project = self.pool.get('project.project').browse(cr, uid, project_id, context=context)
|
||||||
if project and project.partner_id:
|
if project and project.partner_id:
|
||||||
return {'value': {'partner_id': project.partner_id.id, 'email_from': project.partner_id.email}}
|
values['partner_id'] = project.partner_id.id
|
||||||
return {}
|
values['email_from'] = project.partner_id.email
|
||||||
|
values['stage_id'] = self.stage_find(cr, uid, [], project_id, [('fold', '=', False)], context=context)
|
||||||
|
else:
|
||||||
|
values['partner_id'] = False
|
||||||
|
values['email_from'] = False
|
||||||
|
values['stage_id'] = False
|
||||||
|
return {'value': values}
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
'id': fields.integer('ID', readonly=True),
|
'id': fields.integer('ID', readonly=True),
|
||||||
|
|||||||
@@ -159,13 +159,14 @@ class AccountInvoice(models.Model):
|
|||||||
#line['tax_ids'] is like [(4, tax_id, None), (4, tax_id2, None)...]
|
#line['tax_ids'] is like [(4, tax_id, None), (4, tax_id2, None)...]
|
||||||
taxes = self.env['account.tax'].browse([x[1] for x in line['tax_ids']])
|
taxes = self.env['account.tax'].browse([x[1] for x in line['tax_ids']])
|
||||||
price_unit = taxes.compute_all(price_unit, currency=inv.currency_id, quantity=1.0)['total_excluded']
|
price_unit = taxes.compute_all(price_unit, currency=inv.currency_id, quantity=1.0)['total_excluded']
|
||||||
|
price_before = line.get('price', 0.0)
|
||||||
line.update({'price': round(valuation_price_unit * line['quantity'], account_prec)})
|
line.update({'price': round(valuation_price_unit * line['quantity'], account_prec)})
|
||||||
diff_res.append({
|
diff_res.append({
|
||||||
'type': 'src',
|
'type': 'src',
|
||||||
'name': i_line.name[:64],
|
'name': i_line.name[:64],
|
||||||
'price_unit': round(price_unit - valuation_price_unit, account_prec),
|
'price_unit': round(price_unit - valuation_price_unit, account_prec),
|
||||||
'quantity': line['quantity'],
|
'quantity': line['quantity'],
|
||||||
'price': round((price_unit - valuation_price_unit) * line['quantity'], account_prec),
|
'price': round(price_before - line.get('price', 0.0), account_prec),
|
||||||
'account_id': acc,
|
'account_id': acc,
|
||||||
'product_id': line['product_id'],
|
'product_id': line['product_id'],
|
||||||
'uom_id': line['uom_id'],
|
'uom_id': line['uom_id'],
|
||||||
|
|||||||
@@ -77,12 +77,15 @@ class PurchaseOrder(models.Model):
|
|||||||
types = type_obj.search([('code', '=', 'incoming'), ('warehouse_id', '=', False)])
|
types = type_obj.search([('code', '=', 'incoming'), ('warehouse_id', '=', False)])
|
||||||
return types[:1]
|
return types[:1]
|
||||||
|
|
||||||
@api.depends('order_line.move_ids.picking_id')
|
@api.depends('order_line.move_ids')
|
||||||
def _compute_picking(self):
|
def _compute_picking(self):
|
||||||
for order in self:
|
for order in self:
|
||||||
pickings = self.env['stock.picking']
|
pickings = self.env['stock.picking']
|
||||||
for line in order.order_line:
|
for line in order.order_line:
|
||||||
moves = line.move_ids.filtered(lambda r: r.state != 'cancel')
|
# We keep a limited scope on purpose. Ideally, we should also use move_orig_ids and
|
||||||
|
# do some recursive search, but that could be prohibitive if not done correctly.
|
||||||
|
moves = line.move_ids | line.move_ids.mapped('returned_move_ids')
|
||||||
|
moves = moves.filtered(lambda r: r.state != 'cancel')
|
||||||
pickings |= moves.mapped('picking_id')
|
pickings |= moves.mapped('picking_id')
|
||||||
order.picking_ids = pickings
|
order.picking_ids = pickings
|
||||||
order.picking_count = len(pickings)
|
order.picking_count = len(pickings)
|
||||||
|
|||||||
@@ -33,6 +33,16 @@ class procurement_order(osv.osv):
|
|||||||
|
|
||||||
def _run(self, cr, uid, procurement, context=None):
|
def _run(self, cr, uid, procurement, context=None):
|
||||||
if self._is_procurement_task(cr, uid, procurement, context=context) and not procurement.task_id:
|
if self._is_procurement_task(cr, uid, procurement, context=context) and not procurement.task_id:
|
||||||
|
# If the SO was confirmed, cancelled, set to draft then confirmed, avoid creating a new
|
||||||
|
# task.
|
||||||
|
if procurement.sale_line_id:
|
||||||
|
existing_task = self.pool['project.task'].search(
|
||||||
|
cr, uid, [('sale_line_id', '=', procurement.sale_line_id.id)],
|
||||||
|
context=context
|
||||||
|
)
|
||||||
|
if existing_task:
|
||||||
|
return existing_task
|
||||||
|
|
||||||
#create a task for the procurement
|
#create a task for the procurement
|
||||||
return self._create_service_task(cr, uid, procurement, context=context)
|
return self._create_service_task(cr, uid, procurement, context=context)
|
||||||
return super(procurement_order, self)._run(cr, uid, procurement, context=context)
|
return super(procurement_order, self)._run(cr, uid, procurement, context=context)
|
||||||
|
|||||||
+27
-9
@@ -412,10 +412,12 @@ class stock_quant(osv.osv):
|
|||||||
quants_reconcile = []
|
quants_reconcile = []
|
||||||
to_move_quants = []
|
to_move_quants = []
|
||||||
self._check_location(cr, uid, location_to, context=context)
|
self._check_location(cr, uid, location_to, context=context)
|
||||||
|
check_lot = False
|
||||||
for quant, qty in quants:
|
for quant, qty in quants:
|
||||||
if not quant:
|
if not quant:
|
||||||
#If quant is None, we will create a quant to move (and potentially a negative counterpart too)
|
#If quant is None, we will create a quant to move (and potentially a negative counterpart too)
|
||||||
quant = self._quant_create(cr, uid, qty, move, lot_id=lot_id, owner_id=owner_id, src_package_id=src_package_id, dest_package_id=dest_package_id, force_location_from=location_from, force_location_to=location_to, context=context)
|
quant = self._quant_create(cr, uid, qty, move, lot_id=lot_id, owner_id=owner_id, src_package_id=src_package_id, dest_package_id=dest_package_id, force_location_from=location_from, force_location_to=location_to, context=context)
|
||||||
|
check_lot = True
|
||||||
else:
|
else:
|
||||||
self._quant_split(cr, uid, quant, qty, context=context)
|
self._quant_split(cr, uid, quant, qty, context=context)
|
||||||
to_move_quants.append(quant)
|
to_move_quants.append(quant)
|
||||||
@@ -434,6 +436,26 @@ class stock_quant(osv.osv):
|
|||||||
for quant in quants_reconcile:
|
for quant in quants_reconcile:
|
||||||
self._quant_reconcile_negative(cr, uid, quant, move, context=context)
|
self._quant_reconcile_negative(cr, uid, quant, move, context=context)
|
||||||
|
|
||||||
|
# In case of serial tracking, check if the product does not exist somewhere internally already
|
||||||
|
# Checking that a positive quant already exists in an internal location is too restrictive.
|
||||||
|
# Indeed, if a warehouse is configured with several steps (e.g. "Pick + Pack + Ship") and
|
||||||
|
# one step is forced (creates a quant of qty = -1.0), it is not possible afterwards to
|
||||||
|
# correct the inventory unless the product leaves the stock.
|
||||||
|
picking_type = move.picking_id and move.picking_id.picking_type_id or False
|
||||||
|
if check_lot and lot_id and move.product_id.tracking == 'serial' and (not picking_type or (picking_type.use_create_lots or picking_type.use_existing_lots)):
|
||||||
|
other_quants = self.search(cr, uid, [('product_id', '=', move.product_id.id), ('lot_id', '=', lot_id),
|
||||||
|
('location_id.usage', '=', 'internal')], context=context)
|
||||||
|
|
||||||
|
if other_quants:
|
||||||
|
# We raise an error if:
|
||||||
|
# - the total quantity is strictly larger than 1.0
|
||||||
|
# - there are more than one negative quant, to avoid situations where the user would
|
||||||
|
# force the quantity at several steps of the process
|
||||||
|
other_quants = self.browse(cr, uid, other_quants, context=context)
|
||||||
|
if sum(other_quants.mapped('qty')) > 1.0 or len([q for q in other_quants.mapped('qty') if q < 0]) > 1:
|
||||||
|
lot_name = self.pool['stock.production.lot'].browse(cr, uid, lot_id, context=context).name
|
||||||
|
raise UserError(_('The serial number %s is already in stock.') % lot_name + _("Otherwise make sure the right stock/owner is set."))
|
||||||
|
|
||||||
def move_quants_write(self, cr, uid, quants, move, location_dest_id, dest_package_id, lot_id = False, entire_pack=False, context=None):
|
def move_quants_write(self, cr, uid, quants, move, location_dest_id, dest_package_id, lot_id = False, entire_pack=False, context=None):
|
||||||
context=context or {}
|
context=context or {}
|
||||||
vals = {'location_id': location_dest_id.id,
|
vals = {'location_id': location_dest_id.id,
|
||||||
@@ -458,7 +480,8 @@ class stock_quant(osv.osv):
|
|||||||
if ops:
|
if ops:
|
||||||
restrict_lot_id = lot_id
|
restrict_lot_id = lot_id
|
||||||
location = ops.location_id
|
location = ops.location_id
|
||||||
domain += [('owner_id', '=', ops.owner_id.id)]
|
if ops.owner_id:
|
||||||
|
domain += [('owner_id', '=', ops.owner_id.id)]
|
||||||
if ops.package_id and not ops.product_id:
|
if ops.package_id and not ops.product_id:
|
||||||
domain += [('package_id', 'child_of', ops.package_id.id)]
|
domain += [('package_id', 'child_of', ops.package_id.id)]
|
||||||
elif ops.package_id and ops.product_id:
|
elif ops.package_id and ops.product_id:
|
||||||
@@ -469,7 +492,8 @@ class stock_quant(osv.osv):
|
|||||||
else:
|
else:
|
||||||
restrict_lot_id = move.restrict_lot_id.id
|
restrict_lot_id = move.restrict_lot_id.id
|
||||||
location = move.location_id
|
location = move.location_id
|
||||||
domain += [('owner_id', '=', move.restrict_partner_id.id)]
|
if move.restrict_partner_id:
|
||||||
|
domain += [('owner_id', '=', move.restrict_partner_id.id)]
|
||||||
domain += [('location_id', 'child_of', move.location_id.id)]
|
domain += [('location_id', 'child_of', move.location_id.id)]
|
||||||
if context.get('force_company'):
|
if context.get('force_company'):
|
||||||
domain += [('company_id', '=', context.get('force_company'))]
|
domain += [('company_id', '=', context.get('force_company'))]
|
||||||
@@ -565,16 +589,10 @@ class stock_quant(osv.osv):
|
|||||||
negative_quant_id = self.create(cr, SUPERUSER_ID, negative_vals, context=context)
|
negative_quant_id = self.create(cr, SUPERUSER_ID, negative_vals, context=context)
|
||||||
vals.update({'propagated_from_id': negative_quant_id})
|
vals.update({'propagated_from_id': negative_quant_id})
|
||||||
|
|
||||||
# In case of serial tracking, check if the product does not exist somewhere internally already
|
|
||||||
picking_type = move.picking_id and move.picking_id.picking_type_id or False
|
picking_type = move.picking_id and move.picking_id.picking_type_id or False
|
||||||
if lot_id and move.product_id.tracking == 'serial' and (not picking_type or (picking_type.use_create_lots or picking_type.use_existing_lots)):
|
if lot_id and move.product_id.tracking == 'serial' and (not picking_type or (picking_type.use_create_lots or picking_type.use_existing_lots)):
|
||||||
if qty != 1.0:
|
if qty != 1.0:
|
||||||
raise UserError(_('You should only receive by the piece with the same serial number'))
|
raise UserError(_('You should only receive by the piece with the same serial number'))
|
||||||
other_quants = self.search(cr, uid, [('product_id', '=', move.product_id.id), ('lot_id', '=', lot_id),
|
|
||||||
('qty', '>', 0.0), ('location_id.usage', '=', 'internal')], context=context)
|
|
||||||
if other_quants:
|
|
||||||
lot_name = self.pool['stock.production.lot'].browse(cr, uid, lot_id, context=context).name
|
|
||||||
raise UserError(_('The serial number %s is already in stock.') % lot_name + _("Otherwise make sure the right stock/owner is set."))
|
|
||||||
|
|
||||||
#create the quant as superuser, because we want to restrict the creation of quant manually: we should always use this method to create quants
|
#create the quant as superuser, because we want to restrict the creation of quant manually: we should always use this method to create quants
|
||||||
quant_id = self.create(cr, SUPERUSER_ID, vals, context=context)
|
quant_id = self.create(cr, SUPERUSER_ID, vals, context=context)
|
||||||
@@ -2461,7 +2479,7 @@ class stock_move(osv.osv):
|
|||||||
rounding = ops.product_id.uom_id.rounding
|
rounding = ops.product_id.uom_id.rounding
|
||||||
for pack_lot in ops.pack_lot_ids:
|
for pack_lot in ops.pack_lot_ids:
|
||||||
lot_qty[pack_lot.lot_id.id] = uom_obj._compute_qty(cr, uid, ops.product_uom_id.id, pack_lot.qty, ops.product_id.uom_id.id)
|
lot_qty[pack_lot.lot_id.id] = uom_obj._compute_qty(cr, uid, ops.product_uom_id.id, pack_lot.qty, ops.product_id.uom_id.id)
|
||||||
for record in ops.linked_move_operation_ids:
|
for record in ops.linked_move_operation_ids.filtered(lambda x: x.move_id.id in main_domain):
|
||||||
move_qty = record.qty
|
move_qty = record.qty
|
||||||
move = record.move_id
|
move = record.move_id
|
||||||
domain = main_domain[move.id]
|
domain = main_domain[move.id]
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
import models # noqa
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
import stock_dropshipping # noqa
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from openerp import models, api
|
||||||
|
|
||||||
|
class AccountInvoice(models.Model):
|
||||||
|
_inherit = 'account.invoice'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _anglo_saxon_sale_move_lines(self, i_line):
|
||||||
|
for sale_line in i_line.sale_line_ids:
|
||||||
|
for proc in sale_line.procurement_ids:
|
||||||
|
if proc.purchase_line_id:
|
||||||
|
# if the invoice line is related to sale order lines having one of its
|
||||||
|
# procurement_ids with a purchase_line_id set, it means that it is a
|
||||||
|
# confirmed dropship and in that case we mustn't create the cost of
|
||||||
|
# sale line (because the product won't enter the stock)
|
||||||
|
return []
|
||||||
|
return super(AccountInvoice, self)._anglo_saxon_sale_move_lines(i_line)
|
||||||
@@ -640,8 +640,8 @@ class Database(http.Controller):
|
|||||||
try:
|
try:
|
||||||
# country code could be = "False" which is actually True in python
|
# country code could be = "False" which is actually True in python
|
||||||
country_code = post.get('country_code') or False
|
country_code = post.get('country_code') or False
|
||||||
request.session.proxy("db").create_database(master_pwd, name, bool(post.get('demo')), lang, password, post.get('login'), country_code)
|
request.session.proxy("db").create_database(master_pwd, name, bool(post.get('demo')), lang, password, post['login'], country_code)
|
||||||
request.session.authenticate(name, 'admin', password)
|
request.session.authenticate(name, post['login'], password)
|
||||||
return http.local_redirect('/web/')
|
return http.local_redirect('/web/')
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
error = "Database creation error: %s" % e
|
error = "Database creation error: %s" % e
|
||||||
|
|||||||
@@ -1565,7 +1565,7 @@ var FieldStatus = common.AbstractField.extend({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 300),
|
}, 300, true),
|
||||||
});
|
});
|
||||||
|
|
||||||
var FieldMonetary = FieldFloat.extend({
|
var FieldMonetary = FieldFloat.extend({
|
||||||
|
|||||||
@@ -679,7 +679,6 @@ var PivotView = View.extend({
|
|||||||
if (width > 1) {
|
if (width > 1) {
|
||||||
var total_cell = {width:nbr_measures, height: depth, title:""};
|
var total_cell = {width:nbr_measures, height: depth, title:""};
|
||||||
if (nbr_measures === 1) {
|
if (nbr_measures === 1) {
|
||||||
total_cell.title = this.measures[this.active_measures[0]].string;
|
|
||||||
total_cell.total = true;
|
total_cell.total = true;
|
||||||
}
|
}
|
||||||
result[0].push(total_cell);
|
result[0].push(total_cell);
|
||||||
|
|||||||
@@ -69,7 +69,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<label for="login" class="control-label">Email</label>
|
<label for="login" class="control-label">Email</label>
|
||||||
<input id="login" type="text" name="login" class="form-control" autocomplete="off"/>
|
<input id="login" type="text" name="login" class="form-control" required="required" autocomplete="off"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -40,7 +40,10 @@ var Tips = Class.extend({
|
|||||||
on_view: function() {
|
on_view: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
bus.on('DOM_updated', this, function() {
|
if (this._DOM_updated) {
|
||||||
|
bus.off('DOM_updated', this, this._DOM_updated);
|
||||||
|
}
|
||||||
|
this._DOM_updated = function() {
|
||||||
var action_id = self.view.ViewManager.action ? self.view.ViewManager.action.id : null;
|
var action_id = self.view.ViewManager.action ? self.view.ViewManager.action.id : null;
|
||||||
var model = self.view.fields_view.model;
|
var model = self.view.fields_view.model;
|
||||||
var mode = self.view.fields_view.type;
|
var mode = self.view.fields_view.type;
|
||||||
@@ -49,14 +52,17 @@ var Tips = Class.extend({
|
|||||||
self.on_form_view(action_id, model);
|
self.on_form_view(action_id, model);
|
||||||
}
|
}
|
||||||
else if (self.view.hasOwnProperty('editor')) {
|
else if (self.view.hasOwnProperty('editor')) {
|
||||||
self.view.on('view_list_rendered', self, function() {
|
if (self._view_list_rendered) {
|
||||||
self.eval_tips(action_id, model, mode);
|
self.view.off('view_list_rendered', self, self._view_list_rendered);
|
||||||
});
|
}
|
||||||
|
self._view_list_rendered = function() { self.eval_tips(action_id, model, mode); };
|
||||||
|
self.view.on('view_list_rendered', self, self._view_list_rendered);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.eval_tips(action_id, model, mode);
|
self.eval_tips(action_id, model, mode);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
bus.on('DOM_updated', this, this._DOM_updated);
|
||||||
},
|
},
|
||||||
|
|
||||||
on_form_view: function(action_id, model) {
|
on_form_view: function(action_id, model) {
|
||||||
@@ -64,19 +70,26 @@ var Tips = Class.extend({
|
|||||||
var type = this.view.datarecord ? this.view.datarecord.type : null
|
var type = this.view.datarecord ? this.view.datarecord.type : null
|
||||||
|
|
||||||
if ($('.oe_chatter').length > 0) {
|
if ($('.oe_chatter').length > 0) {
|
||||||
this.view.on('chatter_messages_displayed', this, function() {
|
if (this._chatter_messages_displayed) {
|
||||||
self.eval_tips(action_id, model, 'form', type);
|
this.view.off('chatter_messages_displayed', this, this._chatter_messages_displayed);
|
||||||
});
|
}
|
||||||
|
this._chatter_messages_displayed = function() { self.eval_tips(action_id, model, 'form', type); };
|
||||||
|
this.view.on('chatter_messages_displayed', this, this._chatter_messages_displayed);
|
||||||
} else {
|
} else {
|
||||||
this.eval_tips(action_id, model, 'form', type);
|
this.eval_tips(action_id, model, 'form', type);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.view.on('to_edit_mode', this, function() {
|
if (this._to_edit_mode) {
|
||||||
self.remove_tips();
|
this.view.off('to_edit_mode', this, this._to_edit_mode);
|
||||||
});
|
}
|
||||||
this.view.on('to_view_mode', this, function() {
|
this._to_edit_mode = function() { self.remove_tips(); };
|
||||||
self.eval_tips(action_id, model, 'form', type);
|
this.view.on('to_edit_mode', this, this._to_edit_mode);
|
||||||
});
|
|
||||||
|
if (this._to_view_mode) {
|
||||||
|
this.view.off('to_view_mode', this, this._to_view_mode);
|
||||||
|
}
|
||||||
|
this._to_view_mode = function() { self.eval_tips(action_id, model, 'form', type); };
|
||||||
|
this.view.on('to_view_mode', this, this._to_view_mode);
|
||||||
},
|
},
|
||||||
|
|
||||||
eval_tips: function(action_id, model, mode, type) {
|
eval_tips: function(action_id, model, mode, type) {
|
||||||
@@ -123,11 +136,12 @@ var Tips = Class.extend({
|
|||||||
|
|
||||||
var Tip = Class.extend({
|
var Tip = Class.extend({
|
||||||
init: function(tip) {
|
init: function(tip) {
|
||||||
|
var self = this;
|
||||||
this.tip = tip;
|
this.tip = tip;
|
||||||
|
|
||||||
this.highlight_selector = tip.highlight_selector;
|
this.highlight_selector = tip.highlight_selector;
|
||||||
this.end_selector = tip.end_selector ? tip.end_selector : tip.highlight_selector;
|
this.end_selector = tip.end_selector ? tip.end_selector : tip.highlight_selector;
|
||||||
this.triggers = tip.trigger_selector ? tip.trigger_selector.split(',') : [];
|
this.triggers = tip.trigger_selector ? tip.trigger_selector.split(',') : [];
|
||||||
|
this._resize = function() { self._set_breathing_position(); };
|
||||||
},
|
},
|
||||||
|
|
||||||
do_tip: function() {
|
do_tip: function() {
|
||||||
@@ -171,17 +185,9 @@ var Tip = Class.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// resize
|
// resize
|
||||||
bus.on('resize', this, function() {
|
bus.on('resize', this, this._resize);
|
||||||
self._set_breathing_position();
|
bus.on('image_loaded', this, this._resize);
|
||||||
});
|
bus.on('please_reposition_tip', this, this._resize);
|
||||||
|
|
||||||
bus.on('image_loaded', this, function() {
|
|
||||||
self._set_breathing_position();
|
|
||||||
});
|
|
||||||
|
|
||||||
bus.on('please_reposition_tip', this, function() {
|
|
||||||
self._set_breathing_position();
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@@ -246,6 +252,7 @@ var Tip = Class.extend({
|
|||||||
this.$overlay.on('click', function($ev) {
|
this.$overlay.on('click', function($ev) {
|
||||||
self.end_tip();
|
self.end_tip();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('keyup.web_tip', function($ev) {
|
$(document).on('keyup.web_tip', function($ev) {
|
||||||
if ($ev.which === 27) { // esc
|
if ($ev.which === 27) { // esc
|
||||||
self.end_tip();
|
self.end_tip();
|
||||||
@@ -253,9 +260,8 @@ var Tip = Class.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// resize
|
// resize
|
||||||
bus.on('resize', this, function() {
|
this._resize_position = function() { self._set_popover_position(); };
|
||||||
self._set_popover_position();
|
bus.on('resize', this, this._resize_position);
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
scroll_to_tip: function(){
|
scroll_to_tip: function(){
|
||||||
@@ -278,11 +284,16 @@ var Tip = Class.extend({
|
|||||||
this.$overlay.remove();
|
this.$overlay.remove();
|
||||||
this.$cross.remove();
|
this.$cross.remove();
|
||||||
|
|
||||||
_.each($('.oe_tip_fix_parent'), function(el) {
|
$('.oe_tip_fix_parent').each(function() {
|
||||||
$(el).removeClass('oe_tip_fix_parent');
|
$(this).removeClass('oe_tip_fix_parent');
|
||||||
});
|
});
|
||||||
$(document).off('keyup.web_tip');
|
$(document).off('keyup.web_tip');
|
||||||
|
|
||||||
|
bus.off('resize', this, this._resize_position);
|
||||||
|
bus.off('resize', this, this._resize);
|
||||||
|
bus.off('image_loaded', this, this._resize);
|
||||||
|
bus.off('please_reposition_tip', this, this._resize);
|
||||||
|
|
||||||
Tips.call('consume', [this.tip.id], {});
|
Tips.call('consume', [this.tip.id], {});
|
||||||
this.tip.is_consumed = true;
|
this.tip.is_consumed = true;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -666,6 +666,9 @@ class res_partner(osv.osv):
|
|||||||
'zoom': zoom,
|
'zoom': zoom,
|
||||||
'sensor': 'false',
|
'sensor': 'false',
|
||||||
}
|
}
|
||||||
|
google_maps_api_key = self.pool['ir.config_parameter'].get_param(cr, openerp.SUPERUSER_ID, 'google_maps_api_key', context=context)
|
||||||
|
if google_maps_api_key:
|
||||||
|
params['key'] = google_maps_api_key
|
||||||
return urlplus('//maps.googleapis.com/maps/api/staticmap' , params)
|
return urlplus('//maps.googleapis.com/maps/api/staticmap' , params)
|
||||||
|
|
||||||
def google_map_link(self, cr, uid, ids, zoom=10, context=None):
|
def google_map_link(self, cr, uid, ids, zoom=10, context=None):
|
||||||
|
|||||||
@@ -908,7 +908,14 @@ Sitemap: <t t-esc="url_root"/>sitemap.xml
|
|||||||
"fields": ["name", "address", "phone", "mobile", "fax", "email"]}'/>
|
"fields": ["name", "address", "phone", "mobile", "fax", "email"]}'/>
|
||||||
</address>
|
</address>
|
||||||
<!-- FORWARD-PORT UP TO SAAS-12 -->
|
<!-- FORWARD-PORT UP TO SAAS-12 -->
|
||||||
<a t-att-href="res_company.google_map_link()" target="_BLANK">Visualize on Google Maps</a>
|
<t t-if="'key=' in res_company.google_map_img()">
|
||||||
|
<a t-att-href="res_company.google_map_link()" target="_BLANK">
|
||||||
|
<img class="thumbnail img-responsive" t-att-src="res_company.google_map_img()" />
|
||||||
|
</a>
|
||||||
|
</t>
|
||||||
|
<t t-if="'key=' not in res_company.google_map_img()">
|
||||||
|
<a t-att-href="res_company.google_map_link()" target="_BLANK">Visualize on Google Maps</a>
|
||||||
|
</t>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template id="contactus" name="Contact us" page="True">
|
<template id="contactus" name="Contact us" page="True">
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ class WebsiteCrmPartnerAssign(WebsitePartnerPage):
|
|||||||
partners = partners[pager['offset']:pager['offset'] + self._references_per_page]
|
partners = partners[pager['offset']:pager['offset'] + self._references_per_page]
|
||||||
|
|
||||||
google_map_partner_ids = ','.join(map(str, [p.id for p in partners]))
|
google_map_partner_ids = ','.join(map(str, [p.id for p in partners]))
|
||||||
|
google_maps_api_key = request.env['ir.config_parameter'].sudo().get_param('google_maps_api_key')
|
||||||
|
|
||||||
values = {
|
values = {
|
||||||
'countries': countries,
|
'countries': countries,
|
||||||
@@ -130,6 +131,7 @@ class WebsiteCrmPartnerAssign(WebsitePartnerPage):
|
|||||||
'pager': pager,
|
'pager': pager,
|
||||||
'searches': post,
|
'searches': post,
|
||||||
'search_path': "%s" % werkzeug.url_encode(post),
|
'search_path': "%s" % werkzeug.url_encode(post),
|
||||||
|
'google_maps_api_key': google_maps_api_key,
|
||||||
}
|
}
|
||||||
return request.render("website_crm_partner_assign.index", values, status=partners and 200 or 404)
|
return request.render("website_crm_partner_assign.index", values, status=partners and 200 or 404)
|
||||||
|
|
||||||
|
|||||||
@@ -119,25 +119,27 @@
|
|||||||
|
|
||||||
<template id="ref_country" inherit_id="website_crm_partner_assign.index" customize_show="True" name="Left World Map">
|
<template id="ref_country" inherit_id="website_crm_partner_assign.index" customize_show="True" name="Left World Map">
|
||||||
<xpath expr="//ul[@id='reseller_countries']" position="after">
|
<xpath expr="//ul[@id='reseller_countries']" position="after">
|
||||||
<!-- modal for large map -->
|
<t t-if="google_maps_api_key">
|
||||||
<div class="modal fade partner_map_modal" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
|
<!-- modal for large map -->
|
||||||
<div class="modal-dialog modal-lg">
|
<div class="modal fade partner_map_modal" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
|
||||||
<div class="modal-content">
|
<div class="modal-dialog modal-lg">
|
||||||
<div class="modal-header">
|
<div class="modal-content">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<div class="modal-header">
|
||||||
<h4 class="modal-title">World Map</h4>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 class="modal-title">World Map</h4>
|
||||||
|
</div>
|
||||||
|
<iframe t-attf-src="/google_map/?width=898&height=485&partner_ids=#{ google_map_partner_ids }&partner_url=/partners/"
|
||||||
|
style="width:898px; height:485px; border:0; padding:0; margin:0;"></iframe>
|
||||||
</div>
|
</div>
|
||||||
<iframe t-attf-src="/google_map/?width=898&height=485&partner_ids=#{ google_map_partner_ids }&partner_url=/partners/"
|
</div>
|
||||||
style="width:898px; height:485px; border:0; padding:0; margin:0;"></iframe>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<!-- modal end -->
|
||||||
</div>
|
<h3>World Map<button class="btn btn-link" data-toggle="modal" data-target=".partner_map_modal"><span class="fa fa-external-link" /></button></h3>
|
||||||
<!-- modal end -->
|
<ul class="nav">
|
||||||
<h3>World Map<button class="btn btn-link" data-toggle="modal" data-target=".partner_map_modal"><span class="fa fa-external-link" /></button></h3>
|
<iframe t-attf-src="/google_map?width=260&height=240&partner_ids=#{ google_map_partner_ids }&partner_url=/partners/"
|
||||||
<ul class="nav">
|
style="width:260px; height:240px; border:0; padding:0; margin:0;" scrolling="no"></iframe>
|
||||||
<iframe t-attf-src="/google_map?width=260&height=240&partner_ids=#{ google_map_partner_ids }&partner_url=/partners/"
|
</ul>
|
||||||
style="width:260px; height:240px; border:0; padding:0; margin:0;" scrolling="no"></iframe>
|
</t>
|
||||||
</ul>
|
|
||||||
</xpath>
|
</xpath>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ class WebsiteCustomer(http.Controller):
|
|||||||
context=request.context)
|
context=request.context)
|
||||||
google_map_partner_ids = ','.join(map(str, partner_ids))
|
google_map_partner_ids = ','.join(map(str, partner_ids))
|
||||||
partners = partner_obj.browse(request.cr, openerp.SUPERUSER_ID, partner_ids, request.context)
|
partners = partner_obj.browse(request.cr, openerp.SUPERUSER_ID, partner_ids, request.context)
|
||||||
|
google_maps_api_key = request.env['ir.config_parameter'].sudo().get_param('google_maps_api_key')
|
||||||
|
|
||||||
tag_obj = request.registry['res.partner.tag']
|
tag_obj = request.registry['res.partner.tag']
|
||||||
tag_ids = tag_obj.search(cr, uid, [('website_published', '=', True), ('partner_ids', 'in', partner_ids)],
|
tag_ids = tag_obj.search(cr, uid, [('website_published', '=', True), ('partner_ids', 'in', partner_ids)],
|
||||||
@@ -103,6 +104,7 @@ class WebsiteCustomer(http.Controller):
|
|||||||
'search_path': "?%s" % werkzeug.url_encode(post),
|
'search_path': "?%s" % werkzeug.url_encode(post),
|
||||||
'tag': tag,
|
'tag': tag,
|
||||||
'tags': tags,
|
'tags': tags,
|
||||||
|
'google_maps_api_key': google_maps_api_key,
|
||||||
}
|
}
|
||||||
return request.website.render("website_customer.index", values)
|
return request.website.render("website_customer.index", values)
|
||||||
|
|
||||||
|
|||||||
@@ -68,25 +68,27 @@
|
|||||||
<!-- Option: left column: World Map -->
|
<!-- Option: left column: World Map -->
|
||||||
<template id="opt_country" inherit_id="website_customer.index" customize_show="True" name="Show Map">
|
<template id="opt_country" inherit_id="website_customer.index" customize_show="True" name="Show Map">
|
||||||
<xpath expr="//div[@id='ref_left_column']" position="inside">
|
<xpath expr="//div[@id='ref_left_column']" position="inside">
|
||||||
<!-- modal for large map -->
|
<t t-if="google_maps_api_key">
|
||||||
<div class="modal fade customer_map_modal" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
|
<!-- modal for large map -->
|
||||||
<div class="modal-dialog modal-lg">
|
<div class="modal fade customer_map_modal" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
|
||||||
<div class="modal-content">
|
<div class="modal-dialog modal-lg">
|
||||||
<div class="modal-header">
|
<div class="modal-content">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<div class="modal-header">
|
||||||
<h4 class="modal-title">World Map</h4>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 class="modal-title">World Map</h4>
|
||||||
|
</div>
|
||||||
|
<iframe t-attf-src="/google_map/?width=898&height=485&partner_ids=#{ google_map_partner_ids }&partner_url=/customers/"
|
||||||
|
style="width:898px; height:485px; border:0; padding:0; margin:0;"></iframe>
|
||||||
</div>
|
</div>
|
||||||
<iframe t-attf-src="/google_map/?width=898&height=485&partner_ids=#{ google_map_partner_ids }&partner_url=/customers/"
|
</div>
|
||||||
style="width:898px; height:485px; border:0; padding:0; margin:0;"></iframe>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<!-- modal end -->
|
||||||
</div>
|
<h3>World Map<button class="btn btn-link" data-toggle="modal" data-target=".customer_map_modal"><span class="fa fa-external-link" /></button></h3>
|
||||||
<!-- modal end -->
|
<ul class="nav">
|
||||||
<h3>World Map<button class="btn btn-link" data-toggle="modal" data-target=".customer_map_modal"><span class="fa fa-external-link" /></button></h3>
|
<iframe t-attf-src="/google_map?width=260&height=240&partner_ids=#{ google_map_partner_ids }&partner_url=/customers/"
|
||||||
<ul class="nav">
|
style="width:260px; height:240px; border:0; padding:0; margin:0;" scrolling="no"></iframe>
|
||||||
<iframe t-attf-src="/google_map?width=260&height=240&partner_ids=#{ google_map_partner_ids }&partner_url=/customers/"
|
</ul>
|
||||||
style="width:260px; height:240px; border:0; padding:0; margin:0;" scrolling="no"></iframe>
|
</t>
|
||||||
</ul>
|
|
||||||
</xpath>
|
</xpath>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -332,7 +332,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<!-- FORWARD-PORT UP TO SAAS-12 -->
|
<!-- FORWARD-PORT UP TO SAAS-12 -->
|
||||||
<a t-att-href="event.google_map_link()" target="_BLANK">Visualize on Google Maps</a>
|
<t t-if="'key=' in event.google_map_img()">
|
||||||
|
<a t-att-href="event.google_map_link()" target="_BLANK">
|
||||||
|
<img class="thumbnail img-responsive" t-att-src="event.google_map_img()" />
|
||||||
|
</a>
|
||||||
|
</t>
|
||||||
|
<t t-if="'key=' not in event.google_map_img()">
|
||||||
|
<a t-att-href="event.google_map_link()" target="_BLANK">Visualize on Google Maps</a>
|
||||||
|
</t>
|
||||||
<div itemprop="location" class="mt16 mb8" t-field="event.address_id" t-field-options='{
|
<div itemprop="location" class="mt16 mb8" t-field="event.address_id" t-field-options='{
|
||||||
"widget": "contact",
|
"widget": "contact",
|
||||||
"fields": ["address", "phone", "mobile", "fax", "email"]
|
"fields": ["address", "phone", "mobile", "fax", "email"]
|
||||||
|
|||||||
@@ -52,8 +52,10 @@ class GoogleMap(http.Controller):
|
|||||||
else:
|
else:
|
||||||
partner_url = '/partners/'
|
partner_url = '/partners/'
|
||||||
|
|
||||||
|
google_maps_api_key = request.env['ir.config_parameter'].sudo().get_param('google_maps_api_key')
|
||||||
values = {
|
values = {
|
||||||
'partner_url': partner_url,
|
'partner_url': partner_url,
|
||||||
'partner_data': json.dumps(partner_data)
|
'partner_data': json.dumps(partner_data),
|
||||||
|
'google_maps_api_key': google_maps_api_key,
|
||||||
}
|
}
|
||||||
return request.website.render("website_google_map.google_map", values)
|
return request.website.render("website_google_map.google_map", values)
|
||||||
|
|||||||
@@ -14,7 +14,13 @@
|
|||||||
var odoo_partner_data = <t t-raw="partner_data"/>;
|
var odoo_partner_data = <t t-raw="partner_data"/>;
|
||||||
</script>
|
</script>
|
||||||
<div id="odoo-google-map"></div>
|
<div id="odoo-google-map"></div>
|
||||||
<script src="//maps.google.com/maps/api/js?sensor=false"></script>
|
<t t-if="google_maps_api_key">
|
||||||
|
<script t-attf-src="//maps.google.com/maps/api/js?key=#{google_maps_api_key}"></script>
|
||||||
|
</t>
|
||||||
|
<t t-if="not google_maps_api_key">
|
||||||
|
<script src="//maps.google.com/maps/api/js"></script>
|
||||||
|
</t>
|
||||||
|
|
||||||
<script type="text/javascript" src="/website_google_map/static/src/lib/markerclusterer.js"></script>
|
<script type="text/javascript" src="/website_google_map/static/src/lib/markerclusterer.js"></script>
|
||||||
<script type="text/javascript" src="/website_google_map/static/src/js/website_google_map.js"></script>
|
<script type="text/javascript" src="/website_google_map/static/src/js/website_google_map.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ class WebsiteMembership(http.Controller):
|
|||||||
count_members += len(free_partner_ids)
|
count_members += len(free_partner_ids)
|
||||||
|
|
||||||
google_map_partner_ids = ",".join(map(str, google_map_partner_ids))
|
google_map_partner_ids = ",".join(map(str, google_map_partner_ids))
|
||||||
|
google_maps_api_key = request.env['ir.config_parameter'].sudo().get_param('google_maps_api_key')
|
||||||
|
|
||||||
partners = { p.id: p for p in partner_obj.browse(request.cr, SUPERUSER_ID, list(page_partner_ids), request.context)}
|
partners = { p.id: p for p in partner_obj.browse(request.cr, SUPERUSER_ID, list(page_partner_ids), request.context)}
|
||||||
|
|
||||||
@@ -162,6 +163,7 @@ class WebsiteMembership(http.Controller):
|
|||||||
'pager': pager,
|
'pager': pager,
|
||||||
'post': post,
|
'post': post,
|
||||||
'search': "?%s" % werkzeug.url_encode(post),
|
'search': "?%s" % werkzeug.url_encode(post),
|
||||||
|
'google_maps_api_key': google_maps_api_key,
|
||||||
}
|
}
|
||||||
return request.website.render("website_membership.index", values)
|
return request.website.render("website_membership.index", values)
|
||||||
|
|
||||||
|
|||||||
@@ -104,25 +104,27 @@
|
|||||||
<template id="opt_index_google_map" name="Left World Map"
|
<template id="opt_index_google_map" name="Left World Map"
|
||||||
customize_show="True" inherit_id="website_membership.index">
|
customize_show="True" inherit_id="website_membership.index">
|
||||||
<xpath expr="//div[@id='left_column']/ul[last()]" position="after">
|
<xpath expr="//div[@id='left_column']/ul[last()]" position="after">
|
||||||
<!-- modal for large map -->
|
<t t-if="google_maps_api_key">
|
||||||
<div class="modal fade partner_map_modal" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
|
<!-- modal for large map -->
|
||||||
<div class="modal-dialog modal-lg">
|
<div class="modal fade partner_map_modal" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
|
||||||
<div class="modal-content">
|
<div class="modal-dialog modal-lg">
|
||||||
<div class="modal-header">
|
<div class="modal-content">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<div class="modal-header">
|
||||||
<h4 class="modal-title">World Map</h4>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 class="modal-title">World Map</h4>
|
||||||
|
</div>
|
||||||
|
<iframe t-attf-src="/google_map/?width=898&height=485&partner_ids=#{ google_map_partner_ids }&partner_url=/members/"
|
||||||
|
style="width:898px; height:485px; border:0; padding:0; margin:0;"></iframe>
|
||||||
</div>
|
</div>
|
||||||
<iframe t-attf-src="/google_map/?width=898&height=485&partner_ids=#{ google_map_partner_ids }&partner_url=/members/"
|
</div>
|
||||||
style="width:898px; height:485px; border:0; padding:0; margin:0;"></iframe>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<!-- modal end -->
|
||||||
</div>
|
<h3>World Map<button class="btn btn-link" data-toggle="modal" data-target=".partner_map_modal"><span class="fa fa-external-link" /></button></h3>
|
||||||
<!-- modal end -->
|
<ul class="nav">
|
||||||
<h3>World Map<button class="btn btn-link" data-toggle="modal" data-target=".partner_map_modal"><span class="fa fa-external-link" /></button></h3>
|
<iframe t-attf-src="/google_map/?width=260&height=240&partner_ids=#{ google_map_partner_ids }&partner_url=/members/"
|
||||||
<ul class="nav">
|
style="width:260px; height:240px; border:0; padding:0; margin:0;"></iframe>
|
||||||
<iframe t-attf-src="/google_map/?width=260&height=240&partner_ids=#{ google_map_partner_ids }&partner_url=/members/"
|
</ul>
|
||||||
style="width:260px; height:240px; border:0; padding:0; margin:0;"></iframe>
|
</t>
|
||||||
</ul>
|
|
||||||
</xpath>
|
</xpath>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,8 @@ class website_sale(http.Controller):
|
|||||||
|
|
||||||
def _get_search_order(self, post):
|
def _get_search_order(self, post):
|
||||||
# OrderBy will be parsed in orm and so no direct sql injection
|
# OrderBy will be parsed in orm and so no direct sql injection
|
||||||
return 'website_published desc,%s' % post.get('order', 'website_sequence desc')
|
# id is added to be sure that order is a unique sort key
|
||||||
|
return 'website_published desc,%s , id desc' % post.get('order', 'website_sequence desc')
|
||||||
|
|
||||||
@http.route(['/shop/change_pricelist/<model("product.pricelist"):pl_id>'], type='http', auth="public", website=True)
|
@http.route(['/shop/change_pricelist/<model("product.pricelist"):pl_id>'], type='http', auth="public", website=True)
|
||||||
def pricelist_change(self, pl_id, **post):
|
def pricelist_change(self, pl_id, **post):
|
||||||
|
|||||||
@@ -775,7 +775,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center" id="td-price" name="price">
|
<td class="text-center" id="td-price" name="price">
|
||||||
<del t-attf-class="text-danger #{'' if line.product_id.lst_price > line.discounted_price else 'hidden'}" style="text-decoration: line-through; white-space: nowrap;"
|
<del t-attf-class="text-danger #{'' if line.product_id.lst_price - line.discounted_price > 0.1 else 'hidden'}" style="text-decoration: line-through; white-space: nowrap;"
|
||||||
t-field="line.product_id.lst_price" t-field-options='{
|
t-field="line.product_id.lst_price" t-field-options='{
|
||||||
"widget": "monetary",
|
"widget": "monetary",
|
||||||
"from_currency": "line.company_id.currency_id",
|
"from_currency": "line.company_id.currency_id",
|
||||||
|
|||||||
+184
-16
@@ -23,11 +23,11 @@ Although the database will follow the same process described on that form.
|
|||||||
|
|
||||||
The required steps are:
|
The required steps are:
|
||||||
|
|
||||||
* creating a request
|
* :ref:`creating a request <upgrade-api-create-method>`
|
||||||
* uploading a database dump
|
* :ref:`uploading a database dump <upgrade-api-upload-method>`
|
||||||
* running the upgrade process
|
* :ref:`running the upgrade process <upgrade-api-process-method>`
|
||||||
* obtaining the status of the database request
|
* :ref:`obtaining the status of the database request <upgrade-api-status-method>`
|
||||||
* downloading the upgraded database dump
|
* :ref:`downloading the upgraded database dump <upgrade-api-download-method>`
|
||||||
|
|
||||||
The methods
|
The methods
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
@@ -60,9 +60,11 @@ The ``create`` method
|
|||||||
:param str filename: (required) a purely informative name for you database dump file
|
:param str filename: (required) a purely informative name for you database dump file
|
||||||
:param str timezone: (optional) the timezone used by your server. Only for Odoo source version < 6.1
|
:param str timezone: (optional) the timezone used by your server. Only for Odoo source version < 6.1
|
||||||
:return: request result
|
:return: request result
|
||||||
:rtype: json dictionary
|
:rtype: JSON dictionary
|
||||||
|
|
||||||
The *create* method returns a json dictionary containing the following keys:
|
The *create* method returns a JSON dictionary containing the following keys:
|
||||||
|
|
||||||
|
.. _upgrade-api-json-failure:
|
||||||
|
|
||||||
``failures``
|
``failures``
|
||||||
''''''''''''
|
''''''''''''
|
||||||
@@ -201,11 +203,17 @@ Here are 2 examples of database upgrade request creation using:
|
|||||||
Uploading your database dump
|
Uploading your database dump
|
||||||
============================
|
============================
|
||||||
|
|
||||||
This action upload your database dump.
|
There are 2 methods to upload your database dump:
|
||||||
|
|
||||||
|
* the ``upload`` method using the HTTPS protocol
|
||||||
|
* the ``request_sftp_access`` method using the SFTP protocol
|
||||||
|
|
||||||
The ``upload`` method
|
The ``upload`` method
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
It's the most simple and most straightforward way of uploading your database dump.
|
||||||
|
It uses the HTTPS protocol.
|
||||||
|
|
||||||
.. py:function:: https://upgrade.odoo.com/database/v1/upload
|
.. py:function:: https://upgrade.odoo.com/database/v1/upload
|
||||||
|
|
||||||
Uploads a database dump
|
Uploads a database dump
|
||||||
@@ -213,12 +221,12 @@ The ``upload`` method
|
|||||||
:param str key: (required) your private key
|
:param str key: (required) your private key
|
||||||
:param str request: (required) your request id
|
:param str request: (required) your request id
|
||||||
:return: request result
|
:return: request result
|
||||||
:rtype: json dictionary
|
:rtype: JSON dictionary
|
||||||
|
|
||||||
The request id and the private key are obtained using the :ref:`create method
|
The request id and the private key are obtained using the :ref:`create method
|
||||||
<upgrade-api-create-method>`
|
<upgrade-api-create-method>`
|
||||||
|
|
||||||
The result is a json dictionary containing the list of ``failures``, which
|
The result is a JSON dictionary containing the list of ``failures``, which
|
||||||
should be empty if everything went fine.
|
should be empty if everything went fine.
|
||||||
|
|
||||||
.. rst-class:: setup doc-aside
|
.. rst-class:: setup doc-aside
|
||||||
@@ -230,8 +238,6 @@ should be empty if everything went fine.
|
|||||||
import os
|
import os
|
||||||
import pycurl
|
import pycurl
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from io import BytesIO
|
|
||||||
import json
|
|
||||||
|
|
||||||
UPLOAD_URL = "https://upgrade.odoo.com/database/v1/upload"
|
UPLOAD_URL = "https://upgrade.odoo.com/database/v1/upload"
|
||||||
DUMPFILE = "openchs.70.cdump"
|
DUMPFILE = "openchs.70.cdump"
|
||||||
@@ -267,6 +273,144 @@ should be empty if everything went fine.
|
|||||||
HEADER="Content-Type: application/octet-stream"
|
HEADER="Content-Type: application/octet-stream"
|
||||||
curl -H $HEADER --data-binary "@${DUMPFILE}" "${UPLOAD_URL}?${URL_PARAMS}"
|
curl -H $HEADER --data-binary "@${DUMPFILE}" "${UPLOAD_URL}?${URL_PARAMS}"
|
||||||
|
|
||||||
|
.. _upgrade-api-request-sftp-access-method:
|
||||||
|
|
||||||
|
The ``request_sftp_access`` method
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
This method is recommanded for big database dumps.
|
||||||
|
It uses the SFTP protocol and supports resuming.
|
||||||
|
|
||||||
|
It will create a temporary SFTP server where you can connect to and allow you
|
||||||
|
to upload your database dump using an SFTP client.
|
||||||
|
|
||||||
|
.. py:function:: https://upgrade.odoo.com/database/v1/request_sftp_access
|
||||||
|
|
||||||
|
Creates an SFTP server
|
||||||
|
|
||||||
|
:param str key: (required) your private key
|
||||||
|
:param str request: (required) your request id
|
||||||
|
:param str ssh_keys: (required) the path to a file listing the ssh public keys you'd like to use
|
||||||
|
:return: request result
|
||||||
|
:rtype: JSON dictionary
|
||||||
|
|
||||||
|
The request id and the private key are obtained using the :ref:`create method
|
||||||
|
<upgrade-api-create-method>`
|
||||||
|
|
||||||
|
The file listing your ssh public keys should be roughly similar to a standard ``authorized_keys`` file.
|
||||||
|
This file should only contains public keys, blank lines or comments (lines starting with the ``#`` character)
|
||||||
|
|
||||||
|
Your database upgrade request should be in the ``draft`` state.
|
||||||
|
|
||||||
|
The ``request_sftp_access`` method returns a JSON dictionary containing the following keys:
|
||||||
|
|
||||||
|
|
||||||
|
.. rst-class:: setup doc-aside
|
||||||
|
|
||||||
|
.. switcher::
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import os
|
||||||
|
import pycurl
|
||||||
|
from urllib import urlencode
|
||||||
|
|
||||||
|
UPLOAD_URL = "https://upgrade.odoo.com/database/v1/request_sftp_access"
|
||||||
|
SSH_KEYS="/path/to/your/authorized_keys"
|
||||||
|
|
||||||
|
fields = dict([
|
||||||
|
('request', '10534'),
|
||||||
|
('key', 'Aw7pItGVKFuZ_FOR3U8VFQ=='),
|
||||||
|
])
|
||||||
|
postfields = urlencode(fields)
|
||||||
|
|
||||||
|
c = pycurl.Curl()
|
||||||
|
c.setopt(pycurl.URL, UPLOAD_URL+"?"+postfields)
|
||||||
|
c.setopt(pycurl.POST, 1)
|
||||||
|
c.setopt(c.HTTPPOST,[("ssh_keys",
|
||||||
|
(c.FORM_FILE, SSH_KEYS,
|
||||||
|
c.FORM_CONTENTTYPE, "text/plain"))
|
||||||
|
])
|
||||||
|
|
||||||
|
c.perform()
|
||||||
|
c.close()
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
REQUEST_SFTP_ACCESS_URL="https://upgrade.odoo.com/database/v1/request_sftp_access"
|
||||||
|
SSH_KEYS=/path/to/your/authorized_keys
|
||||||
|
KEY="Aw7pItGVKFuZ_FOR3U8VFQ=="
|
||||||
|
REQUEST_ID="10534"
|
||||||
|
URL_PARAMS="key=${KEY}&request=${REQUEST}"
|
||||||
|
|
||||||
|
curl -sS "${REQUEST_SFTP_ACCESS_URL}?${URL_PARAMS}" -F ssh_keys=@${SSH_KEYS} > request_sftp_result.json
|
||||||
|
|
||||||
|
# check for failures
|
||||||
|
failures=$(cat request_sftp_result.json | jq -r '.failures[]')
|
||||||
|
if [ "$failures" != "" ]; then
|
||||||
|
echo $failures | jq -r '.'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
``failures``
|
||||||
|
''''''''''''
|
||||||
|
|
||||||
|
The list of errors. See :ref:`failures <upgrade-api-json-failure>` for an
|
||||||
|
explanation about the JSON dictionary returned in case of failure.
|
||||||
|
|
||||||
|
``request``
|
||||||
|
'''''''''''
|
||||||
|
|
||||||
|
If the call is successful, the value associated to the *request* key
|
||||||
|
will be a dictionary containing your SFTP connexion parameters:
|
||||||
|
|
||||||
|
* ``hostname``: the host address to connect to
|
||||||
|
* ``sftp_port``: the port to connect to
|
||||||
|
* ``sftp_user``: the SFTP user to use for connecting
|
||||||
|
* ``shared_file``: the filename you need to use (identical to the ``filename`` value you have used when creating the request in the :ref:`create method <upgrade-api-create-method>`.)
|
||||||
|
* ``request_id``: the related upgrade request id (only informative, ,not required for the connection)
|
||||||
|
* ``sample_command``: a sample command using the 'sftp' client
|
||||||
|
|
||||||
|
You should normally be able to connect using the sample command as is.
|
||||||
|
|
||||||
|
You will only have access to the ``shared_file``. No other files will be
|
||||||
|
accessible and you will not be able to create new files in your shared
|
||||||
|
environment on the SFTP server.
|
||||||
|
|
||||||
|
Using the 'sftp' client
|
||||||
|
+++++++++++++++++++++++
|
||||||
|
|
||||||
|
Once you have successfully connected using your SFTP client, you can upload
|
||||||
|
your database dump. Here is a sample session using the 'sftp' client:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
$ sftp -P 2200 user_10534@upgrade.odoo.com
|
||||||
|
Connected to upgrade.odoo.com.
|
||||||
|
sftp> put /path/to/openchs.70.cdump openchs.70.cdump
|
||||||
|
Uploading /path/to/openchs.70.cdump to /openchs.70.cdump
|
||||||
|
sftp> ls -l openchs.70.cdump
|
||||||
|
-rw-rw-rw- 0 0 0 849920 Aug 30 15:58 openchs.70.cdump
|
||||||
|
|
||||||
|
If your connection is interrupted, you can continue your file transfer using
|
||||||
|
the ``-a`` command line switch:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
sftp> put -a /path/to/openchs.70.cdump openchs.70.cdump
|
||||||
|
Resuming upload of /path/to/openchs.70.cdump to /openchs.70.cdump
|
||||||
|
|
||||||
|
If you don't want to manually type the command and need to automate your
|
||||||
|
database upgrade using a script, you can use a batch file or pipe your commands to 'sftp':
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
echo "put /path/to/openchs.70.cdump openchs.70.cdump" | sftp -b - -P 2200 user_10534@upgrade.odoo.com
|
||||||
|
|
||||||
|
The ``-b`` parameter takes a filename. If the filename is ``-``, it reads the commands from standard input.
|
||||||
|
|
||||||
|
|
||||||
.. _upgrade-api-process-method:
|
.. _upgrade-api-process-method:
|
||||||
|
|
||||||
Asking to process your request
|
Asking to process your request
|
||||||
@@ -284,12 +428,12 @@ The ``process`` method
|
|||||||
:param str key: (required) your private key
|
:param str key: (required) your private key
|
||||||
:param str request: (required) your request id
|
:param str request: (required) your request id
|
||||||
:return: request result
|
:return: request result
|
||||||
:rtype: json dictionary
|
:rtype: JSON dictionary
|
||||||
|
|
||||||
The request id and the private key are obtained using the :ref:`create method
|
The request id and the private key are obtained using the :ref:`create method
|
||||||
<upgrade-api-create-method>`
|
<upgrade-api-create-method>`
|
||||||
|
|
||||||
The result is a json dictionary containing the list of ``failures``, which
|
The result is a JSON dictionary containing the list of ``failures``, which
|
||||||
should be empty if everything went fine.
|
should be empty if everything went fine.
|
||||||
|
|
||||||
.. rst-class:: setup doc-aside
|
.. rst-class:: setup doc-aside
|
||||||
@@ -350,12 +494,12 @@ The ``status`` method
|
|||||||
:param str key: (required) your private key
|
:param str key: (required) your private key
|
||||||
:param str request: (required) your request id
|
:param str request: (required) your request id
|
||||||
:return: request result
|
:return: request result
|
||||||
:rtype: json dictionary
|
:rtype: JSON dictionary
|
||||||
|
|
||||||
The request id and the private key are obtained using the :ref:`create method
|
The request id and the private key are obtained using the :ref:`create method
|
||||||
<upgrade-api-create-method>`
|
<upgrade-api-create-method>`
|
||||||
|
|
||||||
The result is a json dictionary containing various information about your
|
The result is a JSON dictionary containing various information about your
|
||||||
database upgrade request.
|
database upgrade request.
|
||||||
|
|
||||||
.. rst-class:: setup doc-aside
|
.. rst-class:: setup doc-aside
|
||||||
@@ -502,4 +646,28 @@ The ``request`` key contains various useful information about your request:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.. _upgrade-api-download-method:
|
||||||
|
|
||||||
|
Downloading your database dump
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Beside downloading your migrated database using the URL provided by the
|
||||||
|
:ref:`status method <upgrade-api-status-method>`, you can also use the SFTP
|
||||||
|
protocol as described in the :ref:`request_sftp_access method
|
||||||
|
<upgrade-api-request-sftp-access-method>`
|
||||||
|
|
||||||
|
The diffence is that you'll only be able to download the migrated database. No
|
||||||
|
uploading will be possible.
|
||||||
|
|
||||||
|
Your database upgrade request should be in the ``done`` state.
|
||||||
|
|
||||||
|
Once you have successfully connected using your SFTP client, you can download
|
||||||
|
your database dump. Here is a sample session using the 'sftp' client:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
$ sftp -P 2200 user_10534@upgrade.odoo.com
|
||||||
|
Connected to upgrade.odoo.com.
|
||||||
|
sftp> get upgraded_openchs.70.cdump /path/to/upgraded_openchs.70.cdump
|
||||||
|
Downloading /upgraded_openchs.70.cdump to /path/to/upgraded_openchs.70.cdump
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import json
|
|||||||
import werkzeug
|
import werkzeug
|
||||||
import HTMLParser
|
import HTMLParser
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
from lxml.etree import LxmlError
|
||||||
|
|
||||||
import openerp
|
import openerp
|
||||||
from openerp import tools, api
|
from openerp import tools, api
|
||||||
@@ -341,7 +342,13 @@ class view(osv.osv):
|
|||||||
if values.get('inherit_id'):
|
if values.get('inherit_id'):
|
||||||
values['type'] = self.browse(cr, uid, values['inherit_id'], context).type
|
values['type'] = self.browse(cr, uid, values['inherit_id'], context).type
|
||||||
else:
|
else:
|
||||||
values['type'] = etree.fromstring(values['arch']).tag
|
|
||||||
|
try:
|
||||||
|
values['type'] = etree.fromstring(values.get('arch') or values.get('arch_base')).tag
|
||||||
|
except LxmlError:
|
||||||
|
# don't raise here, the constraint that runs `self._check_xml` will
|
||||||
|
# do the job properly.
|
||||||
|
pass
|
||||||
|
|
||||||
if not values.get('name'):
|
if not values.get('name'):
|
||||||
values['name'] = "%s %s" % (values.get('model'), values['type'])
|
values['name'] = "%s %s" % (values.get('model'), values['type'])
|
||||||
@@ -360,7 +367,7 @@ class view(osv.osv):
|
|||||||
|
|
||||||
# If view is modified we remove the arch_fs information thus activating the arch_db
|
# If view is modified we remove the arch_fs information thus activating the arch_db
|
||||||
# version. An `init` of the view will restore the arch_fs for the --dev mode
|
# version. An `init` of the view will restore the arch_fs for the --dev mode
|
||||||
if 'arch' in vals and 'install_mode_data' not in context:
|
if ('arch' in vals or 'arch_base' in vals) and 'install_mode_data' not in context:
|
||||||
vals['arch_fs'] = False
|
vals['arch_fs'] = False
|
||||||
|
|
||||||
# drop the corresponding view customizations (used for dashboards for example), otherwise
|
# drop the corresponding view customizations (used for dashboards for example), otherwise
|
||||||
|
|||||||
@@ -21,11 +21,18 @@ class test_guess_mimetype(unittest.TestCase):
|
|||||||
|
|
||||||
def test_default_mimetype_empty(self):
|
def test_default_mimetype_empty(self):
|
||||||
mimetype = guess_mimetype('')
|
mimetype = guess_mimetype('')
|
||||||
self.assertEqual(mimetype, 'application/octet-stream')
|
# odoo implementation returns application/octet-stream by default
|
||||||
|
# if available, python-magic returns application/x-empty
|
||||||
|
self.assertIn(mimetype, ('application/octet-stream', 'application/x-empty'))
|
||||||
|
|
||||||
def test_default_mimetype(self):
|
def test_default_mimetype(self):
|
||||||
mimetype = guess_mimetype('', default='test')
|
mimetype = guess_mimetype('', default='test')
|
||||||
self.assertEqual(mimetype, 'test')
|
# if available, python-magic returns application/x-empty
|
||||||
|
self.assertIn(mimetype, ('test', 'application/x-empty'))
|
||||||
|
|
||||||
|
def test_mimetype_octet_stream(self):
|
||||||
|
mimetype = guess_mimetype('\0')
|
||||||
|
self.assertEqual(mimetype, 'application/octet-stream')
|
||||||
|
|
||||||
def test_mimetype_png(self):
|
def test_mimetype_png(self):
|
||||||
content = base64.b64decode(PNG)
|
content = base64.b64decode(PNG)
|
||||||
@@ -35,7 +42,8 @@ class test_guess_mimetype(unittest.TestCase):
|
|||||||
def test_mimetype_bmp(self):
|
def test_mimetype_bmp(self):
|
||||||
content = base64.b64decode(BMP)
|
content = base64.b64decode(BMP)
|
||||||
mimetype = guess_mimetype(content, default='test')
|
mimetype = guess_mimetype(content, default='test')
|
||||||
self.assertEqual(mimetype, 'image/bmp')
|
# mimetype should match image/bmp, image/x-ms-bmp, ...
|
||||||
|
self.assertRegexpMatches(mimetype, r'image/.*\bbmp')
|
||||||
|
|
||||||
def test_mimetype_jpg(self):
|
def test_mimetype_jpg(self):
|
||||||
content = base64.b64decode(JPG)
|
content = base64.b64decode(JPG)
|
||||||
|
|||||||
@@ -153,9 +153,9 @@ else:
|
|||||||
|
|
||||||
# magic from pypi https://pypi.python.org/pypi/python-magic/
|
# magic from pypi https://pypi.python.org/pypi/python-magic/
|
||||||
if hasattr(magic,'from_buffer'):
|
if hasattr(magic,'from_buffer'):
|
||||||
guess_mimetype = lambda bin_data: magic.from_buffer(bin_data, mime=True)
|
guess_mimetype = lambda bin_data, default=None: magic.from_buffer(bin_data, mime=True)
|
||||||
# magic from file(1) https://packages.debian.org/squeeze/python-magic
|
# magic from file(1) https://packages.debian.org/squeeze/python-magic
|
||||||
elif hasattr(magic,'open'):
|
elif hasattr(magic,'open'):
|
||||||
ms = magic.open(magic.MAGIC_MIME_TYPE)
|
ms = magic.open(magic.MAGIC_MIME_TYPE)
|
||||||
ms.load()
|
ms.load()
|
||||||
guess_mimetype = ms.buffer
|
guess_mimetype = lambda bin_data, default=None: ms.buffer(bin_data)
|
||||||
|
|||||||
Reference in New Issue
Block a user