[MERGE] forward port of branch 8.0 up to edeb5a8
This commit is contained in:
@@ -1585,7 +1585,7 @@ class account_move(osv.osv):
|
||||
|
||||
obj_move_line.write(cr, uid, line_draft_ids, {
|
||||
'state': 'valid'
|
||||
}, context, check=False)
|
||||
}, context=context, check=False)
|
||||
|
||||
account = {}
|
||||
account2 = {}
|
||||
@@ -1604,7 +1604,7 @@ class account_move(osv.osv):
|
||||
obj_move_line.write(cr, uid, [line.id], {
|
||||
'tax_code_id': code,
|
||||
'tax_amount': amount
|
||||
}, context, check=False)
|
||||
}, context=context, check=False)
|
||||
elif journal.centralisation:
|
||||
# If the move is not balanced, it must be centralised...
|
||||
|
||||
@@ -1619,7 +1619,7 @@ class account_move(osv.osv):
|
||||
self._centralise(cr, uid, move, 'credit', context=context)
|
||||
obj_move_line.write(cr, uid, line_draft_ids, {
|
||||
'state': 'valid'
|
||||
}, context, check=False)
|
||||
}, context=context, check=False)
|
||||
else:
|
||||
# We can't validate it (it's unbalanced)
|
||||
# Setting the lines as draft
|
||||
@@ -1627,7 +1627,7 @@ class account_move(osv.osv):
|
||||
if not_draft_line_ids:
|
||||
obj_move_line.write(cr, uid, not_draft_line_ids, {
|
||||
'state': 'draft'
|
||||
}, context, check=False)
|
||||
}, context=context, check=False)
|
||||
# Create analytic lines for the valid moves
|
||||
for record in valid_moves:
|
||||
obj_move_line.create_analytic_lines(cr, uid, [line.id for line in record.line_id], context)
|
||||
|
||||
@@ -3,7 +3,7 @@ from datetime import datetime
|
||||
from mock import patch
|
||||
|
||||
from openerp.tests.common import TransactionCase
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_compare
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
@@ -32,65 +32,71 @@ class TestTheoreticalAmount(TransactionCase):
|
||||
self.patcher = patch('openerp.addons.account_budget.account_budget.datetime', wraps=datetime)
|
||||
self.mock_datetime = self.patcher.start()
|
||||
|
||||
def assertFloatEqual(self, value1, value2, *args, **kwargs):
|
||||
""" Compare two values of the field theoritical_amount """
|
||||
digits = type(self.line).theoritical_amount.digits
|
||||
result = float_compare(value1, value2, precision_digits=digits[1])
|
||||
return self.assertFalse(result, *args, **kwargs)
|
||||
|
||||
def test_01(self):
|
||||
"""Start"""
|
||||
date = datetime.strptime('2014-01-01 00:00:00', DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertEqual(self.line.theoritical_amount, 0)
|
||||
self.assertFloatEqual(self.line.theoritical_amount, 0)
|
||||
|
||||
def test_02(self):
|
||||
"""After 24 hours"""
|
||||
date = datetime.strptime('2014-01-02 00:00:00', DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertEqual(self.line.theoritical_amount, -1)
|
||||
self.assertFloatEqual(self.line.theoritical_amount, -1)
|
||||
|
||||
def test_03(self):
|
||||
"""After 36 hours"""
|
||||
date = datetime.strptime('2014-01-02 12:00:00', DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertEqual(self.line.theoritical_amount, -1.5)
|
||||
self.assertFloatEqual(self.line.theoritical_amount, -1.5)
|
||||
|
||||
def test_04(self):
|
||||
"""After 48 hours"""
|
||||
date = datetime.strptime('2014-01-03 00:00:00', DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertEqual(self.line.theoritical_amount, -2)
|
||||
self.assertFloatEqual(self.line.theoritical_amount, -2)
|
||||
|
||||
def test_05(self):
|
||||
"""After 10 days"""
|
||||
date = datetime.strptime('2014-01-11 00:00:00', DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertEqual(self.line.theoritical_amount, -10)
|
||||
self.assertFloatEqual(self.line.theoritical_amount, -10)
|
||||
|
||||
def test_06(self):
|
||||
"""After 50 days"""
|
||||
date = datetime.strptime('2014-02-20 00:00:00', DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertEqual(self.line.theoritical_amount, -50)
|
||||
self.assertFloatEqual(self.line.theoritical_amount, -50)
|
||||
|
||||
def test_07(self):
|
||||
"""After 182 days, exactly half of the budget line"""
|
||||
date = datetime.strptime('2014-07-02 00:00:00', DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertEqual(self.line.theoritical_amount, -182)
|
||||
self.assertFloatEqual(self.line.theoritical_amount, -182)
|
||||
|
||||
def test_08(self):
|
||||
"""After 308 days at noon"""
|
||||
date = datetime.strptime('2014-11-05 12:00:00', DEFAULT_SERVER_DATETIME_FORMAT) # remember, remember
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertEqual(self.line.theoritical_amount, -308.5)
|
||||
self.assertFloatEqual(self.line.theoritical_amount, -308.5)
|
||||
|
||||
def test_09(self):
|
||||
"""One day before"""
|
||||
date = datetime.strptime('2014-12-30 00:00:00', DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertEqual(self.line.theoritical_amount, -363)
|
||||
self.assertFloatEqual(self.line.theoritical_amount, -363)
|
||||
|
||||
def test_10(self):
|
||||
"""At last"""
|
||||
date = datetime.strptime('2014-12-31 00:00:00', DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertEqual(self.line.theoritical_amount, -364)
|
||||
self.assertFloatEqual(self.line.theoritical_amount, -364)
|
||||
|
||||
def tearDown(self):
|
||||
self.patcher.stop()
|
||||
|
||||
@@ -1494,6 +1494,7 @@ class account_voucher_line(osv.osv):
|
||||
'amount_unreconciled': fields.function(_compute_balance, multi='dc', type='float', string='Open Balance', store=True, digits_compute=dp.get_precision('Account')),
|
||||
'company_id': fields.related('voucher_id','company_id', relation='res.company', type='many2one', string='Company', store=True, readonly=True),
|
||||
'currency_id': fields.function(_currency_id, string='Currency', type='many2one', relation='res.currency', readonly=True),
|
||||
'state': fields.related('voucher_id', 'state', type='char', string='State', readonly=True),
|
||||
}
|
||||
_defaults = {
|
||||
'name': '',
|
||||
|
||||
@@ -163,10 +163,11 @@
|
||||
<label for="line_dr_ids"/>
|
||||
<field name="line_dr_ids" context="{'journal_id':journal_id, 'type':type, 'partner_id':partner_id}" on_change="onchange_line_ids(line_dr_ids, line_cr_ids, amount, currency_id, type, context)">
|
||||
<tree string="Supplier Invoices and Outstanding transactions" editable="bottom" colors="gray:amount==0">
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="move_line_id" context="{'journal_id':parent.journal_id, 'partner_id':parent.partner_id}"
|
||||
on_change="onchange_move_line_id(move_line_id)"
|
||||
domain="[('account_id.type','=','payable'), ('reconcile_id','=', False), ('partner_id','=',parent.partner_id)]"
|
||||
required="1"/>
|
||||
attrs="{'required': [('state', 'in', [False, 'draft'])]}"/>
|
||||
<field name="account_id" groups="base.group_no_one" domain="[('type','=','payable')]"/>
|
||||
<field name="date_original" readonly="1"/>
|
||||
<field name="date_due" readonly="1"/>
|
||||
@@ -179,10 +180,11 @@
|
||||
<label for="line_cr_ids" attrs="{'invisible': [('pre_line','=',False)]}"/>
|
||||
<field name="line_cr_ids" attrs="{'invisible': [('pre_line','=',False)]}" context="{'journal_id':journal_id, 'partner_id':partner_id}" on_change="onchange_line_ids(line_dr_ids, line_cr_ids, amount, currency_id, type, context)">
|
||||
<tree string="Credits" editable="bottom" colors="gray:amount==0">
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="move_line_id" context="{'journal_id':parent.journal_id, 'partner_id':parent.partner_id}"
|
||||
on_change="onchange_move_line_id(move_line_id)"
|
||||
domain="[('account_id.type','=','payable'), ('reconcile_id','=', False), ('partner_id','=',parent.partner_id)]"
|
||||
required="1"/>
|
||||
attrs="{'required': [('state', 'in', [False, 'draft'])]}"/>
|
||||
<field name="account_id" groups="base.group_no_one" domain="[('type','=','payable')]"/>
|
||||
<field name="date_original" readonly="1"/>
|
||||
<field name="date_due" readonly="1"/>
|
||||
|
||||
@@ -195,6 +195,7 @@ class EscposDriver(Thread):
|
||||
localips = ['0.0.0.0','127.0.0.1','127.0.1.1']
|
||||
hosting_ap = os.system('pgrep hostapd') == 0
|
||||
ssid = subprocess.check_output('iwconfig 2>&1 | grep \'ESSID:"\' | sed \'s/.*"\\(.*\\)"/\\1/\'', shell=True).rstrip()
|
||||
mac = subprocess.check_output('ifconfig | grep -B 1 \'inet addr\' | grep -o \'HWaddr .*\' | sed \'s/HWaddr //\'', shell=True).rstrip()
|
||||
ips = [ c.split(':')[1].split(' ')[0] for c in commands.getoutput("/sbin/ifconfig").split('\n') if 'inet addr' in c ]
|
||||
ips = [ ip for ip in ips if ip not in localips ]
|
||||
eprint.text('\n\n')
|
||||
@@ -218,6 +219,7 @@ class EscposDriver(Thread):
|
||||
eprint.text(ip+'\n')
|
||||
|
||||
if len(ips) >= 1:
|
||||
eprint.text('\nMAC Address:\n' + mac + '\n')
|
||||
eprint.text('\nHomepage:\nhttp://'+ips[0]+':8069\n')
|
||||
|
||||
eprint.text('\n\n')
|
||||
|
||||
@@ -46,7 +46,7 @@ class LivechatController(http.Controller):
|
||||
'regex_url' : rule.regex_url,
|
||||
}
|
||||
info['rule'] = json.dumps(rule and rule_data or False)
|
||||
return request.render('im_livechat.loader', info)
|
||||
return request.render('im_livechat.loader', info, headers=[('Content-Type', 'application/javascript')])
|
||||
|
||||
@http.route('/im_livechat/get_session', type="json", auth="none")
|
||||
def get_session(self, channel_id, anonymous_name, **kwargs):
|
||||
|
||||
@@ -10,13 +10,15 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<p t-if="o.address_id == o.partner_invoice_id">Invoice and shipping address:</p>
|
||||
<p t-if="o.address_id != o.partner_invoice_id">Invoice address: </p>
|
||||
<div t-field="o.partner_invoice_id"
|
||||
t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/>
|
||||
<p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
|
||||
<div t-if="o.address_id != o.partner_invoice_id">
|
||||
<p>Shipping address :</p>
|
||||
<strong t-if="o.address_id == o.partner_invoice_id">Invoice and shipping address:</strong>
|
||||
<div t-if="o.partner_invoice_id">
|
||||
<strong t-if="o.address_id != o.partner_invoice_id">Invoice address: </strong>
|
||||
<div t-field="o.partner_invoice_id"
|
||||
t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/>
|
||||
<p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
|
||||
</div>
|
||||
<div t-if="o.address_id != o.partner_invoice_id" class="mt8">
|
||||
<strong>Shipping address :</strong>
|
||||
<div t-field="o.address_id"
|
||||
t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/>
|
||||
<p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
|
||||
|
||||
@@ -28,13 +28,17 @@ class res_partner(osv.osv):
|
||||
def _purchase_invoice_count(self, cr, uid, ids, field_name, arg, context=None):
|
||||
PurchaseOrder = self.pool['purchase.order']
|
||||
Invoice = self.pool['account.invoice']
|
||||
return {
|
||||
partner_id: {
|
||||
'purchase_order_count': PurchaseOrder.search_count(cr,uid, [('partner_id', 'child_of', partner_id)], context=context),
|
||||
'supplier_invoice_count': Invoice.search_count(cr,uid, [('partner_id', 'child_of', partner_id), ('type','=','in_invoice')], context=context)
|
||||
}
|
||||
for partner_id in ids
|
||||
}
|
||||
res = {}
|
||||
|
||||
for partner_id in ids:
|
||||
res[partner_id] = {}
|
||||
|
||||
if 'purchase_order_count' in field_name:
|
||||
res[partner_id]['purchase_order_count'] = PurchaseOrder.search_count(cr,uid, [('partner_id', 'child_of', partner_id)], context=context)
|
||||
if 'supplier_invoice_count' in field_name:
|
||||
res[partner_id]['supplier_invoice_count'] = Invoice.search_count(cr,uid, [('partner_id', 'child_of', partner_id), ('type','=','in_invoice')], context=context)
|
||||
|
||||
return res
|
||||
|
||||
def _commercial_fields(self, cr, uid, context=None):
|
||||
return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['property_product_pricelist_purchase']
|
||||
|
||||
@@ -384,6 +384,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
|
||||
},
|
||||
add_field: function(field_id, string) {
|
||||
var field_list = this.$el.find('#fields_list');
|
||||
field_id = this.records[field_id] || field_id;
|
||||
if (this.$el.find("#fields_list option[value='" + field_id + "']")
|
||||
&& !this.$el.find("#fields_list option[value='" + field_id + "']").length) {
|
||||
field_list.append(new Option(string, field_id));
|
||||
|
||||
@@ -471,7 +471,7 @@ class website(osv.osv):
|
||||
:rtype: bool
|
||||
"""
|
||||
endpoint = rule.endpoint
|
||||
methods = endpoint.routing.get('method') or ['GET']
|
||||
methods = endpoint.routing.get('methods') or ['GET']
|
||||
|
||||
converters = rule._converters.values()
|
||||
if not ('GET' in methods
|
||||
|
||||
@@ -392,28 +392,46 @@ class TestNewFields(common.TransactionCase):
|
||||
|
||||
@mute_logger('openerp.addons.base.ir.ir_model')
|
||||
def test_41_new_related(self):
|
||||
""" test the behavior of related fields on new records. """
|
||||
discussion = self.env.ref('test_new_api.discussion_0')
|
||||
access = self.env.ref('test_new_api.access_discussion')
|
||||
|
||||
""" test the behavior of related fields starting on new records. """
|
||||
# make discussions unreadable for demo user
|
||||
access = self.env.ref('test_new_api.access_discussion')
|
||||
access.write({'perm_read': False})
|
||||
|
||||
# create an environment for demo user
|
||||
demo_env = self.env(user=self.env.ref('base.user_demo'))
|
||||
self.assertEqual(demo_env.user.login, "demo")
|
||||
env = self.env(user=self.env.ref('base.user_demo'))
|
||||
self.assertEqual(env.user.login, "demo")
|
||||
|
||||
# create a new message as demo user
|
||||
values = {'discussion': discussion.id}
|
||||
message = demo_env['test_new_api.message'].new(values)
|
||||
discussion = self.env.ref('test_new_api.discussion_0')
|
||||
message = env['test_new_api.message'].new({'discussion': discussion})
|
||||
self.assertEqual(message.discussion, discussion)
|
||||
|
||||
# read the related field discussion_name
|
||||
self.assertEqual(message.discussion.env, demo_env)
|
||||
self.assertEqual(message.discussion.env, env)
|
||||
self.assertEqual(message.discussion_name, discussion.name)
|
||||
with self.assertRaises(AccessError):
|
||||
message.discussion.name
|
||||
|
||||
@mute_logger('openerp.addons.base.ir.ir_model')
|
||||
def test_42_new_related(self):
|
||||
""" test the behavior of related fields traversing new records. """
|
||||
# make discussions unreadable for demo user
|
||||
access = self.env.ref('test_new_api.access_discussion')
|
||||
access.write({'perm_read': False})
|
||||
|
||||
# create an environment for demo user
|
||||
env = self.env(user=self.env.ref('base.user_demo'))
|
||||
self.assertEqual(env.user.login, "demo")
|
||||
|
||||
# create a new discussion and a new message as demo user
|
||||
discussion = env['test_new_api.discussion'].new({'name': 'Stuff'})
|
||||
message = env['test_new_api.message'].new({'discussion': discussion})
|
||||
self.assertEqual(message.discussion, discussion)
|
||||
|
||||
# read the related field discussion_name
|
||||
self.assertNotEqual(message.sudo().env, message.env)
|
||||
self.assertEqual(message.discussion_name, discussion.name)
|
||||
|
||||
def test_50_defaults(self):
|
||||
""" test default values. """
|
||||
fields = ['discussion', 'body', 'author', 'size']
|
||||
|
||||
+16
-5
@@ -30,6 +30,7 @@ import logging
|
||||
import pytz
|
||||
import xmlrpclib
|
||||
|
||||
from openerp.sql_db import LazyCursor
|
||||
from openerp.tools import float_round, frozendict, html_sanitize, ustr, OrderedSet
|
||||
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
|
||||
@@ -58,6 +59,15 @@ def _check_value(value):
|
||||
""" Return ``value``, or call its getter if ``value`` is a :class:`SpecialValue`. """
|
||||
return value.get() if isinstance(value, SpecialValue) else value
|
||||
|
||||
def copy_cache(records, env):
|
||||
""" Recursively copy the cache of ``records`` to the environment ``env``. """
|
||||
for record, target in zip(records, records.with_env(env)):
|
||||
if not target._cache:
|
||||
for name, value in record._cache.iteritems():
|
||||
target[name] = value
|
||||
if isinstance(value, BaseModel):
|
||||
copy_cache(value, env)
|
||||
|
||||
|
||||
def resolve_all_mro(cls, name, reverse=False):
|
||||
""" Return the (successively overridden) values of attribute ``name`` in ``cls``
|
||||
@@ -538,10 +548,9 @@ class Field(object):
|
||||
# when related_sudo, bypass access rights checks when reading values
|
||||
others = records.sudo() if self.related_sudo else records
|
||||
for record, other in zip(records, others):
|
||||
if not record.id:
|
||||
if not record.id and record.env != other.env:
|
||||
# draft records: copy record's cache to other's cache first
|
||||
for name, value in record._cache.iteritems():
|
||||
other[name] = value
|
||||
copy_cache(record, other.env)
|
||||
# traverse the intermediate fields; follow the first record at each step
|
||||
for name in self.related[:-1]:
|
||||
other = other[name][:1]
|
||||
@@ -1062,7 +1071,7 @@ class Float(Field):
|
||||
@property
|
||||
def digits(self):
|
||||
if callable(self._digits):
|
||||
with fields._get_cursor() as cr:
|
||||
with LazyCursor() as cr:
|
||||
return self._digits(cr)
|
||||
else:
|
||||
return self._digits
|
||||
@@ -1087,6 +1096,8 @@ class Float(Field):
|
||||
def convert_to_cache(self, value, record, validate=True):
|
||||
# apply rounding here, otherwise value in cache may be wrong!
|
||||
value = float(value or 0.0)
|
||||
if not validate:
|
||||
return value
|
||||
digits = self.digits
|
||||
return float_round(value, precision_digits=digits[1]) if digits else value
|
||||
|
||||
@@ -1874,7 +1885,7 @@ class Id(Field):
|
||||
raise TypeError("field 'id' cannot be assigned")
|
||||
|
||||
# imported here to avoid dependency cycle issues
|
||||
from openerp import SUPERUSER_ID, registry
|
||||
from openerp import SUPERUSER_ID
|
||||
from .exceptions import Warning, AccessError, MissingError
|
||||
from .models import BaseModel, MAGIC_COLUMNS
|
||||
from .osv import fields
|
||||
|
||||
@@ -65,7 +65,7 @@ class AddonsImportHook(object):
|
||||
thus `import openerp.addons.module`.
|
||||
"""
|
||||
|
||||
def find_module(self, module_name, package_path):
|
||||
def find_module(self, module_name, package_path=None):
|
||||
module_parts = module_name.split('.')
|
||||
if len(module_parts) == 3 and module_name.startswith('openerp.addons.'):
|
||||
return self # We act as a loader too.
|
||||
|
||||
@@ -43,21 +43,18 @@ import pytz
|
||||
import re
|
||||
import xmlrpclib
|
||||
from operator import itemgetter
|
||||
from contextlib import contextmanager
|
||||
from psycopg2 import Binary
|
||||
|
||||
import openerp
|
||||
import openerp.tools as tools
|
||||
from openerp.sql_db import LazyCursor
|
||||
from openerp.tools.translate import _
|
||||
from openerp.tools import float_repr, float_round, frozendict, html_sanitize
|
||||
import simplejson
|
||||
from openerp import SUPERUSER_ID, registry
|
||||
from openerp import SUPERUSER_ID
|
||||
|
||||
@contextmanager
|
||||
def _get_cursor():
|
||||
# yield a valid cursor from any environment or create a new one if none found
|
||||
with registry().cursor() as cr:
|
||||
yield cr
|
||||
# deprecated; kept for backward compatibility only
|
||||
_get_cursor = LazyCursor
|
||||
|
||||
EMPTY_DICT = frozendict()
|
||||
|
||||
@@ -396,7 +393,7 @@ class float(_column):
|
||||
@property
|
||||
def digits(self):
|
||||
if self._digits_compute:
|
||||
with _get_cursor() as cr:
|
||||
with LazyCursor() as cr:
|
||||
return self._digits_compute(cr)
|
||||
else:
|
||||
return self._digits
|
||||
@@ -1323,7 +1320,7 @@ class function(_column):
|
||||
@property
|
||||
def digits(self):
|
||||
if self._digits_compute:
|
||||
with _get_cursor() as cr:
|
||||
with LazyCursor() as cr:
|
||||
return self._digits_compute(cr)
|
||||
else:
|
||||
return self._digits
|
||||
|
||||
@@ -443,6 +443,40 @@ class TestCursor(Cursor):
|
||||
self.execute("ROLLBACK TO SAVEPOINT test_cursor")
|
||||
self.execute("SAVEPOINT test_cursor")
|
||||
|
||||
class LazyCursor(object):
|
||||
""" A proxy object to a cursor. The cursor itself is allocated only if it is
|
||||
needed. This class is useful for cached methods, that use the cursor
|
||||
only in the case of a cache miss.
|
||||
"""
|
||||
def __init__(self, dbname=None):
|
||||
self._dbname = dbname
|
||||
self._cursor = None
|
||||
self._depth = 0
|
||||
|
||||
@property
|
||||
def dbname(self):
|
||||
return self._dbname or threading.currentThread().dbname
|
||||
|
||||
def __getattr__(self, name):
|
||||
cr = self._cursor
|
||||
if cr is None:
|
||||
from openerp import registry
|
||||
cr = self._cursor = registry(self.dbname).cursor()
|
||||
for _ in xrange(self._depth):
|
||||
cr.__enter__()
|
||||
return getattr(cr, name)
|
||||
|
||||
def __enter__(self):
|
||||
self._depth += 1
|
||||
if self._cursor is not None:
|
||||
self._cursor.__enter__()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self._depth -= 1
|
||||
if self._cursor is not None:
|
||||
self._cursor.__exit__(exc_type, exc_value, traceback)
|
||||
|
||||
class PsycoConnection(psycopg2.extensions.connection):
|
||||
pass
|
||||
|
||||
|
||||
Reference in New Issue
Block a user