[MERGE] upstream
This commit is contained in:
@@ -27,13 +27,19 @@ from openerp.osv import fields, osv
|
||||
class account_fiscal_position(osv.osv):
|
||||
_name = 'account.fiscal.position'
|
||||
_description = 'Fiscal Position'
|
||||
_order = 'sequence'
|
||||
_columns = {
|
||||
'sequence': fields.integer('Sequence'),
|
||||
'name': fields.char('Fiscal Position', required=True),
|
||||
'active': fields.boolean('Active', help="By unchecking the active field, you may hide a fiscal position without deleting it."),
|
||||
'company_id': fields.many2one('res.company', 'Company'),
|
||||
'account_ids': fields.one2many('account.fiscal.position.account', 'position_id', 'Account Mapping'),
|
||||
'tax_ids': fields.one2many('account.fiscal.position.tax', 'position_id', 'Tax Mapping'),
|
||||
'note': fields.text('Notes'),
|
||||
'auto_apply': fields.boolean('Automatic', help="Apply automatically this fiscal position."),
|
||||
'vat_required': fields.boolean('VAT required', help="Apply only if partner has a VAT number."),
|
||||
'country_id': fields.many2one('res.country', 'Countries', help="Apply only if delivery or invoicing country match."),
|
||||
'country_group_id': fields.many2one('res.country.group', 'Country Group', help="Apply only if delivery or invocing country match the group."),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
@@ -66,6 +72,33 @@ class account_fiscal_position(osv.osv):
|
||||
break
|
||||
return account_id
|
||||
|
||||
def get_fiscal_position(self, cr, uid, company_id, partner_id, delivery_id=None, context=None):
|
||||
if not partner_id:
|
||||
return False
|
||||
# This can be easily overriden to apply more complex fiscal rules
|
||||
part_obj = self.pool['res.partner']
|
||||
partner = part_obj.browse(cr, uid, partner_id, context=context)
|
||||
|
||||
# partner manually set fiscal position always win
|
||||
if partner.property_account_position:
|
||||
return part.property_account_position.id
|
||||
|
||||
# if no delivery use invocing
|
||||
if delivery_id:
|
||||
delivery = part_obj.browse(cr, uid, delivery_id, context=context)
|
||||
else:
|
||||
delivery = partner
|
||||
|
||||
domain = [
|
||||
('auto_apply', '=', True),
|
||||
'|', ('vat_required', '=', False), ('vat_required', '=', partner.vat_subjected),
|
||||
'|', ('country_id', '=', None), ('country_id', '=', delivery.country_id.id),
|
||||
'|', ('country_group_id', '=', None), ('country_group_id.country_ids', '=', delivery.country_id.id)
|
||||
]
|
||||
fiscal_position_ids = self.search(cr, uid, domain, context=context)
|
||||
if fiscal_position_ids:
|
||||
return fiscal_position_ids[0]
|
||||
return False
|
||||
|
||||
class account_fiscal_position_tax(osv.osv):
|
||||
_name = 'account.fiscal.position.tax'
|
||||
@@ -206,6 +239,7 @@ class res_partner(osv.osv):
|
||||
return self.write(cr, uid, ids, {'last_reconciliation_date': time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
|
||||
|
||||
_columns = {
|
||||
'vat_subjected': fields.boolean('VAT Legal Statement', help="Check this box if the partner is subjected to the VAT. It will be used for the VAT legal statement."),
|
||||
'credit': fields.function(_credit_debit_get,
|
||||
fnct_search=_credit_search, string='Total Receivable', multi='dc', help="Total amount this customer owes you."),
|
||||
'debit': fields.function(_credit_debit_get, fnct_search=_debit_search, string='Total Payable', multi='dc', help="Total amount you have to pay to this supplier."),
|
||||
|
||||
@@ -12,6 +12,13 @@
|
||||
<field name="active"/>
|
||||
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="auto_apply"/>
|
||||
<field name="sequence"/>
|
||||
<field name="vat_required" attrs="{'readonly': [('auto_apply', '=', False)]}"/>
|
||||
<field name="country_id" attrs="{'readonly': ['|', ('country_group_id','!=',False), ('auto_apply', '=', False)]}" />
|
||||
<field name="country_group_id" attrs="{'readonly': ['|', ('country_id','!=',False), ('auto_apply', '=', False)]}"/>
|
||||
</group>
|
||||
<separator string="Taxes Mapping"/>
|
||||
<field name="tax_ids" widget="one2many_list">
|
||||
<tree string="Tax Mapping" editable="bottom">
|
||||
@@ -44,6 +51,7 @@
|
||||
<field name="model">account.fiscal.position</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Fiscal Position">
|
||||
<field name="sequence"/>
|
||||
<field name="name"/>
|
||||
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
|
||||
</tree>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="Invoices Analysis">
|
||||
<field name="date"/>
|
||||
<filter icon="terp-go-year" string="Year" name="year" domain="[('date','<=', time.strftime('%%Y-%%m-%%d')),('date','>=',time.strftime('%%Y-01-01'))]" help="year"/>
|
||||
<filter icon="terp-go-year" string="Year" name="year" domain="['|',('date', '=', False),'&',('date','<=', time.strftime('%%Y-%%m-%%d')),('date','>=',time.strftime('%%Y-01-01'))]" help="year"/>
|
||||
<separator/>
|
||||
<filter string="Draft" icon="terp-document-new" domain="[('state','=','draft')]" help = "Draft Invoices"/>
|
||||
<filter string="Pro-forma" icon="terp-gtk-media-pause" domain="['|', ('state','=','proforma'),('state','=','proforma2')]" help = "Pro-forma Invoices"/>
|
||||
|
||||
@@ -133,10 +133,6 @@ class res_partner(osv.osv):
|
||||
def vat_change(self, cr, uid, ids, value, context=None):
|
||||
return {'value': {'vat_subjected': bool(value)}}
|
||||
|
||||
_columns = {
|
||||
'vat_subjected': fields.boolean('VAT Legal Statement', help="Check this box if the partner is subjected to the VAT. It will be used for the VAT legal statement.")
|
||||
}
|
||||
|
||||
def _commercial_fields(self, cr, uid, context=None):
|
||||
return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['vat_subjected']
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<record id="config_google_drive_client_id" model="ir.config_parameter">
|
||||
<field name="key">google_drive_client_id</field>
|
||||
<field name="value">598905559630.apps.googleusercontent.com</field>
|
||||
<field name="group_ids" eval="[(4, ref('base.group_user'))]" />
|
||||
<field name="group_ids" eval="[(4, ref('base.group_system'))]" />
|
||||
</record>
|
||||
|
||||
<record id="config_google_drive_client_secret" model="ir.config_parameter">
|
||||
|
||||
@@ -1984,51 +1984,6 @@ openerp.mail = function (session) {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------
|
||||
* UserMenu
|
||||
* ------------------------------------------------------------
|
||||
*
|
||||
* Add a link on the top user bar for write a full mail
|
||||
*/
|
||||
session.web.ComposeMessageTopButton = session.web.Widget.extend({
|
||||
template:'mail.ComposeMessageTopButton',
|
||||
|
||||
start: function () {
|
||||
this.$el.on('click', this.on_compose_message);
|
||||
this._super();
|
||||
},
|
||||
|
||||
on_compose_message: function (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var action = {
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'mail.compose.message',
|
||||
view_mode: 'form',
|
||||
view_type: 'form',
|
||||
views: [[false, 'form']],
|
||||
target: 'new',
|
||||
context: {},
|
||||
};
|
||||
session.client.action_manager.do_action(action);
|
||||
},
|
||||
});
|
||||
|
||||
session.web.UserMenu.include({
|
||||
do_update: function(){
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
this.update_promise.then(function() {
|
||||
var mail_button = new session.web.ComposeMessageTopButton();
|
||||
mail_button.appendTo(session.webclient.$el.parents().find('.oe_systray'));
|
||||
openerp.web.bus.trigger('resize'); // Re-trigger the reflow logic
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------
|
||||
* Sub-widgets loading
|
||||
|
||||
@@ -335,18 +335,6 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!--
|
||||
mail.compose_message.button_top_bar
|
||||
render of the button on the user bar for open wizard compose message
|
||||
-->
|
||||
<t t-name="mail.ComposeMessageTopButton">
|
||||
<li title='Compose new Message'>
|
||||
<a href="#">
|
||||
<i id="oe_topbar_compose_full_email_icon" class="fa fa-envelope-o"/>
|
||||
</a>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
<!-- mail.thread.message.vote
|
||||
Template used to display Like/Unlike in a mail.message
|
||||
-->
|
||||
|
||||
@@ -69,11 +69,11 @@
|
||||
purchase_requisition = self.pool.get("purchase.requisition")
|
||||
purchase_ids = self.search(cr, uid, [('requisition_id','=',ref("requisition1"))])
|
||||
assert purchase_ids, "RFQ is not created."
|
||||
rfq = self.browse(cr, uid, purchase_ids[0], context=context)
|
||||
requisition = rfq.requisition_id
|
||||
supplier = rfq.partner_id
|
||||
assert supplier.id == ref('base.res_partner_12'), "RFQ Partner is not correspond."
|
||||
assert len(rfq.order_line) == len(requisition.line_ids), "Lines are not correspond."
|
||||
for rfq in self.browse(cr, uid, purchase_ids, context=context):
|
||||
if rfq.partner_id.id == ref('base.res_partner_12'):
|
||||
break
|
||||
else:
|
||||
assert False, "No PO found for res_partner_12."
|
||||
-
|
||||
I confirmed RFQ which has best price.
|
||||
-
|
||||
|
||||
+18
-5
@@ -320,6 +320,16 @@ class sale_order(osv.osv):
|
||||
context_lang.update({'lang': partner_lang})
|
||||
return self.pool.get('res.users').browse(cr, uid, uid, context=context_lang).company_id.sale_note
|
||||
|
||||
def onchange_delivery_id(self, cr, uid, ids, company_id, partner_id, delivery_id, fiscal_position, context=None):
|
||||
r = {'value': {}}
|
||||
if not fiscal_position:
|
||||
if not company_id:
|
||||
company_id = self._get_default_company(cr, uid, context=context)
|
||||
fiscal_position = self.pool['account.fiscal.position'].get_fiscal_position(cr, uid, company_id, partner_id, delivery_id, context=context)
|
||||
if fiscal_position:
|
||||
r['value']['fiscal_position'] = fiscal_position
|
||||
return r
|
||||
|
||||
def onchange_partner_id(self, cr, uid, ids, part, context=None):
|
||||
if not part:
|
||||
return {'value': {'partner_invoice_id': False, 'partner_shipping_id': False, 'payment_term': False, 'fiscal_position': False}}
|
||||
@@ -328,15 +338,15 @@ class sale_order(osv.osv):
|
||||
addr = self.pool.get('res.partner').address_get(cr, uid, [part.id], ['delivery', 'invoice', 'contact'])
|
||||
pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False
|
||||
payment_term = part.property_payment_term and part.property_payment_term.id or False
|
||||
fiscal_position = part.property_account_position and part.property_account_position.id or False
|
||||
dedicated_salesman = part.user_id and part.user_id.id or uid
|
||||
val = {
|
||||
'partner_invoice_id': addr['invoice'],
|
||||
'partner_shipping_id': addr['delivery'],
|
||||
'payment_term': payment_term,
|
||||
'fiscal_position': fiscal_position,
|
||||
'user_id': dedicated_salesman,
|
||||
}
|
||||
delivery_onchange = self.onchange_delivery_id(cr, uid, ids, False, part.id, addr['delivery'], False, context=context)
|
||||
val.update(delivery_onchange['value'])
|
||||
if pricelist:
|
||||
val['pricelist_id'] = pricelist
|
||||
sale_note = self.get_salenote(cr, uid, ids, part.id, context=context)
|
||||
@@ -345,11 +355,14 @@ class sale_order(osv.osv):
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
context = {}
|
||||
if vals.get('name', '/') == '/':
|
||||
vals['name'] = self.pool.get('ir.sequence').get(cr, uid, 'sale.order') or '/'
|
||||
if vals.get('partner_id') and any(f not in vals for f in ['partner_invoice_id', 'partner_shipping_id', 'pricelist_id']):
|
||||
defaults = self.onchange_partner_id(cr, uid, [], vals['partner_id'], context)['value']
|
||||
if vals.get('partner_id') and any(f not in vals for f in ['partner_invoice_id', 'partner_shipping_id', 'pricelist_id', 'fiscal_position']):
|
||||
defaults = self.onchange_partner_id(cr, uid, [], vals['partner_id'], context=context)['value']
|
||||
if not vals.get('fiscal_position') and vals.get('partner_shipping_id'):
|
||||
delivery_onchange = self.onchange_delivery_id(cr, uid, [], vals.get('company_id'), None, vals['partner_id'], vals.get('partner_shipping_id'), context=context)
|
||||
defaults.update(delivery_onchange['value'])
|
||||
vals = dict(defaults, **vals)
|
||||
context.update({'mail_create_nolog': True})
|
||||
new_id = super(sale_order, self).create(cr, uid, vals, context=context)
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
<group>
|
||||
<field name="partner_id" on_change="onchange_partner_id(partner_id, context)" domain="[('customer','=',True)]" context="{'search_default_customer':1, 'show_address': 1}" options='{"always_reload": True}'/>
|
||||
<field name="partner_invoice_id" groups="sale.group_delivery_invoice_address" context="{'default_type':'invoice'}"/>
|
||||
<field name="partner_shipping_id" groups="sale.group_delivery_invoice_address" context="{'default_type':'delivery'}"/>
|
||||
<field name="partner_shipping_id" on_change="onchange_delivery_id(company_id, partner_id, partner_shipping_id, fiscal_position)" groups="sale.group_delivery_invoice_address" context="{'default_type':'delivery'}"/>
|
||||
<field name="project_id" context="{'partner_id':partner_invoice_id, 'default_pricelist_id':pricelist_id, 'default_name':name, 'default_type': 'contract'}" groups="sale.group_analytic_accounting" domain="[('type','in',['view','normal','contract'])]"/>
|
||||
</group>
|
||||
<group>
|
||||
|
||||
@@ -1377,7 +1377,6 @@ instance.web.WebClient = instance.web.Client.extend({
|
||||
var state = event.getState(true);
|
||||
if(!state.action && state.menu_id) {
|
||||
self.menu.is_bound.done(function() {
|
||||
self.menu.do_reload();
|
||||
self.menu.menu_click(state.menu_id);
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import test_converter
|
||||
import test_requests
|
||||
import test_crawl
|
||||
import test_ui
|
||||
import test_views
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import unittest2
|
||||
|
||||
class URLCase(unittest2.TestCase):
|
||||
"""
|
||||
URLCase moved out of test_requests, otherwise discovery attempts to
|
||||
instantiate and run it
|
||||
"""
|
||||
def __init__(self, user, url, source, result):
|
||||
super(URLCase, self).__init__()
|
||||
self.user = user
|
||||
self.url = url
|
||||
self.source = source
|
||||
self.result = result
|
||||
|
||||
@property
|
||||
def username(self):
|
||||
return self.user or "Anonymous Coward"
|
||||
|
||||
def __str__(self):
|
||||
if self.source:
|
||||
return "%s (from %s, as %s)" % (self.url, self.source, self.username)
|
||||
return "%s (as %s)" % (self.url, self.username)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
def shortDescription(self):
|
||||
return ""
|
||||
|
||||
def runTest(self):
|
||||
code = self.result.getcode()
|
||||
self.assertIn(
|
||||
code, xrange(200, 300),
|
||||
"Fetching %s as %s returned an error response (%d)" % (
|
||||
self.url, self.username, code))
|
||||
@@ -0,0 +1,90 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
import urlparse
|
||||
import unittest2
|
||||
import urllib2
|
||||
import time
|
||||
import werkzeug.urls
|
||||
|
||||
import lxml.html
|
||||
|
||||
import openerp
|
||||
from openerp import tools
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class Crawler(openerp.tests.HttpCase):
|
||||
""" Test suite crawling an openerp CMS instance and checking that all
|
||||
internal links lead to a 200 response.
|
||||
|
||||
If a username and a password are provided, authenticates the user before
|
||||
starting the crawl
|
||||
"""
|
||||
|
||||
at_install = False
|
||||
post_install = True
|
||||
|
||||
def crawl(self, url, seen=None, msg=''):
|
||||
if seen == None:
|
||||
seen = set()
|
||||
if url in seen:
|
||||
return seen
|
||||
else:
|
||||
seen.add(url)
|
||||
|
||||
_logger.info("%s %s", msg, url)
|
||||
r = self.url_open(url)
|
||||
code = r.getcode()
|
||||
self.assertIn( code, xrange(200, 300), "%s Fetching %s returned error response (%d)" % (msg, url, code))
|
||||
|
||||
if r.info().gettype() == 'text/html':
|
||||
doc = lxml.html.fromstring(r.read())
|
||||
for link in doc.xpath('//a[@href]'):
|
||||
href = link.get('href')
|
||||
|
||||
parts = urlparse.urlsplit(href)
|
||||
# href with any fragment removed
|
||||
href = urlparse.urlunsplit((
|
||||
parts.scheme,
|
||||
parts.netloc,
|
||||
parts.path,
|
||||
parts.query,
|
||||
''
|
||||
))
|
||||
|
||||
# FIXME: handle relative link (not parts.path.startswith /)
|
||||
if parts.netloc or \
|
||||
not parts.path.startswith('/') or \
|
||||
parts.path == '/web' or\
|
||||
parts.path.startswith('/web/') or \
|
||||
parts.path.startswith('/en_US/') or \
|
||||
(parts.scheme and parts.scheme not in ('http', 'https')):
|
||||
continue
|
||||
|
||||
self.crawl(href, seen, msg)
|
||||
return seen
|
||||
|
||||
|
||||
def test_10_crawl_public(self):
|
||||
t0 = time.time()
|
||||
seen = self.crawl('/', msg='Anonymous Coward')
|
||||
count = len(seen)
|
||||
duration = time.time() - t0
|
||||
_logger.log(25, "public crawled %s urls in %.2fs, %.3fs per query", count, duration, duration/count)
|
||||
|
||||
def test_20_crawl_demo(self):
|
||||
t0 = time.time()
|
||||
self.authenticate('demo', 'demo')
|
||||
seen = self.crawl('/', msg='demo')
|
||||
count = len(seen)
|
||||
duration = time.time() - t0
|
||||
_logger.log(25, "demo crawled %s urls in %.2fs, %.3fs per query", count, duration, duration/count)
|
||||
|
||||
def test_30_crawl_admin(self):
|
||||
t0 = time.time()
|
||||
self.authenticate('admin', 'admin')
|
||||
seen = self.crawl('/', msg='admin')
|
||||
count = len(seen)
|
||||
duration = time.time() - t0
|
||||
_logger.log(25, "admin crawled %s urls in %.2fs, %.3fs per query", count, duration, duration/count)
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import urlparse
|
||||
import unittest2
|
||||
import urllib2
|
||||
import werkzeug.urls
|
||||
|
||||
import lxml.html
|
||||
|
||||
import openerp
|
||||
from openerp import tools
|
||||
|
||||
import cases
|
||||
|
||||
__all__ = ['load_tests', 'CrawlSuite']
|
||||
|
||||
class RedirectHandler(urllib2.HTTPRedirectHandler):
|
||||
"""
|
||||
HTTPRedirectHandler is predicated upon HTTPErrorProcessor being used and
|
||||
works by intercepting 3xy "errors".
|
||||
|
||||
Inherit from it to handle 3xy non-error responses instead, as we're not
|
||||
using the error processor
|
||||
"""
|
||||
|
||||
def http_response(self, request, response):
|
||||
code, msg, hdrs = response.code, response.msg, response.info()
|
||||
|
||||
if 300 <= code < 400:
|
||||
return self.parent.error(
|
||||
'http', request, response, code, msg, hdrs)
|
||||
|
||||
return response
|
||||
|
||||
https_response = http_response
|
||||
|
||||
class CrawlSuite(unittest2.TestSuite):
|
||||
""" Test suite crawling an openerp CMS instance and checking that all
|
||||
internal links lead to a 200 response.
|
||||
|
||||
If a username and a password are provided, authenticates the user before
|
||||
starting the crawl
|
||||
"""
|
||||
|
||||
at_install = False
|
||||
post_install = True
|
||||
|
||||
def __init__(self, user=None, password=None):
|
||||
super(CrawlSuite, self).__init__()
|
||||
|
||||
registry = openerp.registry(tools.config['db_name'])
|
||||
try:
|
||||
# switch registry to test mode, so that requests can be made
|
||||
registry.enter_test_mode()
|
||||
|
||||
self.opener = urllib2.OpenerDirector()
|
||||
self.opener.add_handler(urllib2.UnknownHandler())
|
||||
self.opener.add_handler(urllib2.HTTPHandler())
|
||||
self.opener.add_handler(urllib2.HTTPSHandler())
|
||||
self.opener.add_handler(urllib2.HTTPCookieProcessor())
|
||||
self.opener.add_handler(RedirectHandler())
|
||||
|
||||
self._authenticate(user, password)
|
||||
self.user = user
|
||||
|
||||
finally:
|
||||
registry.leave_test_mode()
|
||||
|
||||
def _request(self, path):
|
||||
return self.opener.open(urlparse.urlunsplit([
|
||||
'http', 'localhost:%s' % tools.config['xmlrpc_port'],
|
||||
path, '', ''
|
||||
]))
|
||||
|
||||
def _authenticate(self, user, password):
|
||||
# force tools.config['db_name'] in user session so opening `/` doesn't
|
||||
# blow up in multidb situations
|
||||
self.opener.open('http://localhost:{port}/web/?db={db}'.format(
|
||||
port=tools.config['xmlrpc_port'],
|
||||
db=werkzeug.urls.url_quote_plus(tools.config['db_name']),
|
||||
))
|
||||
if user is not None:
|
||||
url = 'http://localhost:{port}/login?{query}'.format(
|
||||
port=tools.config['xmlrpc_port'],
|
||||
query=werkzeug.urls.url_encode({
|
||||
'db': tools.config['db_name'],
|
||||
'login': user,
|
||||
'key': password,
|
||||
})
|
||||
)
|
||||
auth = self.opener.open(url)
|
||||
assert auth.getcode() < 400, "Auth failure %d" % auth.getcode()
|
||||
|
||||
def _wrapped_run(self, result, debug=False):
|
||||
registry = openerp.registry(tools.config['db_name'])
|
||||
try:
|
||||
# switch registry to test mode, so that requests can be made
|
||||
registry.enter_test_mode()
|
||||
|
||||
paths = [URL('/')]
|
||||
seen = set(paths)
|
||||
|
||||
while paths:
|
||||
url = paths.pop(0)
|
||||
r = self._request(url.url)
|
||||
url.to_case(self.user, r).run(result)
|
||||
|
||||
if r.info().gettype() != 'text/html':
|
||||
continue
|
||||
|
||||
doc = lxml.html.fromstring(r.read())
|
||||
for link in doc.xpath('//a[@href]'):
|
||||
href = link.get('href')
|
||||
|
||||
parts = urlparse.urlsplit(href)
|
||||
# href with any fragment removed
|
||||
href = urlparse.urlunsplit((
|
||||
parts.scheme,
|
||||
parts.netloc,
|
||||
parts.path,
|
||||
parts.query,
|
||||
''
|
||||
))
|
||||
|
||||
# avoid repeats, even for links we won't crawl no need to
|
||||
# bother splitting them if we've already ignored them
|
||||
# previously
|
||||
if href in seen: continue
|
||||
seen.add(href)
|
||||
|
||||
# FIXME: handle relative link (not parts.path.startswith /)
|
||||
if parts.netloc or \
|
||||
not parts.path.startswith('/') or \
|
||||
parts.path == '/web' or\
|
||||
parts.path.startswith('/web/') or \
|
||||
(parts.scheme and parts.scheme not in ('http', 'https')):
|
||||
continue
|
||||
|
||||
paths.append(URL(href, url.url))
|
||||
|
||||
finally:
|
||||
registry.leave_test_mode()
|
||||
|
||||
class URL(object):
|
||||
def __init__(self, url, source=None):
|
||||
self.url = url
|
||||
self.source = source
|
||||
|
||||
def to_case(self, user, result):
|
||||
return cases.URLCase(user, self.url, self.source, result)
|
||||
|
||||
def load_tests(loader, base, _):
|
||||
base.addTest(CrawlSuite())
|
||||
base.addTest(CrawlSuite('admin', 'admin'))
|
||||
base.addTest(CrawlSuite('demo', 'demo'))
|
||||
return base
|
||||
@@ -456,6 +456,7 @@ class website_sale(http.Controller):
|
||||
if partner_id and request.website.partner_id.id != partner_id:
|
||||
orm_partner.write(cr, SUPERUSER_ID, [partner_id], billing_info, context=context)
|
||||
else:
|
||||
# create partner
|
||||
partner_id = orm_partner.create(cr, SUPERUSER_ID, billing_info, context=context)
|
||||
|
||||
# create a new shipping partner
|
||||
@@ -472,7 +473,9 @@ class website_sale(http.Controller):
|
||||
'partner_invoice_id': partner_id,
|
||||
'partner_shipping_id': shipping_id or partner_id
|
||||
}
|
||||
order_info.update(registry.get('sale.order').onchange_partner_id(cr, SUPERUSER_ID, [], partner_id, context=context)['value'])
|
||||
order_info.update(order_obj.onchange_partner_id(cr, SUPERUSER_ID, [], partner_id, context=context)['value'])
|
||||
order_info.update(order_obj.onchange_delivery_id(cr, SUPERUSER_ID, [], order.company_id.id, partner_id, shipping_id, None, context=context)['value'])
|
||||
|
||||
order_info.pop('user_id')
|
||||
|
||||
order_obj.write(cr, SUPERUSER_ID, [order.id], order_info, context=context)
|
||||
@@ -512,6 +515,8 @@ class website_sale(http.Controller):
|
||||
self.checkout_form_save(values["checkout"])
|
||||
request.session['sale_last_order_id'] = order.id
|
||||
|
||||
request.website.sale_get_order(update_pricelist=True, context=context)
|
||||
|
||||
return request.redirect("/shop/payment")
|
||||
|
||||
#------------------------------------------------------
|
||||
|
||||
@@ -128,7 +128,7 @@ class website(orm.Model):
|
||||
def sale_product_domain(self, cr, uid, ids, context=None):
|
||||
return [("sale_ok", "=", True)]
|
||||
|
||||
def sale_get_order(self, cr, uid, ids, force_create=False, code=None, context=None):
|
||||
def sale_get_order(self, cr, uid, ids, force_create=False, code=None, update_pricelist=None, context=None):
|
||||
sale_order_obj = self.pool['sale.order']
|
||||
sale_order_id = request.session.get('sale_order_id')
|
||||
sale_order = None
|
||||
@@ -157,26 +157,20 @@ class website(orm.Model):
|
||||
request.session['sale_order_id'] = None
|
||||
return None
|
||||
|
||||
def update_pricelist(pricelist_id):
|
||||
values = {'pricelist_id': pricelist_id}
|
||||
values.update(sale_order.onchange_pricelist_id(pricelist_id, None)['value'])
|
||||
sale_order.write(values)
|
||||
for line in sale_order.order_line:
|
||||
sale_order._cart_update(product_id=line.product_id.id, add_qty=0)
|
||||
|
||||
# check for change of pricelist with a coupon
|
||||
if code and code != sale_order.pricelist_id.code:
|
||||
pricelist_ids = self.pool['product.pricelist'].search(cr, SUPERUSER_ID, [('code', '=', code)], context=context)
|
||||
if pricelist_ids:
|
||||
pricelist_id = pricelist_ids[0]
|
||||
request.session['sale_order_code_pricelist_id'] = pricelist_id
|
||||
update_pricelist(pricelist_id)
|
||||
update_pricelist = True
|
||||
request.session['sale_order_code_pricelist_id'] = False
|
||||
|
||||
pricelist_id = request.session.get('sale_order_code_pricelist_id') or partner.property_product_pricelist.id
|
||||
|
||||
# check for change of partner_id ie after signup
|
||||
if sale_order.partner_id.id != partner.id and request.website.partner_id.id != partner.id:
|
||||
flag_pricelist = False
|
||||
pricelist_id = request.session.get('sale_order_code_pricelist_id') or partner.property_product_pricelist.id
|
||||
if pricelist_id != sale_order.pricelist_id.id:
|
||||
flag_pricelist = True
|
||||
fiscal_position = sale_order.fiscal_position and sale_order.fiscal_position.id or False
|
||||
@@ -190,7 +184,15 @@ class website(orm.Model):
|
||||
sale_order_obj.write(cr, SUPERUSER_ID, [sale_order_id], values, context=context)
|
||||
|
||||
if flag_pricelist or values.get('fiscal_position') != fiscal_position:
|
||||
update_pricelist(pricelist_id)
|
||||
update_pricelist = True
|
||||
|
||||
# update the pricelist
|
||||
if update_pricelist:
|
||||
values = {'pricelist_id': pricelist_id}
|
||||
values.update(sale_order.onchange_pricelist_id(pricelist_id, None)['value'])
|
||||
sale_order.write(values)
|
||||
for line in sale_order.order_line:
|
||||
sale_order._cart_update(product_id=line.product_id.id, add_qty=0)
|
||||
|
||||
# update browse record
|
||||
if (code and code != sale_order.pricelist_id.code) or sale_order.partner_id.id != partner.id:
|
||||
|
||||
@@ -68,7 +68,7 @@ def cmd_setup_git():
|
||||
git_dir = os.getcwd()
|
||||
if git_dir:
|
||||
# push sane config for git < 2.0, and hooks
|
||||
run('git','config','push.default','simple')
|
||||
#run('git','config','push.default','simple')
|
||||
# alias
|
||||
run('git','config','alias.st','status')
|
||||
# merge bzr style
|
||||
|
||||
@@ -349,6 +349,7 @@ class ir_ui_menu(osv.osv):
|
||||
menu_domain = [('parent_id', '=', False)]
|
||||
return self.search(cr, uid, menu_domain, context=context)
|
||||
|
||||
@tools.ormcache()
|
||||
def load_menus(self, cr, uid, context=None):
|
||||
""" Loads all menu items (all applications and their sub-menus).
|
||||
|
||||
|
||||
@@ -84,6 +84,14 @@ addresses belonging to this country.\n\nYou can use the python-style string pate
|
||||
context=context)
|
||||
|
||||
|
||||
class CountryGroup(osv.osv):
|
||||
_description="Country Group"
|
||||
_name = 'res.country.group'
|
||||
_columns = {
|
||||
'name': fields.char('Name', required=True),
|
||||
'country_ids': fields.many2many('res.country', string='Countries'),
|
||||
}
|
||||
|
||||
class CountryState(osv.osv):
|
||||
_description="Country state"
|
||||
_name = 'res.country.state'
|
||||
|
||||
@@ -1516,5 +1516,18 @@
|
||||
<field name="image" type="base64" file="base/static/img/country_flags/zw.png"></field>
|
||||
<field name="currency_id" ref="ZWD"/>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<record id="europe" model="res.country.group">
|
||||
<field name="name">Europe</field>
|
||||
<field name="country_ids" eval="[(6,0,[
|
||||
ref('at'),ref('be'),ref('bg'),ref('hr'),ref('cy'),
|
||||
ref('cz'),ref('dk'),ref('ee'),ref('fi'),ref('fr'),
|
||||
ref('de'),ref('gr'),ref('hu'),ref('ie'),ref('it'),
|
||||
ref('lv'),ref('lt'),ref('lu'),ref('mt'),ref('nl'),
|
||||
ref('pl'),ref('pt'),ref('ro'),ref('sk'),ref('si'),
|
||||
ref('es'),ref('se'),ref('uk')])]"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -50,6 +50,38 @@
|
||||
|
||||
<menuitem action="action_country" id="menu_country_partner" parent="menu_localisation" sequence="0" groups="base.group_no_one"/>
|
||||
|
||||
<record id="view_country_group_tree" model="ir.ui.view">
|
||||
<field name="name">res.country.group.tree</field>
|
||||
<field name="model">res.country.group</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Country Group">
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_country_group_form" model="ir.ui.view">
|
||||
<field name="name">res.country.group.form</field>
|
||||
<field name="model">res.country.group</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Country Group">
|
||||
<field name="name"/>
|
||||
<field name="country_ids" widget="many2many_tags"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_country_group" model="ir.actions.act_window">
|
||||
<field name="name">Country Group</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.country.group</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="help">Display and manage the list of all countries group. You can create or delete country group to make sure the ones you are working on will be maintained.</field>
|
||||
</record>
|
||||
|
||||
|
||||
<menuitem action="action_country_group" id="menu_country_group" name="Country Group" parent="menu_localisation" sequence="1" groups="base.group_no_one"/>
|
||||
|
||||
<!--
|
||||
State
|
||||
-->
|
||||
|
||||
@@ -45,8 +45,10 @@
|
||||
"access_res_company_group_user","res_company group_user","model_res_company",,1,0,0,0
|
||||
"access_res_country_group_all","res_country group_user_all","model_res_country",,1,0,0,0
|
||||
"access_res_country_state_group_all","res_country_state group_user_all","model_res_country_state",,1,0,0,0
|
||||
"access_res_country_group_group_all","res_country_group group_user_all","model_res_country_group",,1,0,0,0
|
||||
"access_res_country_group_user","res_country group_user","model_res_country","group_partner_manager",1,1,1,1
|
||||
"access_res_country_state_group_user","res_country_state group_user","model_res_country_state","group_partner_manager",1,1,1,1
|
||||
"access_res_country_group_group_user","res_country_group group_user","model_res_country_group","group_partner_manager",1,1,1,1
|
||||
"access_res_currency_group_all","res_currency group_all","model_res_currency",,1,0,0,0
|
||||
"access_res_currency_rate_group_all","res_currency_rate group_all","model_res_currency_rate",,1,0,0,0
|
||||
"access_res_currency_rate_type_group_all","res_currency_rate_type group_all","model_res_currency_rate_type",,1,0,0,0
|
||||
|
||||
|
@@ -226,17 +226,17 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
||||
if hasattr(package, kind):
|
||||
delattr(package, kind)
|
||||
|
||||
_logger.log(25, "%s loaded in %.2fs", package.name, time.time() - tm0)
|
||||
#_logger.log(25, "%s loaded in %.2fs", package.name, time.time() - tm0)
|
||||
|
||||
registry._init_modules.add(package.name)
|
||||
cr.commit()
|
||||
|
||||
_logger.log(25, "%s modules loaded in %.2fs", len(graph), time.time() - ta0)
|
||||
_logger.log(25, "%s modules loaded in %.2fs", len(graph), time.time() - ta0)
|
||||
|
||||
# The query won't be valid for models created later (i.e. custom model
|
||||
# created after the registry has been loaded), so empty its result.
|
||||
registry.fields_by_model = None
|
||||
|
||||
|
||||
cr.commit()
|
||||
|
||||
return loaded_modules, processed_modules
|
||||
|
||||
@@ -428,7 +428,8 @@ def run_unit_tests(module_name, dbname, position=runs_at_install):
|
||||
tm0 = time.time()
|
||||
_logger.info('%s running tests.', m.__name__)
|
||||
result = unittest2.TextTestRunner(verbosity=2, stream=TestStream(m.__name__)).run(suite)
|
||||
_logger.log(25, "%s tested in %.2fs", m.__name__, time.time() - tm0)
|
||||
if time.time() - tm0 > 5:
|
||||
_logger.log(25, "%s tested in %.2fs", m.__name__, time.time() - tm0)
|
||||
if not result.wasSuccessful():
|
||||
r = False
|
||||
_logger.error("Module %s: %d failures, %d errors", module_name, len(result.failures), len(result.errors))
|
||||
|
||||
+4
-1
@@ -68,6 +68,8 @@ def LocalService(name):
|
||||
with registry.cursor() as cr:
|
||||
return registry['ir.actions.report.xml']._lookup_report(cr, name[len('report.'):])
|
||||
|
||||
path_prefix = os.path.realpath(os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
class PostgreSQLHandler(logging.Handler):
|
||||
""" PostgreSQL Loggin Handler will store logs in the database, by default
|
||||
the current database, can be set using --log-db=DBNAME
|
||||
@@ -87,7 +89,8 @@ class PostgreSQLHandler(logging.Handler):
|
||||
msg = "%s\n%s" % (msg, traceback)
|
||||
# we do not use record.levelname because it may have been changed by ColoredFormatter.
|
||||
levelname = logging.getLevelName(record.levelno)
|
||||
val = ('server', ct_db, record.name, levelname, msg, record.pathname, record.lineno, record.funcName)
|
||||
|
||||
val = ('server', ct_db, record.name, levelname, msg, record.pathname[len(path_prefix)+1:], record.lineno, record.funcName)
|
||||
cr.execute("""
|
||||
INSERT INTO ir_logging(create_date, type, dbname, name, level, message, path, line, func)
|
||||
VALUES (NOW() at time zone 'UTC', %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
|
||||
+40
-6
@@ -5,6 +5,7 @@ helpers and classes to write tests.
|
||||
|
||||
"""
|
||||
import errno
|
||||
import glob
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
@@ -130,6 +131,25 @@ class SingleTransactionCase(BaseCase):
|
||||
cls.cr.rollback()
|
||||
cls.cr.close()
|
||||
|
||||
class RedirectHandler(urllib2.HTTPRedirectHandler):
|
||||
"""
|
||||
HTTPRedirectHandler is predicated upon HTTPErrorProcessor being used and
|
||||
works by intercepting 3xy "errors".
|
||||
|
||||
Inherit from it to handle 3xy non-error responses instead, as we're not
|
||||
using the error processor
|
||||
"""
|
||||
|
||||
def http_response(self, request, response):
|
||||
code, msg, hdrs = response.code, response.msg, response.info()
|
||||
|
||||
if 300 <= code < 400:
|
||||
return self.parent.error(
|
||||
'http', request, response, code, msg, hdrs)
|
||||
|
||||
return response
|
||||
|
||||
https_response = http_response
|
||||
|
||||
class HttpCase(TransactionCase):
|
||||
""" Transactionnal HTTP TestCase with url_open and phantomjs helpers.
|
||||
@@ -151,19 +171,29 @@ class HttpCase(TransactionCase):
|
||||
self.session_id = self.session.sid
|
||||
self.session.db = DB
|
||||
openerp.http.root.session_store.save(self.session)
|
||||
self.localstorage_path = mkdtemp()
|
||||
# setup an url opener helper
|
||||
self.opener = urllib2.OpenerDirector()
|
||||
self.opener.add_handler(urllib2.UnknownHandler())
|
||||
self.opener.add_handler(urllib2.HTTPHandler())
|
||||
self.opener.add_handler(urllib2.HTTPSHandler())
|
||||
self.opener.add_handler(urllib2.HTTPCookieProcessor())
|
||||
self.opener.add_handler(RedirectHandler())
|
||||
self.opener.addheaders.append(('Cookie', 'session_id=%s' % self.session_id))
|
||||
|
||||
def tearDown(self):
|
||||
rmtree(self.localstorage_path)
|
||||
self.registry.leave_test_mode()
|
||||
super(HttpCase, self).tearDown()
|
||||
|
||||
def url_open(self, url, data=None, timeout=10):
|
||||
opener = urllib2.build_opener()
|
||||
opener.addheaders.append(('Cookie', 'session_id=%s' % self.session_id))
|
||||
if url.startswith('/'):
|
||||
url = "http://localhost:%s%s" % (PORT, url)
|
||||
return opener.open(url, data, timeout)
|
||||
return self.opener.open(url, data, timeout)
|
||||
|
||||
def authenticate(self, user, password):
|
||||
if user is not None:
|
||||
url = '/login?%s' % werkzeug.urls.url_encode({'db': DB,'login': user, 'key': password})
|
||||
auth = self.url_open(url)
|
||||
assert auth.getcode() < 400, "Auth failure %d" % auth.getcode()
|
||||
|
||||
def phantom_poll(self, phantom, timeout):
|
||||
""" Phantomjs Test protocol.
|
||||
@@ -223,6 +253,11 @@ class HttpCase(TransactionCase):
|
||||
|
||||
def phantom_run(self, cmd, timeout):
|
||||
_logger.info('phantom_run executing %s', ' '.join(cmd))
|
||||
|
||||
ls_glob = os.path.expanduser('~/.qws/share/data/Ofi Labs/PhantomJS/http_localhost_%s.*'%PORT)
|
||||
for i in glob.glob(ls_glob):
|
||||
_logger.info('phantomjs unlink localstorage %s', i)
|
||||
os.unlink(i)
|
||||
try:
|
||||
phantom = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
except OSError:
|
||||
@@ -265,7 +300,6 @@ class HttpCase(TransactionCase):
|
||||
# phantom.args[1] == options
|
||||
cmd = [
|
||||
'phantomjs',
|
||||
'--local-storage-path', self.localstorage_path,
|
||||
jsfile, phantomtest, json.dumps(options)
|
||||
]
|
||||
self.phantom_run(cmd, timeout)
|
||||
|
||||
@@ -116,6 +116,8 @@ function PhantomTest() {
|
||||
phantom.exit(1);
|
||||
} else {
|
||||
console.log('loaded', url, status);
|
||||
// clear localstorage leftovers
|
||||
self.page.evaluate(function () { localStorage.clear() });
|
||||
// process ready
|
||||
waitFor(function() {
|
||||
console.log("PhantomTest.run: wait for condition:", ready);
|
||||
|
||||
Reference in New Issue
Block a user