diff --git a/addons/account/account.py b/addons/account/account.py index bf072507aa0..ab3d065421f 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -155,6 +155,7 @@ class account_account_type(osv.osv): return res def _save_report_type(self, cr, uid, account_type_id, field_name, field_value, arg, context=None): + field_value = field_value or 'none' obj_data = self.pool.get('ir.model.data') obj_financial_report = self.pool.get('account.financial.report') #unlink if it exists somewhere in the financial reports related to BS or PL @@ -596,10 +597,7 @@ class account_account(osv.osv): return res def copy(self, cr, uid, id, default=None, context=None, done_list=None, local=False): - if default is None: - default = {} - else: - default = default.copy() + default = {} if default is None else default.copy() if done_list is None: done_list = [] account = self.browse(cr, uid, id, context=context) @@ -781,10 +779,7 @@ class account_journal(osv.osv): ] def copy(self, cr, uid, id, default=None, context=None, done_list=None, local=False): - if default is None: - default = {} - else: - default = default.copy() + default = {} if default is None else default.copy() if done_list is None: done_list = [] journal = self.browse(cr, uid, id, context=context) @@ -1185,6 +1180,7 @@ class account_fiscalyear(osv.osv): } def copy(self, cr, uid, id, default=None, context=None): + default = {} if default is None else default.copy() default.update({ 'period_ids': [], 'end_journal_period_id': False @@ -1444,14 +1440,8 @@ class account_move(osv.osv): return result def copy(self, cr, uid, id, default=None, context=None): - if context is None: - default = {} - else: - default = default.copy() - if context is None: - context = {} - else: - context = context.copy() + default = {} if default is None else default.copy() + context = {} if context is None else context.copy() default.update({ 'state':'draft', 'name':'/', diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index 423026251c2..f97f3f6af3c 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -1070,7 +1070,8 @@ class account_voucher(osv.osv): if line.amount == line.amount_unreconciled: if not line.move_line_id.amount_residual: raise osv.except_osv(_('Wrong bank statement line'),_("You have to delete the bank statement line which the payment was reconciled to manually. Please check the payment of the partner %s by the amount of %s.")%(line.voucher_id.partner_id.name, line.voucher_id.amount)) - currency_rate_difference = line.move_line_id.amount_residual - amount + sign = voucher_brw.type in ('payment', 'purchase') and -1 or 1 + currency_rate_difference = sign * (line.move_line_id.amount_residual - amount) else: currency_rate_difference = 0.0 move_line = { diff --git a/addons/auth_oauth/controllers/main.py b/addons/auth_oauth/controllers/main.py index ae01df5f6f7..7baac5d67c4 100644 --- a/addons/auth_oauth/controllers/main.py +++ b/addons/auth_oauth/controllers/main.py @@ -43,7 +43,7 @@ class OAuthController(openerpweb.Controller): url = "/#action=login&oauth_error=1" except Exception,e: # signup error - _logger.exception('oops') + _logger.exception("OAuth2: %s" % str(e)) url = "/#action=login&oauth_error=2" return openerp.addons.web.controllers.main.set_cookie_and_redirect(req, url) diff --git a/addons/auth_oauth/res_users.py b/addons/auth_oauth/res_users.py index fd26e89c2e3..9c1f88bd7f4 100644 --- a/addons/auth_oauth/res_users.py +++ b/addons/auth_oauth/res_users.py @@ -15,13 +15,17 @@ class res_users(osv.Model): _inherit = 'res.users' _columns = { - 'oauth_provider_id': fields.many2one('auth.oauth.provider','OAuth Provider'), + 'oauth_provider_id': fields.many2one('auth.oauth.provider', 'OAuth Provider'), 'oauth_uid': fields.char('OAuth User ID', help="Oauth Provider user_id"), - 'oauth_access_token': fields.char('OAuth Token', readonly=True), + 'oauth_access_token': fields.char('OAuth Access Token', readonly=True), } + _sql_constraints = [ + ('uniq_users_oauth_provider_oauht_uid', 'unique(auth_provider_id, auth_uid)', 'OAuth UID must be unique per provider'), + ] + def auth_oauth_rpc(self, cr, uid, endpoint, access_token, context=None): - params = urllib.urlencode({ 'access_token': access_token }) + params = urllib.urlencode({'access_token': access_token}) if urlparse.urlparse(endpoint)[4]: url = endpoint + '&' + params else: @@ -41,22 +45,26 @@ class res_users(osv.Model): validation = self.auth_oauth_rpc(cr, uid, p.validation_endpoint, access_token) if validation.get("error"): - raise openerp.exceptions.AccessDenied + raise Exception(validation['error']) if p.data_endpoint: data = self.auth_oauth_rpc(cr, uid, p.data_endpoint, access_token) validation.update(data) # required oauth_uid = validation['user_id'] if not oauth_uid: - raise openerp.exceptions.AccessDenied + raise openerp.exceptions.AccessDenied() email = validation.get('email', 'provider_%d_user_%d' % (p.id, oauth_uid)) # optional name = validation.get('name', email) - res = self.search(cr, uid, [("oauth_uid", "=", oauth_uid)]) + res = self.search(cr, uid, [("oauth_uid", "=", oauth_uid), ('oauth_provider_id', '=', provider)]) if res: - self.write(cr, uid, res[0], { 'oauth_access_token': access_token }) + assert len(res) == 1 + self.write(cr, uid, res[0], {'oauth_access_token': access_token}) else: - # New user + # New user if signup module available + if not hasattr(self, '_signup_create_user'): + raise openerp.exceptions.AccessDenied() + new_user = { 'name': name, 'login': email, @@ -66,7 +74,9 @@ class res_users(osv.Model): 'oauth_access_token': access_token, 'active': True, } - self.auth_signup_create(cr, uid, new_user) + # TODO pass signup token to allow attach new user to right partner + self._signup_create_user(cr, uid, new_user) + credentials = (cr.dbname, email, access_token) return credentials @@ -74,7 +84,7 @@ class res_users(osv.Model): try: return super(res_users, self).check_credentials(cr, uid, password) except openerp.exceptions.AccessDenied: - res = self.search(cr, SUPERUSER_ID, [('id','=',uid),('oauth_access_token','=',password)]) + res = self.search(cr, SUPERUSER_ID, [('id', '=', uid), ('oauth_access_token', '=', password)]) if not res: raise diff --git a/addons/base_import/__openerp__.py b/addons/base_import/__openerp__.py index eff20bc90ab..f5a707650d8 100644 --- a/addons/base_import/__openerp__.py +++ b/addons/base_import/__openerp__.py @@ -26,13 +26,14 @@ Re-implement openerp's file import system: 'author': 'OpenERP SA', 'depends': ['base'], 'installable': True, - 'auto_install': False, # set to true and allow uninstall? + 'auto_install': True, 'css': [ 'static/lib/select2/select2.css', 'static/src/css/import.css', ], 'js': [ 'static/lib/select2/select2.js', + 'static/lib/javascript-state-machine/state-machine.js', 'static/src/js/import.js', ], 'qweb': ['static/src/xml/import.xml'], diff --git a/addons/base_import/models.py b/addons/base_import/models.py index 1e5ad002b84..996dbf7964d 100644 --- a/addons/base_import/models.py +++ b/addons/base_import/models.py @@ -83,6 +83,10 @@ class ir_import(orm.TransientModel): }] fields_got = self.pool[model].fields_get(cr, uid, context=context) for name, field in fields_got.iteritems(): + # an empty string means the field is deprecated, @deprecated must + # be absent or False to mean not-deprecated + if field.get('deprecated', False) is not False: + continue if field.get('readonly'): states = field.get('states') if not states: @@ -97,7 +101,7 @@ class ir_import(orm.TransientModel): 'id': name, 'name': name, 'string': field['string'], - # Y U NO ALWAYS HAVE REQUIRED + # Y U NO ALWAYS HAS REQUIRED 'required': bool(field.get('required')), 'fields': [], } @@ -124,8 +128,8 @@ class ir_import(orm.TransientModel): """ csv_iterator = csv.reader( StringIO(record.file), - quotechar=options['quoting'], - delimiter=options['separator']) + quotechar=str(options['quoting']), + delimiter=str(options['separator'])) csv_nonempty = itertools.ifilter(None, csv_iterator) # TODO: guess encoding with chardet? Or https://github.com/aadsm/jschardet encoding = options.get('encoding', 'utf-8') @@ -307,22 +311,14 @@ class ir_import(orm.TransientModel): except ValueError, e: return [{ 'type': 'error', - 'message': str(e), + 'message': unicode(e), 'record': False, }] - try: - _logger.info('importing %d rows...', len(data)) - (code, record, message, _wat) = self.pool[record.res_model].import_data( - cr, uid, import_fields, data, context=context) - _logger.info('done') - - except Exception, e: - _logger.exception("Import failed") - # TODO: remove when exceptions stop being an "expected" - # behavior of import_data on some (most) invalid - # input. - code, record, message = -1, None, str(e) + _logger.info('importing %d rows...', len(data)) + import_result = self.pool[record.res_model].load( + cr, uid, import_fields, data, context=context) + _logger.info('done') # If transaction aborted, RELEASE SAVEPOINT is going to raise # an InternalError (ROLLBACK should work, maybe). Ignore that. @@ -339,14 +335,4 @@ class ir_import(orm.TransientModel): except psycopg2.InternalError: pass - if code != -1: - return [] - - # TODO: add key for error location? - # TODO: error not within normal preview, how to display? Re-preview - # with higher ``count``? - return [{ - 'type': 'error', - 'message': message, - 'record': record or False - }] + return import_result['messages'] diff --git a/addons/base_import/static/lib/javascript-state-machine/LICENSE b/addons/base_import/static/lib/javascript-state-machine/LICENSE new file mode 100644 index 00000000000..8ad703ca4ef --- /dev/null +++ b/addons/base_import/static/lib/javascript-state-machine/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2012 Jake Gordon and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/addons/base_import/static/lib/javascript-state-machine/README.md b/addons/base_import/static/lib/javascript-state-machine/README.md new file mode 100644 index 00000000000..64c045e69d7 --- /dev/null +++ b/addons/base_import/static/lib/javascript-state-machine/README.md @@ -0,0 +1,327 @@ +Javascript Finite State Machine (v2.1.0) +======================================== + +This standalone javascript micro-framework provides a finite state machine for your pleasure. + + * You can find the [code here](https://github.com/jakesgordon/javascript-state-machine) + * You can find a [description here](http://codeincomplete.com/posts/2011/8/19/javascript_state_machine_v2/) + * You can find a [working demo here](http://codeincomplete.com/posts/2011/8/19/javascript_state_machine_v2/example/) + +Download +======== + +You can download [state-machine.js](https://github.com/jakesgordon/javascript-state-machine/raw/master/state-machine.js), +or the [minified version](https://github.com/jakesgordon/javascript-state-machine/raw/master/state-machine.min.js) + +Alternatively: + + git clone git@github.com:jakesgordon/javascript-state-machine + + + * All code is in state-machine.js + * Minified version provided in state-machine.min.js + * No 3rd party library is required + * Demo can be found in /index.html + * QUnit tests can be found in /test/index.html + +Usage +===== + +Include `state-machine.min.js` in your application. + +In its simplest form, create a standalone state machine using: + + var fsm = StateMachine.create({ + initial: 'green', + events: [ + { name: 'warn', from: 'green', to: 'yellow' }, + { name: 'panic', from: 'yellow', to: 'red' }, + { name: 'calm', from: 'red', to: 'yellow' }, + { name: 'clear', from: 'yellow', to: 'green' } + ]}); + +... will create an object with a method for each event: + + * fsm.warn() - transition from 'green' to 'yellow' + * fsm.panic() - transition from 'yellow' to 'red' + * fsm.calm() - transition from 'red' to 'yellow' + * fsm.clear() - transition from 'yellow' to 'green' + +along with the following members: + + * fsm.current - contains the current state + * fsm.is(s) - return true if state `s` is the current state + * fsm.can(e) - return true if event `e` can be fired in the current state + * fsm.cannot(e) - return true if event `e` cannot be fired in the current state + +Multiple 'from' and 'to' states for a single event +================================================== + +If an event is allowed **from** multiple states, and always transitions to the same +state, then simply provide an array of states in the `from` attribute of an event. However, +if an event is allowed from multiple states, but should transition **to** a different +state depending on the current state, then provide multiple event entries with +the same name: + + var fsm = StateMachine.create({ + initial: 'hungry', + events: [ + { name: 'eat', from: 'hungry', to: 'satisfied' }, + { name: 'eat', from: 'satisfied', to: 'full' }, + { name: 'eat', from: 'full', to: 'sick' }, + { name: 'rest', from: ['hungry', 'satisfied', 'full', 'sick'], to: 'hungry' }, + ]}); + +This example will create an object with 2 event methods: + + * fsm.eat() + * fsm.rest() + +The `rest` event will always transition to the `hungry` state, while the `eat` event +will transition to a state that is dependent on the current state. + +>> NOTE: The `rest` event could use a wildcard '*' for the 'from' state if it should be +allowed from any current state. + +>> NOTE: The `rest` event in the above example can also be specified as multiple events with +the same name if you prefer the verbose approach. + +Callbacks +========= + +4 callbacks are available if your state machine has methods using the following naming conventions: + + * onbefore**event** - fired before the event + * onleave**state** - fired when leaving the old state + * onenter**state** - fired when entering the new state + * onafter**event** - fired after the event + +You can affect the event in 3 ways: + + * return `false` from an `onbeforeevent` handler to cancel the event. + * return `false` from an `onleavestate` handler to cancel the event. + * return `ASYNC` from an `onleavestate` handler to perform an asynchronous state transition (see next section) + +For convenience, the 2 most useful callbacks can be shortened: + + * on**event** - convenience shorthand for onafter**event** + * on**state** - convenience shorthand for onenter**state** + +In addition, a generic `onchangestate()` callback can be used to call a single function for _all_ state changes: + +All callbacks will be passed the same arguments: + + * **event** name + * **from** state + * **to** state + * _(followed by any arguments you passed into the original event method)_ + +Callbacks can be specified when the state machine is first created: + + var fsm = StateMachine.create({ + initial: 'green', + events: [ + { name: 'warn', from: 'green', to: 'yellow' }, + { name: 'panic', from: 'yellow', to: 'red' }, + { name: 'calm', from: 'red', to: 'yellow' }, + { name: 'clear', from: 'yellow', to: 'green' } + ], + callbacks: { + onpanic: function(event, from, to, msg) { alert('panic! ' + msg); }, + onclear: function(event, from, to, msg) { alert('thanks to ' + msg); }, + ongreen: function(event, from, to) { document.body.className = 'green'; }, + onyellow: function(event, from, to) { document.body.className = 'yellow'; }, + onred: function(event, from, to) { document.body.className = 'red'; }, + } + }); + + fsm.panic('killer bees'); + fsm.clear('sedatives in the honey pots'); + ... + +Additionally, they can be added and removed from the state machine at any time: + + fsm.ongreen = null; + fsm.onyellow = null; + fsm.onred = null; + fsm.onchangestate = function(event, from, to) { document.body.className = to; }; + +Asynchronous State Transitions +============================== + +Sometimes, you need to execute some asynchronous code during a state transition and ensure the +new state is not entered until your code has completed. + +A good example of this is when you transition out of a `menu` state, perhaps you want to gradually +fade the menu away, or slide it off the screen and don't want to transition to your `game` state +until after that animation has been performed. + +You can now return `StateMachine.ASYNC` from your `onleavestate` handler and the state machine +will be _'put on hold'_ until you are ready to trigger the transition using the new `transition()` +method. + +For example, using jQuery effects: + + var fsm = StateMachine.create({ + + initial: 'menu', + + events: [ + { name: 'play', from: 'menu', to: 'game' }, + { name: 'quit', from: 'game', to: 'menu' } + ], + + callbacks: { + + onentermenu: function() { $('#menu').show(); }, + onentergame: function() { $('#game').show(); }, + + onleavemenu: function() { + $('#menu').fadeOut('fast', function() { + fsm.transition(); + }); + return StateMachine.ASYNC; // tell StateMachine to defer next state until we call transition (in fadeOut callback above) + }, + + onleavegame: function() { + $('#game').slideDown('slow', function() { + fsm.transition(); + }; + return StateMachine.ASYNC; // tell StateMachine to defer next state until we call transition (in slideDown callback above) + } + + } + }); + + +State Machine Classes +===================== + +You can also turn all instances of a _class_ into an FSM by applying +the state machine functionality to the prototype, including your callbacks +in your prototype, and providing a `startup` event for use when constructing +instances: + + MyFSM = function() { // my constructor function + this.startup(); + }; + + MyFSM.prototype = { + + onpanic: function(event, from, to) { alert('panic'); }, + onclear: function(event, from, to) { alert('all is clear'); }, + + // my other prototype methods + + }; + + StateMachine.create({ + target: MyFSM.prototype, + events: [ + { name: 'startup', from: 'none', to: 'green' }, + { name: 'warn', from: 'green', to: 'yellow' }, + { name: 'panic', from: 'yellow', to: 'red' }, + { name: 'calm', from: 'red', to: 'yellow' }, + { name: 'clear', from: 'yellow', to: 'green' } + ]}); + + +This should be easy to adjust to fit your appropriate mechanism for object construction. + +Initialization Options +====================== + +How the state machine should initialize can depend on your application requirements, so +the library provides a number of simple options. + +By default, if you dont specify any initial state, the state machine will be in the `'none'` +state and you would need to provide an event to take it out of this state: + + var fsm = StateMachine.create({ + events: [ + { name: 'startup', from: 'none', to: 'green' }, + { name: 'panic', from: 'green', to: 'red' }, + { name: 'calm', from: 'red', to: 'green' }, + ]}); + alert(fsm.current); // "none" + fsm.startup(); + alert(fsm.current); // "green" + +If you specify the name of your initial event (as in all the earlier examples), then an +implicit `startup` event will be created for you and fired when the state machine is constructed. + + var fsm = StateMachine.create({ + initial: 'green', + events: [ + { name: 'panic', from: 'green', to: 'red' }, + { name: 'calm', from: 'red', to: 'green' }, + ]}); + alert(fsm.current); // "green" + +If your object already has a `startup` method you can use a different name for the initial event + + var fsm = StateMachine.create({ + initial: { state: 'green', event: 'init' }, + events: [ + { name: 'panic', from: 'green', to: 'red' }, + { name: 'calm', from: 'red', to: 'green' }, + ]}); + alert(fsm.current); // "green" + +Finally, if you want to wait to call the initial state transition event until a later date you +can `defer` it: + + var fsm = StateMachine.create({ + initial: { state: 'green', event: 'init', defer: true }, + events: [ + { name: 'panic', from: 'green', to: 'red' }, + { name: 'calm', from: 'red', to: 'green' }, + ]}); + alert(fsm.current); // "none" + fsm.init(); + alert(fsm.current); // "green" + +Of course, we have now come full circle, this last example is pretty much functionally the +same as the first example in this section where you simply define your own startup event. + +So you have a number of choices available to you when initializing your state machine. + +Handling Failures +====================== + +By default, if you try to call an event method that is not allowed in the current state, the +state machine will throw an exception. If you prefer to handle the problem yourself, you can +define a custom `error` handler: + + var fsm = StateMachine.create({ + initial: 'green', + error: function(eventName, from, to, args, errorCode, errorMessage) { + return 'event ' + eventName + ' was naughty :- ' + errorMessage; + }, + events: [ + { name: 'panic', from: 'green', to: 'red' }, + { name: 'calm', from: 'red', to: 'green' }, + ]}); + alert(fsm.calm()); // "event calm was naughty :- event not allowed in current state green" + +Release Notes +============= + +See [RELEASE NOTES](https://github.com/jakesgordon/javascript-state-machine/blob/master/RELEASE_NOTES.md) file. + +License +======= + +See [LICENSE](https://github.com/jakesgordon/javascript-state-machine/blob/master/LICENSE) file. + +Contact +======= + +If you have any ideas, feedback, requests or bug reports, you can reach me at +[jake@codeincomplete.com](mailto:jake@codeincomplete.com), or via +my website: [Code inComplete](http://codeincomplete.com/posts/2011/8/19/javascript_state_machine_v2/) + + + + + diff --git a/addons/base_import/static/lib/javascript-state-machine/RELEASE_NOTES.md b/addons/base_import/static/lib/javascript-state-machine/RELEASE_NOTES.md new file mode 100644 index 00000000000..06abf402d38 --- /dev/null +++ b/addons/base_import/static/lib/javascript-state-machine/RELEASE_NOTES.md @@ -0,0 +1,32 @@ +Version 2.1.0 (January 7th 2012) +-------------------------------- + + * Wrapped in self executing function to be more easily used with loaders like `require.js` or `curl.js` (issue #15) + * Allow event to be cancelled by returning `false` from `onleavestate` handler (issue #13) - WARNING: this breaks backward compatibility for async transitions (you now need to return `StateMachine.ASYNC` instead of `false`) + * Added explicit return values for event methods (issue #12) + * Added support for wildcard events that can be fired 'from' any state (issue #11) + * Added support for no-op events that transition 'to' the same state (issue #5) + * extended custom error callback to handle any exceptions caused by caller provided callbacks + * added custom error callback to override exception when an illegal state transition is attempted (thanks to cboone) + * fixed typos (thanks to cboone) + * fixed issue #4 - ensure before/after event hooks are called even if the event doesn't result in a state change + +Version 2.0.0 (August 19th 2011) +-------------------------------- + + * adding support for asynchronous state transitions (see README) - with lots of qunit tests (see test/async.js). + * consistent arguments for ALL callbacks, first 3 args are ALWAYS event name, from state and to state, followed by whatever arguments the user passed to the original event method. + * added a generic `onchangestate(event,from,to)` callback to detect all state changes with a single function. + * allow callbacks to be declared at creation time (instead of having to attach them afterwards) + * renamed 'hooks' => 'callbacks' + * [read more...](http://codeincomplete.com/posts/2011/8/19/javascript_state_machine_v2/) + +Version 1.2.0 (June 21st 2011) +------------------------------ + * allows the same event to transition to different states, depending on the current state (see 'Multiple...' section in README.md) + * [read more...](http://codeincomplete.com/posts/2011/6/21/javascript_state_machine_v1_2_0/) + +Version 1.0.0 (June 1st 2011) +----------------------------- + * initial version + * [read more...](http://codeincomplete.com/posts/2011/6/1/javascript_state_machine/) diff --git a/addons/base_import/static/lib/javascript-state-machine/Rakefile b/addons/base_import/static/lib/javascript-state-machine/Rakefile new file mode 100644 index 00000000000..beb8702a7f0 --- /dev/null +++ b/addons/base_import/static/lib/javascript-state-machine/Rakefile @@ -0,0 +1,8 @@ + +desc "create minified version of state-machine.js" +task :minify do + require File.expand_path(File.join(File.dirname(__FILE__), 'minifier/minifier')) + Minifier.enabled = true + Minifier.minify('state-machine.js') +end + diff --git a/addons/base_import/static/lib/javascript-state-machine/index.html b/addons/base_import/static/lib/javascript-state-machine/index.html new file mode 100644 index 00000000000..2d6cb62617b --- /dev/null +++ b/addons/base_import/static/lib/javascript-state-machine/index.html @@ -0,0 +1,39 @@ + + +
+