From 48dbf92a9c0751af0d27af0890e4d19275ab2027 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Fri, 27 May 2011 12:55:23 +0530 Subject: [PATCH 001/896] [EDI]: develop a ir_edi_document class bzr revid: aar@tinyerp.com-20110527072523-v93lpjnpxm765bqd --- openerp/addons/base/ir/__init__.py | 2 +- openerp/addons/base/ir/ir_edi.py | 215 +++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 openerp/addons/base/ir/ir_edi.py diff --git a/openerp/addons/base/ir/__init__.py b/openerp/addons/base/ir/__init__.py index 9b4eb96df5f..673c4f7e0ab 100644 --- a/openerp/addons/base/ir/__init__.py +++ b/openerp/addons/base/ir/__init__.py @@ -36,6 +36,6 @@ import ir_rule import wizard import ir_config_parameter import osv_memory_autovacuum - +import ir_edi # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py new file mode 100644 index 00000000000..8b980d4cf4c --- /dev/null +++ b/openerp/addons/base/ir/ir_edi.py @@ -0,0 +1,215 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from osv import osv,fields +import json +import hashlib +import time +import base64 +import urllib2 + +class ir_edi_document(osv.osv): + _name = 'ir.edi.document' + _description = 'ir_edi_document' + _columns = { + 'name': fields.char('EDI token', size = 128), + 'document': fields.text() + + } + + def safe_unique_id(model, database_id): + + """Generate a unique string to represent a (model,database id) pair + without being too long, without revealing the database id, and + with a very low probability of collisions. + """ + msg = "%s-%s-%s" % (model, time.time(), database_id) + digest = hashlib.sha1(msg).digest() + digest = ''.join(chr(ord(x) ^ ord(y)) for (x,y) in zip(digest[:9], digest[9:-2])) + # finally, use the b64-encoded folded digest as ID part of the unique ID: + digest = base64.urlsafe_b64encode(digest) + + return '%s' % (digest) + + def new_edi_token(self,cr,uid,ids,browse_records,database_id,context=None): + + """Generate new unique tokent value to uniquely identify each edi_document""" + + db_uuid = self.safe_unique_id(browse_records, database_id) + edi_token = hashlib.sha256('%s-%s-%s' % (time.time(),db_uuid,time.time())).hexdigest() + + + return edi_token + + def serialize(self,edi_list_of_dicts): + + """Serialize the list of dictionaries using json dumps method""" + + serialized_list = json.dumps(edi_list_of_dicts) + return serialized_list + + def generate_edi(self,browse_record_list): + + """ Generate the list of dictionaries using edi_export method of edi class """ + + edi = edi() + edi_list = [] + for record in browse_record_list: + edi_struct = edi.edi_export(record) + edi_list.append(edi_struct[0]) + return self.serialize(edi_list) + + def get_document(self,cr,uid,ids,edi_token,context=None): + + """Get the edi document from database using given edi token """ + + records = self.search(cr,uid,[('name','=',edi_token)],context=context) + if records: + doc_string = self.read(cr,uid,records,['document'],context=context) + else: + raise osv.except_osv(_('Error !'), + _('Desired EDI Document does not Exist')) + + return True + + def load_edi(self,cr,uid,ids,edi_list_of_dicts,context=None): + + """loads the values from list of dictionaries to the corresponding OpenERP records + using the edi_import method of edi class""" + edi = edi() + edi_list = [] + for record in edi_list_of_dicts: + edi_record = edi.edi_import(record) + + + return True + + def deserialize(self,edi_document_string): + """ Deserialized the edi document string""" + + deserialize_string = json.loads(edi_document_string) + return self.load_edi(deserialize_string) + + def export_doc(self, cr, uid, ids,browse_records,database_id,context=None): + """the method handles the flow of the edi document generation and store it in + the database and return the edi_token of the particular document""" + + edi_document = { + 'name': self.new_edi_token(browse_records,database_id), + 'document': self.generate_edi(browse_records) + } + + unique_id = self.create(cr,uid,edi_document,context=context) + return edi_document['name'] + + def import_doc(self, cr, uid, ids, edi_document=None,edi_url=None,context=None): + + """the method handles the flow of importing particular edi document and + updates the database values on the basis of the edi document using + edi_loads method """ + + if edi_document is not None: + self.deserialize(edi_document) + elif edi_url is not None: + edi_document = urllib2.urlopen(edi_url).read() + self.deserialize(edi_document) + + return True + +ir_edi_document() + + +class edi(object): + _name = 'edi' + _description = 'edi document handler' + + """Mixin class for OSV objects that want be exposed as EDI documents. + Classes that inherit from this mixin class should override the + ``edi_import()`` and ``edi_export()`` methods to implement their + specific behavior, based on the primitives provided by this superclass.""" + + + def edi_metadata(self, cr, uid, browse_rec_list, context=None): + """Return a list representing the boilerplate EDI structure for + exporting the record in the given browse_rec_list, including + the metadata fields""" + # generic implementation! + return + + def edi_m2o(self, browse_rec, context=None): + """Return a list representing a M2O EDI value for + the given browse_record.""" + # generic implementation! + return + + def edi_o2m(self, browse_rec_list, context=None): + """Return a list representing a O2M EDI value for + the browse_records from the given browse_record_list.""" + # generic implementation! + return + + def edi_m2m(self, browse_rec_list, context=None): + """Return a list representing a M2M EDI value for + the browse_records from the given browse_record_list.""" + # generic implementation! + return + + def edi_export(self, cr, uid, ids, edi_struct=None, context=None): + """Returns a list of dicts representing an edi.document containing the + browse_records with ``ids``, using the generic algorithm. + :param edi_struct: if provided, edi_struct should be a dictionary + with a skeleton of the OSV fields to export as edi. + Basic fields can have any key as value, but o2m + values should have a sample skeleton dict as value. + For example, for a res.partner record: + edi_struct: { + 'name': True, + 'company_id': True, + 'address': { + 'name': True, + 'street': True, + } + } + Any field not specified in the edi_struct will not + be included in the exported data. + """ + # generic implementation! + + edi_struct = { + 'Openerp_fields':'', + '__model':'', + '__module':'', + '__id':'', + '__last_update':'', + '__version':'', + '__attachments':'', + + } + + + + return edi_struct + def edi_import(self, cr, uid, edi_document, context=None): + """Imports a list of dicts representing an edi.document, using the + generic algorithm. + """ + # generic implementation! + pass From 2e7ee8692142df851053967954e8eb55ffb13203 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Fri, 27 May 2011 19:01:31 +0530 Subject: [PATCH 002/896] [EDI]: working on a edi class bzr revid: aar@tinyerp.com-20110527133131-a1romjw8fft97950 --- openerp/addons/base/ir/ir_edi.py | 35 +++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index 8b980d4cf4c..2187b4f89ca 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -74,7 +74,7 @@ class ir_edi_document(osv.osv): edi_list = [] for record in browse_record_list: edi_struct = edi.edi_export(record) - edi_list.append(edi_struct[0]) + edi_list.append(edi_struct) return self.serialize(edi_list) def get_document(self,cr,uid,ids,edi_token,context=None): @@ -193,8 +193,8 @@ class edi(object): """ # generic implementation! - edi_struct = { - 'Openerp_fields':'', + self.edi_document = { + '__model':'', '__module':'', '__id':'', @@ -203,10 +203,31 @@ class edi(object): '__attachments':'', } - - - - return edi_struct + data_object = self.pool.get('ir.model.data') + fields_object = self.pool.get('ir.model.fields') + for field in edi_struct.keys(): + if field in self.edi_document.keys(): + if field == 'model': + model_name = edi_struct['model'] + + record_ids = data_object.search(cr,uid,[('model','=','hr.holidays')]) + for fname in data_object.browse(cr,uid,record_ids): + if fname.name: + xml_ID = fname.name + self.edi_metadata(field,model_name) + else: + self.metadata(field) + else: + + f_ids = fields_object.search(cr,uid,[('name','=',field),('model','=',model_name)],context=context) + for fname in fields_object.browse(cr,uid,f_ids): + if fname.ttype == 'many2one': + self.edi_document[field] = self.edi_m2o(edi_struct[field]) + elif fname.ttype == 'one2many': + self.edi_ducument[field] = self.edi_o2m(edi_struct[field]) + elif fname.ttype == 'many2many': + self.edi_document[field] = self.edi_m2m(edi_struct[field]) + return self.edi_document def edi_import(self, cr, uid, edi_document, context=None): """Imports a list of dicts representing an edi.document, using the generic algorithm. From 2853ca743f27769f67803e405353c8656d7600b6 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Sat, 28 May 2011 19:09:43 +0530 Subject: [PATCH 003/896] [EDI]: working on a edi class bzr revid: aar@tinyerp.com-20110528133943-1r1hle6k75dvr17o --- openerp/addons/base/ir/ir_edi.py | 37 +++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index 2187b4f89ca..251108cbf04 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -147,12 +147,16 @@ class edi(object): specific behavior, based on the primitives provided by this superclass.""" - def edi_metadata(self, cr, uid, browse_rec_list, context=None): + def edi_metadata(self, cr, uid, browse_rec_list,model=None, context=None): """Return a list representing the boilerplate EDI structure for exporting the record in the given browse_rec_list, including the metadata fields""" # generic implementation! - return + if model is not None: + self.edi_document['__model'] = model + + + return True def edi_m2o(self, browse_rec, context=None): """Return a list representing a M2O EDI value for @@ -195,28 +199,30 @@ class edi(object): self.edi_document = { - '__model':'', - '__module':'', - '__id':'', - '__last_update':'', - '__version':'', - '__attachments':'', + 'model':'', + 'module':'', + 'id':'', + 'last_update':'', + 'version':'', + 'attachments':'', } + if 'model' not in edi_struct.keys(): + return False data_object = self.pool.get('ir.model.data') + fields_object = self.pool.get('ir.model.fields') - for field in edi_struct.keys(): - if field in self.edi_document.keys(): + + for field in self.edi_document.keys(): + + if field in edi_struct.keys(): if field == 'model': model_name = edi_struct['model'] - - record_ids = data_object.search(cr,uid,[('model','=','hr.holidays')]) + record_ids = data_object.search(cr,uid,[('model','=',model_name)]) for fname in data_object.browse(cr,uid,record_ids): if fname.name: xml_ID = fname.name - self.edi_metadata(field,model_name) - else: - self.metadata(field) + else: f_ids = fields_object.search(cr,uid,[('name','=',field),('model','=',model_name)],context=context) @@ -229,6 +235,7 @@ class edi(object): self.edi_document[field] = self.edi_m2m(edi_struct[field]) return self.edi_document def edi_import(self, cr, uid, edi_document, context=None): + """Imports a list of dicts representing an edi.document, using the generic algorithm. """ From d6f19568d0b08c2b2110a357fa443d176c9c515e Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Mon, 30 May 2011 19:04:28 +0530 Subject: [PATCH 004/896] [EDI]: working on edi import bzr revid: aar@tinyerp.com-20110530133428-v68yubyv3rb9r4xb --- openerp/addons/base/ir/ir_edi.py | 161 ++++++++++++++++++++----------- 1 file changed, 107 insertions(+), 54 deletions(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index 251108cbf04..9d66d4ed866 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -26,6 +26,20 @@ import time import base64 import urllib2 +def safe_unique_id(model, database_id): + + """Generate a unique string to represent a (model,database id) pair + without being too long, without revealing the database id, and + with a very low probability of collisions. + """ + msg = "%s-%s-%s" % (model, time.time(), database_id) + digest = hashlib.sha1(msg).digest() + digest = ''.join(chr(ord(x) ^ ord(y)) for (x,y) in zip(digest[:9], digest[9:-2])) + # finally, use the b64-encoded folded digest as ID part of the unique ID: + digest = base64.urlsafe_b64encode(digest) + + return '%s' % (digest) + class ir_edi_document(osv.osv): _name = 'ir.edi.document' _description = 'ir_edi_document' @@ -35,25 +49,13 @@ class ir_edi_document(osv.osv): } - def safe_unique_id(model, database_id): - - """Generate a unique string to represent a (model,database id) pair - without being too long, without revealing the database id, and - with a very low probability of collisions. - """ - msg = "%s-%s-%s" % (model, time.time(), database_id) - digest = hashlib.sha1(msg).digest() - digest = ''.join(chr(ord(x) ^ ord(y)) for (x,y) in zip(digest[:9], digest[9:-2])) - # finally, use the b64-encoded folded digest as ID part of the unique ID: - digest = base64.urlsafe_b64encode(digest) - - return '%s' % (digest) + def new_edi_token(self,cr,uid,ids,browse_records,database_id,context=None): """Generate new unique tokent value to uniquely identify each edi_document""" - db_uuid = self.safe_unique_id(browse_records, database_id) + db_uuid = safe_unique_id(browse_records, database_id) edi_token = hashlib.sha256('%s-%s-%s' % (time.time(),db_uuid,time.time())).hexdigest() @@ -88,7 +90,7 @@ class ir_edi_document(osv.osv): raise osv.except_osv(_('Error !'), _('Desired EDI Document does not Exist')) - return True + return doc_string def load_edi(self,cr,uid,ids,edi_list_of_dicts,context=None): @@ -147,34 +149,91 @@ class edi(object): specific behavior, based on the primitives provided by this superclass.""" - def edi_metadata(self, cr, uid, browse_rec_list,model=None, context=None): + def edi_metadata(self, cr, uid, ids, browse_rec_list, context=None): """Return a list representing the boilerplate EDI structure for exporting the record in the given browse_rec_list, including the metadata fields""" # generic implementation! - if model is not None: - self.edi_document['__model'] = model + data_object = self.pool.get('ir.model.data') + if self.edi_document['__model'] is not None: + model_ids = data_object.search(cr,uid,[('model','=',self.edi_document['__model'])],context=context) + for data in data_object.browse(cr,uid,model_ids): + self.edi_document['__module'] = data.module + if data.name is not None: + self.edi_document['__id'] = context['active_id']+':'+data.name + self.edi_document['__last_updata'] = context['__last_update'].values()[0] return True - def edi_m2o(self, browse_rec, context=None): + def edi_m2o(self, cr, uid, ids, browse_rec,model=None,context=None): """Return a list representing a M2O EDI value for the given browse_record.""" # generic implementation! - return + res = [] + data_object = self.pool.get('ir.model.data') + uu_id = data_model.search(cr,uid,[('model','=',model)]) + for xml in data_model.browse(cr,uid,uu_id): + if xml: + xml_ID = xml.name + else: + xml_ID = None + for data in browse_rec: + if data.name is not None: + if xml_ID is not None: + res.append(safe_unique_id(model,data.id)+':'+xml_ID) + else: + res.append(safe_unique_id(model,data.id)+':'+model+'-'+safe_unique_id(model,data.id)) + res.append(data.name) + return res - def edi_o2m(self, browse_rec_list, context=None): + def edi_o2m(self, cr, uid, ids, browse_rec_list, model=None, context=None): """Return a list representing a O2M EDI value for the browse_records from the given browse_record_list.""" # generic implementation! - return + res = [] + init_data = {} + data_object = self.pool.get('ir.model.data') + uu_id = data_model.search(cr,uid,[('model','=',model)]) + for xml in data_model.browse(cr,uid,uu_id): + if xml: + xml_ID = xml.name + else: + xml_ID = None + data_model = self.pool.get(model) + for record in browse_rec_list: + if xml_ID is not None: + init_data['__id'] = safe_unique_id(model,record.id)+':'+xml_ID + else: + init_data['__id'] = safe_unique_id(model,record.id)+':'+model+'-'+safe_unique_id(model,record.id) + init_data['__last_update'] =record.write_date + init_data.update(datamodel.read(cr,uid,[record],[],context=context)[0]) + res.append(init_data) + return res - def edi_m2m(self, browse_rec_list, context=None): + def edi_m2m(self, cr, uid, ids, browse_rec_list,model=None, context=None): """Return a list representing a M2M EDI value for the browse_records from the given browse_record_list.""" # generic implementation! - return + res = [] + init_data = [] + data_object = self.pool.get('ir.model.data') + uu_id = data_model.search(cr,uid,[('model','=',model)]) + for xml in data_model.browse(cr,uid,uu_id): + if xml: + xml_ID = xml.name + else: + xml_ID = None + data_model = self.pool.get(model) + for record in browse_rec_list: + if xml_ID is not None: + init_data.append(safe_unique_id(model,record.id)+':'+xml_ID) + else: + init_data.append(safe_unique_id(model,record.id)+':'+model+'-'+safe_unique_id(model,record.id)) + if record.name is not None: + init_data.append('name:'+record.name) + res.append(init_data) + return res def edi_export(self, cr, uid, ids, edi_struct=None, context=None): """Returns a list of dicts representing an edi.document containing the @@ -199,40 +258,32 @@ class edi(object): self.edi_document = { - 'model':'', - 'module':'', - 'id':'', - 'last_update':'', - 'version':'', - 'attachments':'', + '__model':'', + '__module':'', + '__id':'', + '__last_update':'', + '__version':'', + '__attachments':'', } - if 'model' not in edi_struct.keys(): - return False - data_object = self.pool.get('ir.model.data') - - fields_object = self.pool.get('ir.model.fields') + self.edi_document['__model'] = self._name - for field in self.edi_document.keys(): - - if field in edi_struct.keys(): - if field == 'model': - model_name = edi_struct['model'] - record_ids = data_object.search(cr,uid,[('model','=',model_name)]) - for fname in data_object.browse(cr,uid,record_ids): - if fname.name: - xml_ID = fname.name + + fields_object = self.pool.get('ir.model.fields') + model_object = self.pool.get(self.edi_document['__model']) + edi_metadata(context=context) + for field in self.edi_struct.keys(): + f_ids = fields_object.search(cr,uid,[('name','=',field),('model','=',self.edi_document['__model'])],context=context) + for fname in fields_object.browse(cr,uid,f_ids): + if fname.ttype == 'many2one': - else: - - f_ids = fields_object.search(cr,uid,[('name','=',field),('model','=',model_name)],context=context) - for fname in fields_object.browse(cr,uid,f_ids): - if fname.ttype == 'many2one': - self.edi_document[field] = self.edi_m2o(edi_struct[field]) - elif fname.ttype == 'one2many': - self.edi_ducument[field] = self.edi_o2m(edi_struct[field]) - elif fname.ttype == 'many2many': - self.edi_document[field] = self.edi_m2m(edi_struct[field]) + self.edi_document[field] = self.edi_m2o(edi_struct[field],fname.relation) + elif fname.ttype == 'one2many': + self.edi_ducument[field] = self.edi_o2m(edi_struct[field],fname.relation) + elif fname.ttype == 'many2many': + self.edi_document[field] = self.edi_m2m(edi_struct[field],fname.relation) + else: + self.edi_document[field] = model_object.read(cr,uid,[context['active_id']],[field],context=context) return self.edi_document def edi_import(self, cr, uid, edi_document, context=None): @@ -240,4 +291,6 @@ class edi(object): generic algorithm. """ # generic implementation! + for field in edi_document.keys(): + pass From 4deb609685c4fa002d13bb102fb141b09b4f6782 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Wed, 1 Jun 2011 19:03:10 +0530 Subject: [PATCH 005/896] [EDI]: working on edi import bzr revid: aar@tinyerp.com-20110601133310-nklnhlymx8u38nsg --- openerp/addons/base/ir/ir_edi.py | 81 ++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index 9d66d4ed866..a87453fd277 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -38,7 +38,7 @@ def safe_unique_id(model, database_id): # finally, use the b64-encoded folded digest as ID part of the unique ID: digest = base64.urlsafe_b64encode(digest) - return '%s' % (digest) + return '%s-%s' % (model,digest) class ir_edi_document(osv.osv): _name = 'ir.edi.document' @@ -51,7 +51,7 @@ class ir_edi_document(osv.osv): - def new_edi_token(self,cr,uid,ids,browse_records,database_id,context=None): + def new_edi_token(self,browse_records,database_id): """Generate new unique tokent value to uniquely identify each edi_document""" @@ -71,15 +71,14 @@ class ir_edi_document(osv.osv): def generate_edi(self,browse_record_list): """ Generate the list of dictionaries using edi_export method of edi class """ - - edi = edi() + edi_list = [] for record in browse_record_list: - edi_struct = edi.edi_export(record) + edi_struct = record.edi_export(record) edi_list.append(edi_struct) return self.serialize(edi_list) - def get_document(self,cr,uid,ids,edi_token,context=None): + def get_document(self,cr,uid,edi_token,context=None): """Get the edi document from database using given edi token """ @@ -96,21 +95,21 @@ class ir_edi_document(osv.osv): """loads the values from list of dictionaries to the corresponding OpenERP records using the edi_import method of edi class""" - edi = edi() + edi_list = [] for record in edi_list_of_dicts: - edi_record = edi.edi_import(record) + edi_record = record.edi_import(record) return True - def deserialize(self,edi_document_string): + def deserialize(self,cr,uid,edi_document_string,context=None): """ Deserialized the edi document string""" deserialize_string = json.loads(edi_document_string) - return self.load_edi(deserialize_string) + return self.load_edi(cr,uid,deserialize_string,context=context) - def export_doc(self, cr, uid, ids,browse_records,database_id,context=None): + def export_doc(self, cr, uid, browse_records, database_id, context=None): """the method handles the flow of the edi document generation and store it in the database and return the edi_token of the particular document""" @@ -129,10 +128,10 @@ class ir_edi_document(osv.osv): edi_loads method """ if edi_document is not None: - self.deserialize(edi_document) + self.deserialize(cr,uid,edi_document,context=context) elif edi_url is not None: edi_document = urllib2.urlopen(edi_url).read() - self.deserialize(edi_document) + self.deserialize(cr,uid,edi_document,context=context) return True @@ -149,7 +148,7 @@ class edi(object): specific behavior, based on the primitives provided by this superclass.""" - def edi_metadata(self, cr, uid, ids, browse_rec_list, context=None): + def edi_metadata(self, cr, uid,browse_rec_list, context=None): """Return a list representing the boilerplate EDI structure for exporting the record in the given browse_rec_list, including the metadata fields""" @@ -162,14 +161,18 @@ class edi(object): self.edi_document['__module'] = data.module if data.name is not None: self.edi_document['__id'] = context['active_id']+':'+data.name + else: + self.edi_document['__id'] = context['active_id']+':'+safe_unique_id(data.model,) self.edi_document['__last_updata'] = context['__last_update'].values()[0] return True - def edi_m2o(self, cr, uid, ids, browse_rec,model=None,context=None): + def edi_m2o(self, cr, uid,browse_rec): """Return a list representing a M2O EDI value for the given browse_record.""" # generic implementation! + + model = browse_rec._name res = [] data_object = self.pool.get('ir.model.data') uu_id = data_model.search(cr,uid,[('model','=',model)]) @@ -183,23 +186,26 @@ class edi(object): if xml_ID is not None: res.append(safe_unique_id(model,data.id)+':'+xml_ID) else: - res.append(safe_unique_id(model,data.id)+':'+model+'-'+safe_unique_id(model,data.id)) + res.append(safe_unique_id(model,data.id)+':'+safe_unique_id(model,data.id)) res.append(data.name) return res - def edi_o2m(self, cr, uid, ids, browse_rec_list, model=None, context=None): + def edi_o2m(self, cr, uid,line = None,browse_rec_list, context=None): """Return a list representing a O2M EDI value for the browse_records from the given browse_record_list.""" # generic implementation! res = [] init_data = {} + model = browse_rec_list._name data_object = self.pool.get('ir.model.data') uu_id = data_model.search(cr,uid,[('model','=',model)]) + for xml in data_model.browse(cr,uid,uu_id): if xml: xml_ID = xml.name else: xml_ID = None + data_model = self.pool.get(model) for record in browse_rec_list: if xml_ID is not None: @@ -207,14 +213,16 @@ class edi(object): else: init_data['__id'] = safe_unique_id(model,record.id)+':'+model+'-'+safe_unique_id(model,record.id) init_data['__last_update'] =record.write_date - init_data.update(datamodel.read(cr,uid,[record],[],context=context)[0]) + init_data.update(self.determine_type(cr,uid,ids,line,record,context=context)) + res.append(init_data) return res - def edi_m2m(self, cr, uid, ids, browse_rec_list,model=None, context=None): + def edi_m2m(self, cr, uid, line = None, browse_rec_list, context=None): """Return a list representing a M2M EDI value for the browse_records from the given browse_record_list.""" # generic implementation! + model = browse_rec_list[0]._name res = [] init_data = [] data_object = self.pool.get('ir.model.data') @@ -271,20 +279,43 @@ class edi(object): fields_object = self.pool.get('ir.model.fields') model_object = self.pool.get(self.edi_document['__model']) - edi_metadata(context=context) - for field in self.edi_struct.keys(): + edi_metadata(edi_struct,context=context) + record = model_object.read(cr,uid,ids,[],context=context) + for field in edi_struct.keys(): f_ids = fields_object.search(cr,uid,[('name','=',field),('model','=',self.edi_document['__model'])],context=context) + for fname in fields_object.browse(cr,uid,f_ids): if fname.ttype == 'many2one': - self.edi_document[field] = self.edi_m2o(edi_struct[field],fname.relation) + self.edi_document[field] = self.edi_m2o(cr, uid, browse_rec = record[0][field]) elif fname.ttype == 'one2many': - self.edi_ducument[field] = self.edi_o2m(edi_struct[field],fname.relation) + + self.edi_ducument[field] = self.edi_o2m(cr,uid,line = edi_struct[field],browse_rec_list = record[0][field]) elif fname.ttype == 'many2many': - self.edi_document[field] = self.edi_m2m(edi_struct[field],fname.relation) + self.edi_document[field] = self.edi_m2m(cr,uid,line = edi_struct[field],browse_rec_list = record[0][field]) else: - self.edi_document[field] = model_object.read(cr,uid,[context['active_id']],[field],context=context) + self.edi_document[field] = record[0][field] + + return self.edi_document + def determine_type(self,cr,uid,ids,record_line,browse_record=None,context=None): + model = browse_record._name + fields_object = self.pool.get('ir.model.fields') + obj = self.pool.get(model) + field_data = obj.read(cr,uid,browse_record.id,record_line.keys())[0] + for field in field_data.keys(): + field_ids = field_object.search(cr,uid,[field]) + for field_type in field_object.browse(cr,uid,field_ids): + if field_type.ttype == 'many2one': + record_line.update(field,self.edi_m2o(cr,uid,browse_rec = field_data[field])) + elif field_type.ttype == 'one2many': + pass + elif field_type.ttype == 'many2many': + pass + else: + record_line.update(field,field_data[field]) + return record_line + def edi_import(self, cr, uid, edi_document, context=None): """Imports a list of dicts representing an edi.document, using the From 1e3ae46ec7a48a80b8625ae18aa654c936a8876c Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Thu, 2 Jun 2011 19:12:18 +0530 Subject: [PATCH 006/896] [EDI]: developing edi_import method bzr revid: aar@tinyerp.com-20110602134218-32aolwbdbwkp63qp --- openerp/addons/base/ir/ir_edi.py | 78 ++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index a87453fd277..d5146b4c655 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -240,7 +240,9 @@ class edi(object): init_data.append(safe_unique_id(model,record.id)+':'+model+'-'+safe_unique_id(model,record.id)) if record.name is not None: init_data.append('name:'+record.name) - res.append(init_data) + res.append(init_data) + + # blob: return res def edi_export(self, cr, uid, ids, edi_struct=None, context=None): @@ -279,7 +281,7 @@ class edi(object): fields_object = self.pool.get('ir.model.fields') model_object = self.pool.get(self.edi_document['__model']) - edi_metadata(edi_struct,context=context) + edi_metadata(cr, uid, edi_struct, context=context) record = model_object.read(cr,uid,ids,[],context=context) for field in edi_struct.keys(): f_ids = fields_object.search(cr,uid,[('name','=',field),('model','=',self.edi_document['__model'])],context=context) @@ -322,6 +324,76 @@ class edi(object): generic algorithm. """ # generic implementation! + new = True + resource = {} + model = edi_document['__model'] + model_object = self.pool.get(model) + data_model = self.pool.get('ir.model.data') + field_model = self.pool.get('ir.model.fields') for field in edi_document.keys(): + if field == '__id': + db_id , xml_ID = edi_document[field].split(':') + self.data_ids = data_model.search(cr,uid,[('name','=',xml_ID)]) - pass + + f_ids = fields_object.search(cr,uid,[('name','=',field),('model','=',model)],context=context) + for fname in fields_object.browse(cr,uid,f_ids): + + if fname.ttype == 'many2one': + field_ids,field_xml = edi_document[field][0].split(':') + field_ids = int(field_ids) + model_ids = data_model.search(cr,uid,[('name','=',field_xml)]) + if not len(model_ids): + obj = self.pool.get(fname.relation) + model1_ids = obj.search(cr,uid,[('name','=',edi_document[field][1])]) + if len(model1_ids) == 1: + resourse[field] = data_model.create(cr,uid,{'module':'edi_import','name':field_xml,'res_id':model1_ids})[0] + + elif not len(model1_ids) or len(model1_ids) > 1: + edi_document[field][1] = obj.create(cr,uid,{'name':edi_document[field][1]}) + resource[field] = data_model.create(cr,uid,{'module':'edi_import','name':field_xml,'res_id':edi_document[field][0]})[0] + else: + resource[field] = model_ids[0] + + elif fname.ttype == 'one2many': + for records in edi_document[field]: + res = {} + field_ids,field_xml = records['__id'].split(':') + field_ids = int(field_ids) + model3_ids = data_model.search(cr,uid,[('name','=',field_xml)]) + for fields in records.keys(): + if fields != '__id' or fields != '__last_update': + res[fields] = records[fields] + if not len(model3_ids): + resource[field][records] = (4,self.edi_import(cr,uid,res,context = context)[0]) + else: + resource[field][records] = (4,model3_ids[0])obj = self.pool.get(model) + record_ids = obj.create(cr,uid,resource,context=context) + return record_ids + + elif fname.ttype == 'many2many': + for records in edi_document[field]: + field_ids,field_xml = records[0].split(':') + field_ids = int(field_ids) + model4_ids = data_model.search(cr,uid,[('name','=',field_xml)]) + if not len(model4_ids): + obj = self.pool.get(fname.relation) + name,value = records[1].split(':') + model5_ids = obj.search(cr,uid,[(name,'=',value)]) + if len(model5_ids) == 1: + resource[field][records] = data_model.create(cr,uid,{'module':'edi_import','name':field_xml,'res_id':model5_ids})[0] + elif not len(model5_ids) or len(model5_ids) > 1: + edi_document[field][records][1] = obj.create(cr,uid,{'name':edi_document[field][1]}) + resource[field] = (4,data_model.create(cr,uid,{'module':'edi_import','name':field_xml,'res_id':edi_document[field][records][1]})[0]) + else: + resource[field] = (4,model4_ids[0]) + else: + resource[field] = edi_document[field] + if len(self.data_ids): + obj = self.pool.get(model) + record_ids = obj.write(cr,uid,resource,context=context) + return record_ids + else: + obj = self.pool.get(model) + record_ids = obj.create(cr,uid,resource,context=context) + return record_ids From f1ce7642fbf5edcb1cb392f4a7a1feb0a3725888 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 3 Jun 2011 12:59:50 +0530 Subject: [PATCH 007/896] [REVIEW] edi: optimize code bzr revid: hmo@tinyerp.com-20110603072950-441p4kd4vyvzpo7a --- openerp/addons/base/ir/ir_edi.py | 344 +++++++++++++++++-------------- 1 file changed, 190 insertions(+), 154 deletions(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index d4d3692203d..2a0c9282de9 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -27,10 +27,24 @@ import base64 import urllib2 def safe_unique_id(model, database_id): - """Generate a unique string to represent a (model,database id) pair without being too long, without revealing the database id, and with a very low probability of collisions. + + Each EDI record and each relationship value are represented using a unique + database identifier. These database identifiers include the database unique + ID, as a way to uniquely refer to any record within any OpenERP instance, + without conflict. + + For OpenERP records that have an existing "XML ID" (i.e. an entry in + ir.model.data), the EDI unique identifier for this record will be made of + "%s:%s" % (the database's UUID, the XML ID). The database's UUID MUST + NOT contain a colon characters (this is guaranteed by the UUID algorithm). + + For OpenERP records that have no existing "XML ID", a new one should be + created during the EDI export. It is recommended that the generated XML ID + contains a readable reference to the record model, plus a unique value that + hides the database ID. """ msg = "%s-%s-%s" % (model, time.time(), database_id) digest = hashlib.sha1(msg).digest() @@ -42,10 +56,10 @@ def safe_unique_id(model, database_id): class ir_edi_document(osv.osv): _name = 'ir.edi.document' - _description = 'ir_edi_document' + _description = 'To represent the EDI export of any OpenERP record, it could be an invoice, a project task, etc. _columns = { - 'name': fields.char("EDI token", size = 128), - 'document': fields.text("Document") + 'name': fields.char("EDI token", size = 128, help="EDI Token is a unique identifier for the EDI document."), + 'document': fields.text("Document", help="hold the serialization of the EDI document.") } _sql_constraints = [ @@ -53,90 +67,115 @@ class ir_edi_document(osv.osv): ] - def new_edi_token(self,browse_records,database_id): - - """Generate new unique tokent value to uniquely identify each edi_document""" - - db_uuid = safe_unique_id(browse_records, database_id) - edi_token = hashlib.sha256('%s-%s-%s' % (time.time(),db_uuid,time.time())).hexdigest() - - + def new_edi_token(self, record): + """ + Return a new, random unique token to identify an edi.document + :param record: It's a object of browse_record of any model + """ + db_uuid = safe_unique_id(record._name, record.id) + edi_token = hashlib.sha256('%s-%s-%s' % (time.time(), db_uuid, time.time())).hexdigest() return edi_token - def serialize(self,edi_list_of_dicts): - - """Serialize the list of dictionaries using json dumps method""" - - serialized_list = json.dumps(edi_list_of_dicts) + def serialize(self, edi_documents): + """Serialize the list of dictionaries using json dumps method + perform a JSON serialization of a list of dicts prepared by generate_edi() and return a UTF-8 encoded string that could be passed to deserialize() + :param edi_dicts: it's list of edi_dict + """ + serialized_list = json.dumps(edi_dicts) return serialized_list - def generate_edi(self,browse_record_list): - - """ Generate the list of dictionaries using edi_export method of edi class """ - + def generate_edi(self, cr, uid, records, context=None): + """ + Generate the list of dictionaries using edi_export method of edi class + :param records: it's a object of browse_record_list of any model + """ edi_list = [] - for record in browse_record_list: - edi_struct = record.edi_export(record) + for record in record_list: + edi_struct = record.edi_export(cr, uid, record, context=context) edi_list.append(edi_struct) return self.serialize(edi_list) - def get_document(self,cr,uid,edi_token,context=None): + def get_document(self, cr, uid, edi_token, context=None): + """ + Get the edi document from database using given edi token + returns the string serialization that is in the database (column: document) for the given edi_token or raise. + """ - """Get the edi document from database using given edi token """ - - records = self.search(cr,uid,[('name','=',edi_token)],context=context) + records = self.name_search(cr, uid, edi_token, context=context) if records: - doc_string = self.read(cr,uid,records,['document'],context=context) + edi = self.browse(cr, uid, records[0], context=context) + return edi.document else: raise osv.except_osv(_('Error !'), _('Desired EDI Document does not Exist')) - return doc_string + - def load_edi(self,cr,uid,ids,edi_list_of_dicts,context=None): - - """loads the values from list of dictionaries to the corresponding OpenERP records - using the edi_import method of edi class""" - - edi_list = [] - for record in edi_list_of_dicts: - edi_record = record.edi_import(record) - - - return True - - def deserialize(self,cr,uid,edi_document_string,context=None): - """ Deserialized the edi document string""" - - deserialize_string = json.loads(edi_document_string) - return self.load_edi(cr,uid,deserialize_string,context=context) - - def export_doc(self, cr, uid, browse_records, database_id, context=None): - """the method handles the flow of the edi document generation and store it in - the database and return the edi_token of the particular document""" - - edi_document = { - 'name': self.new_edi_token(browse_records,database_id), - 'document': self.generate_edi(browse_records) - } - - unique_id = self.create(cr,uid,edi_document,context=context) - return edi_document['name'] - - def import_doc(self, cr, uid, ids, edi_document=None,edi_url=None,context=None): - - """the method handles the flow of importing particular edi document and - updates the database values on the basis of the edi document using - edi_loads method """ - - if edi_document is not None: - self.deserialize(cr,uid,edi_document,context=context) - elif edi_url is not None: - edi_document = urllib2.urlopen(edi_url).read() - self.deserialize(cr,uid,edi_document,context=context) + def load_edi(self, cr, uid, edi_documents, context=None): + """ + loads the values from list of dictionaries to the corresponding OpenERP records + using the edi_import method of edi class + For each edi record (dict) in the list, call the corresponding osv.edi_import() method, based on the __model attribute (see section 2.4 of for spec of + osv.edi_import) + :param edi_dicts: list of edi_dict + """ + + for edi_document in edi_documents: + model = edi_document.get('__model') + assert model, _('model should be provided in EDI Dict') + model_obj = self.pool.get(model) + model_obj.edi_import(cr, uid, edi_document, context=context) return True + def deserialize(self, edi_document_string): + """ Deserialized the edi document string + perform JSON deserialization from an edi document string, and returns a list of dicts + """ + edi_document = json.loads(edi_document_string) + return edi_document + + def export(self, cr, uid, records, context=None): + """ + The method handles the flow of the edi document generation and store it in + the database and return the edi_token of the particular document + Steps: + * call generate_edi() to get a serialization and new_edi_token() to get a unique ID + * serialize the list returned by generate_edi() using serialize(), and save it in database with unique ID. + * return the unique ID + + : param records: list of browse_record of any model + """ + exported_ids = [] + for record in records: + document = self.generate_edi(cr, uid, [record], context) + token = self.new_edi_token(record) + self.create(cr, uid, { + 'name': token, + 'document': document + }, context=context) + + exported_ids.append(token) + return exported_ids + + def import(self, cr, uid, edi_document=None, edi_url=None, context=None): + """ + The method handles the flow of importing particular edi document and + updates the database values on the basis of the edi document using + edi_loads method + + * N: a serialized edi.document or the URL to download a serialized document + * If a URL is provided, download it first to get the document + * Calls deserialize() to get the resulting list of dicts from the document + * Call load_edi() with the list of dicts, to create or update the corresponding OpenERP records based on the edi.document. + """ + + if edi_url and not edi_document: + edi_document = urllib2.urlopen(edi_url).read() + assert edi_document, _('EDI Document should be provided') + edi_documents = self.deserialize(edi_document) + return self.load_edi(cr, uid, edi_documents, context=context) + ir_edi_document() @@ -150,102 +189,99 @@ class edi(object): specific behavior, based on the primitives provided by this superclass.""" - def edi_metadata(self, cr, uid,browse_rec_list, context=None): + def edi_metadata(self, cr, uid, records, context=None): """Return a list representing the boilerplate EDI structure for exporting the record in the given browse_rec_list, including - the metadata fields""" - # generic implementation! - data_object = self.pool.get('ir.model.data') + the metadata fields - if self.edi_document['__model'] is not None: - model_ids = data_object.search(cr,uid,[('model','=',self.edi_document['__model'])],context=context) - for data in data_object.browse(cr,uid,model_ids): - self.edi_document['__module'] = data.module - if data.name is not None: - self.edi_document['__id'] = context['active_id']+':'+data.name - else: - self.edi_document['__id'] = context['active_id']+':'+safe_unique_id(data.model,) - self.edi_document['__last_updata'] = context['__last_update'].values()[0] + The metadata fields MUST always include: + - __model': the OpenERP model name + - __module': the OpenERP module containing the model + - __id': the unique (cross-DB) identifier for this record + - __last_update': last update date of record, ISO date string in UTC + - __version': a list of components for the version + - __attachments': a list (possibly empty) of dicts describing the files attached to this record. + """ + # generic implementation! + attachment_object = self.pool.get('ir.attachment') + edi_dict_list = [] + for record in records: + attachment_ids = attachment_object.search(cr, uid, [('res_model','=', record._name), ('res_id', '=', record.id)]) + attachment_dict_list = [] + for attachment in attachment_object.browse(cr, uid, attachment_ids, context=context): + attachment_dict_list.append({ + 'name' : attachment.name, + 'content': base64.encodestring(attachment.datas), + 'file_name': attachment.datas_fname, + }) + edi_dict = { + '__model' : record._name, + '__module' : record._module, + '__id': record.id, + '__last_update': record.write_date, #TODO: convert into UTC + '__version': [], # ? + '__attachments': attachment_dict_list + } + edi_dict_list.append(edi_dict) - return True + return edi_dict_list - def edi_m2o(self, cr, uid,browse_rec): + def edi_m2o(self, cr, uid, record, context=None): """Return a list representing a M2O EDI value for - the given browse_record.""" + the given browse_record. + M2O are passed as pair (ID, Name) + Exmaple: ['db-uuid:xml-id', 'Partner Name'] + """ # generic implementation! - - model = browse_rec._name - res = [] data_object = self.pool.get('ir.model.data') - uu_id = data_model.search(cr,uid,[('model','=',model)]) - for xml in data_model.browse(cr,uid,uu_id): - if xml: - xml_ID = xml.name - else: - xml_ID = None - for data in browse_rec: - if data.name is not None: - if xml_ID is not None: - res.append(safe_unique_id(model,data.id)+':'+xml_ID) - else: - res.append(safe_unique_id(model,data.id)+':'+safe_unique_id(model,data.id)) - res.append(data.name) - return res + db_uuid = safe_unique_id(record._name, record.id) + xml_ids = data_object.search(cr, uid, [('model','=',record._name),('res_id','=',record.id)]) + if xml_ids: + xml_id = data_object.browse(cr, uid, xml_ids[0], context=context) + xml_id = xml_id.name + db_uuid = '%s:%s' %(db_uuid, xml_id) + name = record.name + return [db_uuid, name] + - def edi_o2m(self, cr, uid,line = None,browse_rec_list, context=None): + def edi_o2m(self, cr, uid, records, context=None): """Return a list representing a O2M EDI value for - the browse_records from the given browse_record_list.""" - # generic implementation! - res = [] - init_data = {} - model = browse_rec_list._name - data_object = self.pool.get('ir.model.data') - uu_id = data_model.search(cr,uid,[('model','=',model)]) - - for xml in data_model.browse(cr,uid,uu_id): - if xml: - xml_ID = xml.name - else: - xml_ID = None - - data_model = self.pool.get(model) - for record in browse_rec_list: - if xml_ID is not None: - init_data['__id'] = safe_unique_id(model,record.id)+':'+xml_ID - else: - init_data['__id'] = safe_unique_id(model,record.id)+':'+model+'-'+safe_unique_id(model,record.id) - init_data['__last_update'] =record.write_date - init_data.update(self.determine_type(cr,uid,ids,line,record,context=context)) - - res.append(init_data) - return res + the browse_records from the given browse_record_list. - def edi_m2m(self, cr, uid, line = None, browse_rec_list, context=None): - """Return a list representing a M2M EDI value for - the browse_records from the given browse_record_list.""" + Example: + [ # O2M fields would be a list of dicts, with their + { '__id': 'db-uuid:xml-id', # own __id. + '__last_update': 'iso date', # The update date, just in case... + 'name': 'some name', + ... + }], + """ # generic implementation! - model = browse_rec_list[0]._name - res = [] - init_data = [] - data_object = self.pool.get('ir.model.data') - uu_id = data_model.search(cr,uid,[('model','=',model)]) - for xml in data_model.browse(cr,uid,uu_id): - if xml: - xml_ID = xml.name - else: - xml_ID = None - data_model = self.pool.get(model) - for record in browse_rec_list: - if xml_ID is not None: - init_data.append(safe_unique_id(model,record.id)+':'+xml_ID) - else: - init_data.append(safe_unique_id(model,record.id)+':'+model+'-'+safe_unique_id(model,record.id)) - if record.name is not None: - init_data.append('name:'+record.name) - res.append(init_data) - - # blob: - return res + dict_list = [] + for record in records: + model_obj = self.pool.get(record._name) + dict_list += model_obj.edi_export(cr, uid, [record.id], context=context) + return dict_list + + + def edi_m2m(self, cr, uid, records, context=None): + """Return a list representing a M2M EDI value for + the browse_records from the given browse_record_list. + + Example: + 'related_tasks': [ # M2M fields would exported as a list of pairs, + ['db-uuid:xml-id1', # similar to a list of M2O values. + 'Task 01: bla bla'], + ['db-uuid:xml-id2', + 'Task 02: bla bla'] + ] + """ + # generic implementation! + dict_list = [] + for record in records: + dict_list.append(self.edi_o2m(cr, uid, record, context=None)) + + return dict_list def edi_export(self, cr, uid, ids, edi_struct=None, context=None): """Returns a list of dicts representing an edi.document containing the From ef7ee72f406efb58ea5c9863d4b3fa5d698f547a Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 3 Jun 2011 14:34:08 +0530 Subject: [PATCH 008/896] [REVIEW] edi: refactor code in import/export method bzr revid: hmo@tinyerp.com-20110603090408-fcumns7va18trdwx --- openerp/addons/base/ir/ir_edi.py | 235 +++++++++++++++---------------- 1 file changed, 112 insertions(+), 123 deletions(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index 2a0c9282de9..c428bca7cb2 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -89,10 +89,8 @@ class ir_edi_document(osv.osv): Generate the list of dictionaries using edi_export method of edi class :param records: it's a object of browse_record_list of any model """ - edi_list = [] - for record in record_list: - edi_struct = record.edi_export(cr, uid, record, context=context) - edi_list.append(edi_struct) + + edi_list = self.edi_export(cr, uid, records, context=context) return self.serialize(edi_list) def get_document(self, cr, uid, edi_token, context=None): @@ -283,6 +281,8 @@ class edi(object): return dict_list + + def edi_export(self, cr, uid, ids, edi_struct=None, context=None): """Returns a list of dicts representing an edi.document containing the browse_records with ``ids``, using the generic algorithm. @@ -303,135 +303,124 @@ class edi(object): be included in the exported data. """ # generic implementation! - - self.edi_document = { - - '__model':'', - '__module':'', - '__id':'', - '__last_update':'', - '__version':'', - '__attachments':'', - - } - self.edi_document['__model'] = self._name + if context is None: + context = {} + _fields = self.fields_get(cr, uid, context=context) - fields_object = self.pool.get('ir.model.fields') - model_object = self.pool.get(self.edi_document['__model']) - edi_metadata(cr, uid, edi_struct, context=context) - record = model_object.read(cr,uid,ids,[],context=context) - for field in edi_struct.keys(): - f_ids = fields_object.search(cr,uid,[('name','=',field),('model','=',self.edi_document['__model'])],context=context) - - for fname in fields_object.browse(cr,uid,f_ids): - if fname.ttype == 'many2one': - - self.edi_document[field] = self.edi_m2o(cr, uid, browse_rec = record[0][field]) - elif fname.ttype == 'one2many': - - self.edi_ducument[field] = self.edi_o2m(cr,uid,line = edi_struct[field],browse_rec_list = record[0][field]) - elif fname.ttype == 'many2many': - self.edi_document[field] = self.edi_m2m(cr,uid,line = edi_struct[field],browse_rec_list = record[0][field]) + fields_to_export = edi_struct and edi_struct.keys() or _fields.key() + + edi_dict_list = [] + for row in self.browse(cr, uid, ids, fields_to_export, context): + edi_dict = {} + edi_dict.update(self.edi_metadata(cr, uid, [row.id], context=context)[0]) + for field in fields_to_export: + cols = self._fields[field] + if cols['type'] == 'many2one': + m2o_record = row.getattrs(field) + value = self.m2o(cr, uid, m2o_record, context=context) + elif cols['type'] == 'many2many': + m2m_records = row.getattrs(field) + value = self.m2m(cr, uid, m2m_records, context=context) + elif cols['type'] == 'one2many': + o2m_records = row.getattrs(field) + value = self.o2m(cr, uid, o2m_records, context=context) else: - self.edi_document[field] = record[0][field] + value = row.getattrs(field) + edi_dict[field] = value + edi_dict_list.append(edi_dict) + return edi_dict_list - - return self.edi_document - def determine_type(self,cr,uid,ids,record_line,browse_record=None,context=None): - model = browse_record._name - fields_object = self.pool.get('ir.model.fields') - obj = self.pool.get(model) - field_data = obj.read(cr,uid,browse_record.id,record_line.keys())[0] - for field in field_data.keys(): - field_ids = field_object.search(cr,uid,[field]) - for field_type in field_object.browse(cr,uid,field_ids): - if field_type.ttype == 'many2one': - record_line.update(field,self.edi_m2o(cr,uid,browse_rec = field_data[field])) - elif field_type.ttype == 'one2many': - pass - elif field_type.ttype == 'many2many': - pass - else: - record_line.update(field,field_data[field]) - return record_line def edi_import(self, cr, uid, edi_document, context=None): """Imports a list of dicts representing an edi.document, using the generic algorithm. + + All relationship fields are exported in a special way, and provide their own + unique identifier, so that we can avoid duplication of records when importing. + Note: for all ir.model.data entries, the "module" value to use for read/write + should always be "edi_import", and the "name" value should be the full + db_id provided in the EDI. + + 1: Many2One + M2O fields are always exported as a pair [db_id, name], where db_id + is in the form "db_uuid:xml_id", both being values that come directly + from the original database. + The import should behave like this: + a. Look in ir.model.data for a record that matches the db_id. + If found, replace the m2o value with the correct database ID and stop. + If not found, continue to next step. + b. Perform name_search(name) to look for a record that matches the + given m2o name. If only one record is found, create the missing + ir.model.data record to link it to the db_id, and the replace the m2o + value with the correct database ID, then stop. If zero result or + multiple results are found, go to next step. + c. Create the new record using the only field value that is known: the + name, and create the ir.model.data entry to map to it. + This should work for many models, and if not, the module should + provide a custom EDI import logic to care for it. + + 2: One2Many + O2M fields are always exported as a list of dicts, where each dict corresponds + to a full EDI record. The import should not update existing records + if they already exist, it should only link them to the parent object. + a. Look for a record that matches the db_id provided in the __id field. If + found, keep the corresponding database id, and connect it to the parent + using a write value like (4,db_id). + b. If not found via db_id, create a new entry using the same method that + imports a full EDI record (recursive call!), grab the resulting db id, + and use it to connect to the parent via a write value like (4, db_id). + + 3: Many2Many + M2M fields are always exported as a list of pairs similar to M2O. + For each pair in the M2M: + a. Perform the same steps as for a Many2One (see 1.2.1.1) + b. After finding the database ID of the final record in the database, + connect it to the parent record via a write value like (4, db_id). """ # generic implementation! - new = True - resource = {} - model = edi_document['__model'] - model_object = self.pool.get(model) - data_model = self.pool.get('ir.model.data') - field_model = self.pool.get('ir.model.fields') + fields = edi_document.keys() + fields_to_import = [] + data_line = [] + _fields = self.fields_get(cr, uid, context=context) for field in edi_document.keys(): - if field == '__id': - db_id , xml_ID = edi_document[field].split(':') - self.data_ids = data_model.search(cr,uid,[('name','=',xml_ID)]) - - - f_ids = fields_object.search(cr,uid,[('name','=',field),('model','=',model)],context=context) - for fname in fields_object.browse(cr,uid,f_ids): - - if fname.ttype == 'many2one': - field_ids,field_xml = edi_document[field][0].split(':') - field_ids = int(field_ids) - model_ids = data_model.search(cr,uid,[('name','=',field_xml)]) - if not len(model_ids): - obj = self.pool.get(fname.relation) - model1_ids = obj.search(cr,uid,[('name','=',edi_document[field][1])]) - if len(model1_ids) == 1: - resourse[field] = data_model.create(cr,uid,{'module':'edi_import','name':field_xml,'res_id':model1_ids})[0] - - elif not len(model1_ids) or len(model1_ids) > 1: - edi_document[field][1] = obj.create(cr,uid,{'name':edi_document[field][1]}) - resource[field] = data_model.create(cr,uid,{'module':'edi_import','name':field_xml,'res_id':edi_document[field][0]})[0] - else: - resource[field] = model_ids[0] - - elif fname.ttype == 'one2many': - for records in edi_document[field]: - res = {} - field_ids,field_xml = records['__id'].split(':') - field_ids = int(field_ids) - model3_ids = data_model.search(cr,uid,[('name','=',field_xml)]) - for fields in records.keys(): - if fields != '__id' or fields != '__last_update': - res[fields] = records[fields] - if not len(model3_ids): - resource[field][records] = (4,self.edi_import(cr,uid,res,context = context)[0]) - else: - resource[field][records] = (4,model3_ids[0])obj = self.pool.get(model) - record_ids = obj.create(cr,uid,resource,context=context) - return record_ids - - elif fname.ttype == 'many2many': - for records in edi_document[field]: - field_ids,field_xml = records[0].split(':') - field_ids = int(field_ids) - model4_ids = data_model.search(cr,uid,[('name','=',field_xml)]) - if not len(model4_ids): - obj = self.pool.get(fname.relation) - name,value = records[1].split(':') - model5_ids = obj.search(cr,uid,[(name,'=',value)]) - if len(model5_ids) == 1: - resource[field][records] = data_model.create(cr,uid,{'module':'edi_import','name':field_xml,'res_id':model5_ids})[0] - elif not len(model5_ids) or len(model5_ids) > 1: - edi_document[field][records][1] = obj.create(cr,uid,{'name':edi_document[field][1]}) - resource[field] = (4,data_model.create(cr,uid,{'module':'edi_import','name':field_xml,'res_id':edi_document[field][records][1]})[0]) - else: - resource[field] = (4,model4_ids[0]) + if not field.startwith('__'): + field_to_import.append(field) + edi_field_value = edi_document[field] + if _fields[field]['type'] == 'many2one': + #Look in ir.model.data for a record that matches the db_id. + #If found, replace the m2o value with the correct database ID and stop. + #If not found, continue to next step. + + #Perform name_search(name) to look for a record that matches the + #given m2o name. If only one record is found, create the missing + #ir.model.data record to link it to the db_id, and the replace the m2o + #value with the correct database ID, then stop. If zero result or + #multiple results are found, go to next step. + + #Create the new record using the only field value that is known: the + #name, and create the ir.model.data entry to map to it. + #This should work for many models, and if not, the module should + #provide a custom EDI import logic to care for it. + pass + elif _fields[field]['type'] == 'one2many': + #Look for a record that matches the db_id provided in the __id field. If + #found, keep the corresponding database id, and connect it to the parent + #using a write value like (4,db_id). + + #If not found via db_id, create a new entry using the same method that + #imports a full EDI record (recursive call!), grab the resulting db id, + #and use it to connect to the parent via a write value like (4, db_id). + elif _fields[field]['type'] == 'many2many': + #Perform the same steps as for a Many2One (see 1.2.1.1) + + + + #After finding the database ID of the final record in the database, + #connect it to the parent record via a write value like (4, db_id). else: - resource[field] = edi_document[field] - if len(self.data_ids): - obj = self.pool.get(model) - record_ids = obj.write(cr,uid,resource,context=context) - return record_ids - else: - obj = self.pool.get(model) - record_ids = obj.create(cr,uid,resource,context=context) - return record_ids + data_line.append(edi_field_value) + + return self.import_data(cr, uid, field_to_import, [data_line], mode='init', current_module='edi_import', noupdate=False, context=context, filename=None) + From eaec4a25f23e4182bc5620f1a9dcef664f811861 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 3 Jun 2011 14:41:09 +0530 Subject: [PATCH 009/896] [REVIEW] edi: refactor code in generate_edi() bzr revid: hmo@tinyerp.com-20110603091109-e1gn7mr0gpcr88sy --- openerp/addons/base/ir/ir_edi.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index c428bca7cb2..1fc9371b66c 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -90,7 +90,10 @@ class ir_edi_document(osv.osv): :param records: it's a object of browse_record_list of any model """ - edi_list = self.edi_export(cr, uid, records, context=context) + edi_list = [] + for record in records: + record_model_obj = self.pool.get(record._name) + edi_list += record_model_obj.edi_export(cr, uid, [record], context=context) return self.serialize(edi_list) def get_document(self, cr, uid, edi_token, context=None): From 9cb3205a6432ccc7ba2458416786774b39bc50d6 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Fri, 3 Jun 2011 15:25:26 +0530 Subject: [PATCH 010/896] [FIX] fix problem of search multiple conditions O2M with active=False then not work. lp bug: https://launchpad.net/bugs/711919 fixed bzr revid: ysa@tinyerp.com-20110603095526-s26wgkahwc1d6zfd --- openerp/osv/expression.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 1922d5e8388..4c74a11506a 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -108,6 +108,10 @@ class expression(object): self.__main_table = table self.__all_tables.add(table) + active = False + for exp in self.__exp: + if exp[0] == 'active': + active = exp i = -1 while i + 1','<=','>='] ) and 'in' or operator - + dict_op = {'not in':'!=','in':'=','=':'in','!=':'not in','<>':'not in'} if isinstance(right,tuple): right = list(right) @@ -319,7 +326,7 @@ class expression(object): elif isinstance(right,(list,tuple)): m2o_str = True for ele in right: - if not isinstance(ele, basestring): + if not isinstance(ele, basestring): m2o_str = False break elif right == []: @@ -335,7 +342,7 @@ class expression(object): new_op = '!=' #Is it ok to put 'left' and not 'id' ? self.__exp[i] = (left,new_op,False) - + if m2o_str: self.__exp[i] = _get_expression(field_obj,cr, uid, left, right, operator, context=context) else: From 2a9a5e2a0e3fdf1bc104e1a5c923baf85f1d963b Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Fri, 3 Jun 2011 15:42:17 +0530 Subject: [PATCH 011/896] Improve code. bzr revid: ysa@tinyerp.com-20110603101217-v69rcqtpcfics925 --- openerp/osv/expression.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 4c74a11506a..8fb85ce7612 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -154,10 +154,10 @@ class expression(object): # Making search easier when there is a left operand as field.o2m or field.m2m if field._type in ['many2many','one2many']: right = field_obj.search(cr, uid, [(fargs[1], operator, right)], context=context) + domain = [(fargs[0],'in', right)] if active: - right1 = table.search(cr, uid, [(fargs[0],'in', right),active], context=context) - else: - right1 = table.search(cr, uid, [(fargs[0],'in', right)], context=context) + domain.append(active) + right1 = table.search(cr, uid, domain, context=context) if right1 == []: self.__exp[i] = ( 'id', '=', 0 ) else: From 332d385390a48cd82670182c6b76e92bbf35397e Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 3 Jun 2011 16:15:39 +0530 Subject: [PATCH 012/896] [REVIEW] edi: refactor code of edi_import bzr revid: hmo@tinyerp.com-20110603104539-nh0i45zv6zna2wau --- openerp/addons/base/ir/ir_edi.py | 96 ++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index 1fc9371b66c..35b1b411a38 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -386,44 +386,92 @@ class edi(object): fields = edi_document.keys() fields_to_import = [] data_line = [] + model_data = self.pool.get('ir.model.data') _fields = self.fields_get(cr, uid, context=context) + current_module = 'edi_import' + values = {} for field in edi_document.keys(): if not field.startwith('__'): field_to_import.append(field) edi_field_value = edi_document[field] - if _fields[field]['type'] == 'many2one': - #Look in ir.model.data for a record that matches the db_id. - #If found, replace the m2o value with the correct database ID and stop. - #If not found, continue to next step. + if _fields[field]['type'] in ('many2one', 'many2many'): - #Perform name_search(name) to look for a record that matches the - #given m2o name. If only one record is found, create the missing - #ir.model.data record to link it to the db_id, and the replace the m2o - #value with the correct database ID, then stop. If zero result or - #multiple results are found, go to next step. + if _fields[field]['type'] == 'many2one': + edi_parent_documents = [edi_field_value] + else: + edi_parent_documents = edi_field_value - #Create the new record using the only field value that is known: the - #name, and create the ir.model.data entry to map to it. - #This should work for many models, and if not, the module should - #provide a custom EDI import logic to care for it. - pass + parent_lines = [] + + for edi_parent_document in edi_parent_documents: + #Look in ir.model.data for a record that matches the db_id. + #If found, replace the m2o value with the correct database ID and stop. + #If not found, continue to next step + + db_uuid, xml_id = tuple(edi_parent_document[0].split(':')) + data_ids = model_data.name_search(cr, uid, xml_id) + if data_ids: + d = model_data.browse(cr, uid, data_ids[0], context=context) + parent_lines.append(d.res_id) + + else: + #Perform name_search(name) to look for a record that matches the + #given m2o name. If only one record is found, create the missing + #ir.model.data record to link it to the db_id, and the replace the m2o + #value with the correct database ID, then stop. If zero result or + #multiple results are found, go to next step. + #Create the new record using the only field value that is known: the + #name, and create the ir.model.data entry to map to it. + + relation_object = self.get.pool(_fields[field]['relation']) + relation_ids = relation_object.name_search(cr, uid, edi_parent_document[1]) + if relation_ids and len(relation_ids) == 1: + relation_id = relation_ids[0] + else: + relation_id = relation_object.create(cr, uid, {'name': edi_parent_document[1]}, context=context) + model_data.create(cr, uid, { + 'name': xml_id, + 'model': relation_object._name, + #'module':relation_object._module, + 'res_id':relation_id, + },context=context) + + parent_lines.append(relation_id) + + if _fields[field]['type'] == 'many2one': + values[field] = parent_lines[0] + else: + many2many_ids = [] + for m2m_id in parent_lines: + many2many_ids.append((4,m2m_id)) + values[field] = many2many_ids + + elif _fields[field]['type'] == 'one2many': #Look for a record that matches the db_id provided in the __id field. If #found, keep the corresponding database id, and connect it to the parent #using a write value like (4,db_id). - #If not found via db_id, create a new entry using the same method that #imports a full EDI record (recursive call!), grab the resulting db id, #and use it to connect to the parent via a write value like (4, db_id). - elif _fields[field]['type'] == 'many2many': - #Perform the same steps as for a Many2One (see 1.2.1.1) - - - - #After finding the database ID of the final record in the database, - #connect it to the parent record via a write value like (4, db_id). + + relations = [] + relation_object = self.get.pool(_fields[field]['relation']) + for edi_relation_document in edi_field_value: + db_uuid, xml_id = tuple(edi_relation_document.get('__id').split(':')) + data_ids = model_data.name_search(cr, uid, xml_id) + if data_ids: + d = model_data.browse(cr, uid, data_ids[0], context=context) + relations.append(d.res_id) + else: + r = relation_object.edi_import(cr, uid, edi_relation_document, context=context) + relations.append(r) + one2many_ids = [] + for o2m_id in relations: + one2many_ids.append((4,o2m_id)) + values[field] = one2many_ids else: - data_line.append(edi_field_value) + values[field] = edi_field_value - return self.import_data(cr, uid, field_to_import, [data_line], mode='init', current_module='edi_import', noupdate=False, context=context, filename=None) + return model_data._update(cr, uid, self._name, current_module, values, context=context) From 023ce68e841d91ae3610bf50774f61b2be7de090 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Mon, 6 Jun 2011 11:22:57 +0530 Subject: [PATCH 013/896] [EDI]: worked on the local time to UTC time conversion bzr revid: aar@tinyerp.com-20110606055257-d33gwzaidwxufn64 --- openerp/addons/base/ir/ir_edi.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index 4ed7b0a4d3a..2cb40c4dfa7 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -26,6 +26,7 @@ import time import base64 import urllib2 import release +import datetime def safe_unique_id(model, database_id): """Generate a unique string to represent a (model,database id) pair @@ -216,14 +217,21 @@ class edi(object): 'content': base64.encodestring(attachment.datas), 'file_name': attachment.datas_fname, }) + ''' Here we are obtaining the release version ''' version = [] for ver in release.version.split('.'): version.append(int(ver)) + ''' Here we are converting local time into UTC time''' + UTC_OFFSET_TIMEDELTA = datetime.datetime.utcnow() - datetime.datetime.now() + local_datetime = datetime.datetime.strptime(record.write_date, "%Y-%m-%d %H:%M:%S") + result_utc_datetime = local_datetime - UTC_OFFSET_TIMEDELTA + write_date = result_utc_datetime.strftime("%Y-%m-%d %H:%M:%S") + edi_dict = { '__model' : record._name, '__module' : record._module, '__id': record.id, - '__last_update': record.write_date, #TODO: convert into UTC + '__last_update': write_date, #TODO: convert into UTC '__version': version, '__attachments': attachment_dict_list } From 110483e93d4a6cf1f461002be93a607b4fb1ec65 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Wed, 8 Jun 2011 16:11:28 +0530 Subject: [PATCH 014/896] [FIX] fix problem of ir.property created if same value exists then record not create in ir.property object. lp bug: https://launchpad.net/bugs/726592 fixed bzr revid: ysa@tinyerp.com-20110608104128-7mdykmk0ovqenz6u --- openerp/osv/fields.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 306e7d007f3..ec1845344ab 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -1047,7 +1047,14 @@ class property(function): default_val = self._get_default(obj, cr, uid, prop_name, context) - if id_val is not default_val: + property_create = False + if hasattr(default_val, '_id'): + if default_val._id != id_val: + property_create = True + elif id_val != default_val: + property_create = True + + if property_create: def_id = self._field_get(cr, uid, obj._name, prop_name) company = obj.pool.get('res.company') cid = company._company_default_get(cr, uid, obj._name, def_id, From 4203df9c260fda5bcc5f2e60504483d1078b770c Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Mon, 13 Jun 2011 11:40:41 +0530 Subject: [PATCH 015/896] [IMP] add yaml test case. bzr revid: ysa@tinyerp.com-20110613061041-15edtf67mj0oa8hq --- .../addons/base/test/test_osv_expression.yml | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/openerp/addons/base/test/test_osv_expression.yml b/openerp/addons/base/test/test_osv_expression.yml index 4916fe06203..1f76a210293 100644 --- a/openerp/addons/base/test/test_osv_expression.yml +++ b/openerp/addons/base/test/test_osv_expression.yml @@ -40,7 +40,7 @@ - !assert {model: res.partner, search: "[('category_id', '=', False)]"}: - category_id in (False, None, []) -- +- Filtering on invalid value across x2many relationship should return an empty set - !assert {model: res.partner, search: "[('address.city','=','foo')]", count: 0, string: "Searching for address.city = foo should give empty results"} @@ -110,4 +110,21 @@ res_ids = self.search(cr, uid, [('company_id.partner_id', 'not in', [])]) res_ids.sort() assert res_ids == all_ids, "Searching against empty set failed, returns %r" % res_ids - +- + Testing for Many2Many field with category supplier and active=False +- + !python {model: res.partner }: | + vals = {'category_id': [(6, 0, [ref("base.res_partner_category_8")])], + 'name': 'OpenERP Test', + 'active': False, + 'address': [(0, 0, {'country_id': ref("base.be")})] + } + self.create(cr, uid, vals, context=context) + res_ids = self.search(cr, uid, [('category_id', 'ilike', 'supplier'), ('active', '=', False)]) + assert len(res_ids) != 0, "Record not Found with category supplier and active False." +- + Testing for One2Many field with country Belgium and active=False +- + !python {model: res.partner }: | + res_ids = self.search(cr, uid, [('address.country_id','=','Belgium'),('active','=',False)]) + assert len(res_ids) != 0, "Record not Found with country Belgium and active False." From ab62f7b7802571f56a56fce7389b777d6fee2583 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Tue, 21 Jun 2011 18:46:32 +0530 Subject: [PATCH 016/896] [EDI] revised edi class bzr revid: aar@tinyerp.com-20110621131632-rdwn6in74q5t1wkj --- openerp/addons/base/__openerp__.py | 3 +- openerp/addons/base/ir/ir_edi.py | 316 +++++++++++++++++------------ 2 files changed, 191 insertions(+), 128 deletions(-) diff --git a/openerp/addons/base/__openerp__.py b/openerp/addons/base/__openerp__.py index 75e0a8c56d0..b0c437352f9 100644 --- a/openerp/addons/base/__openerp__.py +++ b/openerp/addons/base/__openerp__.py @@ -71,7 +71,7 @@ 'res/partner/wizard/partner_clear_ids_view.xml', 'res/partner/wizard/partner_wizard_ean_check_view.xml', 'res/partner/partner_data.xml', - + #'test/test_edi_document.yml', 'res/ir_property_view.xml', 'security/base_security.xml', 'publisher_warranty/publisher_warranty_view.xml', @@ -96,6 +96,7 @@ 'test/test_ir_rule.yml', # <-- These tests modify/add/delete ir_rules. 'test/test_config_users.yml', 'test/test_ir_values.yml', + ], 'installable': True, 'active': True, diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index 2cb40c4dfa7..fe8320491bf 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -20,14 +20,13 @@ ############################################################################## from osv import osv,fields -import json +import simplejson import hashlib import time import base64 import urllib2 -import release -import datetime - +import unicodedata +import openerp.release as release def safe_unique_id(model, database_id): """Generate a unique string to represent a (model,database id) pair without being too long, without revealing the database id, and @@ -58,7 +57,7 @@ def safe_unique_id(model, database_id): class ir_edi_document(osv.osv): _name = 'ir.edi.document' - _description = 'To represent the EDI export of any OpenERP record, it could be an invoice, a project task, etc.' + _description = 'To represent' _columns = { 'name': fields.char("EDI token", size = 128, help="EDI Token is a unique identifier for the EDI document."), 'document': fields.text("Document", help="hold the serialization of the EDI document.") @@ -83,7 +82,7 @@ class ir_edi_document(osv.osv): perform a JSON serialization of a list of dicts prepared by generate_edi() and return a UTF-8 encoded string that could be passed to deserialize() :param edi_dicts: it's list of edi_dict """ - serialized_list = json.dumps(edi_dicts) + serialized_list = simplejson.dumps(edi_documents) return serialized_list def generate_edi(self, cr, uid, records, context=None): @@ -95,7 +94,7 @@ class ir_edi_document(osv.osv): edi_list = [] for record in records: record_model_obj = self.pool.get(record._name) - edi_list += record_model_obj.edi_export(cr, uid, [record], context=context) + edi_list += record_model_obj.edi_export(cr, uid, [record],context=context) return self.serialize(edi_list) def get_document(self, cr, uid, edi_token, context=None): @@ -104,14 +103,12 @@ class ir_edi_document(osv.osv): returns the string serialization that is in the database (column: document) for the given edi_token or raise. """ - records = self.name_search(cr, uid, edi_token, context=context) + records = self.search(cr, uid, [('name','=',edi_token)]) if records: - edi = self.browse(cr, uid, records[0], context=context) - return edi.document - else: - raise osv.except_osv(_('Error !'), - _('Desired EDI Document does not Exist')) - + for edi in self.browse(cr, uid, records, context=context): + return edi.document + else: + pass def load_edi(self, cr, uid, edi_documents, context=None): @@ -135,10 +132,11 @@ class ir_edi_document(osv.osv): """ Deserialized the edi document string perform JSON deserialization from an edi document string, and returns a list of dicts """ - edi_document = json.loads(edi_document_string) + edi_document = simplejson.loads(edi_document_string) + return edi_document - def export(self, cr, uid, records, context=None): + def export_edi(self, cr, uid, records, context=None): """ The method handles the flow of the edi document generation and store it in the database and return the edi_token of the particular document @@ -161,7 +159,7 @@ class ir_edi_document(osv.osv): exported_ids.append(token) return exported_ids - def import(self, cr, uid, edi_document=None, edi_url=None, context=None): + def import_edi(self, cr, uid, edi_document=None, edi_url=None, context=None): """ The method handles the flow of importing particular edi document and updates the database values on the basis of the edi document using @@ -175,8 +173,10 @@ class ir_edi_document(osv.osv): if edi_url and not edi_document: edi_document = urllib2.urlopen(edi_url).read() - assert edi_document, _('EDI Document should be provided') - edi_documents = self.deserialize(edi_document) + #assert edi_document, _('EDI Document should be provided') + else: + + edi_documents = self.deserialize(edi_document) return self.load_edi(cr, uid, edi_documents, context=context) ir_edi_document() @@ -206,8 +206,11 @@ class edi(object): - __attachments': a list (possibly empty) of dicts describing the files attached to this record. """ # generic implementation! + model_data = self.pool.get('ir.model.data') + data_ids = [] attachment_object = self.pool.get('ir.attachment') edi_dict_list = [] + db_uuid = '' for record in records: attachment_ids = attachment_object.search(cr, uid, [('res_model','=', record._name), ('res_id', '=', record.id)]) attachment_dict_list = [] @@ -217,22 +220,24 @@ class edi(object): 'content': base64.encodestring(attachment.datas), 'file_name': attachment.datas_fname, }) - ''' Here we are obtaining the release version ''' version = [] - for ver in release.version.split('.'): - version.append(int(ver)) - ''' Here we are converting local time into UTC time''' - UTC_OFFSET_TIMEDELTA = datetime.datetime.utcnow() - datetime.datetime.now() - local_datetime = datetime.datetime.strptime(record.write_date, "%Y-%m-%d %H:%M:%S") - result_utc_datetime = local_datetime - UTC_OFFSET_TIMEDELTA - write_date = result_utc_datetime.strftime("%Y-%m-%d %H:%M:%S") + + for ver in release.major_version.split('.'): + version.append(ver) + data_ids = model_data.search(cr, uid, [('res_id','=',record.id)]) + + if len(data_ids): + xml_obj = model_data.browse(cr,uid,data_ids[0]) + uuid = safe_unique_id(record._name,record.id) + db_uuid = '%s:%s' % (uuid,xml_obj.name) + edi_dict = { '__model' : record._name, '__module' : record._module, - '__id': record.id, - '__last_update': write_date, #TODO: convert into UTC - '__version': version, + '__id': db_uuid, + '__last_update': False, #record.write_date, #TODO: convert into UTC + '__version': version, # ? '__attachments': attachment_dict_list } edi_dict_list.append(edi_dict) @@ -249,10 +254,15 @@ class edi(object): data_object = self.pool.get('ir.model.data') db_uuid = safe_unique_id(record._name, record.id) xml_ids = data_object.search(cr, uid, [('model','=',record._name),('res_id','=',record.id)]) + if xml_ids: xml_id = data_object.browse(cr, uid, xml_ids[0], context=context) xml_id = xml_id.name - db_uuid = '%s:%s' %(db_uuid, xml_id) + else: + xml_id = record._name + db_uuid = '%s:%s' %(db_uuid, xml_id) + + name = record.name return [db_uuid, name] @@ -271,9 +281,12 @@ class edi(object): """ # generic implementation! dict_list = [] + for record in records: + model_obj = self.pool.get(record._name) - dict_list += model_obj.edi_export(cr, uid, [record.id], context=context) + dict_list += model_obj.edi_export(cr, uid, [record], context=context) + return dict_list @@ -291,14 +304,15 @@ class edi(object): """ # generic implementation! dict_list = [] + for record in records: - dict_list.append(self.edi_o2m(cr, uid, record, context=None)) - + dict_list.append(self.edi_o2m(cr, uid, [record], context=None)) + return dict_list - def edi_export(self, cr, uid, ids, edi_struct=None, context=None): + def edi_export(self, cr, uid, records, edi_struct=None, context=None): """Returns a list of dicts representing an edi.document containing the browse_records with ``ids``, using the generic algorithm. :param edi_struct: if provided, edi_struct should be a dictionary @@ -318,32 +332,39 @@ class edi(object): be included in the exported data. """ # generic implementation! + if context is None: context = {} _fields = self.fields_get(cr, uid, context=context) - fields_to_export = edi_struct and edi_struct.keys() or _fields.key() - + fields_to_export = edi_struct and edi_struct.keys() or _fields.keys() edi_dict_list = [] - for row in self.browse(cr, uid, ids, fields_to_export, context): + value = None + for row in records: edi_dict = {} - edi_dict.update(self.edi_metadata(cr, uid, [row.id], context=context)[0]) + edi_dict.update(self.edi_metadata(cr, uid, [row], context=context)[0]) + for field in fields_to_export: - cols = self._fields[field] - if cols['type'] == 'many2one': - m2o_record = row.getattrs(field) - value = self.m2o(cr, uid, m2o_record, context=context) - elif cols['type'] == 'many2many': - m2m_records = row.getattrs(field) - value = self.m2m(cr, uid, m2m_records, context=context) - elif cols['type'] == 'one2many': - o2m_records = row.getattrs(field) - value = self.o2m(cr, uid, o2m_records, context=context) + cols = _fields[field] + if _fields[field].has_key('function') or _fields[field].has_key('related_columns'): + + pass else: - value = row.getattrs(field) + if cols['type'] == 'many2one': + m2o_record = getattr(row, field) + value = self.edi_m2o(cr, uid, m2o_record, context=context) + elif cols['type'] == 'many2many': + m2m_records = getattr(row, field) + value = self.edi_m2m(cr, uid, m2m_records, context=context) + elif cols['type'] == 'one2many': + o2m_records = getattr(row, field) + value = self.edi_o2m(cr, uid, o2m_records, context=context ) + else: + value = getattr(row, field) edi_dict[field] = value edi_dict_list.append(edi_dict) + return edi_dict_list @@ -395,6 +416,19 @@ class edi(object): connect it to the parent record via a write value like (4, db_id). """ # generic implementation! + + def convert(data): + if isinstance(data, unicode): + return str(data) + elif isinstance(data, dict): + return dict(map(convert, data.iteritems())) + elif isinstance(data, (list, tuple, set, frozenset)): + return type(data)(map(convert, data)) + else: + return data + #edi_document = convert(edi_document) + + fields = edi_document.keys() fields_to_import = [] data_line = [] @@ -403,87 +437,115 @@ class edi(object): current_module = 'edi_import' values = {} for field in edi_document.keys(): - if not field.startwith('__'): - field_to_import.append(field) + if field.startswith('__') and field == '__id': + db_uuid, xml_ids = tuple(edi_document[field].split(':')) + + elif not field.startswith('__'): + + + fields_to_import.append(field) edi_field_value = edi_document[field] - if _fields[field]['type'] in ('many2one', 'many2many'): - - if _fields[field]['type'] == 'many2one': - edi_parent_documents = [edi_field_value] - else: - edi_parent_documents = edi_field_value - - parent_lines = [] - - for edi_parent_document in edi_parent_documents: - #Look in ir.model.data for a record that matches the db_id. - #If found, replace the m2o value with the correct database ID and stop. - #If not found, continue to next step - - db_uuid, xml_id = tuple(edi_parent_document[0].split(':')) - data_ids = model_data.name_search(cr, uid, xml_id) - if data_ids: - d = model_data.browse(cr, uid, data_ids[0], context=context) - parent_lines.append(d.res_id) - - else: - #Perform name_search(name) to look for a record that matches the - #given m2o name. If only one record is found, create the missing - #ir.model.data record to link it to the db_id, and the replace the m2o - #value with the correct database ID, then stop. If zero result or - #multiple results are found, go to next step. - #Create the new record using the only field value that is known: the - #name, and create the ir.model.data entry to map to it. - - relation_object = self.get.pool(_fields[field]['relation']) - relation_ids = relation_object.name_search(cr, uid, edi_parent_document[1]) - if relation_ids and len(relation_ids) == 1: - relation_id = relation_ids[0] - else: - relation_id = relation_object.create(cr, uid, {'name': edi_parent_document[1]}, context=context) - model_data.create(cr, uid, { - 'name': xml_id, - 'model': relation_object._name, - #'module':relation_object._module, - 'res_id':relation_id, - },context=context) - - parent_lines.append(relation_id) - - if _fields[field]['type'] == 'many2one': - values[field] = parent_lines[0] - else: - many2many_ids = [] - for m2m_id in parent_lines: - many2many_ids.append((4,m2m_id)) - values[field] = many2many_ids - + if _fields[field].has_key('function') or _fields[field].has_key('related_columns'): - elif _fields[field]['type'] == 'one2many': - #Look for a record that matches the db_id provided in the __id field. If - #found, keep the corresponding database id, and connect it to the parent - #using a write value like (4,db_id). - #If not found via db_id, create a new entry using the same method that - #imports a full EDI record (recursive call!), grab the resulting db id, - #and use it to connect to the parent via a write value like (4, db_id). - - relations = [] - relation_object = self.get.pool(_fields[field]['relation']) - for edi_relation_document in edi_field_value: - db_uuid, xml_id = tuple(edi_relation_document.get('__id').split(':')) - data_ids = model_data.name_search(cr, uid, xml_id) - if data_ids: - d = model_data.browse(cr, uid, data_ids[0], context=context) - relations.append(d.res_id) + pass + else: + if _fields[field]['type'] in ('many2one', 'many2many'): + + if _fields[field]['type'] == 'many2one': + edi_parent_documents = [edi_field_value] + else: + edi_parent_documents = edi_field_value + + parent_lines = [] + + for edi_parent_document in edi_parent_documents: + #Look in ir.model.data for a record that matches the db_id. + #If found, replace the m2o value with the correct database ID and stop. + #If not found, continue to next step + if edi_parent_document[0].find(':') != -1 and edi_parent_document[1] != None: + db_uuid, xml_id = tuple(edi_parent_document[0].split(':')) + data_ids = model_data.name_search(cr, uid, xml_id) + if data_ids: + for data in model_data.browse(cr, uid, [data_ids[0][0]], context=context): + parent_lines.append(data.res_id) + print "..............................................",data.res_id + else: + #Perform name_search(name) to look for a record that matches the + #given m2o name. If only one record is found, create the missing + #ir.model.data record to link it to the db_id, and the replace the m2o + #value with the correct database ID, then stop. If zero result or + #multiple results are found, go to next step. + #Create the new record using the only field value that is known: the + #name, and create the ir.model.data entry to map to it. + + relation_object = self.pool.get(_fields[field]['relation']) + print "parent_document",edi_parent_document[1] + relation_ids = relation_object.search(cr, uid, [('name','=',edi_parent_document[1])]) + if relation_ids and len(relation_ids) == 1: + relation_id = relation_ids[0] + else: + relation_id = relation_object.create(cr, uid, {'name': edi_parent_document[1]}, context=context) + model_data.create(cr, uid, { + 'name': xml_id, + 'model': relation_object._name, + 'module':relation_object._module, + 'res_id':relation_id + }, context=context) + + parent_lines.append(relation_id) + + else: + pass + if len(parent_lines): + if _fields[field]['type'] == 'many2one': + values[field] = parent_lines[0] + + else: + many2many_ids = [] + for m2m_id in parent_lines: + many2many_ids.append((4,m2m_id)) + values[field] = many2many_ids + + + elif _fields[field]['type'] == 'one2many': + #Look for a record that matches the db_id provided in the __id field. If + #found, keep the corresponding database id, and connect it to the parent + #using a write value like (4,db_id). + #If not found via db_id, create a new entry using the same method that + #imports a full EDI record (recursive call!), grab the resulting db id, + #and use it to connect to the parent via a write value like (4, db_id). + + relations = [] + relation_object = self.pool.get(_fields[field]['relation']) + for edi_relation_document in edi_field_value: + '''print "pppppppppppppppppppppppppppppppppppppppppp" + print edi_field_value + print ":::::::::::::::::::::::::::::::::::::::::::::" + print edi_relation_document + print edi_parent_document + print "ppppppppppppppppppppppppppppppppppp" + if edi_relation_document['__id'].find(':') != -1: + db_uuid, xml_id = tuple(edi_relation_document['__id'].split(':')) + print xml_id + data_ids = model_data.name_search(cr, uid, xml_id) + print "dddddddddddddddddddddddddddddddddddddddddddddd",data_ids + if data_ids: + for data in model_data.browse(cr,uid,[data_ids[0][0]]): + relations.append(data.res_id) + else:''' r = relation_object.edi_import(cr, uid, edi_relation_document, context=context) relations.append(r) - one2many_ids = [] - for o2m_id in relations: - one2many_ids.append((4,o2m_id)) - values[field] = one2many_ids - else: - values[field] = edi_field_value + print "relation id ssssssssssssssssssss",relations + one2many_ids = [] + for o2m_id in relations: + one2many_ids.append((4,o2m_id)) + values[field] = one2many_ids + else: + values[field] = edi_field_value + for val in values.keys(): + print '::::::::::::::::::::::',val,'::::::::::;;;;;;;;;;;;;;;;;;;;;;' + print values[val] return model_data._update(cr, uid, self._name, current_module, values, context=context) - +# vim: ts=4 sts=4 sw=4 si et \ No newline at end of file From e60caf7d51b5f86f3597d610708a42ba3eeadbc6 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Tue, 21 Jun 2011 18:49:27 +0530 Subject: [PATCH 017/896] [EDI] revised edi class bzr revid: aar@tinyerp.com-20110621131927-872hkmor321f1gdf --- openerp/addons/base/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/__openerp__.py b/openerp/addons/base/__openerp__.py index b0c437352f9..bccaf7f660b 100644 --- a/openerp/addons/base/__openerp__.py +++ b/openerp/addons/base/__openerp__.py @@ -71,7 +71,7 @@ 'res/partner/wizard/partner_clear_ids_view.xml', 'res/partner/wizard/partner_wizard_ean_check_view.xml', 'res/partner/partner_data.xml', - #'test/test_edi_document.yml', + 'test/test_edi_document.yml', 'res/ir_property_view.xml', 'security/base_security.xml', 'publisher_warranty/publisher_warranty_view.xml', From 395b953f47d1098732aab9999535a8e6bca88329 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Tue, 21 Jun 2011 18:51:33 +0530 Subject: [PATCH 018/896] [EDI] revised edi class bzr revid: aar@tinyerp.com-20110621132133-g24z7zi1ln16h06d --- openerp/addons/base/test/test_edi_documents.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 openerp/addons/base/test/test_edi_documents.yml diff --git a/openerp/addons/base/test/test_edi_documents.yml b/openerp/addons/base/test/test_edi_documents.yml new file mode 100644 index 00000000000..1b66caaf4dc --- /dev/null +++ b/openerp/addons/base/test/test_edi_documents.yml @@ -0,0 +1,16 @@ +- + Tesing of Export functionality +- + !python {model: ir.edi.document}: | + partner_obj = self.pool.get('res.partner') + partner_ids = partner_obj.search(cr, uid, []) + + if partner_ids: + partners = partner_obj.browse(cr, uid, partner_ids) + tokens = self.export_edi(cr, uid, partners) + print '>>>>>>>>>>',tokens + for token in tokens: + document = self.get_document(cr, uid, token, context=context) + a = self.import_edi(cr, uid, edi_document = document) + print '////////////',a + From 594e01f1064dafa63d2b77433aa0d782c1abef4c Mon Sep 17 00:00:00 2001 From: "Ujjvala Collins (OpenERP)" Date: Wed, 22 Jun 2011 11:25:58 +0530 Subject: [PATCH 019/896] [FIX] sale: Fixed problem of unselect/make empty packaging field in the sale order line. lp bug: https://launchpad.net/bugs/795487 fixed bzr revid: uco@tinyerp.com-20110622055558-sk5lidkpklygqws5 --- addons/sale/sale.py | 9 ++++++--- addons/sale/sale_view.xml | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 0b436f0dc91..431e0a3d63a 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1013,9 +1013,11 @@ class sale_order_line(osv.osv): def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, - lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False): + lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None): if not partner_id: raise osv.except_osv(_('No Customer Defined !'), _('You have to select a customer in the sales form !\nPlease set one customer before choosing a product.')) + if not context: + context = {} warning = {} warning_msgs = '' product_uom_obj = self.pool.get('product.uom') @@ -1023,7 +1025,7 @@ class sale_order_line(osv.osv): product_obj = self.pool.get('product.product') if partner_id: lang = partner_obj.browse(cr, uid, partner_id).lang - context = {'lang': lang, 'partner_id': partner_id} + context.update({'lang': lang, 'partner_id': partner_id}) if not product: return {'value': {'th_weight': 0, 'product_packaging': False, @@ -1034,7 +1036,8 @@ class sale_order_line(osv.osv): result = {} product_obj = product_obj.browse(cr, uid, product, context=context) - if not packaging and product_obj.packaging: + from_packaging = context.get('from_packaging', False) + if not packaging and product_obj.packaging and not from_packaging: packaging = product_obj.packaging[0].id result['product_packaging'] = packaging diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 6c444ad174d..efaca80c221 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -142,7 +142,7 @@ From 6d4b05e3d4840629fe04bffb75ef078174dfa65b Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Wed, 22 Jun 2011 14:41:23 +0530 Subject: [PATCH 020/896] [EDI] refactored code of edi class bzr revid: aar@tinyerp.com-20110622091123-6zal2su3vy8nv1nm --- openerp/addons/base/__openerp__.py | 2 +- openerp/addons/base/ir/ir_edi.py | 79 +++---------------- .../addons/base/test/test_edi_documents.yml | 4 +- 3 files changed, 15 insertions(+), 70 deletions(-) diff --git a/openerp/addons/base/__openerp__.py b/openerp/addons/base/__openerp__.py index bccaf7f660b..6406ec7b716 100644 --- a/openerp/addons/base/__openerp__.py +++ b/openerp/addons/base/__openerp__.py @@ -71,7 +71,6 @@ 'res/partner/wizard/partner_clear_ids_view.xml', 'res/partner/wizard/partner_wizard_ean_check_view.xml', 'res/partner/partner_data.xml', - 'test/test_edi_document.yml', 'res/ir_property_view.xml', 'security/base_security.xml', 'publisher_warranty/publisher_warranty_view.xml', @@ -96,6 +95,7 @@ 'test/test_ir_rule.yml', # <-- These tests modify/add/delete ir_rules. 'test/test_config_users.yml', 'test/test_ir_values.yml', + 'test/test_edi_documents.yml', ], 'installable': True, diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index fe8320491bf..fa41da161cc 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -20,12 +20,11 @@ ############################################################################## from osv import osv,fields -import simplejson import hashlib +import json import time import base64 import urllib2 -import unicodedata import openerp.release as release def safe_unique_id(model, database_id): """Generate a unique string to represent a (model,database id) pair @@ -82,7 +81,7 @@ class ir_edi_document(osv.osv): perform a JSON serialization of a list of dicts prepared by generate_edi() and return a UTF-8 encoded string that could be passed to deserialize() :param edi_dicts: it's list of edi_dict """ - serialized_list = simplejson.dumps(edi_documents) + serialized_list = json.dumps(edi_documents) return serialized_list def generate_edi(self, cr, uid, records, context=None): @@ -94,7 +93,7 @@ class ir_edi_document(osv.osv): edi_list = [] for record in records: record_model_obj = self.pool.get(record._name) - edi_list += record_model_obj.edi_export(cr, uid, [record],context=context) + edi_list += record_model_obj.edi_export(cr, uid, [record], context=context) return self.serialize(edi_list) def get_document(self, cr, uid, edi_token, context=None): @@ -103,13 +102,12 @@ class ir_edi_document(osv.osv): returns the string serialization that is in the database (column: document) for the given edi_token or raise. """ - records = self.search(cr, uid, [('name','=',edi_token)]) + records = self.name_search(cr, uid, edi_token) if records: - for edi in self.browse(cr, uid, records, context=context): - return edi.document + edi = self.browse(cr, uid, records, context=context) + return edi.document else: pass - def load_edi(self, cr, uid, edi_documents, context=None): """ @@ -132,7 +130,7 @@ class ir_edi_document(osv.osv): """ Deserialized the edi document string perform JSON deserialization from an edi document string, and returns a list of dicts """ - edi_document = simplejson.loads(edi_document_string) + edi_document = json.loads(edi_document_string) return edi_document @@ -173,15 +171,13 @@ class ir_edi_document(osv.osv): if edi_url and not edi_document: edi_document = urllib2.urlopen(edi_url).read() - #assert edi_document, _('EDI Document should be provided') + assert edi_document, _('EDI Document should be provided') else: - edi_documents = self.deserialize(edi_document) return self.load_edi(cr, uid, edi_documents, context=context) ir_edi_document() - class edi(object): _name = 'edi' _description = 'edi document handler' @@ -191,7 +187,6 @@ class edi(object): ``edi_import()`` and ``edi_export()`` methods to implement their specific behavior, based on the primitives provided by this superclass.""" - def edi_metadata(self, cr, uid, records, context=None): """Return a list representing the boilerplate EDI structure for exporting the record in the given browse_rec_list, including @@ -221,11 +216,10 @@ class edi(object): 'file_name': attachment.datas_fname, }) version = [] - for ver in release.major_version.split('.'): version.append(ver) - data_ids = model_data.search(cr, uid, [('res_id','=',record.id)]) + data_ids = model_data.search(cr, uid, [('res_id','=',record.id),('model','=',record._name)]) if len(data_ids): xml_obj = model_data.browse(cr,uid,data_ids[0]) @@ -261,12 +255,9 @@ class edi(object): else: xml_id = record._name db_uuid = '%s:%s' %(db_uuid, xml_id) - - name = record.name return [db_uuid, name] - def edi_o2m(self, cr, uid, records, context=None): """Return a list representing a O2M EDI value for the browse_records from the given browse_record_list. @@ -289,7 +280,6 @@ class edi(object): return dict_list - def edi_m2m(self, cr, uid, records, context=None): """Return a list representing a M2M EDI value for the browse_records from the given browse_record_list. @@ -310,8 +300,6 @@ class edi(object): return dict_list - - def edi_export(self, cr, uid, records, edi_struct=None, context=None): """Returns a list of dicts representing an edi.document containing the browse_records with ``ids``, using the generic algorithm. @@ -336,19 +324,15 @@ class edi(object): if context is None: context = {} _fields = self.fields_get(cr, uid, context=context) - - fields_to_export = edi_struct and edi_struct.keys() or _fields.keys() edi_dict_list = [] value = None for row in records: edi_dict = {} edi_dict.update(self.edi_metadata(cr, uid, [row], context=context)[0]) - for field in fields_to_export: cols = _fields[field] if _fields[field].has_key('function') or _fields[field].has_key('related_columns'): - pass else: if cols['type'] == 'many2one': @@ -364,10 +348,8 @@ class edi(object): value = getattr(row, field) edi_dict[field] = value edi_dict_list.append(edi_dict) - return edi_dict_list - def edi_import(self, cr, uid, edi_document, context=None): """Imports a list of dicts representing an edi.document, using the @@ -417,18 +399,6 @@ class edi(object): """ # generic implementation! - def convert(data): - if isinstance(data, unicode): - return str(data) - elif isinstance(data, dict): - return dict(map(convert, data.iteritems())) - elif isinstance(data, (list, tuple, set, frozenset)): - return type(data)(map(convert, data)) - else: - return data - #edi_document = convert(edi_document) - - fields = edi_document.keys() fields_to_import = [] data_line = [] @@ -437,23 +407,15 @@ class edi(object): current_module = 'edi_import' values = {} for field in edi_document.keys(): - if field.startswith('__') and field == '__id': - db_uuid, xml_ids = tuple(edi_document[field].split(':')) - - elif not field.startswith('__'): - - + if not field.startswith('__'): fields_to_import.append(field) edi_field_value = edi_document[field] if _fields[field].has_key('function') or _fields[field].has_key('related_columns'): - pass else: if _fields[field]['type'] in ('many2one', 'many2many'): - if _fields[field]['type'] == 'many2one': edi_parent_documents = [edi_field_value] - else: edi_parent_documents = edi_field_value @@ -469,7 +431,6 @@ class edi(object): if data_ids: for data in model_data.browse(cr, uid, [data_ids[0][0]], context=context): parent_lines.append(data.res_id) - print "..............................................",data.res_id else: #Perform name_search(name) to look for a record that matches the #given m2o name. If only one record is found, create the missing @@ -506,8 +467,6 @@ class edi(object): for m2m_id in parent_lines: many2many_ids.append((4,m2m_id)) values[field] = many2many_ids - - elif _fields[field]['type'] == 'one2many': #Look for a record that matches the db_id provided in the __id field. If #found, keep the corresponding database id, and connect it to the parent @@ -519,24 +478,15 @@ class edi(object): relations = [] relation_object = self.pool.get(_fields[field]['relation']) for edi_relation_document in edi_field_value: - '''print "pppppppppppppppppppppppppppppppppppppppppp" - print edi_field_value - print ":::::::::::::::::::::::::::::::::::::::::::::" - print edi_relation_document - print edi_parent_document - print "ppppppppppppppppppppppppppppppppppp" if edi_relation_document['__id'].find(':') != -1: db_uuid, xml_id = tuple(edi_relation_document['__id'].split(':')) - print xml_id data_ids = model_data.name_search(cr, uid, xml_id) - print "dddddddddddddddddddddddddddddddddddddddddddddd",data_ids if data_ids: for data in model_data.browse(cr,uid,[data_ids[0][0]]): relations.append(data.res_id) - else:''' - r = relation_object.edi_import(cr, uid, edi_relation_document, context=context) - relations.append(r) - print "relation id ssssssssssssssssssss",relations + else: + r = relation_object.edi_import(cr, uid, edi_relation_document, context=context) + relations.append(r) one2many_ids = [] for o2m_id in relations: one2many_ids.append((4,o2m_id)) @@ -544,8 +494,5 @@ class edi(object): else: values[field] = edi_field_value - for val in values.keys(): - print '::::::::::::::::::::::',val,'::::::::::;;;;;;;;;;;;;;;;;;;;;;' - print values[val] return model_data._update(cr, uid, self._name, current_module, values, context=context) # vim: ts=4 sts=4 sw=4 si et \ No newline at end of file diff --git a/openerp/addons/base/test/test_edi_documents.yml b/openerp/addons/base/test/test_edi_documents.yml index 1b66caaf4dc..06656800d4f 100644 --- a/openerp/addons/base/test/test_edi_documents.yml +++ b/openerp/addons/base/test/test_edi_documents.yml @@ -4,13 +4,11 @@ !python {model: ir.edi.document}: | partner_obj = self.pool.get('res.partner') partner_ids = partner_obj.search(cr, uid, []) - if partner_ids: partners = partner_obj.browse(cr, uid, partner_ids) tokens = self.export_edi(cr, uid, partners) - print '>>>>>>>>>>',tokens for token in tokens: document = self.get_document(cr, uid, token, context=context) a = self.import_edi(cr, uid, edi_document = document) - print '////////////',a + From e8ed589a8786193e6e8eac6ccc527505cd87ee5c Mon Sep 17 00:00:00 2001 From: "Ujjvala Collins (OpenERP)" Date: Wed, 22 Jun 2011 15:14:13 +0530 Subject: [PATCH 021/896] [FIX] sale: Fixed problem of on change product when product does not have package. bzr revid: uco@tinyerp.com-20110622094413-ijesue23tagbju0r --- addons/sale/sale.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 431e0a3d63a..9e4675bfe05 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1040,6 +1040,9 @@ class sale_order_line(osv.osv): if not packaging and product_obj.packaging and not from_packaging: packaging = product_obj.packaging[0].id result['product_packaging'] = packaging + elif not product_obj.packaging: + packaging = False + result['product_packaging'] = False if packaging: default_uom = product_obj.uom_id and product_obj.uom_id.id From b5050763692468b73263455c54b28ad0351bba5c Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Fri, 24 Jun 2011 14:16:56 +0530 Subject: [PATCH 022/896] [FIX] add get method in refrence field and check if refrence id not exists then return false. lp bug: https://launchpad.net/bugs/783961 fixed bzr revid: ysa@tinyerp.com-20110624084656-c2jd3xskk10fwt35 --- openerp/osv/fields.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index f55e4b9c027..aa042ca6541 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -148,9 +148,23 @@ class integer_big(_column): class reference(_column): _type = 'reference' + _classic_read = False # post-process to handle missing target + def __init__(self, string, selection, size, **args): _column.__init__(self, string=string, size=size, selection=selection, **args) + def get(self, cr, obj, ids, name, uid=None, context=None, values=None): + result = {} + # copy initial values fetched previously. + for row in values: + result[row['id']] = row[name] + # verify target object exists + for id, value in result.iteritems(): + if value: + model, res_id = value.split(',') + if not obj.pool.get(model).exists(cr, uid, [int(res_id)], context=context): + result[id] = False + return result class char(_column): _type = 'char' From 2f5ada053acc9bf83c77b9a369f46a4eb8889e1a Mon Sep 17 00:00:00 2001 From: "Ujjvala Collins (OpenERP)" Date: Fri, 24 Jun 2011 15:09:06 +0530 Subject: [PATCH 023/896] [FIX] sale, sale_layout: Added onchange method on product packaging field. bzr revid: uco@tinyerp.com-20110624093906-2oonk5dge6f2ond7 --- addons/sale/sale.py | 76 +++++++++++++++---------- addons/sale/sale_view.xml | 2 +- addons/sale_layout/sale_layout_view.xml | 2 +- 3 files changed, 48 insertions(+), 32 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 9e4675bfe05..e5afac4d87e 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1010,43 +1010,26 @@ class sale_order_line(osv.osv): default = {} default.update({'state': 'draft', 'move_ids': [], 'invoiced': False, 'invoice_lines': []}) return super(sale_order_line, self).copy_data(cr, uid, id, default, context=context) - - def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, - uom=False, qty_uos=0, uos=False, name='', partner_id=False, - lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None): - if not partner_id: - raise osv.except_osv(_('No Customer Defined !'), _('You have to select a customer in the sales form !\nPlease set one customer before choosing a product.')) - if not context: - context = {} - warning = {} - warning_msgs = '' - product_uom_obj = self.pool.get('product.uom') - partner_obj = self.pool.get('res.partner') + + def onchange_product_packaging(self, cr, uid, ids, pricelist, product, qty, uom, packaging, flag): product_obj = self.pool.get('product.product') - if partner_id: - lang = partner_obj.browse(cr, uid, partner_id).lang - context.update({'lang': lang, 'partner_id': partner_id}) - - if not product: - return {'value': {'th_weight': 0, 'product_packaging': False, - 'product_uos_qty': qty}, 'domain': {'product_uom': [], - 'product_uos': []}} - if not date_order: - date_order = time.strftime('%Y-%m-%d') - + product_uom_obj = self.pool.get('product.uom') + warning = {} result = {} - product_obj = product_obj.browse(cr, uid, product, context=context) - from_packaging = context.get('from_packaging', False) - if not packaging and product_obj.packaging and not from_packaging: - packaging = product_obj.packaging[0].id + warning_msgs = '' + if not product: + return {'value': {'product_packaging': False}} + products = product_obj.browse(cr, uid, product) + if not packaging and products.packaging and not flag: + packaging = products.packaging[0].id result['product_packaging'] = packaging - elif not product_obj.packaging: + elif not products.packaging: packaging = False result['product_packaging'] = False if packaging: - default_uom = product_obj.uom_id and product_obj.uom_id.id - pack = self.pool.get('product.packaging').browse(cr, uid, packaging, context=context) + default_uom = products.uom_id and products.uom_id.id + pack = self.pool.get('product.packaging').browse(cr, uid, packaging) q = product_uom_obj._compute_qty(cr, uid, uom, pack.qty, default_uom) # qty = qty - qty % q + q if qty and (q and not (qty % q) == 0): @@ -1061,6 +1044,39 @@ class sale_order_line(osv.osv): warning_msgs += _("Picking Information ! : ") + warn_msg + "\n\n" result['product_uom_qty'] = qty + if warning_msgs: + warning = { + 'title': _('Configuration Error !'), + 'message' : warning_msgs + } + + return {'value': result, 'warning': warning} + + def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, + uom=False, qty_uos=0, uos=False, name='', partner_id=False, + lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False): + if not partner_id: + raise osv.except_osv(_('No Customer Defined !'), _('You have to select a customer in the sales form !\nPlease set one customer before choosing a product.')) + warning_msgs = '' + product_uom_obj = self.pool.get('product.uom') + partner_obj = self.pool.get('res.partner') + product_obj = self.pool.get('product.product') + if partner_id: + lang = partner_obj.browse(cr, uid, partner_id).lang + context = {'lang': lang, 'partner_id': partner_id} + + if not product: + return {'value': {'th_weight': 0, 'product_packaging': False, + 'product_uos_qty': qty}, 'domain': {'product_uom': [], + 'product_uos': []}} + if not date_order: + date_order = time.strftime('%Y-%m-%d') + + res = self.onchange_product_packaging(cr, uid, ids, pricelist, product, qty, uom, packaging, False) + result = res.get('value', {}) + warning = res.get('warning', {}) + product_obj = product_obj.browse(cr, uid, product, context=context) + uom2 = False if uom: uom2 = product_uom_obj.browse(cr, uid, uom) diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index efaca80c221..0b17b5ca71c 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -142,7 +142,7 @@ diff --git a/addons/sale_layout/sale_layout_view.xml b/addons/sale_layout/sale_layout_view.xml index 6dee4ddd62f..25c56a03ae6 100644 --- a/addons/sale_layout/sale_layout_view.xml +++ b/addons/sale_layout/sale_layout_view.xml @@ -38,7 +38,7 @@ name="product_packaging" attrs="{'readonly':[('layout_type','!=','article')]}" context="{'partner_id':parent.partner_id,'quantity':product_uom_qty,'pricelist':parent.pricelist_id,'shop':parent.shop_id,'uom':product_uom}" - on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, 'lang' in context and context['lang'], False, parent.date_order, product_packaging, parent.fiscal_position)" + on_change="onchange_product_packaging(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_packaging, True)" domain="[('product_id','=',product_id)]" groups="base.group_extended"/> From 5ac271fa1fa85fc24a34c76ca5258d45a422955f Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Mon, 27 Jun 2011 13:59:45 +0530 Subject: [PATCH 024/896] [IMP] edi: improve metadata function to create xml record if does not found bzr revid: hmo@tinyerp.com-20110627082945-xtoibwmlsl8187yg --- openerp/addons/base/ir/ir_edi.py | 50 ++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index ad9fd2a2adf..8db5b397116 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -26,6 +26,9 @@ import time import base64 import urllib2 import openerp.release as release + +edi_module = 'edi_import' + def safe_unique_id(model, database_id): """Generate a unique string to represent a (model,database id) pair without being too long, without revealing the database id, and @@ -201,8 +204,9 @@ class edi(object): - __version': a list of components for the version - __attachments': a list (possibly empty) of dicts describing the files attached to this record. """ - # generic implementation! - model_data = self.pool.get('ir.model.data') + if context is None: + context = {} + model_data_pool = self.pool.get('ir.model.data') data_ids = [] attachment_object = self.pool.get('ir.attachment') edi_dict_list = [] @@ -214,6 +218,7 @@ class edi(object): except: pass version.append(ver) + for record in records: attachment_ids = attachment_object.search(cr, uid, [('res_model','=', record._name), ('res_id', '=', record.id)]) attachment_dict_list = [] @@ -225,21 +230,32 @@ class edi(object): }) - data_ids = model_data.search(cr, uid, [('res_id','=',record.id),('model','=',record._name)]) - + data_ids = model_data_pool.search(cr, uid, [('res_id','=',record.id),('model','=',record._name)]) + uuid = safe_unique_id(record._name, record.id) + if len(data_ids): - xml_record = model_data.browse(cr, uid, data_ids[0]) - uuid = safe_unique_id(record._name, record.id) - db_uuid = '%s:%s.%s' % (uuid, xml_record.module, xml_record.name) - + xml_record_id = data_ids[0] + else: + xml_record_id = model_data_pool.create(cr, uid, { + 'name': uuid, + 'model': record._name, + 'module': edi_module, + 'res_id': record.id}, context=context) + + xml_record = model_data_pool.browse(cr, uid, xml_record_id, context=context) + db_uuid = '%s:%s.%s' % (uuid, xml_record.module, xml_record.name) + edi_dict = { - '__model' : record._name, - '__module' : record._module, '__id': db_uuid, '__last_update': False, #record.write_date, #TODO: convert into UTC - '__version': version, - '__attachments': attachment_dict_list } + if not context.get('o2m_export'): + edi_dict.update({ + '__model' : record._name, + '__module' : record._module, + '__version': version, + '__attachments': attachment_dict_list + }) edi_dict_list.append(edi_dict) return edi_dict_list @@ -278,11 +294,14 @@ class edi(object): """ # generic implementation! dict_list = [] - + if context is None: + context = {} + ctx = context.copy() + ctx.update({'o2m_export':True}) for record in records: model_obj = self.pool.get(record._name) - dict_list += model_obj.edi_export(cr, uid, [record], context=context) + dict_list += model_obj.edi_export(cr, uid, [record], context=ctx) return dict_list @@ -410,7 +429,6 @@ class edi(object): data_line = [] model_data = self.pool.get('ir.model.data') _fields = self.fields_get(cr, uid, context=context) - current_module = 'edi_import' values = {} for field in edi_document.keys(): if not field.startswith('__'): @@ -500,5 +518,5 @@ class edi(object): else: values[field] = edi_field_value - return model_data._update(cr, uid, self._name, current_module, values, context=context) + return model_data._update(cr, uid, self._name, edi_module, values, context=context) # vim: ts=4 sts=4 sw=4 si et From 25dcd09489096441f2588b06db2ad23bab0fd462 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Mon, 27 Jun 2011 17:51:45 +0530 Subject: [PATCH 025/896] [IMP] EDI: introduce new methods in edi class edi_import_relation: create record of relation object if does not match with relation value edi_xml_id: generate xml id with unique db id. create record of ir.model.data if xml id is not found for particular record. bzr revid: hmo@tinyerp.com-20110627122145-v2d2wtkjut68rcba --- openerp/addons/base/ir/ir_edi.py | 74 ++++++++++++++++---------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index 8db5b397116..9672b9a2207 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -190,6 +190,22 @@ class edi(object): Classes that inherit from this mixin class should override the ``edi_import()`` and ``edi_export()`` methods to implement their specific behavior, based on the primitives provided by this superclass.""" + + def edi_xml_id(self, cr, uid, record, context=None): + model_data_pool = self.pool.get('ir.model.data') + uuid = safe_unique_id(record._name, record.id) + data_ids = model_data_pool.search(cr, uid, [('res_id','=',record.id),('model','=',record._name)]) + if len(data_ids): + xml_record_id = data_ids[0] + else: + xml_record_id = model_data_pool.create(cr, uid, { + 'name': uuid, + 'model': record._name, + 'module': edi_module, + 'res_id': record.id}, context=context) + xml_record = model_data_pool.browse(cr, uid, xml_record_id, context=context) + db_uuid = '%s:%s.%s' % (uuid, xml_record.module, xml_record.name) + return db_uuid def edi_metadata(self, cr, uid, records, context=None): """Return a list representing the boilerplate EDI structure for @@ -206,7 +222,6 @@ class edi(object): """ if context is None: context = {} - model_data_pool = self.pool.get('ir.model.data') data_ids = [] attachment_object = self.pool.get('ir.attachment') edi_dict_list = [] @@ -230,21 +245,7 @@ class edi(object): }) - data_ids = model_data_pool.search(cr, uid, [('res_id','=',record.id),('model','=',record._name)]) - uuid = safe_unique_id(record._name, record.id) - - if len(data_ids): - xml_record_id = data_ids[0] - else: - xml_record_id = model_data_pool.create(cr, uid, { - 'name': uuid, - 'model': record._name, - 'module': edi_module, - 'res_id': record.id}, context=context) - - xml_record = model_data_pool.browse(cr, uid, xml_record_id, context=context) - db_uuid = '%s:%s.%s' % (uuid, xml_record.module, xml_record.name) - + db_uuid = self.edi_xml_id(cr, uid, record, context=context) edi_dict = { '__id': db_uuid, '__last_update': False, #record.write_date, #TODO: convert into UTC @@ -267,20 +268,13 @@ class edi(object): Exmaple: ['db-uuid:xml-id', 'Partner Name'] """ # generic implementation! - data_object = self.pool.get('ir.model.data') - db_uuid = safe_unique_id(record._name, record.id) - xml_ids = data_object.search(cr, uid, [('model','=',record._name),('res_id','=',record.id)]) - - if xml_ids: - xml_id = data_object.browse(cr, uid, xml_ids[0], context=context) - xml_id = '%s.%s' %(xml_id.module, xml_id.name) - else: - xml_id = record._name - db_uuid = '%s:%s' %(db_uuid, xml_id) - name = record.name + db_uuid = self.edi_xml_id(cr, uid, record, context=context) + relation_model_pool = self.pool.get(record._name) + name = relation_model_pool.name_get(cr, uid, [record.id], context=context) + name = name and name[0][1] or False return [db_uuid, name] - def edi_o2m(self, cr, uid, records, context=None): + def edi_o2m(self, cr, uid, records, edi_struct=None, context=None): """Return a list representing a O2M EDI value for the browse_records from the given browse_record_list. @@ -301,7 +295,7 @@ class edi(object): for record in records: model_obj = self.pool.get(record._name) - dict_list += model_obj.edi_export(cr, uid, [record], context=ctx) + dict_list += model_obj.edi_export(cr, uid, [record], edi_struct=edi_struct, context=ctx) return dict_list @@ -368,12 +362,22 @@ class edi(object): elif cols['type'] == 'many2many': value = self.edi_m2m(cr, uid, record, context=context) elif cols['type'] == 'one2many': - value = self.edi_o2m(cr, uid, record, context=context ) + value = self.edi_o2m(cr, uid, record, edi_struct=edi_struct.get(field, {}), context=context ) else: value = record edi_dict[field] = value edi_dict_list.append(edi_dict) return edi_dict_list + + def edi_import_relation(self, cr, uid, relation_model, relation_value, values={}, context=None): + relation_object = self.pool.get(relation_model) + relation_ids = relation_object.name_search(cr, uid, relation_value, context=context) + if relation_ids and len(relation_ids) == 1: + relation_id = relation_ids[0][0] + else: + values.update({'name': relation_value}) + relation_id = relation_object.create(cr, uid, values, context=context) + return relation_id def edi_import(self, cr, uid, edi_document, context=None): @@ -465,13 +469,9 @@ class edi(object): #multiple results are found, go to next step. #Create the new record using the only field value that is known: the #name, and create the ir.model.data entry to map to it. - - relation_object = self.pool.get(_fields[field]['relation']) - relation_ids = relation_object.search(cr, uid, [('name','=',edi_parent_document[1])]) - if relation_ids and len(relation_ids) == 1: - relation_id = relation_ids[0] - else: - relation_id = relation_object.create(cr, uid, {'name': edi_parent_document[1]}, context=context) + relation_model = _fields[field]['relation'] + relation_id = self.edi_import_relation(cr, uid, relation_model, edi_parent_document[1], context=context) + relation_object = self.pool.get(relation_model) model_data.create(cr, uid, { 'name': xml_id, 'model': relation_object._name, From 5cc4b243b683f7ecf69b1ae2e18247d82950e50b Mon Sep 17 00:00:00 2001 From: mtr Date: Tue, 28 Jun 2011 17:16:33 +0530 Subject: [PATCH 026/896] [FIX] hr_holidays:-Exception raised on confirmation of leave request is now translated in user's lang. lp bug: https://launchpad.net/bugs/798739 fixed bzr revid: mtr@mtr-20110628114633-78h4k4q3d8jectjo --- addons/hr_holidays/hr_holidays.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 7074fd5cecc..4c000aab968 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -224,14 +224,20 @@ class hr_holidays(osv.osv): return True def holidays_validate(self, cr, uid, ids, *args): - self.check_holidays(cr, uid, ids) + context = {} + lang = self.pool.get('res.users').browse(cr, uid, uid).context_lang + context.update({'lang': lang}) + self.check_holidays(cr, uid, ids, context=context) obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) manager = ids2 and ids2[0] or False return self.write(cr, uid, ids, {'state':'validate1', 'manager_id': manager}) def holidays_validate2(self, cr, uid, ids, *args): - self.check_holidays(cr, uid, ids) + context = {} + lang = self.pool.get('res.users').browse(cr, uid, uid).context_lang + context.update({'lang': lang}) + self.check_holidays(cr, uid, ids, context=context) obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) manager = ids2 and ids2[0] or False @@ -277,7 +283,10 @@ class hr_holidays(osv.osv): return True def holidays_confirm(self, cr, uid, ids, *args): - self.check_holidays(cr, uid, ids) + context = {} + lang = self.pool.get('res.users').browse(cr, uid, uid).context_lang + context.update({'lang': lang}) + self.check_holidays(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state':'confirm'}) def holidays_refuse(self, cr, uid, ids, *args): @@ -302,8 +311,10 @@ class hr_holidays(osv.osv): return True - def check_holidays(self, cr, uid, ids): + def check_holidays(self, cr, uid, ids, context=None): holi_status_obj = self.pool.get('hr.holidays.status') + if context is None: + context = {} for record in self.browse(cr, uid, ids): if record.holiday_type == 'employee' and record.type == 'remove': if record.employee_id and not record.holiday_status_id.limit: From 4aa687cf3fad299a4cc6098f7fc12f35c36d5338 Mon Sep 17 00:00:00 2001 From: "ron@tinyerp.com" <> Date: Wed, 29 Jun 2011 10:48:00 +0530 Subject: [PATCH 027/896] [FIX]purchase,procurement:Fixed=>Generate Sale order via Non admin User Po generated with wrong warehouse and company ,For information Traceback Generated after run the ORDERPOINT it is a sequence problem and it has been fixed in lp:787052 : lp bug: https://launchpad.net/bugs/802322 fixed bzr revid: ron@tinyerp.com-20110629051800-u71iztvvtbgsyzuq --- addons/procurement/schedulers.py | 1 + addons/purchase/purchase.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/addons/procurement/schedulers.py b/addons/procurement/schedulers.py index 3a6e3336472..d2787185c18 100644 --- a/addons/procurement/schedulers.py +++ b/addons/procurement/schedulers.py @@ -183,6 +183,7 @@ class procurement_order(osv.osv): 'product_qty': -product.virtual_available, 'product_uom': product.uom_id.id, 'location_id': location_id, + 'company_id': warehouse.company_id.id, 'procure_method': 'make_to_order', }) wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 6f6ab0c1b44..8867cb7f3a7 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -775,6 +775,8 @@ class procurement_order(osv.osv): prod_obj = self.pool.get('product.product') acc_pos_obj = self.pool.get('account.fiscal.position') po_obj = self.pool.get('purchase.order') + wharehouse_obj = self.pool.get('stock.warehouse') + for procurement in self.browse(cr, uid, ids, context=context): res_id = procurement.move_id.id partner = procurement.product_id.seller_id # Taken Main Supplier of Product of Procurement. @@ -821,6 +823,8 @@ class procurement_order(osv.osv): 'partner_id': partner_id, 'partner_address_id': address_id, 'location_id': procurement.location_id.id, + 'warehouse_id': wharehouse_obj.search(cr, uid, [('company_id','=',procurement.company_id.id or company)])[0], + 'company_id': procurement.company_id.id or company, 'pricelist_id': pricelist_id, 'order_line': [(0,0,line)], 'company_id': procurement.company_id.id, From 3be8619d32f3ca9ca3859fde34585ccb3f3d3ae8 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Wed, 29 Jun 2011 12:10:37 +0530 Subject: [PATCH 028/896] [IMP] web_service: introduce EDI Services to provide service like get_edi_document, import_edi_document, import_edi_url bzr revid: hmo@tinyerp.com-20110629064037-2wb76qua48m1g2bh --- openerp/service/web_services.py | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/openerp/service/web_services.py b/openerp/service/web_services.py index 39457807868..37dadc195fb 100644 --- a/openerp/service/web_services.py +++ b/openerp/service/web_services.py @@ -37,6 +37,47 @@ import openerp.modules import locale import logging from cStringIO import StringIO +import threading + +class edi(netsvc.ExportService): + def exp_get_edi_document(self, edi_token): + db_name = getattr(threading.currentThread(), 'dbname', None) + if db_name: + cr = pooler.get_db_only(db_name).cursor() + else: + raise Exception("No database cursor found!") + pool = pooler.get_pool(db_name) + edi_pool = pool.get('ir.edi.document') + return edi_pool.get_document(cr, 1, edi_token) + + def exp_import_edi_document(self, db, uid, passwd, edi_document, context=None): + cr = pooler.get_db_only(db).cursor() + pool = pooler.get_pool(db) + edi_pool = pool.get('ir.edi.document') + return edi_pool.import_edi(cr, uid, edi_document=edi_document, context=context) + + def exp_import_edi_url(self, db, uid, passwd, edi_url, context=None): + cr = pooler.get_db_only(db).cursor() + pool = pooler.get_pool(db) + edi_pool = pool.get('ir.edi.document') + return edi_pool.import_edi(cr, uid, edi_url=edi_url, context=context) + + def __init__(self, name="edi"): + netsvc.ExportService.__init__(self, name) + self.joinGroup("web-services") + + def dispatch(self, method, auth, params): + if method in ['import_edi_document', 'import_edi_url']: + (db, uid, passwd ) = params[0:3] + security.check(db, uid, passwd) + elif method in ['get_edi_document']: + # params = params + # No security check for these methods + pass + else: + raise KeyError("Method not found: %s" % method) + fn = getattr(self, 'exp_'+method) + return fn(*params) class db(netsvc.ExportService): def __init__(self, name="db"): @@ -763,6 +804,7 @@ def start_web_services(): objects_proxy() wizard() report_spool() + edi() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From 784b6af17c37b278c30c8b8d14e8ad3be9751abb Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Wed, 29 Jun 2011 15:49:28 +0530 Subject: [PATCH 029/896] [FIX] edi: In export_edi method, edi_struct should be have blank dict if None value bzr revid: hmo@tinyerp.com-20110629101928-djc9hvlm0pmqm362 --- openerp/addons/base/ir/ir_edi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index 9672b9a2207..0ee29e13a3a 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -342,6 +342,8 @@ class edi(object): if context is None: context = {} + if edi_struct is None: + edi_struct = {} _fields = self.fields_get(cr, uid, context=context) fields_to_export = edi_struct and edi_struct.keys() or _fields.keys() edi_dict_list = [] From 6c27f121fb6621d0964a163ae6a62b1b363a9c8f Mon Sep 17 00:00:00 2001 From: "ron@tinyerp.com" <> Date: Wed, 29 Jun 2011 18:55:39 +0530 Subject: [PATCH 030/896] [IMP]purchase:fixed=>Traceback Generated after run the ORDERPOINT bzr revid: ron@tinyerp.com-20110629132539-9y8zxts1v39olmrt --- addons/purchase/purchase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 8867cb7f3a7..e06a5ef988c 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -776,7 +776,6 @@ class procurement_order(osv.osv): acc_pos_obj = self.pool.get('account.fiscal.position') po_obj = self.pool.get('purchase.order') wharehouse_obj = self.pool.get('stock.warehouse') - for procurement in self.browse(cr, uid, ids, context=context): res_id = procurement.move_id.id partner = procurement.product_id.seller_id # Taken Main Supplier of Product of Procurement. @@ -819,6 +818,7 @@ class procurement_order(osv.osv): 'taxes_id': [(6,0,taxes)] }) purchase_id = po_obj.create(cr, uid, { + 'name': self.pool.get('ir.sequence').get(cr, uid, 'purchase.order') or _('PO:%s') %procurement.name, 'origin': procurement.origin, 'partner_id': partner_id, 'partner_address_id': address_id, From c2ba873ab842a98efa4f12cb9b73784c6fd577bc Mon Sep 17 00:00:00 2001 From: mtr Date: Thu, 30 Jun 2011 14:06:02 +0530 Subject: [PATCH 031/896] [IMP] hr_holidays: improved code for translation using context bzr revid: mtr@mtr-20110630083602-au21nsi3yyw97qnp --- addons/hr_holidays/hr_holidays.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 4c000aab968..3dfc9370ecc 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -224,9 +224,8 @@ class hr_holidays(osv.osv): return True def holidays_validate(self, cr, uid, ids, *args): - context = {} lang = self.pool.get('res.users').browse(cr, uid, uid).context_lang - context.update({'lang': lang}) + context = {'lang': lang} self.check_holidays(cr, uid, ids, context=context) obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) @@ -234,9 +233,8 @@ class hr_holidays(osv.osv): return self.write(cr, uid, ids, {'state':'validate1', 'manager_id': manager}) def holidays_validate2(self, cr, uid, ids, *args): - context = {} lang = self.pool.get('res.users').browse(cr, uid, uid).context_lang - context.update({'lang': lang}) + context = {'lang': lang} self.check_holidays(cr, uid, ids, context=context) obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) @@ -283,9 +281,8 @@ class hr_holidays(osv.osv): return True def holidays_confirm(self, cr, uid, ids, *args): - context = {} lang = self.pool.get('res.users').browse(cr, uid, uid).context_lang - context.update({'lang': lang}) + context = {'lang': lang} self.check_holidays(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state':'confirm'}) @@ -313,8 +310,6 @@ class hr_holidays(osv.osv): def check_holidays(self, cr, uid, ids, context=None): holi_status_obj = self.pool.get('hr.holidays.status') - if context is None: - context = {} for record in self.browse(cr, uid, ids): if record.holiday_type == 'employee' and record.type == 'remove': if record.employee_id and not record.holiday_status_id.limit: From a7e16487e484cffa2a0b817340808816c2ee139c Mon Sep 17 00:00:00 2001 From: Mustufa Rangwala Date: Thu, 30 Jun 2011 14:59:05 +0530 Subject: [PATCH 032/896] [IMP] Hr_holidays: Improve the code by making generic method for context lang get bzr revid: mra@mra-laptop-20110630092905-4ggbh1i7bnm7ibqb --- addons/hr_holidays/hr_holidays.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 3dfc9370ecc..7756e218ce0 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -223,9 +223,12 @@ class hr_holidays(osv.osv): wf_service.trg_create(uid, 'hr.holidays', id, cr) return True - def holidays_validate(self, cr, uid, ids, *args): + def get_user_lang(self, cr, uid, ids): lang = self.pool.get('res.users').browse(cr, uid, uid).context_lang - context = {'lang': lang} + return {'lang': lang} + + def holidays_validate(self, cr, uid, ids, *args): + context = self.get_user_lang(cr, uid, ids) self.check_holidays(cr, uid, ids, context=context) obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) @@ -233,8 +236,7 @@ class hr_holidays(osv.osv): return self.write(cr, uid, ids, {'state':'validate1', 'manager_id': manager}) def holidays_validate2(self, cr, uid, ids, *args): - lang = self.pool.get('res.users').browse(cr, uid, uid).context_lang - context = {'lang': lang} + context = self.get_user_lang(cr, uid, ids) self.check_holidays(cr, uid, ids, context=context) obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) @@ -281,8 +283,7 @@ class hr_holidays(osv.osv): return True def holidays_confirm(self, cr, uid, ids, *args): - lang = self.pool.get('res.users').browse(cr, uid, uid).context_lang - context = {'lang': lang} + context = self.get_user_lang(cr, uid, ids) self.check_holidays(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state':'confirm'}) From f059576ba8c7bc2ca8e8f15b96b1438b502f67b7 Mon Sep 17 00:00:00 2001 From: "ron@tinyerp.com" <> Date: Tue, 5 Jul 2011 11:47:50 +0530 Subject: [PATCH 033/896] [IMP]procurment:Fixed=>changed the all select queries which bypassed the record rules in procurment.py and schedular.py,and change the output location configuaration with company_id=False because of that it will be access by child company bzr revid: ron@tinyerp.com-20110705061750-7g73nancsfijk0re --- addons/procurement/procurement.py | 4 ++-- addons/procurement/schedulers.py | 12 +++--------- addons/purchase/purchase.py | 1 - addons/stock/stock_data.xml | 2 +- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py index 743ef85a9ea..f4988afedc0 100644 --- a/addons/procurement/procurement.py +++ b/addons/procurement/procurement.py @@ -371,8 +371,8 @@ class procurement_order(osv.osv): id = procurement.move_id.id if not (procurement.move_id.state in ('done','assigned','cancel')): ok = ok and self.pool.get('stock.move').action_assign(cr, uid, [id]) - cr.execute('select count(id) from stock_warehouse_orderpoint where product_id=%s', (procurement.product_id.id,)) - res = cr.fetchone()[0] + order_point_id = self.pool.get('stock.warehouse.orderpoint').search(cr, uid, [('product_id', '=', procurement.product_id.id)]) + res = len(order_point_id) if not res and not ok: message = _("Not enough stock and no minimum orderpoint rule defined.") elif not res: diff --git a/addons/procurement/schedulers.py b/addons/procurement/schedulers.py index d2787185c18..33abeab34af 100644 --- a/addons/procurement/schedulers.py +++ b/addons/procurement/schedulers.py @@ -67,8 +67,7 @@ class procurement_order(osv.osv): report_except = 0 report_later = 0 while True: - cr.execute("select id from procurement_order where state='confirmed' and procure_method='make_to_order' order by priority,date_planned limit 500 offset %s", (offset,)) - ids = map(lambda x: x[0], cr.fetchall()) + ids = procurement_obj.search(cr, uid, [('state', '=', 'confirmed'), ('procure_method', '=', 'make_to_order')], order='priority,date_planned') for proc in procurement_obj.browse(cr, uid, ids, context=context): if maxdate >= proc.date_planned: wf_service.trg_validate(uid, 'procurement.order', proc.id, 'button_check', cr) @@ -156,11 +155,7 @@ class procurement_order(osv.osv): wf_service = netsvc.LocalService("workflow") warehouse_ids = warehouse_obj.search(cr, uid, [], context=context) - - cr.execute('select p.id from product_product p \ - join product_template t on (p.product_tmpl_id=t.id) \ - where p.active=True and t.purchase_ok=True') - products_id = [x for x, in cr.fetchall()] + products_id = product_obj.search(cr, uid, [('active', '=', True), ('purchase_ok', '=', True)], order='id') for warehouse in warehouse_obj.browse(cr, uid, warehouse_ids, context=context): context['warehouse'] = warehouse @@ -242,8 +237,7 @@ class procurement_order(osv.osv): if op.procurement_draft_ids: # Check draft procurement related to this order point pro_ids = [x.id for x in op.procurement_draft_ids] - cr.execute('select id, product_qty from procurement_order where id in %s order by product_qty desc', (tuple(pro_ids), )) - procure_datas = cr.dictfetchall() + procure_datas = procurement_obj.read(cr, uid, pro_ids, ['id','product_qty'], context=context) to_generate = qty for proc_data in procure_datas: if to_generate >= proc_data['product_qty']: diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index e06a5ef988c..137e8125c06 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -824,7 +824,6 @@ class procurement_order(osv.osv): 'partner_address_id': address_id, 'location_id': procurement.location_id.id, 'warehouse_id': wharehouse_obj.search(cr, uid, [('company_id','=',procurement.company_id.id or company)])[0], - 'company_id': procurement.company_id.id or company, 'pricelist_id': pricelist_id, 'order_line': [(0,0,line)], 'company_id': procurement.company_id.id, diff --git a/addons/stock/stock_data.xml b/addons/stock/stock_data.xml index bcde818c78a..3c75615dfae 100644 --- a/addons/stock/stock_data.xml +++ b/addons/stock/stock_data.xml @@ -91,8 +91,8 @@ Output - internal + customer transparent out From d93d0aa0faf14c043eaf7eebf1e49a1b65a0e16f Mon Sep 17 00:00:00 2001 From: mtr Date: Tue, 5 Jul 2011 12:41:51 +0530 Subject: [PATCH 034/896] [IMP] hr_holidays: passed context as an argument in all the methods() bzr revid: mtr@mtr-20110705071151-lhg6wqbphg9hoysw --- addons/hr_holidays/hr_holidays.py | 39 +++++++++++++------------ addons/hr_holidays/hr_holidays_view.xml | 12 ++++---- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 7756e218ce0..e2cf2a5bace 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -161,7 +161,7 @@ class hr_holidays(osv.osv): leave_ids = obj_res_leave.search(cr, uid, [('holiday_id', 'in', ids)], context=context) return obj_res_leave.unlink(cr, uid, leave_ids) - def onchange_type(self, cr, uid, ids, holiday_type): + def onchange_type(self, cr, uid, ids, holiday_type, context=None): result = {'value': {'employee_id': False}} if holiday_type == 'employee': ids_employee = self.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)]) @@ -172,7 +172,7 @@ class hr_holidays(osv.osv): return result # TODO: can be improved using resource calendar method - def _get_number_of_days(self, date_from, date_to): + def _get_number_of_days(self, date_from, date_to, context=None): """Returns a float equals to the timedelta between two dates given as string.""" DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S" @@ -188,10 +188,12 @@ class hr_holidays(osv.osv): raise osv.except_osv(_('Warning!'),_('You cannot delete a leave which is not in draft state !')) return super(hr_holidays, self).unlink(cr, uid, ids, context) - def onchange_date_from(self, cr, uid, ids, date_to, date_from): + def onchange_date_from(self, cr, uid, ids, date_to, date_from, context=None): result = {} + if context is None: + context = {} if date_to and date_from: - diff_day = self._get_number_of_days(date_from, date_to) + diff_day = self._get_number_of_days(date_from, date_to, context=context) result['value'] = { 'number_of_days_temp': round(diff_day)+1 } @@ -212,7 +214,7 @@ class hr_holidays(osv.osv): } return {'warning': warning} - def set_to_draft(self, cr, uid, ids, *args): + def set_to_draft(self, cr, uid, ids, context=None): self.write(cr, uid, ids, { 'state': 'draft', 'manager_id': False, @@ -223,20 +225,18 @@ class hr_holidays(osv.osv): wf_service.trg_create(uid, 'hr.holidays', id, cr) return True - def get_user_lang(self, cr, uid, ids): - lang = self.pool.get('res.users').browse(cr, uid, uid).context_lang - return {'lang': lang} - - def holidays_validate(self, cr, uid, ids, *args): - context = self.get_user_lang(cr, uid, ids) + def holidays_validate(self, cr, uid, ids, context=None): + if context is None: + context = {} self.check_holidays(cr, uid, ids, context=context) obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) manager = ids2 and ids2[0] or False return self.write(cr, uid, ids, {'state':'validate1', 'manager_id': manager}) - def holidays_validate2(self, cr, uid, ids, *args): - context = self.get_user_lang(cr, uid, ids) + def holidays_validate2(self, cr, uid, ids, context=None): + if context is None: + context = {} self.check_holidays(cr, uid, ids, context=context) obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) @@ -282,20 +282,23 @@ class hr_holidays(osv.osv): wf_service.trg_validate(uid, 'hr.holidays', leave_id, 'second_validate', cr) return True - def holidays_confirm(self, cr, uid, ids, *args): - context = self.get_user_lang(cr, uid, ids) + def holidays_confirm(self, cr, uid, ids, context=None): + if context is None: + context = {} self.check_holidays(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state':'confirm'}) - def holidays_refuse(self, cr, uid, ids, *args): + def holidays_refuse(self, cr, uid, ids, context=None): + if context is None: + context = {} obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) manager = ids2 and ids2[0] or False self.write(cr, uid, ids, {'state': 'refuse', 'manager_id2': manager}) - self.holidays_cancel(cr, uid, ids) + self.holidays_cancel(cr, uid, ids, context=context) return True - def holidays_cancel(self, cr, uid, ids, *args): + def holidays_cancel(self, cr, uid, ids, context=None): obj_crm_meeting = self.pool.get('crm.meeting') for record in self.browse(cr, uid, ids): # Delete the meeting diff --git a/addons/hr_holidays/hr_holidays_view.xml b/addons/hr_holidays/hr_holidays_view.xml index 608fda554fb..45f8aa8c3f5 100644 --- a/addons/hr_holidays/hr_holidays_view.xml +++ b/addons/hr_holidays/hr_holidays_view.xml @@ -71,7 +71,7 @@
- + @@ -83,8 +83,8 @@ - - + + @@ -115,7 +115,7 @@ - + @@ -181,8 +181,8 @@ - - + + From 9784142fe7bc141f9695dbf0d6d4ed7d18c08a67 Mon Sep 17 00:00:00 2001 From: mtr Date: Tue, 5 Jul 2011 14:45:25 +0530 Subject: [PATCH 035/896] [IMP] hr_holidays: removed condition checked for context and context passed in xml bzr revid: mtr@mtr-20110705091525-hm0772szzv6xa3zc --- addons/hr_holidays/hr_holidays.py | 10 ---------- addons/hr_holidays/hr_holidays_view.xml | 12 ++++++------ 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index e2cf2a5bace..2fcd594bee0 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -190,8 +190,6 @@ class hr_holidays(osv.osv): def onchange_date_from(self, cr, uid, ids, date_to, date_from, context=None): result = {} - if context is None: - context = {} if date_to and date_from: diff_day = self._get_number_of_days(date_from, date_to, context=context) result['value'] = { @@ -226,8 +224,6 @@ class hr_holidays(osv.osv): return True def holidays_validate(self, cr, uid, ids, context=None): - if context is None: - context = {} self.check_holidays(cr, uid, ids, context=context) obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) @@ -235,8 +231,6 @@ class hr_holidays(osv.osv): return self.write(cr, uid, ids, {'state':'validate1', 'manager_id': manager}) def holidays_validate2(self, cr, uid, ids, context=None): - if context is None: - context = {} self.check_holidays(cr, uid, ids, context=context) obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) @@ -283,14 +277,10 @@ class hr_holidays(osv.osv): return True def holidays_confirm(self, cr, uid, ids, context=None): - if context is None: - context = {} self.check_holidays(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state':'confirm'}) def holidays_refuse(self, cr, uid, ids, context=None): - if context is None: - context = {} obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) manager = ids2 and ids2[0] or False diff --git a/addons/hr_holidays/hr_holidays_view.xml b/addons/hr_holidays/hr_holidays_view.xml index 45f8aa8c3f5..608fda554fb 100644 --- a/addons/hr_holidays/hr_holidays_view.xml +++ b/addons/hr_holidays/hr_holidays_view.xml @@ -71,7 +71,7 @@ - + @@ -83,8 +83,8 @@ - - + + @@ -115,7 +115,7 @@ - + @@ -181,8 +181,8 @@ - - + + From 14daa261888a6ee92db0bbd24fefee9a6a15270c Mon Sep 17 00:00:00 2001 From: "Ujjvala Collins (OpenERP)" Date: Tue, 5 Jul 2011 18:41:02 +0530 Subject: [PATCH 036/896] [REF] sale: Refactored the code, optimization and fixed according to coding standards. bzr revid: uco@tinyerp.com-20110705131102-0r9etxoro001iem4 --- addons/sale/sale.py | 55 ++++++++++++++----------- addons/sale/sale_view.xml | 2 +- addons/sale_layout/sale_layout_view.xml | 2 +- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 398ad00b467..85e507bddd3 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1011,53 +1011,60 @@ class sale_order_line(osv.osv): default.update({'state': 'draft', 'move_ids': [], 'invoiced': False, 'invoice_lines': []}) return super(sale_order_line, self).copy_data(cr, uid, id, default, context=context) - def onchange_product_packaging(self, cr, uid, ids, pricelist, product, qty, uom, packaging, flag): + def product_packaging_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, + partner_id=False, packaging=False, flag=False, context=None): product_obj = self.pool.get('product.product') product_uom_obj = self.pool.get('product.uom') + pack_obj = self.pool.get('product.packaging') warning = {} result = {} warning_msgs = '' if not product: return {'value': {'product_packaging': False}} - products = product_obj.browse(cr, uid, product) - if not packaging and products.packaging and not flag: + + if flag: + res = self.product_id_change(cr, uid, ids, pricelist=pricelist, + product=product, qty=qty, uom=uom, partner_id=partner_id, + packaging=packaging, flag=False) + warning_msgs = res.get('warning') and res['warning']['message'] + + products = product_obj.browse(cr, uid, product, context=context) + if not products.packaging: + packaging = result['product_packaging'] = False + elif not packaging and products.packaging and not flag: packaging = products.packaging[0].id result['product_packaging'] = packaging - elif not products.packaging: - packaging = False - result['product_packaging'] = False - + if packaging: default_uom = products.uom_id and products.uom_id.id - pack = self.pool.get('product.packaging').browse(cr, uid, packaging) + pack = pack_obj.browse(cr, uid, packaging, context=context) q = product_uom_obj._compute_qty(cr, uid, uom, pack.qty, default_uom) # qty = qty - qty % q + q if qty and (q and not (qty % q) == 0): ean = pack.ean or _('(n/a)') qty_pack = pack.qty type_ul = pack.ul - warn_msg = _("You selected a quantity of %d Units.\n" - "But it's not compatible with the selected packaging.\n" - "Here is a proposition of quantities according to the packaging:\n" - "EAN: %s Quantity: %s Type of ul: %s") % \ - (qty, ean, qty_pack, type_ul.name) - warning_msgs += _("Picking Information ! : ") + warn_msg + "\n\n" - result['product_uom_qty'] = qty - - if warning_msgs: - warning = { + if not warning_msgs: + warn_msg = _("You selected a quantity of %d Units.\n" + "But it's not compatible with the selected packaging.\n" + "Here is a proposition of quantities according to the packaging:\n" + "EAN: %s Quantity: %s Type of ul: %s") % \ + (qty, ean, qty_pack, type_ul.name) + warning_msgs += _("Picking Information ! : ") + warn_msg + "\n\n" + warning = { 'title': _('Configuration Error !'), - 'message' : warning_msgs - } + 'message': warning_msgs + } + result['product_uom_qty'] = qty return {'value': result, 'warning': warning} def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False): - if not partner_id: + if not partner_id: raise osv.except_osv(_('No Customer Defined !'), _('You have to select a customer in the sales form !\nPlease set one customer before choosing a product.')) - warning_msgs = '' + warning = {} product_uom_obj = self.pool.get('product.uom') partner_obj = self.pool.get('res.partner') product_obj = self.pool.get('product.product') @@ -1072,9 +1079,9 @@ class sale_order_line(osv.osv): if not date_order: date_order = time.strftime('%Y-%m-%d') - res = self.onchange_product_packaging(cr, uid, ids, pricelist, product, qty, uom, packaging, False) + res = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging) result = res.get('value', {}) - warning = res.get('warning', {}) + warning_msgs = res.get('warning') and res['warning']['message'] or '' product_obj = product_obj.browse(cr, uid, product, context=context) uom2 = False diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 0b17b5ca71c..34e7dc292bc 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -142,7 +142,7 @@ diff --git a/addons/sale_layout/sale_layout_view.xml b/addons/sale_layout/sale_layout_view.xml index 25c56a03ae6..12d07432861 100644 --- a/addons/sale_layout/sale_layout_view.xml +++ b/addons/sale_layout/sale_layout_view.xml @@ -38,7 +38,7 @@ name="product_packaging" attrs="{'readonly':[('layout_type','!=','article')]}" context="{'partner_id':parent.partner_id,'quantity':product_uom_qty,'pricelist':parent.pricelist_id,'shop':parent.shop_id,'uom':product_uom}" - on_change="onchange_product_packaging(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_packaging, True)" + on_change="product_packaging_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, parent.partner_id, product_packaging, True)" domain="[('product_id','=',product_id)]" groups="base.group_extended"/> From 867aa1a56f113da5748a0b44d0dff0a94cefc66b Mon Sep 17 00:00:00 2001 From: Mustufa Rangwala Date: Wed, 6 Jul 2011 14:59:17 +0530 Subject: [PATCH 037/896] [IMP] sale: onchange package minor improvement bzr revid: mra@mra-laptop-20110706092917-44yznbfqyeklrzdn --- addons/sale/sale.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 85e507bddd3..057ed530218 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1010,31 +1010,30 @@ class sale_order_line(osv.osv): default = {} default.update({'state': 'draft', 'move_ids': [], 'invoiced': False, 'invoice_lines': []}) return super(sale_order_line, self).copy_data(cr, uid, id, default, context=context) - - def product_packaging_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, + + def product_packaging_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, partner_id=False, packaging=False, flag=False, context=None): + if not product: + return {'value': {'product_packaging': False}} product_obj = self.pool.get('product.product') product_uom_obj = self.pool.get('product.uom') pack_obj = self.pool.get('product.packaging') warning = {} result = {} warning_msgs = '' - if not product: - return {'value': {'product_packaging': False}} - if flag: - res = self.product_id_change(cr, uid, ids, pricelist=pricelist, + res = self.product_id_change(cr, uid, ids, pricelist=pricelist, product=product, qty=qty, uom=uom, partner_id=partner_id, packaging=packaging, flag=False) warning_msgs = res.get('warning') and res['warning']['message'] - + products = product_obj.browse(cr, uid, product, context=context) if not products.packaging: packaging = result['product_packaging'] = False elif not packaging and products.packaging and not flag: packaging = products.packaging[0].id result['product_packaging'] = packaging - + if packaging: default_uom = products.uom_id and products.uom_id.id pack = pack_obj.browse(cr, uid, packaging, context=context) From b1dc5544d244fbfea6f535e49b30b0c717012665 Mon Sep 17 00:00:00 2001 From: Mustufa Rangwala Date: Wed, 6 Jul 2011 15:05:40 +0530 Subject: [PATCH 038/896] [IMP] sale: onchange package minor improvement bzr revid: mra@mra-laptop-20110706093540-ncsl94dqhd1n4c3m --- addons/sale/sale.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 057ed530218..555b83d4616 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -105,11 +105,11 @@ class sale_order(osv.osv): procurement_order mp on (mp.move_id=m.id) WHERE p.sale_id IN %s GROUP BY m.state, mp.state, p.sale_id, p.type''', (tuple(ids),)) - + for item in cr.dictfetchall(): if item['move_state'] == 'cancel': continue - + if item['picking_type'] == 'in':#this is a returned picking tmp[item['sale_order_id']]['total'] -= item['nbr'] or 0.0 # Deducting the return picking qty if item['procurement_state'] == 'done' or item['move_state'] == 'done': @@ -118,7 +118,7 @@ class sale_order(osv.osv): tmp[item['sale_order_id']]['total'] += item['nbr'] or 0.0 if item['procurement_state'] == 'done' or item['move_state'] == 'done': tmp[item['sale_order_id']]['picked'] += item['nbr'] or 0.0 - + for order in self.browse(cr, uid, ids, context=context): if order.shipped: res[order.id] = 100.0 @@ -710,7 +710,7 @@ class sale_order(osv.osv): 'note': line.notes, 'company_id': order.company_id.id, }) - + if line.product_id: proc_id = self.pool.get('procurement.order').create(cr, uid, { 'name': line.name, @@ -1078,7 +1078,7 @@ class sale_order_line(osv.osv): if not date_order: date_order = time.strftime('%Y-%m-%d') - res = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging) + res = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging, context=context) result = res.get('value', {}) warning_msgs = res.get('warning') and res['warning']['message'] or '' product_obj = product_obj.browse(cr, uid, product, context=context) @@ -1147,7 +1147,7 @@ class sale_order_line(osv.osv): max(0,product_obj.qty_available), product_obj.uom_id.name) warning_msgs += _("Not enough stock ! : ") + warn_msg + "\n\n" # get unit price - + if not pricelist: warn_msg = _('You have to select a pricelist or a customer in the sales form !\n' 'Please set one before choosing a product.') @@ -1168,7 +1168,7 @@ class sale_order_line(osv.osv): if warning_msgs: warning = { 'title': _('Configuration Error !'), - 'message' : warning_msgs + 'message' : warning_msgs } return {'value': result, 'domain': domain, 'warning': warning} @@ -1211,7 +1211,7 @@ class sale_config_picking_policy(osv.osv_memory): ('picking', 'Invoice Based on Deliveries'), ], 'Shipping Default Policy', required=True, help="""The Shipping Policy is used to synchronise invoice and delivery operations. - - The "Invoice Based on Sales Orders" option will create the picking order directly and wait for the user to manually click on the 'Invoice' button to generate the draft invoice. + - The "Invoice Based on Sales Orders" option will create the picking order directly and wait for the user to manually click on the 'Invoice' button to generate the draft invoice. - The "Invoice Based on Deliveries" option is used to create an invoice during the picking process."""), 'step': fields.selection([ ('one', 'Delivery Order Only'), From 7044314fbc63e81d1716e2b428f6292b78052069 Mon Sep 17 00:00:00 2001 From: "Ujjvala Collins (OpenERP)" Date: Thu, 7 Jul 2011 14:16:02 +0530 Subject: [PATCH 039/896] [IMP] account: Improved reports to print translated terms correctly for filters. bzr revid: uco@tinyerp.com-20110707084602-vur1w0pnadrfgktt --- addons/account/report/account_balance.rml | 8 ++++---- addons/account/report/account_balance_sheet.rml | 8 ++++---- .../report/account_balance_sheet_horizontal.rml | 8 ++++---- addons/account/report/account_central_journal.rml | 8 ++++---- addons/account/report/account_general_journal.rml | 8 ++++---- addons/account/report/account_general_ledger.rml | 12 ++++++------ .../report/account_general_ledger_landscape.rml | 12 ++++++------ addons/account/report/account_journal.rml | 6 +++--- addons/account/report/account_partner_balance.rml | 8 ++++---- addons/account/report/account_partner_ledger.rml | 8 ++++---- .../account/report/account_partner_ledger_other.rml | 8 ++++---- addons/account/report/account_profit_horizontal.rml | 8 ++++---- addons/account/report/account_profit_loss.rml | 8 ++++---- 13 files changed, 55 insertions(+), 55 deletions(-) diff --git a/addons/account/report/account_balance.rml b/addons/account/report/account_balance.rml index 3d971862bdc..62eecf548b7 100644 --- a/addons/account/report/account_balance.rml +++ b/addons/account/report/account_balance.rml @@ -220,7 +220,7 @@ Display Account - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Target Moves @@ -234,8 +234,8 @@ [[ get_fiscalyear(data) or '' ]] [[ (data['form']['display_account']=='all' and 'All') or (data['form']['display_account']=='movement' and 'With movements') or 'With balance is not equal to 0']] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date @@ -253,7 +253,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period diff --git a/addons/account/report/account_balance_sheet.rml b/addons/account/report/account_balance_sheet.rml index 205bfff497b..d6acc0348f0 100644 --- a/addons/account/report/account_balance_sheet.rml +++ b/addons/account/report/account_balance_sheet.rml @@ -174,15 +174,15 @@ Chart of Account Fiscal Year - Filter By [[ get_filter(data)!='No Filter' and '' or removeParentNode('font') ]] + Filter By [[ data['form']['filter']!='filter_no' and '' or removeParentNode('font') ]] Display Account Target Moves [[ get_account(data) or removeParentNode('para') ]] [[ get_fiscalyear(data) or '' ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -192,7 +192,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_balance_sheet_horizontal.rml b/addons/account/report/account_balance_sheet_horizontal.rml index 7e4f9f844a5..d34a0f053d8 100644 --- a/addons/account/report/account_balance_sheet_horizontal.rml +++ b/addons/account/report/account_balance_sheet_horizontal.rml @@ -134,15 +134,15 @@ Chart of Account Fiscal Year - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Display Account Target Moves [[ get_account(data) or removeParentNode('para') ]] [[ get_fiscalyear(data) or '' ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -152,7 +152,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_central_journal.rml b/addons/account/report/account_central_journal.rml index 33566083652..956e2e2b6a5 100644 --- a/addons/account/report/account_central_journal.rml +++ b/addons/account/report/account_central_journal.rml @@ -239,15 +239,15 @@ [[ data['model']=='ir.ui.menu' and 'Chart of Account' or removeParentNode('para') ]] Fiscal Year Journal - Filter By [[get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Target Moves [[ get_account(data) or removeParentNode('para') ]] [[ get_fiscalyear(data) or '' ]] [[o.journal_id.name ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -257,7 +257,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_general_journal.rml b/addons/account/report/account_general_journal.rml index f4cc13069c8..5123d7ee19b 100644 --- a/addons/account/report/account_general_journal.rml +++ b/addons/account/report/account_general_journal.rml @@ -224,15 +224,15 @@ [[ data['model']=='ir.ui.menu' and 'Chart of Account' or removeParentNode('para') ]] Fiscal Year Journals - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Target Moves [[ get_account(data) or removeParentNode('para') ]] [[ get_fiscalyear(data) or '' ]] [[', '.join([ lt or '' for lt in get_journal(data) ]) ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -242,7 +242,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_general_ledger.rml b/addons/account/report/account_general_ledger.rml index 15614444822..14c4ce59371 100644 --- a/addons/account/report/account_general_ledger.rml +++ b/addons/account/report/account_general_ledger.rml @@ -353,7 +353,7 @@ Journal - Filters By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filters By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Target Moves @@ -372,8 +372,8 @@ [[', '.join([ lt or '' for lt in get_journal(data) ]) ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date @@ -383,7 +383,7 @@ - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] [[ formatLang(get_start_date(data),date=True) ]] @@ -393,7 +393,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period @@ -403,7 +403,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] [[ get_start_period(data) or removeParentNode('para') ]] diff --git a/addons/account/report/account_general_ledger_landscape.rml b/addons/account/report/account_general_ledger_landscape.rml index ac100c3ecd0..fc2e482a3b1 100644 --- a/addons/account/report/account_general_ledger_landscape.rml +++ b/addons/account/report/account_general_ledger_landscape.rml @@ -373,7 +373,7 @@ Display Account - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Entries Sorted By @@ -398,8 +398,8 @@ [[ (data['form']['display_account']=='all' and 'All') or (data['form']['display_account']=='movement' and 'With movements') or 'With balance is not equal to 0']] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date @@ -409,7 +409,7 @@ - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] [[ formatLang(get_start_date(data),date=True) ]] @@ -419,7 +419,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period @@ -429,7 +429,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] [[ get_start_period(data) or removeParentNode('para') ]] diff --git a/addons/account/report/account_journal.rml b/addons/account/report/account_journal.rml index 9b0ac55bb56..c5c22800cbd 100644 --- a/addons/account/report/account_journal.rml +++ b/addons/account/report/account_journal.rml @@ -197,8 +197,8 @@ [[ get_account(data) or '' ]] [[ get_fiscalyear(data) or '' ]] [[o.journal_id.name ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -208,7 +208,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_partner_balance.rml b/addons/account/report/account_partner_balance.rml index 6425d6a4db6..f913a07df7a 100644 --- a/addons/account/report/account_partner_balance.rml +++ b/addons/account/report/account_partner_balance.rml @@ -187,7 +187,7 @@ Chart of Account Fiscal Year Journals - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Partner's Target Moves @@ -196,8 +196,8 @@ [[ get_fiscalyear(data) or '' ]] [[', '.join([ lt or '' for lt in get_journal(data) ]) ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -207,7 +207,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_partner_ledger.rml b/addons/account/report/account_partner_ledger.rml index c1ef9b22502..3af9c247d42 100644 --- a/addons/account/report/account_partner_ledger.rml +++ b/addons/account/report/account_partner_ledger.rml @@ -378,7 +378,7 @@ [[ data['form']['filter'] in ('filter_no','unreconciled') and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date @@ -388,7 +388,7 @@ - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] [[ formatLang(get_start_date(data),date=True) ]] @@ -398,7 +398,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period @@ -408,7 +408,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] [[ get_start_period(data) or removeParentNode('para') ]] diff --git a/addons/account/report/account_partner_ledger_other.rml b/addons/account/report/account_partner_ledger_other.rml index 2f58fc8ec95..1a19844c180 100644 --- a/addons/account/report/account_partner_ledger_other.rml +++ b/addons/account/report/account_partner_ledger_other.rml @@ -379,7 +379,7 @@ [[ data['form']['filter'] in ('filter_no','unreconciled') and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date @@ -389,7 +389,7 @@ - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] [[ formatLang(get_start_date(data),date=True) ]] @@ -399,7 +399,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period @@ -409,7 +409,7 @@ - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] [[ get_start_period(data) or removeParentNode('para') ]] diff --git a/addons/account/report/account_profit_horizontal.rml b/addons/account/report/account_profit_horizontal.rml index 6c70bb47310..ea03e8f3a9f 100644 --- a/addons/account/report/account_profit_horizontal.rml +++ b/addons/account/report/account_profit_horizontal.rml @@ -151,15 +151,15 @@ Chart of Account Fiscal Year - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Display Account Target Moves [[ get_account(data) or removeParentNode('para') ]] [[ get_fiscalyear(data) or '' ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -169,7 +169,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period diff --git a/addons/account/report/account_profit_loss.rml b/addons/account/report/account_profit_loss.rml index 2b29f479478..e31da90e46e 100644 --- a/addons/account/report/account_profit_loss.rml +++ b/addons/account/report/account_profit_loss.rml @@ -174,15 +174,15 @@ Chart of Account Fiscal Year - Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]] + Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]] Display Account Target Moves [[ get_account(data) or removeParentNode('para') ]] [[ get_fiscalyear(data) or '' ]] - [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] - [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]] + [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]] Start Date End Date @@ -192,7 +192,7 @@ [[ formatLang(get_end_date(data),date=True) ]] - [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]] + [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]] Start Period End Period From c1e2afebe8f3eea7f08dd39dfb1840f0bb867cef Mon Sep 17 00:00:00 2001 From: "tfr@openerp.com" <> Date: Fri, 8 Jul 2011 14:06:28 +0200 Subject: [PATCH 040/896] [FIX] cannot open from of task when project caldav is installed bzr revid: tfr@openerp.com-20110708120628-i6eyl0kdpkgt2ara --- addons/project_caldav/project_caldav.py | 2 +- addons/project_caldav/project_caldav_view.xml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/addons/project_caldav/project_caldav.py b/addons/project_caldav/project_caldav.py index ada89d35e69..c0f780dfc57 100644 --- a/addons/project_caldav/project_caldav.py +++ b/addons/project_caldav/project_caldav.py @@ -42,11 +42,11 @@ class project_task(osv.osv): help='If the task is created the state is \'Draft\'.\n If the task is started, the state becomes \'In Progress\'.\n If review is needed the task is in \'Pending\' state.\ \n If the task is over, the states is set to \'Done\'.'), } + _defaults = { 'state': 'draft', } - def open_task(self, cr, uid, ids, context=None): """ Open Task Form for Project Task. diff --git a/addons/project_caldav/project_caldav_view.xml b/addons/project_caldav/project_caldav_view.xml index d27a4474fc9..f361dc9580b 100644 --- a/addons/project_caldav/project_caldav_view.xml +++ b/addons/project_caldav/project_caldav_view.xml @@ -66,8 +66,7 @@ - + From 461e41910255a1366270cd8e54d263b267c299de Mon Sep 17 00:00:00 2001 From: "tfr@openerp.com" <> Date: Fri, 8 Jul 2011 15:12:03 +0200 Subject: [PATCH 041/896] [FIX] event.todo don't erase project.task column anymore bzr revid: tfr@openerp.com-20110708131203-o9cduv00he161ahl --- addons/project_caldav/project_caldav.py | 2 +- addons/project_caldav/project_caldav_view.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/project_caldav/project_caldav.py b/addons/project_caldav/project_caldav.py index c0f780dfc57..f17ef3050d4 100644 --- a/addons/project_caldav/project_caldav.py +++ b/addons/project_caldav/project_caldav.py @@ -28,7 +28,7 @@ from project.project import task as base_project_task class project_task(osv.osv): _name = "project.task" - _inherit = ["calendar.todo", "project.task"] + _inherit = ["project.task", "calendar.todo"] _columns = { # force inherit from project.project_task so that # calendar.todo.active is masked oute diff --git a/addons/project_caldav/project_caldav_view.xml b/addons/project_caldav/project_caldav_view.xml index f361dc9580b..57e9e75a53b 100644 --- a/addons/project_caldav/project_caldav_view.xml +++ b/addons/project_caldav/project_caldav_view.xml @@ -66,7 +66,7 @@ - + From a2dcc919cea8e2bce788ab656c4d6674f2a67490 Mon Sep 17 00:00:00 2001 From: "tfr@openerp.com" <> Date: Fri, 8 Jul 2011 15:35:02 +0200 Subject: [PATCH 042/896] [CLEAN] clean recurrent option view in task, but deactivate reccurency for task in openerp since it's not well implemented bzr revid: tfr@openerp.com-20110708133502-gqdovbyhm717xdgx --- addons/project_caldav/project_caldav_view.xml | 64 +++++++++++-------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/addons/project_caldav/project_caldav_view.xml b/addons/project_caldav/project_caldav_view.xml index 57e9e75a53b..0d566be4619 100644 --- a/addons/project_caldav/project_caldav_view.xml +++ b/addons/project_caldav/project_caldav_view.xml @@ -65,7 +65,8 @@ form - + + @@ -78,46 +79,57 @@ form - - - - - - - - - - - + + + + + + + + + + - + + - - + + - + + - + - - + + - - - - - - + + + + From 7c75f19da5e025fd7971d10c233c1c5a45037158 Mon Sep 17 00:00:00 2001 From: "tfr@openerp.com" <> Date: Fri, 8 Jul 2011 17:35:07 +0200 Subject: [PATCH 043/896] [FIX] creation of todo bzr revid: tfr@openerp.com-20110708153507-0old9j33jus9grmc --- addons/base_calendar/base_calendar.py | 3 +-- addons/caldav/caldav_node.py | 2 +- addons/project_caldav/project_caldav.py | 2 ++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 5f24b08884a..e50bb52abd9 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -1208,7 +1208,7 @@ rule or repeating pattern of time to exclude from the recurring rule."), def remove_virtual_id(self, ids): - if isinstance(ids, (str, int)): + if isinstance(ids, (str, int, long)): return base_calendar_id2real_id(ids) if isinstance(ids, (list, tuple)): @@ -1239,7 +1239,6 @@ rule or repeating pattern of time to exclude from the recurring rule."), if until_date: continue until_date = arg[2] - res = super(calendar_event, self).search(cr, uid, args_without_date, \ 0, 0, order, context, count=False) res = self.get_recurrent_ids(cr, uid, res, start_date, until_date, limit, context=context) diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py index 5272d0fc1fd..71e9bdc2468 100644 --- a/addons/caldav/caldav_node.py +++ b/addons/caldav/caldav_node.py @@ -324,7 +324,6 @@ class node_calendar(nodes.node_class): uid = self.context.uid res = self.set_data(cr, data) - if res and len(res): # We arbitrarily construct only the first node of the data # that have been imported. ICS may have had more elements, @@ -332,6 +331,7 @@ class node_calendar(nodes.node_class): assert isinstance(res[0], (int, long)) fnodes = fil_obj.get_calendar_objects(cr, uid, [self.calendar_id], self, domain=[('id','=',res[0])], context=ctx) + if self.context.get('DAV-client','') in ('iPhone', 'iCalendar',): # For those buggy clients, register the alias bca_obj = fil_obj.pool.get('basic.calendar.alias') diff --git a/addons/project_caldav/project_caldav.py b/addons/project_caldav/project_caldav.py index f17ef3050d4..782f167d69b 100644 --- a/addons/project_caldav/project_caldav.py +++ b/addons/project_caldav/project_caldav.py @@ -109,6 +109,8 @@ class project_task(osv.osv): self.write(cr, uid, [exists], val) ids.append(exists) else: + #set user_id with id, needed later + val.update({'user_id' : uid}) task_id = self.create(cr, uid, val) ids.append(task_id) return ids From 70b65e0922b75901d594ac782b95464d0f0affa1 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Mon, 11 Jul 2011 13:30:17 +0530 Subject: [PATCH 044/896] [IMP+FIX] edi_service: fixed wrong method call to get_db into import_edi_url service and return list of tuple (mobel,record_id) from load_edi function. it will be useful to redirect document after import successful bzr revid: hmo@tinyerp.com-20110711080017-chgth42d2409o81m --- openerp/addons/base/ir/ir_edi.py | 8 ++++---- openerp/service/web_services.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index 0ee29e13a3a..b37abbb7747 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -122,13 +122,14 @@ class ir_edi_document(osv.osv): :param edi_dicts: list of edi_dict """ - + res = [] for edi_document in edi_documents: model = edi_document.get('__model') assert model, _('model should be provided in EDI Dict') model_obj = self.pool.get(model) - model_obj.edi_import(cr, uid, edi_document, context=context) - return True + record_id = model_obj.edi_import(cr, uid, edi_document, context=context) + res.append((model,record_id)) + return res def deserialize(self, edi_document_string): """ Deserialized the edi document string @@ -175,7 +176,6 @@ class ir_edi_document(osv.osv): if edi_url and not edi_document: edi_document = urllib2.urlopen(edi_url).read() - assert edi_document, _('EDI Document should be provided') edi_documents = self.deserialize(edi_document) return self.load_edi(cr, uid, edi_documents, context=context) diff --git a/openerp/service/web_services.py b/openerp/service/web_services.py index 116eefdd1a1..ec2a3c7507e 100644 --- a/openerp/service/web_services.py +++ b/openerp/service/web_services.py @@ -69,7 +69,7 @@ class edi(netsvc.ExportService): def exp_import_edi_url(self, db, uid, passwd, edi_url, context=None): res = None - cr = pooler.get_db_only(db).cursor() + cr = pooler.get_db(db).cursor() pool = pooler.get_pool(db) edi_pool = pool.get('ir.edi.document') try: From 841cc683c6d0fb91f2dde005cc7c42658bd12442 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Mon, 11 Jul 2011 13:56:03 +0530 Subject: [PATCH 045/896] [IMP] edi: yml of edi document bzr revid: hmo@tinyerp.com-20110711082603-56r1dmtfc2h0lxgt --- openerp/addons/base/test/partner.pdf | 118 ++++++++++++++++++ .../addons/base/test/test_edi_documents.yml | 15 ++- 2 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 openerp/addons/base/test/partner.pdf diff --git a/openerp/addons/base/test/partner.pdf b/openerp/addons/base/test/partner.pdf new file mode 100644 index 00000000000..1b1c7f0daac --- /dev/null +++ b/openerp/addons/base/test/partner.pdf @@ -0,0 +1,118 @@ +%PDF-1.3 +%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com +% 'BasicFonts': class PDFDictionary +1 0 obj +% The standard fonts dictionary +<< /F1 2 0 R + /F2 3 0 R + /F3 4 0 R >> +endobj +% 'F1': class PDFType1Font +2 0 obj +% Font Helvetica +<< /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font >> +endobj +% 'F2': class PDFType1Font +3 0 obj +% Font Helvetica-Bold +<< /BaseFont /Helvetica-Bold + /Encoding /WinAnsiEncoding + /Name /F2 + /Subtype /Type1 + /Type /Font >> +endobj +% 'F3': class PDFType1Font +4 0 obj +% Font Times-Roman +<< /BaseFont /Times-Roman + /Encoding /WinAnsiEncoding + /Name /F3 + /Subtype /Type1 + /Type /Font >> +endobj +% 'Page1': class PDFPage +5 0 obj +% Page dictionary +<< /Contents 9 0 R + /MediaBox [ 0 + 0 + 841.8898 + 595.2756 ] + /Parent 8 0 R + /Resources << /Font 1 0 R + /ProcSet [ /PDF + /Text + /ImageB + /ImageC + /ImageI ] >> + /Rotate 0 + /Trans << >> + /Type /Page >> +endobj +% 'R6': class PDFCatalog +6 0 obj +% Document Root +<< /Outlines 10 0 R + /PageMode /UseNone + /Pages 8 0 R + /Type /Catalog >> +endobj +% 'R7': class PDFInfo +7 0 obj +<< /Author (Generated by OpenERP, Fabien Pinckaers) + /CreationDate (20100421145542) + /Keywords () + /Producer (ReportLab http://www.reportlab.com) + /Subject (\(unspecified\)) + /Title (Partner) >> +endobj +% 'R8': class PDFPages +8 0 obj +% page tree +<< /Count 1 + /Kids [ 5 0 R ] + /Type /Pages >> +endobj +% 'R9': class PDFStream +9 0 obj +% page stream +<< /Filter [ /ASCII85Decode + /FlateDecode ] + /Length 524 >> +stream +Gat=(?#P3ie[>S,B2ME\)S7:@oo6@nF8`I+h##OY3a-n#Qm!>5jiD.9pQ@E1#Xj$=)27)F3RmD%fbqZh,T`gJd0/,?A3`0:_VVm`"l`>oOf9p&HGF7>WCGVJoD2&_+c#BDb)/&0>>rL5@]KY$9YOAqZHTJq8k]!o+?4a5\-KbB#NYJC'`~>endstream + +endobj +% 'R10': class PDFOutlines +10 0 obj +<< /Count 0 + /Type /Outlines >> +endobj +xref +0 11 +0000000000 65535 f +0000000113 00000 n +0000000233 00000 n +0000000398 00000 n +0000000573 00000 n +0000000740 00000 n +0000001017 00000 n +0000001152 00000 n +0000001395 00000 n +0000001500 00000 n +0000002169 00000 n +trailer +<< /ID + % ReportLab generated PDF document -- digest (http://www.reportlab.com) + [(\210\344\006bE\230CUUi\010[\)Qif) (\210\344\006bE\230CUUi\010[\)Qif)] + + /Info 7 0 R + /Root 6 0 R + /Size 11 >> +startxref +2221 +%%EOF diff --git a/openerp/addons/base/test/test_edi_documents.yml b/openerp/addons/base/test/test_edi_documents.yml index 85a350bf3a9..03dbe623ede 100644 --- a/openerp/addons/base/test/test_edi_documents.yml +++ b/openerp/addons/base/test/test_edi_documents.yml @@ -1,5 +1,5 @@ - - create partner + I create partner - !record {model: res.partner, id: res_partner_test33}: name: Ivan @@ -17,7 +17,7 @@ company_id: base.main_company - - I am creating one Document to be stored in ". + I am creating one Document to be attached in partner ". - !record {model: 'ir.attachment', id: document_4}: name : "My partner document" @@ -25,7 +25,7 @@ type : binary - - Attach a pdf file in "My partner document" + I attach a pdf file in "My partner document" - !python {model: 'ir.attachment'}: | import addons @@ -34,15 +34,14 @@ self.write(cr, uid, [ref("document_4")], {'res_id':ref("res_partner_test33"), 'datas_fname': 'partner.pdf', 'datas' : base64.encodestring(pdf_file.read())}, context) - - Testing import and export + I am Tesing of EDI functionality. so first export partner and than import it again. - !python {model: ir.edi.document}: | partner_obj = self.pool.get('res.partner') partners = [partner_obj.browse(cr, uid, ref("res_partner_test33"))] tokens = self.export_edi(cr, uid, partners) - for token in tokens: - document = self.get_document(cr, uid, token, context=context) - print document - a = self.import_edi(cr, uid, edi_document = document) + assert tokens, 'Token is not generated.' + document = self.get_document(cr, uid, tokens[0], context=context) + a = self.import_edi(cr, uid, edi_document = document) From aedeaadb4663a57e0ee9c45b7b372d5bae000946 Mon Sep 17 00:00:00 2001 From: "tfr@openerp.com" <> Date: Mon, 11 Jul 2011 14:20:55 +0200 Subject: [PATCH 046/896] [FIX] caldav write_date problem + attendees synchronization bzr revid: tfr@openerp.com-20110711122055-63syh4xflmko9ajb --- addons/caldav/calendar.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index a70a31bc017..94d28e4ad37 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -121,10 +121,10 @@ def str2mailto(emailstr, multi=False): for mas in mailz: m = mege.match(mas.strip()) if not m: - # one of the rare non-matching strings is "sad" :( - # retz.append({ 'name': mas.strip() }) - # continue - raise ValueError("Invalid email address %r" % mas) + #one of the rare non-matching strings is "sad" :( + retz.append({ 'name': mas.strip() }) + continue + # raise ValueError("Invalid email address %r" % mas) rd = { 'name': m.group(1).strip(), 'email': m.group(5), } if m.group(2): @@ -189,6 +189,9 @@ def map_data(cr, uid, obj, context=None): field = obj.ical_get(map_dict, 'field') field_type = obj.ical_get(map_dict, 'type') if field: + #ignore write date, this field is resered for the orm + if field == 'write_date': + continue if field_type == 'selection': if not map_val: continue @@ -299,7 +302,6 @@ class CalDAV(object): att_data = [] exdates = [] _server_tzinfo = pytz.timezone(tools.get_server_timezone()) - for cal_data in child.getChildren(): if cal_data.name.lower() == 'organizer': dmail = { 'name': cal_data.params.get('CN', ['',])[0], @@ -552,7 +554,7 @@ class CalDAV(object): @param data_id: Get Data’s ID or False @param context: A standard dictionary for contextual values """ - + ical_data = content self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, context) parsedCal = vobject.readOne(ical_data) From 0a583564912137386a36da42c5fefb651add8ee9 Mon Sep 17 00:00:00 2001 From: "tfr@openerp.com" <> Date: Mon, 11 Jul 2011 17:51:29 +0200 Subject: [PATCH 047/896] [IMP] in progress translate rrule into openerp data bzr revid: tfr@openerp.com-20110711155129-jjhuvaby3m5eryuu --- addons/base_calendar/base_calendar.py | 80 ++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index e50bb52abd9..dfa898c633f 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -991,14 +991,80 @@ class calendar_event(osv.osv): """ result = {} + if not isinstance(ids, list): + ids = [ids] + for datas in self.read(cr, uid, ids, ['id','byday','recurrency', 'month_list','end_date', 'rrule_type', 'select1', 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'exrule', 'day', 'week_list' ], context=context): event = datas['id'] if datas.get('interval', 0) < 0: raise osv.except_osv(_('Warning!'), _('Interval can not be Negative')) if datas.get('count', 0) < 0: raise osv.except_osv(_('Warning!'), _('Count can not be Negative')) - result[event] = self.compute_rule_string(datas) + if datas['recurrency']: + result[event] = self.compute_rule_string(datas) + else: + result[event] = "" return result + + def _parse_rrule(self, rule, data): + day_list = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su'] + r = rrule.rrulestr(rule) + data['count'] = r._count + data['interval'] = r._interval + data['end_date'] = r._until and r._until.strftime("%Y-%m-%d %H:%M:%S") + #repeat weekly + if r._byweekday: + for i in xrange(0,7): + if i in r._byweekday: + data[day_list[i]] = True + data['rrule_type'] = 'weekly' + #repeat monthly bynweekday ((weekday, weeknumber), ) + if r._bynweekday: + data['week_list'] = day_list[r._bynweekday[0][0]].upper() + data['byday'] = r._bynweekday[0][1] + data['rrule_type'] = 'monthly' + + #end of recurrence + #in case of repeat for ever that we do not support right now + if not (data.get('count') or data.get('end_date')): + data['count'] = 100 + if data.get('count'): + data['end_type'] = 'count' + else: + data['end_type'] = 'end_date' + return data + + def _write_rrule(self, cr, uid, ids, field_name, field_value, arg, context): + print field_name, field_value + data = { + 'byday' : False, + 'recurrency' : False, + 'month_list' : False, + 'end_date' : False, + 'rrule_type' : False, + 'select1' : False, + 'interval' : 0, + 'count' : False, + 'end_type' : False, + 'mo' : False, + 'tu' : False, + 'we' : False, + 'th' : False, + 'fr' : False, + 'sa' : False, + 'su' : False, + 'exrule' : False, + 'day' : False, + 'week_list' : False + } + + if field_value: + update_data = self._parse_rrule(field_value, dict(data)) + print update_data + #parse_rrule + self.write(cr, uid, ids, data, context=context) + + print "rrule", self._get_rulestring(cr, uid, ids, None, arg, context) _columns = { 'id': fields.integer('ID'), @@ -1022,7 +1088,7 @@ class calendar_event(osv.osv): defines the list of date/time exceptions for a recurring calendar component."), 'exrule': fields.char('Exception Rule', size=352, help="Defines a \ rule or repeating pattern of time to exclude from the recurring rule."), - 'rrule': fields.function(_get_rulestring, type='char', size=124, \ + 'rrule': fields.function(_get_rulestring, fnct_inv=_write_rrule, type='char', size=124, \ store=True, string='Recurrent Rule'), 'rrule_type': fields.selection([('none', ''), ('daily', 'Daily'), \ ('weekly', 'Weekly'), ('monthly', 'Monthly'), \ @@ -1173,7 +1239,7 @@ rule or repeating pattern of time to exclude from the recurring rule."), @param self: the object pointer @param datas: dictionary of freq and interval value. """ - + print "compute rule string" def get_week_string(freq, datas): weekdays = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su'] if freq == 'weekly': @@ -1277,6 +1343,8 @@ rule or repeating pattern of time to exclude from the recurring rule."), except Exception: return True + + def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True): if context is None: context = {} @@ -1284,6 +1352,9 @@ rule or repeating pattern of time to exclude from the recurring rule."), select = [ids] else: select = ids + + + new_ids = [] res = False for event_id in select: @@ -1335,6 +1406,9 @@ rule or repeating pattern of time to exclude from the recurring rule."), context=context) vals.update(updated_vals.get('value', {})) if new_ids: + if 'rrule' in vals.keys(): + print "write, rrule" + print "write ", vals res = super(calendar_event, self).write(cr, uid, new_ids, vals, context=context) if ('alarm_id' in vals or 'base_calendar_alarm_id' in vals)\ From 842b67f5b095a11c2185307102eba11e2e7ceeae Mon Sep 17 00:00:00 2001 From: "tfr@openerp.com" <> Date: Tue, 12 Jul 2011 12:03:12 +0200 Subject: [PATCH 048/896] [IMP] support caldav recurrency bzr revid: tfr@openerp.com-20110712100312-lj0uib1rnx90dbhc --- addons/base_calendar/base_calendar.py | 155 +++++++++++++++----------- 1 file changed, 92 insertions(+), 63 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index dfa898c633f..1e71ea65634 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -1006,66 +1006,9 @@ class calendar_event(osv.osv): result[event] = "" return result - def _parse_rrule(self, rule, data): - day_list = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su'] - r = rrule.rrulestr(rule) - data['count'] = r._count - data['interval'] = r._interval - data['end_date'] = r._until and r._until.strftime("%Y-%m-%d %H:%M:%S") - #repeat weekly - if r._byweekday: - for i in xrange(0,7): - if i in r._byweekday: - data[day_list[i]] = True - data['rrule_type'] = 'weekly' - #repeat monthly bynweekday ((weekday, weeknumber), ) - if r._bynweekday: - data['week_list'] = day_list[r._bynweekday[0][0]].upper() - data['byday'] = r._bynweekday[0][1] - data['rrule_type'] = 'monthly' - - #end of recurrence - #in case of repeat for ever that we do not support right now - if not (data.get('count') or data.get('end_date')): - data['count'] = 100 - if data.get('count'): - data['end_type'] = 'count' - else: - data['end_type'] = 'end_date' - return data + + - def _write_rrule(self, cr, uid, ids, field_name, field_value, arg, context): - print field_name, field_value - data = { - 'byday' : False, - 'recurrency' : False, - 'month_list' : False, - 'end_date' : False, - 'rrule_type' : False, - 'select1' : False, - 'interval' : 0, - 'count' : False, - 'end_type' : False, - 'mo' : False, - 'tu' : False, - 'we' : False, - 'th' : False, - 'fr' : False, - 'sa' : False, - 'su' : False, - 'exrule' : False, - 'day' : False, - 'week_list' : False - } - - if field_value: - update_data = self._parse_rrule(field_value, dict(data)) - print update_data - #parse_rrule - self.write(cr, uid, ids, data, context=context) - - print "rrule", self._get_rulestring(cr, uid, ids, None, arg, context) - _columns = { 'id': fields.integer('ID'), 'sequence': fields.integer('Sequence'), @@ -1088,7 +1031,7 @@ class calendar_event(osv.osv): defines the list of date/time exceptions for a recurring calendar component."), 'exrule': fields.char('Exception Rule', size=352, help="Defines a \ rule or repeating pattern of time to exclude from the recurring rule."), - 'rrule': fields.function(_get_rulestring, fnct_inv=_write_rrule, type='char', size=124, \ + 'rrule': fields.function(_get_rulestring, type='char', size=124, \ store=True, string='Recurrent Rule'), 'rrule_type': fields.selection([('none', ''), ('daily', 'Daily'), \ ('weekly', 'Weekly'), ('monthly', 'Monthly'), \ @@ -1239,7 +1182,6 @@ rule or repeating pattern of time to exclude from the recurring rule."), @param self: the object pointer @param datas: dictionary of freq and interval value. """ - print "compute rule string" def get_week_string(freq, datas): weekdays = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su'] if freq == 'weekly': @@ -1271,7 +1213,85 @@ rule or repeating pattern of time to exclude from the recurring rule."), return '' interval_srting = datas.get('interval') and (';INTERVAL=' + str(datas.get('interval'))) or '' return 'FREQ=' + freq.upper() + get_week_string(freq, datas) + interval_srting + get_end_date(datas) + get_month_string(freq, datas) + + def _get_empty_rrule_data(self): + return { + 'byday' : False, + 'recurrency' : False, + 'end_date' : False, + 'rrule_type' : False, + 'select1' : False, + 'interval' : 0, + 'count' : False, + 'end_type' : False, + 'mo' : False, + 'tu' : False, + 'we' : False, + 'th' : False, + 'fr' : False, + 'sa' : False, + 'su' : False, + 'exrule' : False, + 'day' : False, + 'week_list' : False + } + def _write_rrule(self, cr, uid, ids, field_value, rule_date=False, context=None): + data = self._get_empty_rrule_data() + + if field_value: + data['recurrency'] = True + for event in self.browse(cr, uid, ids, context=context): + rdate = rule_date or event.date + update_data = self._parse_rrule(field_value, dict(data), rdate) + data.update(update_data) + #parse_rrule + self.write(cr, uid, event.id, data, context=context) + + + def _parse_rrule(self, rule, data, date_start): + day_list = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su'] + rrule_type = ['yearly', 'monthly', 'weekly', 'daily'] + r = rrule.rrulestr(rule, dtstart=datetime.strptime(date_start, "%Y-%m-%d %H:%M:%S")) + + if r._freq > 0 and r._freq < 4: + data['rrule_type'] = rrule_type[r._freq] + + data['count'] = r._count + data['interval'] = r._interval + data['end_date'] = r._until and r._until.strftime("%Y-%m-%d %H:%M:%S") + #repeat weekly + if r._byweekday: + for i in xrange(0,7): + if i in r._byweekday: + data[day_list[i]] = True + data['rrule_type'] = 'weekly' + #repeat monthly bynweekday ((weekday, weeknumber), ) + if r._bynweekday: + data['week_list'] = day_list[r._bynweekday[0][0]].upper() + data['byday'] = r._bynweekday[0][1] + data['select1'] = 'day' + data['rrule_type'] = 'monthly' + + if r._bymonthday: + data['day'] = r._bymonthday[0] + data['select1'] = 'date' + data['rrule_type'] = 'monthly' + + #yearly but for openerp it's monthly, take same information as monthly but interval is 12 times + if r._bymonth: + data['interval'] = data['interval'] * 12 + + #FIXEME handle forever case + #end of recurrence + #in case of repeat for ever that we do not support right now + if not (data.get('count') or data.get('end_date')): + data['count'] = 100 + if data.get('count'): + data['end_type'] = 'count' + else: + data['end_type'] = 'end_date' + return data def remove_virtual_id(self, ids): if isinstance(ids, (str, int, long)): @@ -1407,8 +1427,12 @@ rule or repeating pattern of time to exclude from the recurring rule."), vals.update(updated_vals.get('value', {})) if new_ids: if 'rrule' in vals.keys(): - print "write, rrule" - print "write ", vals + if 'date' in vals.keys(): + date_to_write = vals['date'] + else: + date_to_write = False + self._write_rrule(cr, uid, new_ids, vals['rrule'], date_to_write, context) + res = super(calendar_event, self).write(cr, uid, new_ids, vals, context=context) if ('alarm_id' in vals or 'base_calendar_alarm_id' in vals)\ @@ -1535,6 +1559,11 @@ rule or repeating pattern of time to exclude from the recurring rule."), if vals.get('vtimezone', '') and vals.get('vtimezone', '').startswith('/freeassociation.sourceforge.net/tzfile/'): vals['vtimezone'] = vals['vtimezone'][40:] + + if 'date' in vals and 'rrule' in vals and vals['rrule']: + update_datas = self._parse_rrule(vals['rrule'], self._get_empty_rrule_data(), vals['date']) + update_datas['recurrency'] = True + vals.update(update_datas) updated_vals = self.onchange_dates(cr, uid, [], vals.get('date', False), From cac9fb4e0322ad041acc2cdb9e9a90322b9decba Mon Sep 17 00:00:00 2001 From: "tfr@openerp.com" <> Date: Tue, 12 Jul 2011 16:02:13 +0200 Subject: [PATCH 049/896] [FIX] get hostname using tools.get_hostname bzr revid: tfr@openerp.com-20110712140213-flf80sbimzhkmekx --- addons/caldav/wizard/caldav_browse.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/addons/caldav/wizard/caldav_browse.py b/addons/caldav/wizard/caldav_browse.py index f6ecc5dccd5..49b313b9886 100644 --- a/addons/caldav/wizard/caldav_browse.py +++ b/addons/caldav/wizard/caldav_browse.py @@ -24,6 +24,7 @@ from tools import config import base64 import addons from tools.translate import _ +import tools class caldav_browse(osv.osv_memory): @@ -239,12 +240,8 @@ class user_preference(osv.osv_memory): return ids[0] def _get_default_host(self, cr, uid, context): - ids=self.search(cr,uid,[]) - host_name = '' - if ids: - ids = len(ids)> 1 and len(ids)-1 or ids[0] # Use len(ids)-1 for taking the value of last id - host_name = self.browse(cr, uid,[ids],context=context)[0].host_name - return host_name + + return tools.get_hostname(self, cr, uid) _defaults={ 'service': 'webdav', From db70049e4d4ed37c83d71f3cafc8d5722a27b6ad Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 15 Jul 2011 14:05:09 +0530 Subject: [PATCH 050/896] [ADD] base: add configuration parameter to store web.base.url bzr revid: hmo@tinyerp.com-20110715083509-j0dyh2dq1j9c9t0q --- openerp/addons/base/base_data.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openerp/addons/base/base_data.xml b/openerp/addons/base/base_data.xml index 3f8208b8ec4..5b9a1e07e7f 100644 --- a/openerp/addons/base/base_data.xml +++ b/openerp/addons/base/base_data.xml @@ -1,6 +1,10 @@ + + web.base.url + http://localhost:8002 + ir.ui.menu.tree ir.ui.menu From 045b42b03a65b6120b2457f8afd64980514fb230 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 15 Jul 2011 14:05:56 +0530 Subject: [PATCH 051/896] [FIX] base.ir_edi: value of function fields should be exported on EDI Document bzr revid: hmo@tinyerp.com-20110715083556-u60zjo42ync4d47w --- openerp/addons/base/ir/ir_edi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index b37abbb7747..208f69dabac 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -356,9 +356,9 @@ class edi(object): record = getattr(row, field) if not record: continue - if _fields[field].has_key('function') or _fields[field].has_key('related_columns'): - # Do not Export Function Fields and related fields - continue + #if _fields[field].has_key('function') or _fields[field].has_key('related_columns'): + # # Do not Export Function Fields and related fields + # continue elif cols['type'] == 'many2one': value = self.edi_m2o(cr, uid, record, context=context) elif cols['type'] == 'many2many': From 1d7eb0cdc25ce45a0d48881f5732f1b010d47ff1 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 15 Jul 2011 15:20:59 +0530 Subject: [PATCH 052/896] [FIX] service: put autocommit true on EDI import service to store imported data bzr revid: hmo@tinyerp.com-20110715095059-96rrh2pu4acevzj4 --- openerp/addons/base/ir/ir_edi.py | 3 +-- openerp/service/web_services.py | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index 208f69dabac..7347625c622 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -519,6 +519,5 @@ class edi(object): values[field] = one2many_ids else: values[field] = edi_field_value - - return model_data._update(cr, uid, self._name, edi_module, values, context=context) + return self.pool.get(self._name).create(cr, uid, values, context=context) # vim: ts=4 sts=4 sw=4 si et diff --git a/openerp/service/web_services.py b/openerp/service/web_services.py index ec2a3c7507e..58b31ede7ff 100644 --- a/openerp/service/web_services.py +++ b/openerp/service/web_services.py @@ -62,6 +62,7 @@ class edi(netsvc.ExportService): pool = pooler.get_pool(db) edi_pool = pool.get('ir.edi.document') try: + cr.autocommit(True) res = edi_pool.import_edi(cr, uid, edi_document=edi_document, context=context) finally: cr.close() @@ -73,6 +74,7 @@ class edi(netsvc.ExportService): pool = pooler.get_pool(db) edi_pool = pool.get('ir.edi.document') try: + cr.autocommit(True) res = edi_pool.import_edi(cr, uid, edi_url=edi_url, context=context) finally: cr.close() From 69259d01711f41349f251cbc610087c9a3acfc0b Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 15 Jul 2011 16:27:39 +0530 Subject: [PATCH 053/896] [FIX] base.ir_edi: call _update method of ir.model.data on import bzr revid: hmo@tinyerp.com-20110715105739-6vvwjlmghh19pjsu --- openerp/addons/base/ir/ir_edi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_edi.py b/openerp/addons/base/ir/ir_edi.py index 7347625c622..1d13b84aedc 100644 --- a/openerp/addons/base/ir/ir_edi.py +++ b/openerp/addons/base/ir/ir_edi.py @@ -519,5 +519,5 @@ class edi(object): values[field] = one2many_ids else: values[field] = edi_field_value - return self.pool.get(self._name).create(cr, uid, values, context=context) + return model_data._update(cr, uid, self._name, edi_module, values, context=context) # vim: ts=4 sts=4 sw=4 si et From e84d292ceb36d53f40aba7c0a842b4f779ea4006 Mon Sep 17 00:00:00 2001 From: "Ujjvala Collins (OpenERP)" Date: Mon, 18 Jul 2011 15:23:44 +0530 Subject: [PATCH 054/896] [IMP] sale: Improved code by adding context argument wherever required. * product_visible_discount, warning: Added remaining context argument on product_id_change method. bzr revid: uco@tinyerp.com-20110718095344-tdlsys7uueqp9kdy --- addons/product_visible_discount/product_visible_discount.py | 5 +++-- addons/sale/sale.py | 2 +- addons/sale/sale_view.xml | 2 +- addons/sale_layout/sale_layout_view.xml | 2 +- addons/warning/warning.py | 4 ++-- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/addons/product_visible_discount/product_visible_discount.py b/addons/product_visible_discount/product_visible_discount.py index c2f939c1618..2571855ec89 100644 --- a/addons/product_visible_discount/product_visible_discount.py +++ b/addons/product_visible_discount/product_visible_discount.py @@ -40,7 +40,8 @@ class sale_order_line(osv.osv): def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, - lang=False, update_tax=True,date_order=False,packaging=False,fiscal_position=False, flag=False): + lang=False, update_tax=True, date_order=False, packaging=False, + fiscal_position=False, flag=False, context=None): def get_real_price(res_dict, product_id, qty, uom, pricelist): item_obj = self.pool.get('product.pricelist.item') @@ -70,7 +71,7 @@ class sale_order_line(osv.osv): res=super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty, uom, qty_uos, uos, name, partner_id, - lang, update_tax,date_order,fiscal_position=fiscal_position,flag=flag) + lang, update_tax, date_order, fiscal_position=fiscal_position, flag=flag, context=context) context = {'lang': lang, 'partner_id': partner_id} result=res['value'] diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 06bdbf44a10..ad79506f8a8 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -1024,7 +1024,7 @@ class sale_order_line(osv.osv): if flag: res = self.product_id_change(cr, uid, ids, pricelist=pricelist, product=product, qty=qty, uom=uom, partner_id=partner_id, - packaging=packaging, flag=False) + packaging=packaging, flag=False, context=context) warning_msgs = res.get('warning') and res['warning']['message'] products = product_obj.browse(cr, uid, product, context=context) diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 72314f945da..d191cd3553f 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -142,7 +142,7 @@ diff --git a/addons/sale_layout/sale_layout_view.xml b/addons/sale_layout/sale_layout_view.xml index 407d15a5c04..c4e20b979ab 100644 --- a/addons/sale_layout/sale_layout_view.xml +++ b/addons/sale_layout/sale_layout_view.xml @@ -38,7 +38,7 @@ name="product_packaging" attrs="{'readonly':[('layout_type','!=','article')]}" context="{'partner_id':parent.partner_id,'quantity':product_uom_qty,'pricelist':parent.pricelist_id,'shop':parent.shop_id,'uom':product_uom}" - on_change="product_packaging_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, parent.partner_id, product_packaging, True)" + on_change="product_packaging_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, parent.partner_id, product_packaging, True, context)" domain="[('product_id','=',product_id)]" groups="base.group_extended"/> diff --git a/addons/warning/warning.py b/addons/warning/warning.py index ab7a5926653..e5bdf07aef3 100644 --- a/addons/warning/warning.py +++ b/addons/warning/warning.py @@ -197,7 +197,7 @@ class sale_order_line(osv.osv): def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, lang=False, update_tax=True, date_order=False, packaging=False, - fiscal_position=False, flag=False): + fiscal_position=False, flag=False, context=None): warning = {} if not product: return {'value': {'th_weight' : 0, 'product_packaging': False, @@ -218,7 +218,7 @@ class sale_order_line(osv.osv): result = super(sale_order_line, self).product_id_change( cr, uid, ids, pricelist, product, qty, uom, qty_uos, uos, name, partner_id, - lang, update_tax, date_order, packaging, fiscal_position, flag) + lang, update_tax, date_order, packaging, fiscal_position, flag, context=context) if result.get('warning',False): warning['title'] = title and title +' & '+result['warning']['title'] or result['warning']['title'] From 614477f6e92ed35b379b46a74395737b46cd7236 Mon Sep 17 00:00:00 2001 From: "Atik Agewan (OpenERP)" Date: Mon, 18 Jul 2011 19:11:25 +0530 Subject: [PATCH 055/896] [AAD]purchase_double_validation: add yml for purchase_double_validation_installer.py bzr revid: aag@tinyerp.com-20110718134125-cimkcz05krvlxrfy --- .../purchase_double_validation/__openerp__.py | 1 + .../test/purchase_double_validation_test.yml | 117 ++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100755 addons/purchase_double_validation/test/purchase_double_validation_test.yml diff --git a/addons/purchase_double_validation/__openerp__.py b/addons/purchase_double_validation/__openerp__.py index 1a10fb5c0ce..803b48a59c0 100644 --- a/addons/purchase_double_validation/__openerp__.py +++ b/addons/purchase_double_validation/__openerp__.py @@ -39,6 +39,7 @@ that exceeds minimum amount set by configuration wizard. 'purchase_double_validation_workflow.xml', 'purchase_double_validation_installer.xml' ], + 'test': ['test/purchase_double_validation_test.yml'], 'demo_xml': [], 'installable': True, 'active': False, diff --git a/addons/purchase_double_validation/test/purchase_double_validation_test.yml b/addons/purchase_double_validation/test/purchase_double_validation_test.yml new file mode 100755 index 00000000000..ecc8d08a493 --- /dev/null +++ b/addons/purchase_double_validation/test/purchase_double_validation_test.yml @@ -0,0 +1,117 @@ +- + I Create new limit amount from Configure Limit Amount for Purchase wizard. +- + !record {model: purchase.double.validation.installer, id: purchase_double_validation_installer_1}: + limit_amount: 3000 +- + Then I set the Limit Amount +- + !python {model: purchase.double.validation.installer}: | + self.execute(cr, uid, [ref("purchase_double_validation_installer_1")]) +- + In order to test the purchase double validation flow,I start by creating a new product 'MOB1' +- + !record {model: product.product, id: product_product_mob1}: + categ_id: 'product.product_category_3' + cost_method: standard + mes_type: fixed + name: MOB1 + price_margin: 2.0 + procure_method: make_to_stock + property_stock_inventory: stock.location_inventory + property_stock_procurement: stock.location_procurement + property_stock_production: stock.location_production + seller_delay: '1' + standard_price: 2000.0 + supply_method: buy + type: product + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + volume: 0.0 + warranty: 0.0 + weight: 0.0 + weight_net: 0.0 +- + In order to test the purchase double validation flow,I create a new record where "invoice_method" is From Order. +- + Test for purchase double validation in which Total > = Limit Amount. +- + I create purchase order for MOB1 for 3 quantity. +- + !record {model: purchase.order, id: purchase_order_po11}: + company_id: base.main_company + date_order: '2011-01-1' + invoice_method: order + location_id: stock.stock_location_stock + order_line: + - date_planned: '2011-01-11' + name: MOB1 + price_unit: 2000.0 + product_id: 'product_product_mob1' + product_qty: 3.0 + product_uom: product.product_uom_unit + state: draft + partner_address_id: base.res_partner_address_7 + partner_id: base.res_partner_4 + pricelist_id: purchase.list0 +- + Initially purchase order is in the draft state. +- + !assert {model: purchase.order, id: purchase_order_po11}: + - state == 'draft' +- + I confirm the purchase order for MOB1. +- + !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_po11} +- + I check that the order which was initially in the draft state has transmit to confirm state for double validation. +- + !assert {model: purchase.order, id: purchase_order_po11}: + - state == 'confirmed' +- + I Approved purchase order by Supplier for MOB1. +- + !workflow {model: purchase.order, action: purchase_approve, ref: purchase_order_po11} +- + I check that the order which was initially in the confirmed state has transmit to approved state. +- + !assert {model: purchase.order, id: purchase_order_po11}: + - state == 'approved' +- + Test for purchase double validation in which Total < Limit Amount. +- + I create purchase order for MOB1 for 1 quantity. +- + !record {model: purchase.order, id: purchase_order_po12}: + company_id: base.main_company + date_order: '2011-01-02' + invoice_method: order + location_id: stock.stock_location_stock + order_line: + - date_planned: '2011-01-12' + name: MOB1 + price_unit: 2000.0 + product_id: 'product_product_mob1' + product_qty: 1.0 + product_uom: product.product_uom_unit + state: draft + partner_address_id: base.res_partner_address_7 + partner_id: base.res_partner_4 + pricelist_id: purchase.list0 +- + Initially purchase order is in the draft state. +- + !assert {model: purchase.order, id: purchase_order_po12}: + - state == 'draft' +- + I confirm the purchase order for MOB1. +- + !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_po12} +- + I check that the order which was initially in the draft state has transmit to approved state. +- + !assert {model: purchase.order, id: purchase_order_po12}: + - state == 'approved' + + + \ No newline at end of file From 43f07464800836a8da3f3eacd13ddf8dba9e5658 Mon Sep 17 00:00:00 2001 From: Minh Tran Date: Mon, 18 Jul 2011 18:17:46 +0200 Subject: [PATCH 056/896] [imp] Added POS category bzr revid: mit@openerp.com-20110718161746-megdu9xb1jeu19g9 --- addons/point_of_sale/point_of_sale.py | 23 +++++++---- addons/point_of_sale/point_of_sale_view.xml | 46 +++++++++++++++++---- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index 90bfa975a76..62c5438389f 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -425,15 +425,12 @@ class pos_order(osv.osv): move_obj = self.pool.get('stock.move') pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out') orders = self.browse(cr, uid, ids, context=context) - partner_obj = self.pool.get('res.partner') for order in orders: - addr = order.partner_id and partner_obj.address_get(cr, uid, [order.partner_id.id], ['delivery']) or {} if not order.picking_id: new = True picking_id = picking_obj.create(cr, uid, { 'name': pick_name, 'origin': order.name, - 'address_id': addr.get('delivery',False), 'type': 'out', 'state': 'draft', 'move_type': 'direct', @@ -479,7 +476,6 @@ class pos_order(osv.osv): 'state': 'waiting', 'location_id': location_id, 'location_dest_id': stock_dest_id, - 'prodlot_id': line.prodlot_id and line.prodlot_id.id or False }, context=context) wf_service = netsvc.LocalService("workflow") @@ -650,7 +646,6 @@ class pos_order(osv.osv): 'reference': order.name, 'partner_id': order.partner_id.id, 'comment': order.note or '', - 'currency_id': order.pricelist_id.currency_id.id, # considering partner's sale pricelist's currency } inv.update(inv_ref.onchange_partner_id(cr, uid, [], 'out_invoice', order.partner_id.id)['value']) if not inv.get('account_id', None): @@ -897,13 +892,13 @@ class pos_order(osv.osv): for pos in self.browse(cr, uid, ids, context=context): create_contract_nb = False for line in pos.lines: - if line.product_id.product_type == 'MD': + if line.product_id.type == 'MD': create_contract_nb = True break if create_contract_nb: seq = sequence_obj.get(cr, uid, 'pos.user_%s' % pos.user_salesman_id.login) vals['contract_number'] = '%s-%s' % (pos.user_salesman_id.login, seq) - self.write(cr, uid, ids, vals, context=context) + return self.write(cr, uid, ids, vals, context=context) def action_paid(self, cr, uid, ids, context=None): if context is None: @@ -1149,7 +1144,6 @@ class pos_order_line(osv.osv): 'discount': fields.float('Discount (%)', digits=(16, 2)), 'order_id': fields.many2one('pos.order', 'Order Ref', ondelete='cascade'), 'create_date': fields.datetime('Creation Date', readonly=True), - 'prodlot_id': fields.many2one('stock.production.lot', 'Production Lot', help="You can specify Production lot for stock move created when you validate the pos order"), } _defaults = { @@ -1226,6 +1220,15 @@ class pos_order_line(osv.osv): pos_order_line() +class pos_category(osv.osv): + _name = 'pos.category' + _inherit = 'product.category' + _columns = { + 'parent_id': fields.many2one('pos.category','Parent Category', select=True), + 'child_id': fields.one2many('pos.category', 'parent_id', string='Child Categories'), + } +pos_category() + class product_product(osv.osv): _inherit = 'product.product' _columns = { @@ -1233,9 +1236,13 @@ class product_product(osv.osv): 'expense_pdt': fields.boolean('Product for expenses'), 'am_out': fields.boolean('Control for Output Operations'), 'disc_controle': fields.boolean('Discount Control'), + 'img': fields.binary('Pos Image, must be 50x50'), + 'pos_ok': fields.boolean('Can be POS-sold'), + 'pos_categ_id': fields.many2one('pos.category','POS Category', change_default=True, domain="[('type','=','normal')]" ,help="Select a pos category for the current product") } _defaults = { 'disc_controle': True, + 'pos_ok': True, } product_product() diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml index 1435f6cc2b6..80db7ece330 100644 --- a/addons/point_of_sale/point_of_sale_view.xml +++ b/addons/point_of_sale/point_of_sale_view.xml @@ -41,7 +41,6 @@ - @@ -89,7 +88,7 @@ - + @@ -639,11 +638,12 @@ form - - - - - + + + + + +
@@ -653,16 +653,44 @@ form - - + + + + + + + + + pos.category.tree + pos.category + tree + child_id + + + + + + + + Products by POS-Category + ir.actions.act_window + pos.category + [('parent_id','=',False)] + tree + + Here is a list of all your products classified by POS category. You can click a category to get the list of all products linked to this category or to a child of this category. + + + + product.normal.form.inherit product.product From 3e29dbf440d5d70a00391f441683a532122e370e Mon Sep 17 00:00:00 2001 From: "Atik Agewan (OpenERP)" Date: Tue, 19 Jul 2011 11:15:33 +0530 Subject: [PATCH 057/896] [AAD]purchase_requisition: add yml purchase_requisition_test.yml for cover more functionality of module bzr revid: aag@tinyerp.com-20110719054533-64qd1znwp3opsqc2 --- addons/purchase_requisition/__openerp__.py | 4 +- .../test/purchase_requisition_test.yml | 174 ++++++++++++++++++ 2 files changed, 177 insertions(+), 1 deletion(-) create mode 100755 addons/purchase_requisition/test/purchase_requisition_test.yml diff --git a/addons/purchase_requisition/__openerp__.py b/addons/purchase_requisition/__openerp__.py index 5fd224b14f9..7e15c585803 100644 --- a/addons/purchase_requisition/__openerp__.py +++ b/addons/purchase_requisition/__openerp__.py @@ -42,7 +42,9 @@ This new object will regroup and will allow you to easily keep track and order a "security/ir.model.access.csv","purchase_requisition_sequence.xml" ], "active": False, - "test":['test/purchase_requisition_report.yml'], + "test":['test/purchase_requisition_report.yml', + 'test/purchase_requisition_test.yml', + ], "installable": True, "certificate" : "001023264099823179629", } diff --git a/addons/purchase_requisition/test/purchase_requisition_test.yml b/addons/purchase_requisition/test/purchase_requisition_test.yml new file mode 100755 index 00000000000..3cc0a4bb065 --- /dev/null +++ b/addons/purchase_requisition/test/purchase_requisition_test.yml @@ -0,0 +1,174 @@ +- + In order to test the purchase requisition flow,I start by creating a new product 'MOB1' +- + !record {model: product.product, id: product_product_mob1}: + name: MOB1 + categ_id: product.product_category_3 + cost_method: standard + mes_type: fixed + price_margin: 2.0 + procure_method: make_to_stock + property_stock_inventory: stock.location_inventory + property_stock_procurement: stock.location_procurement + property_stock_production: stock.location_production + seller_delay: '1' + standard_price: 2000.0 + supply_method: buy + type: product + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + volume: 0.0 + warranty: 0.0 + weight: 0.0 + weight_net: 0.0 +- + I create purchase requisition for MOB1 for 1 quantity. +- + !record {model: purchase.requisition, id: purchase_requisition_te0}: + company_id: base.main_company + exclusive: multiple + line_ids: + - company_id: base.main_company + product_id: product_product_mob1 + product_qty: 10.0 + product_uom_id: product.product_uom_unit + name: TE00009 +- + Initially purchase requisition is in draft state. +- + !assert {model: purchase.requisition, id: purchase_requisition_te0}: + - state == 'draft' +- + I confirm the purchase requisition for MOB1. +- + !python {model: purchase.requisition}: | + self.tender_in_progress(cr, uid, [ref("purchase_requisition_te0")]) +- + I check that the purchase requisition which was initially in the draft state has transmit to In Progress after confirm it. +- + !assert {model: purchase.requisition, id: purchase_requisition_te0}: + - state == 'in_progress' +- + I Create a Purchase Order for the supplier 'Axelor'. +- + !record {model: purchase.requisition.partner, id: purchase_requisition_partner_0}: + partner_address_id: base.res_partner_address_7 + partner_id: base.res_partner_desertic_hispafuentes +- + I create Quotations For supplier 'Axelor'. +- + !python {model: purchase.requisition.partner}: | + self.create_order(cr, uid, [ref("purchase_requisition_partner_0")], {"active_model": "purchase.requisition", + "active_ids": [ref("purchase_requisition_te0")],"active_id": ref("purchase_requisition_te0"), }) +- + I confirm the purchase order of Axelor. +- + !python {model: purchase.order}: | + req_obj = self.pool.get('purchase.requisition') + ids =req_obj.search(cr, uid, [('name','=','TE00009')]) + purchase_id= self.search(cr, uid, [('requisition_id','in',ids)]) + import netsvc + wf_service = netsvc.LocalService("workflow") + if purchase_id: + wf_service.trg_validate(uid, 'purchase.order',purchase_id[0],'purchase_confirm', cr) + wf_service.trg_validate(uid, 'purchase.order',purchase_id[0],'purchase_approve', cr) +- + I check that Quotation of Axelor is Approved. +- + !python {model: purchase.order}: | + req_obj = self.pool.get('purchase.requisition') + ids =req_obj.search(cr, uid,[('name','=','TE00009')]) + purchase_id= self.search(cr, uid, [('requisition_id','in',ids)])[0] + state=self.browse(cr,uid,purchase_id).state + assert (state=='approved') +- + I click on Done button +- + !python {model: purchase.requisition}: | + self.tender_done(cr, uid, [ref("purchase_requisition_te0")],) +- + I check that after click on Done button state is in done. +- + !assert {model: purchase.requisition, id: purchase_requisition_te0}: + - state == 'done' +- + In order to test the purchase requisition flow for Requisition Type is exclusive +- + I create purchase requisition for Requisition Type is exclusive +- + !record {model: purchase.requisition, id: purchase_requisition_ex1}: + company_id: base.main_company + exclusive: exclusive + line_ids: + - company_id: base.main_company + product_id: product_product_mob1 + product_qty: 10.0 + product_uom_id: product.product_uom_unit + name: TE000010 +- + I Create a First Purchase Order for the supplier 'DistriPC'. +- + !record {model: purchase.requisition.partner, id: purchase_requisition_partner_1}: + partner_address_id: base.res_partner_address_7 + partner_id: base.res_partner_4 +- + I create Quotations For supplier 'DistriPC'. +- + !python {model: purchase.requisition.partner}: | + self.create_order(cr, uid, [ref("purchase_requisition_partner_1")], {"active_model": "purchase.requisition", + "active_ids": [ref("purchase_requisition_ex1")],"active_id": ref("purchase_requisition_ex1"), }) +- + I Create a Second Purchase Order for the supplier ASUStek. +- + !record {model: purchase.requisition.partner, id: purchase_requisition_partner_2}: + partner_address_id: base.res_partner_address_7 + partner_id: base.res_partner_asus +- + I create Quotations For supplier 'ASUStek'. +- + !python {model: purchase.requisition.partner}: | + self.create_order(cr, uid, [ref("purchase_requisition_partner_2")], {"active_model": "purchase.requisition", + "active_ids": [ref("purchase_requisition_ex1")],"active_id": ref("purchase_requisition_ex1"), }) +- + I check that Initially purchase requisition is draft state. +- + !assert {model: purchase.requisition, id: purchase_requisition_ex1}: + - state == 'draft' +- + I confirm the Second purchase order of ASUStek. +- + !python {model: purchase.order}: | + partner_id=self.pool.get('res.partner').search(cr,uid,[('name','=','ASUStek')])[0] + req_obj = self.pool.get('purchase.requisition') + ids =req_obj.search(cr, uid, [('name','=','TE000010')]) + purchase_id= self.search(cr, uid, [('partner_id','=',partner_id),('requisition_id','in',ids)]) + import netsvc + wf_service = netsvc.LocalService("workflow") + if purchase_id: + wf_service.trg_validate(uid, 'purchase.order',purchase_id[0],'purchase_confirm', cr) + wf_service.trg_validate(uid, 'purchase.order',purchase_id[0],'purchase_approve', cr) +- + I check that Quotation of ASUStek is Approved. +- + !python {model: purchase.order}: | + partner_id=self.pool.get('res.partner').search(cr,uid,[('name','=','ASUStek')])[0] + req_obj = self.pool.get('purchase.requisition') + ids =req_obj.search(cr, uid,[('name','=','TE000010')]) + purchase_id= self.search(cr, uid, [('partner_id','=',partner_id),('requisition_id','in',ids)])[0] + state=self.browse(cr,uid,purchase_id).state + assert (state=='approved') +- + I check that Quotation of DistriPC is cancelled. +- + !python {model: purchase.order}: | + partner_id=self.pool.get('res.partner').search(cr,uid,[('name','=','Distrib PC')])[0] + req_obj = self.pool.get('purchase.requisition') + ids =req_obj.search(cr, uid,[('name','=','TE000010')]) + purchase_id= self.search(cr, uid, [('partner_id','=',partner_id),('requisition_id','in',ids)])[0] + state=self.browse(cr,uid,purchase_id).state + assert (state=='cancel') +- + I check that Purchase Requisition is Done. +- + !assert {model: purchase.requisition, id: purchase_requisition_ex1}: + - state == 'done' \ No newline at end of file From c38c7d055cbb198f0696048e5b2fdac6aac12592 Mon Sep 17 00:00:00 2001 From: Manu <> Date: Tue, 19 Jul 2011 18:37:40 +0530 Subject: [PATCH 058/896] [FIX] account : improved the dates in recurring entries lp bug: https://launchpad.net/bugs/800767 fixed bzr revid: bde@tinyerp.com-20110719130740-eq87ant192zguo0n --- addons/account/account.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/account/account.py b/addons/account/account.py index 03a9b628922..64aab95ee29 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -1331,7 +1331,7 @@ class account_move(osv.osv): def _centralise(self, cr, uid, move, mode, context=None): assert mode in ('debit', 'credit'), 'Invalid Mode' #to prevent sql injection - currency_obj = self.pool.get('res.currency') + currency_obj = self.pool.get('res.currency') if context is None: context = {} @@ -1593,7 +1593,7 @@ class account_tax_code(osv.osv): (parent_ids,) + where_params) res=dict(cr.fetchall()) obj_precision = self.pool.get('decimal.precision') - res2 = {} + res2 = {} for record in self.browse(cr, uid, ids, context=context): def _rec_get(record): amount = res.get(record.id, 0.0) @@ -2120,7 +2120,7 @@ class account_model(osv.osv): period_id = period_id[0] for model in self.browse(cr, uid, ids, context=context): - entry['name'] = model.name%{'year':time.strftime('%Y'), 'month':time.strftime('%m'), 'date':time.strftime('%Y-%m')} + entry['name'] = model.name%{'year':context.get('date',time.strftime('%Y')), 'month':context.get('date',time.strftime('%m')), 'date':context.get('date',time.strftime('%d'))} move_id = account_move_obj.create(cr, uid, { 'ref': entry['name'], 'period_id': period_id, @@ -2141,7 +2141,7 @@ class account_model(osv.osv): 'analytic_account_id': analytic_account_id } - date_maturity = time.strftime('%Y-%m-%d') + date_maturity = context.get('date',time.strftime('%Y-%m-%d')) if line.date_maturity == 'partner': if not line.partner_id: raise osv.except_osv(_('Error !'), _("Maturity date of entry line generated by model line '%s' of model '%s' is based on partner payment term!" \ From e955394d4503d207c135a5476c684e2f32e2a96c Mon Sep 17 00:00:00 2001 From: "Bharat (OpenERP)" Date: Wed, 20 Jul 2011 17:09:53 +0530 Subject: [PATCH 059/896] [IMP] account : improved the context date in account model bzr revid: bde@tinyerp.com-20110720113953-h6774pxnk7gqpeu7 --- addons/account/account.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/account/account.py b/addons/account/account.py index 64aab95ee29..0364858fc58 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -21,6 +21,7 @@ import time from datetime import datetime +from time import mktime from dateutil.relativedelta import relativedelta from operator import itemgetter @@ -2120,7 +2121,10 @@ class account_model(osv.osv): period_id = period_id[0] for model in self.browse(cr, uid, ids, context=context): - entry['name'] = model.name%{'year':context.get('date',time.strftime('%Y')), 'month':context.get('date',time.strftime('%m')), 'date':context.get('date',time.strftime('%d'))} + context_date = context.get('date') + str_date = time.strptime(context_date,"%Y-%m-%d") + date = datetime.fromtimestamp(mktime(str_date)) + entry['name'] = model.name%{'year':date.strftime("%Y"), 'month':date.strftime("%m"), 'date': date.strftime("%Y-%m")} move_id = account_move_obj.create(cr, uid, { 'ref': entry['name'], 'period_id': period_id, From 6dc6b8dddc246c05afc4254bfcdde8a68bc0415e Mon Sep 17 00:00:00 2001 From: "Atik Agewan (OpenERP)" Date: Thu, 21 Jul 2011 17:28:17 +0530 Subject: [PATCH 060/896] [AAD]purchase: add yml purchase_order_merge.yml for cover more functionality of merging PO bzr revid: aag@tinyerp.com-20110721115817-io4fomevc9v8egwk --- addons/purchase/__openerp__.py | 1 + addons/purchase/test/purchase_order_merge.yml | 85 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 addons/purchase/test/purchase_order_merge.yml diff --git a/addons/purchase/__openerp__.py b/addons/purchase/__openerp__.py index 1fb49198404..d52c84ceedf 100644 --- a/addons/purchase/__openerp__.py +++ b/addons/purchase/__openerp__.py @@ -65,6 +65,7 @@ Dashboard for purchase management that includes: 'purchase_unit_test.xml', 'test/procurement_buy.yml', 'test/purchase_report.yml', + 'test/purchase_order_merge.yml' ], 'demo': ['purchase_demo.xml'], 'installable': True, diff --git a/addons/purchase/test/purchase_order_merge.yml b/addons/purchase/test/purchase_order_merge.yml new file mode 100644 index 00000000000..fab75f63037 --- /dev/null +++ b/addons/purchase/test/purchase_order_merge.yml @@ -0,0 +1,85 @@ +- + In order to test to merge two purchase order,I start by creating a new product 'Nokia3110c' +- + !record {model: product.product, id: product_product_nokia3110c}: + categ_id: 'product.product_category_3' + cost_method: standard + mes_type: fixed + name: NOKIA 3110c + price_margin: 1.0 + procure_method: make_to_order + property_stock_inventory: stock.location_inventory + property_stock_procurement: stock.location_procurement + property_stock_production: stock.location_production + seller_delay: '1' + standard_price: 5000.0 + supply_method: buy + type: product + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + volume: 0.0 + warranty: 0.0 + weight: 0.0 + weight_net: 0.0 +- + In order to test the merge purchase order flow,I create a new record where "invoice_method" is From Order. +- + I create first purchase order for Nokia3110c. +- + !record {model: purchase.order, id: purchase_order_po_nokia1}: + company_id: base.main_company + date_order: !eval time.strftime('%Y-%m-%d') + invoice_method: order + location_id: stock.stock_location_stock + order_line: + - date_planned: !eval time.strftime('%Y-%m-%d') + name: NOKIA 3110c + price_unit: 100.0 + product_id: 'product_product_nokia3110c' + product_qty: 10.0 + product_uom: product.product_uom_unit + state: draft + partner_address_id: base.res_partner_address_7 + partner_id: base.res_partner_4 + pricelist_id: purchase.list0 +- + Initially first purchase order is in the draft state. +- + !assert {model: purchase.order, id: purchase_order_po_nokia1}: + - state == 'draft' +- + I create second purchase order for Nokia3110c with same supplier,have same stock location, same pricelist as First Purchase order of Nokia3110. +- + !record {model: purchase.order, id: purchase_order_po_nokia2}: + company_id: base.main_company + date_order: !eval time.strftime('%Y-%m-%d') + invoice_method: order + location_id: stock.stock_location_stock + order_line: + - date_planned: !eval time.strftime('%Y-%m-%d') + name: NOKIA 3110c + price_unit: 100.0 + product_id: 'product_product_nokia3110c' + product_qty: 20.0 + product_uom: product.product_uom_unit + state: draft + partner_address_id: base.res_partner_address_7 + partner_id: base.res_partner_4 + pricelist_id: purchase.list0 +- + Initially second purchase order is in the draft state. +- + !assert {model: purchase.order, id: purchase_order_po_nokia2}: + - state == 'draft' +- + I have merged first and second purchase order which are in draft state, belong to the same supplier,have same stock location, same pricelist. +- + !python {model: purchase.order.group}: | + pur_obj=self.pool.get('purchase.order') + pur_id11=pur_obj.browse(cr, uid, ref("purchase_order_po_nokia1")) + pur_id1=pur_id11.id + pur_id22=pur_obj.browse(cr, uid, ref("purchase_order_po_nokia2")) + pur_id2=pur_id22.id + ids = [pur_id1,pur_id2] + self.fields_view_get(cr, uid, context={'active_ids': ids}) + self.merge_orders(cr, uid, [1], context={'active_ids': ids}) From 2f198e85766e432dcf8d628a96ca3d1d55a9bfee Mon Sep 17 00:00:00 2001 From: "ARA (OpenERP)" Date: Tue, 26 Jul 2011 12:09:59 +0530 Subject: [PATCH 061/896] [FIX] account_cancel: re-open invoice assigned to useability/no one lp bug: https://launchpad.net/bugs/807543 fixed bzr revid: ara@tinyerp.com-20110726063959-lh8o2qjc697xe1jo --- addons/account_cancel/account_cancel_view.xml | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/addons/account_cancel/account_cancel_view.xml b/addons/account_cancel/account_cancel_view.xml index 9e8b532e931..786afb9e9f0 100644 --- a/addons/account_cancel/account_cancel_view.xml +++ b/addons/account_cancel/account_cancel_view.xml @@ -12,5 +12,30 @@ + + + invoice.form.inherit + account.invoice + form + + + + +
    +
+ + +
+

Minh Tran

+ +
+ + +
+ +
+
+ + + + + + + + + + + + +
ProductPriceDisc (%)QtyTotal
+
+
+
    +
  • Subtotal: 0 €
  • +
  • Tax: 0 €
  • +
  • Total: 0 €
  • +
+
+
+
+ + + +
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+
+ + +
+ +
+ + + + + + + + + + + + + + + + + From 14528b600e2a48f8b056d9999a1e9d989bc3b56b Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Tue, 27 Sep 2011 17:47:25 +0200 Subject: [PATCH 275/896] [IMP] base_setup: add a new wizard bzr revid: stw@openerp.com-20110927154725-l2tazrlqbjhslm1j --- addons/base_setup/base_setup.py | 153 ++++++++++++++++++++++++- addons/base_setup/base_setup_views.xml | 12 ++ 2 files changed, 164 insertions(+), 1 deletion(-) diff --git a/addons/base_setup/base_setup.py b/addons/base_setup/base_setup.py index 5e0b5c7f13b..f5b07d83d53 100644 --- a/addons/base_setup/base_setup.py +++ b/addons/base_setup/base_setup.py @@ -20,15 +20,166 @@ ############################################################################## import pytz +import simplejson +import cgi import pooler import tools from osv import fields, osv from tools.translate import _ from lxml import etree -from osv import fields, osv + +class ir_module_category(osv.osv): + _inherit = 'ir.module.category' + + _columns = { + 'module_ids' : fields.one2many('ir.module.module', 'category_id', 'Modules'), + } +ir_module_category() #Application and feature chooser, this could be done by introspecting ir.modules +class base_setup_installer2(osv.osv_memory): + _name = 'base.setup.installer2' + + _inherit = 'res.config.installer' + + _columns = { + 'modules' : fields.text('Modules'), + } + + def fields_get(self, cr, uid, fields=None, context=None): + if context is None: + context = {} + if fields is None: + fields = {} + + fields = {} #super(base_setup_installer2, self).fields_get(cr, uid, fields, context=context) + category_proxy = self.pool.get('ir.module.category') + category_ids = category_proxy.search(cr, uid, [], context=context) + for category in category_proxy.browse(cr, uid, category_ids, context=context): + category_name = 'category_%d' % (category.id,) + fields[category_name] = { + 'type' : 'boolean', + 'string' : category.name, + 'name' : category_name, + } + + module_proxy = self.pool.get('ir.module.module') + module_ids = module_proxy.search(cr, uid, [], context=context) + for module in module_proxy.browse(cr, uid, module_ids, context=context): + module_name = 'module_%s' % (module.name,) + module_is_installed = module.state == 'installed' + fields[module_name] = { + 'type' : 'boolean', + 'string' : module.shortdesc, + 'name' : module_name, + 'help' : module.description, + } + + return fields + + def default_get(self, cr, uid, fields=None, context=None): + if context is None: + context = {} + if fields is None: + fields = {} + + result = {} + + if 'dont_compute_virtual_attributes' not in context: + module_proxy = self.pool.get('ir.module.module') + module_ids = module_proxy.search(cr, uid, [], context=context) + for module in module_proxy.browse(cr, uid, module_ids, context=context): + result['module_%s' % (module.name,)] = module.state == 'installed' + category_name = 'category_%d' % (module.category_id.id,) + if not result.get('category_name'): + result[category_name] = module.state == 'installed' + + return result + + def fields_view_get(self, cr, uid, view_id=None, view_type='from', context=None, toolbar=False, submenu=False): + result = super(base_setup_installer2, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu) + + module_category_proxy = self.pool.get('ir.module.category') + module_category_ids = module_category_proxy.search(cr, uid, [('name', '!=', 'Base')], context=context, order='name asc') + arch = ['
'] + arch.append('') + for module_category in module_category_proxy.browse(cr, uid, module_category_ids, context=context): + readonly = False + for module in module_category.module_ids: + if module.state == 'installed': + readonly=True + + if readonly: + arch.append("""""" % (module_category.id, simplejson.dumps({'readonly' : True}),)) + else: + arch.append('' % (module_category.id,)) + + + for module_category in module_category_proxy.browse(cr, uid, module_category_ids, context=context): + if not module_category.module_ids: + continue + + modifiers = { + 'invisible' : [('category_%d' % (module_category.id), '=', False)], + 'readonly' : module.state == 'installed', + } + modifiers = simplejson.dumps(modifiers) + arch.append("""""" % (cgi.escape(module_category.name), modifiers)) + for module in module_category.module_ids: + arch.append("""""" % (module.name, modifiers)) + + arch.append( + '' + '' + '
@@ -134,18 +134,18 @@
From b04d607ec4d005a74bd13b38b172680e21e4933b Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Mon, 3 Oct 2011 17:40:08 +0200 Subject: [PATCH 354/896] [FIX] ir_values: missing trimming of the condition (as done elsewhere). bzr revid: vmt@openerp.com-20111003154008-k8dm2r3yhintm0ql --- openerp/addons/base/ir/ir_values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_values.py b/openerp/addons/base/ir/ir_values.py index d908c2096b3..15e8f3bf3ce 100644 --- a/openerp/addons/base/ir/ir_values.py +++ b/openerp/addons/base/ir/ir_values.py @@ -233,7 +233,7 @@ class ir_values(osv.osv): # remove existing defaults for the same scope search_criteria = [ ('key', '=', 'default'), - ('key2', '=', condition), + ('key2', '=', condition and condition[:200]), ('model', '=', model), ('name', '=', field_name), ('user_id', '=', False if for_all_users else uid), From dd5f24517e6e4fea40bbeb9c3dd5dbdd05d437d5 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Mon, 3 Oct 2011 18:28:33 +0200 Subject: [PATCH 355/896] [fix] some problem with web client api modified bzr revid: nicolas.vanhoren@openerp.com-20111003162833-j911gi9au934thh6 --- .../static/lib/backbone/backbone-0.5.3.min.js | 33 ------------------- addons/point_of_sale/static/src/js/pos.js | 23 ++++++++----- addons/point_of_sale/static/src/pos.html | 2 +- 3 files changed, 16 insertions(+), 42 deletions(-) delete mode 100644 addons/point_of_sale/static/lib/backbone/backbone-0.5.3.min.js diff --git a/addons/point_of_sale/static/lib/backbone/backbone-0.5.3.min.js b/addons/point_of_sale/static/lib/backbone/backbone-0.5.3.min.js deleted file mode 100644 index 3f0d495dc39..00000000000 --- a/addons/point_of_sale/static/lib/backbone/backbone-0.5.3.min.js +++ /dev/null @@ -1,33 +0,0 @@ -// Backbone.js 0.5.3 -// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc. -// Backbone may be freely distributed under the MIT license. -// For all details and documentation: -// http://documentcloud.github.com/backbone -(function(){var h=this,p=h.Backbone,e;e=typeof exports!=="undefined"?exports:h.Backbone={};e.VERSION="0.5.3";var f=h._;if(!f&&typeof require!=="undefined")f=require("underscore")._;var g=h.jQuery||h.Zepto;e.noConflict=function(){h.Backbone=p;return this};e.emulateHTTP=!1;e.emulateJSON=!1;e.Events={bind:function(a,b,c){var d=this._callbacks||(this._callbacks={});(d[a]||(d[a]=[])).push([b,c]);return this},unbind:function(a,b){var c;if(a){if(c=this._callbacks)if(b){c=c[a];if(!c)return this;for(var d= -0,e=c.length;d/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")},has:function(a){return this.attributes[a]!=null},set:function(a,b){b||(b={});if(!a)return this;if(a.attributes)a=a.attributes;var c=this.attributes,d=this._escapedAttributes;if(!b.silent&&this.validate&&!this._performValidation(a,b))return!1;if(this.idAttribute in a)this.id=a[this.idAttribute]; -var e=this._changing;this._changing=!0;for(var g in a){var h=a[g];if(!f.isEqual(c[g],h))c[g]=h,delete d[g],this._changed=!0,b.silent||this.trigger("change:"+g,this,h,b)}!e&&!b.silent&&this._changed&&this.change(b);this._changing=!1;return this},unset:function(a,b){if(!(a in this.attributes))return this;b||(b={});var c={};c[a]=void 0;if(!b.silent&&this.validate&&!this._performValidation(c,b))return!1;delete this.attributes[a];delete this._escapedAttributes[a];a==this.idAttribute&&delete this.id;this._changed= -!0;b.silent||(this.trigger("change:"+a,this,void 0,b),this.change(b));return this},clear:function(a){a||(a={});var b,c=this.attributes,d={};for(b in c)d[b]=void 0;if(!a.silent&&this.validate&&!this._performValidation(d,a))return!1;this.attributes={};this._escapedAttributes={};this._changed=!0;if(!a.silent){for(b in c)this.trigger("change:"+b,this,void 0,a);this.change(a)}return this},fetch:function(a){a||(a={});var b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&& -c(b,d)};a.error=i(a.error,b,a);return(this.sync||e.sync).call(this,"read",this,a)},save:function(a,b){b||(b={});if(a&&!this.set(a,b))return!1;var c=this,d=b.success;b.success=function(a,e,f){if(!c.set(c.parse(a,f),b))return!1;d&&d(c,a,f)};b.error=i(b.error,c,b);var f=this.isNew()?"create":"update";return(this.sync||e.sync).call(this,f,this,b)},destroy:function(a){a||(a={});if(this.isNew())return this.trigger("destroy",this,this.collection,a);var b=this,c=a.success;a.success=function(d){b.trigger("destroy", -b,b.collection,a);c&&c(b,d)};a.error=i(a.error,b,a);return(this.sync||e.sync).call(this,"delete",this,a)},url:function(){var a=k(this.collection)||this.urlRoot||l();if(this.isNew())return a;return a+(a.charAt(a.length-1)=="/"?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this)},isNew:function(){return this.id==null},change:function(a){this.trigger("change",this,a);this._previousAttributes=f.clone(this.attributes);this._changed=!1},hasChanged:function(a){if(a)return this._previousAttributes[a]!= -this.attributes[a];return this._changed},changedAttributes:function(a){a||(a=this.attributes);var b=this._previousAttributes,c=!1,d;for(d in a)f.isEqual(b[d],a[d])||(c=c||{},c[d]=a[d]);return c},previous:function(a){if(!a||!this._previousAttributes)return null;return this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},_performValidation:function(a,b){var c=this.validate(a);if(c)return b.error?b.error(this,c,b):this.trigger("error",this,c,b),!1;return!0}}); -e.Collection=function(a,b){b||(b={});if(b.comparator)this.comparator=b.comparator;f.bindAll(this,"_onModelEvent","_removeReference");this._reset();a&&this.reset(a,{silent:!0});this.initialize.apply(this,arguments)};f.extend(e.Collection.prototype,e.Events,{model:e.Model,initialize:function(){},toJSON:function(){return this.map(function(a){return a.toJSON()})},add:function(a,b){if(f.isArray(a))for(var c=0,d=a.length;c').hide().appendTo("body")[0].contentWindow,this.navigate(a); -this._hasPushState?g(window).bind("popstate",this.checkUrl):"onhashchange"in window&&!b?g(window).bind("hashchange",this.checkUrl):setInterval(this.checkUrl,this.interval);this.fragment=a;m=!0;a=window.location;b=a.pathname==this.options.root;if(this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;else if(this._wantsPushState&&this._hasPushState&&b&&a.hash)this.fragment=a.hash.replace(j,""),window.history.replaceState({}, -document.title,a.protocol+"//"+a.host+this.options.root+this.fragment);if(!this.options.silent)return this.loadUrl()},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.iframe.location.hash));if(a==this.fragment||a==decodeURIComponent(this.fragment))return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(window.location.hash)},loadUrl:function(a){var b=this.fragment=this.getFragment(a); -return f.any(this.handlers,function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){var c=(a||"").replace(j,"");if(!(this.fragment==c||this.fragment==decodeURIComponent(c))){if(this._hasPushState){var d=window.location;c.indexOf(this.options.root)!=0&&(c=this.options.root+c);this.fragment=c;window.history.pushState({},document.title,d.protocol+"//"+d.host+c)}else if(window.location.hash=this.fragment=c,this.iframe&&c!=this.getFragment(this.iframe.location.hash))this.iframe.document.open().close(), -this.iframe.location.hash=c;b&&this.loadUrl(a)}}});e.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.delegateEvents();this.initialize.apply(this,arguments)};var u=/^(\S+)\s*(.*)$/,n=["model","collection","el","id","attributes","className","tagName"];f.extend(e.View.prototype,e.Events,{tagName:"div",$:function(a){return g(a,this.el)},initialize:function(){},render:function(){return this},remove:function(){g(this.el).remove();return this},make:function(a, -b,c){a=document.createElement(a);b&&g(a).attr(b);c&&g(a).html(c);return a},delegateEvents:function(a){if(a||(a=this.events))for(var b in f.isFunction(a)&&(a=a.call(this)),g(this.el).unbind(".delegateEvents"+this.cid),a){var c=this[a[b]];if(!c)throw Error('Event "'+a[b]+'" does not exist');var d=b.match(u),e=d[1];d=d[2];c=f.bind(c,this);e+=".delegateEvents"+this.cid;d===""?g(this.el).bind(e,c):g(this.el).delegate(d,e,c)}},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b= -0,c=n.length;b - + From ea019d556f341bf09f316bc7fb6409980dfd74cf Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Mon, 3 Oct 2011 19:13:02 +0200 Subject: [PATCH 356/896] [IMP] yaml_import: make !ref nodes work for m2o values bzr revid: odo@openerp.com-20111003171302-n1quzvs2pjyn7xr6 --- openerp/tools/yaml_import.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openerp/tools/yaml_import.py b/openerp/tools/yaml_import.py index bb4ce9db87d..91cdda1b896 100644 --- a/openerp/tools/yaml_import.py +++ b/openerp/tools/yaml_import.py @@ -340,6 +340,7 @@ class YamlInterpreter(object): return record_dict def process_ref(self, node, column=None): + assert node.search or node.id, '!ref node should have a `search` attribute or `id` attribute' if node.search: if node.model: model_name = node.model @@ -377,7 +378,10 @@ class YamlInterpreter(object): if column._type in ("many2many", "one2many"): value = [(6, 0, elements)] else: # many2one - value = self._get_first_result(elements) + if isinstance(elements, (list,tuple)): + value = self._get_first_result(elements) + else: + value = elements elif column._type == "many2one": value = self.get_id(expression) elif column._type == "one2many": From fd67e605f4f6ffb595c34e71cb697ec69fc0a000 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Mon, 3 Oct 2011 19:52:06 +0200 Subject: [PATCH 357/896] [FIX] packaging MANIFEST.in sdist includes everything bzr revid: al@openerp.com-20111003175206-afjurox08f55jafh --- MANIFEST.in | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 70715f18bc2..c864492136d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,12 +1,13 @@ +graft debian +graft doc +graft install +graft openerp +graft tests +graft win32 include README include LICENSE include MANIFEST.in -include setup.nsi -include setup.cfg -include setup_rpm.sh -recursive-include win32 *.py *.bat -recursive-include openerp *css *csv *html *png *po *pot *rml *rng *sql *sxw *xml *xsl *yml -graft install -graft debian -graft doc +include gunicorn.conf.py +include openerp-server +include setup* global-exclude *pyc *~ # Exclude possible garbage from previous graft. From 649d172a6436d5cd66f430ab1d0b0a28896ed2d0 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Mon, 3 Oct 2011 20:10:01 +0200 Subject: [PATCH 358/896] [IMP] packaging remove timestamp, use version bzr revid: al@openerp.com-20111003181001-bu4y5t8u937wb0a7 --- openerp/release.py | 1 - setup.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/openerp/release.py b/openerp/release.py index 23a764bc94c..babe0ca4806 100644 --- a/openerp/release.py +++ b/openerp/release.py @@ -36,6 +36,5 @@ url = 'http://www.openerp.com' author = 'OpenERP S.A.' author_email = 'info@openerp.com' license = 'AGPL-3' -timestamp = None # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/setup.py b/setup.py index 08c30faf737..9aabfb0a344 100755 --- a/setup.py +++ b/setup.py @@ -63,8 +63,6 @@ def py2exe_options(): return {} execfile(join(os.path.dirname(__file__), 'openerp', 'release.py')) -if timestamp: - version = version + "-" + timestamp setuptools.setup( name = 'openerp', From 0a85361db963355925be3ef41c5d06da7f7e0040 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of openerp <> Date: Tue, 4 Oct 2011 05:02:04 +0000 Subject: [PATCH 359/896] Launchpad automatic translations update. bzr revid: launchpad_translations_on_behalf_of_openerp-20111004050146-bi1cipzsk35qj5sq bzr revid: launchpad_translations_on_behalf_of_openerp-20111004050204-nabcu9izdjhcovhd --- addons/base_crypt/i18n/es_CL.po | 33 ++- addons/base_tools/i18n/es_CL.po | 32 +++ addons/board/i18n/es_CL.po | 395 ++++++++++++++++++++++++++++++ addons/stock/i18n/es_CL.po | 173 +++++++------ debian/po/es_CL.po | 15 +- openerp/addons/base/i18n/es_CL.po | 8 +- 6 files changed, 563 insertions(+), 93 deletions(-) create mode 100644 addons/base_tools/i18n/es_CL.po create mode 100644 addons/board/i18n/es_CL.po diff --git a/addons/base_crypt/i18n/es_CL.po b/addons/base_crypt/i18n/es_CL.po index fafe03b997b..98e08733973 100644 --- a/addons/base_crypt/i18n/es_CL.po +++ b/addons/base_crypt/i18n/es_CL.po @@ -8,19 +8,19 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-01-11 11:14+0000\n" -"PO-Revision-Date: 2011-06-06 18:09+0000\n" -"Last-Translator: doingit.cl \n" +"PO-Revision-Date: 2011-10-03 17:17+0000\n" +"Last-Translator: David Acevedo Toledo \n" "Language-Team: Spanish (Chile) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-09-05 05:58+0000\n" -"X-Generator: Launchpad (build 13830)\n" +"X-Launchpad-Export-Date: 2011-10-04 05:02+0000\n" +"X-Generator: Launchpad (build 14071)\n" #. module: base_crypt #: sql_constraint:res.users:0 msgid "You can not have two users with the same login !" -msgstr "" +msgstr "¡No puede tener dos usuarios con el mismo nombre!" #. module: base_crypt #: model:ir.model,name:base_crypt.model_res_users @@ -31,18 +31,19 @@ msgstr "res.users" #: constraint:res.users:0 msgid "The chosen company is not in the allowed companies for this user" msgstr "" -"La compañía seleccionada no está autorizada como compañía para este usuario" +"La compañía seleccionada no está dentro de las compañías autorizadas para " +"este usuario" #. module: base_crypt #: code:addons/base_crypt/crypt.py:132 #, python-format msgid "Please specify the password !" -msgstr "" +msgstr "¡Por favor, escriba una contraseña!" #. module: base_crypt #: model:ir.module.module,shortdesc:base_crypt.module_meta_information msgid "Base - Password Encryption" -msgstr "" +msgstr "Base - Encriptación de la Contraseña" #. module: base_crypt #: code:addons/base_crypt/crypt.py:132 @@ -70,3 +71,19 @@ msgid "" "unless you are using a secure protocol such as XML-RPCS.\n" " " msgstr "" +"Este módulo sustituye la contraseña escrita en texto plano por una " +"codificada,\n" +"previniendo que alguien pueda leer la contraseña original.\n" +"Para un usuario existente, el sustitución de la contraseña en texto plano se " +"realiza la primera vez\n" +"que el usuario se conecte después de instalar base_crypt.\n" +"Después de instalar este módulo los usuarios no podrán recuperar su " +"contraseña olvidada,\n" +"un administrador tendrá que cambiarla por una nueva.\n" +"\n" +"Nota: instalar este módulo no significa que pueda ignorar las medidas " +"básicas de seguridad,\n" +"como la contraseña que es enviada por el cliente que sigue sin ser " +"codificada en la red,\n" +"a menos que utilice un protocolo seguro como XML-RPCS.\n" +" " diff --git a/addons/base_tools/i18n/es_CL.po b/addons/base_tools/i18n/es_CL.po new file mode 100644 index 00000000000..47ebd7bd625 --- /dev/null +++ b/addons/base_tools/i18n/es_CL.po @@ -0,0 +1,32 @@ +# Spanish (Chile) translation for openobject-addons +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-11 11:14+0000\n" +"PO-Revision-Date: 2011-10-03 16:53+0000\n" +"Last-Translator: David Acevedo Toledo \n" +"Language-Team: Spanish (Chile) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-10-04 05:02+0000\n" +"X-Generator: Launchpad (build 14071)\n" + +#. module: base_tools +#: model:ir.module.module,shortdesc:base_tools.module_meta_information +msgid "Common base for tools modules" +msgstr "Base común para módulos de herramientas" + +#. module: base_tools +#: model:ir.module.module,description:base_tools.module_meta_information +msgid "" +"\n" +" " +msgstr "" +"\n" +" " diff --git a/addons/board/i18n/es_CL.po b/addons/board/i18n/es_CL.po new file mode 100644 index 00000000000..1a603deddb1 --- /dev/null +++ b/addons/board/i18n/es_CL.po @@ -0,0 +1,395 @@ +# Spanish (Chile) translation for openobject-addons +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-11 11:14+0000\n" +"PO-Revision-Date: 2011-10-03 16:58+0000\n" +"Last-Translator: David Acevedo Toledo \n" +"Language-Team: Spanish (Chile) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-10-04 05:01+0000\n" +"X-Generator: Launchpad (build 14071)\n" + +#. module: board +#: view:res.log.report:0 +msgid " Year " +msgstr " Año " + +#. module: board +#: model:ir.model,name:board.model_board_menu_create +msgid "Menu Create" +msgstr "Crear menú" + +#. module: board +#: view:board.note:0 +#: field:board.note.type,name:0 +#: model:ir.model,name:board.model_board_note_type +msgid "Note Type" +msgstr "Tipo de nota" + +#. module: board +#: view:board.note:0 +#: field:board.note,user_id:0 +msgid "Author" +msgstr "Autor" + +#. module: board +#: model:ir.module.module,shortdesc:board.module_meta_information +msgid "Dashboard main module" +msgstr "Módulo principal de tableros" + +#. module: board +#: view:res.users:0 +msgid "Latest Connections" +msgstr "Últimas conexiones" + +#. module: board +#: code:addons/board/wizard/board_menu_create.py:45 +#, python-format +msgid "User Error!" +msgstr "¡Error de usuario!" + +#. module: board +#: view:board.board:0 +#: model:ir.actions.act_window,name:board.open_board_administration_form +#: model:ir.ui.menu,name:board.menu_board_admin +msgid "Administration Dashboard" +msgstr "Tablero de Administración" + +#. module: board +#: view:board.note:0 +#: field:board.note,note:0 +#: model:ir.model,name:board.model_board_note +msgid "Note" +msgstr "Nota" + +#. module: board +#: view:board.note:0 +#: view:res.log.report:0 +msgid "Group By..." +msgstr "Agrupar por..." + +#. module: board +#: model:ir.model,name:board.model_board_board +msgid "Board" +msgstr "Tablero" + +#. module: board +#: view:board.board:0 +#: model:ir.actions.act_window,name:board.board_weekly_res_log_report_action +#: view:res.log.report:0 +msgid "Weekly Global Activity" +msgstr "Actividad global semanal" + +#. module: board +#: field:board.board.line,name:0 +msgid "Title" +msgstr "Título" + +#. module: board +#: field:res.log.report,nbr:0 +msgid "# of Entries" +msgstr "Nº de entradas" + +#. module: board +#: view:res.log.report:0 +#: field:res.log.report,month:0 +msgid "Month" +msgstr "Mes" + +#. module: board +#: model:ir.actions.act_window,name:board.dashboard_open +msgid "Open Dashboard" +msgstr "Abrir tablero" + +#. module: board +#: view:board.board:0 +#: model:ir.actions.act_window,name:board.board_monthly_res_log_report_action +#: view:res.log.report:0 +msgid "Monthly Activity per Document" +msgstr "Actividad mensual por documento" + +#. module: board +#: view:res.log.report:0 +msgid "Log Analysis" +msgstr "Análisis de registro" + +#. module: board +#: model:ir.actions.act_window,name:board.action_view_board_list_form +#: model:ir.ui.menu,name:board.menu_view_board_form +msgid "Dashboard Definition" +msgstr "Definición de tablero" + +#. module: board +#: selection:res.log.report,month:0 +msgid "March" +msgstr "Marzo" + +#. module: board +#: selection:res.log.report,month:0 +msgid "August" +msgstr "Agosto" + +#. module: board +#: view:board.board:0 +#: model:ir.actions.act_window,name:board.action_user_connection_tree +msgid "User Connections" +msgstr "Conexiones de usuario" + +#. module: board +#: field:res.log.report,creation_date:0 +msgid "Creation Date" +msgstr "Fecha de Creación" + +#. module: board +#: model:ir.actions.act_window,name:board.action_view_board_note_form +#: model:ir.ui.menu,name:board.menu_view_board_note_form +msgid "Publish a note" +msgstr "Publicar una nota" + +#. module: board +#: view:board.menu.create:0 +msgid "Menu Information" +msgstr "Información del menú" + +#. module: board +#: selection:res.log.report,month:0 +msgid "June" +msgstr "Junio" + +#. module: board +#: field:board.note,type:0 +msgid "Note type" +msgstr "Tipo de nota" + +#. module: board +#: field:board.board,line_ids:0 +msgid "Action Views" +msgstr "Vistas de acciones" + +#. module: board +#: model:ir.model,name:board.model_res_log_report +msgid "Log Report" +msgstr "Informe de registro" + +#. module: board +#: view:board.note:0 +#: field:board.note,date:0 +msgid "Date" +msgstr "Fecha" + +#. module: board +#: selection:res.log.report,month:0 +msgid "July" +msgstr "Julio" + +#. module: board +#: view:res.log.report:0 +msgid "Extended Filters..." +msgstr "Filtros extendidos..." + +#. module: board +#: view:res.log.report:0 +#: field:res.log.report,day:0 +msgid "Day" +msgstr "Dia" + +#. module: board +#: view:board.menu.create:0 +msgid "Create Menu For Dashboard" +msgstr "Crear menú para tablero" + +#. module: board +#: selection:res.log.report,month:0 +msgid "February" +msgstr "Febrero" + +#. module: board +#: selection:res.log.report,month:0 +msgid "October" +msgstr "Octubre" + +#. module: board +#: model:ir.model,name:board.model_board_board_line +msgid "Board Line" +msgstr "Línea tablero" + +#. module: board +#: field:board.menu.create,menu_parent_id:0 +msgid "Parent Menu" +msgstr "Menú padre" + +#. module: board +#: view:res.log.report:0 +msgid " Month-1 " +msgstr " Mes-1 " + +#. module: board +#: selection:res.log.report,month:0 +msgid "January" +msgstr "Enero" + +#. module: board +#: view:board.note:0 +msgid "Notes" +msgstr "Notas" + +#. module: board +#: selection:res.log.report,month:0 +msgid "November" +msgstr "Noviembre" + +#. module: board +#: help:board.board.line,sequence:0 +msgid "" +"Gives the sequence order when displaying a list of " +"board lines." +msgstr "" +"Indica el orden de secuencia cuando se muestra una lista de líneas de " +"tablero." + +#. module: board +#: selection:res.log.report,month:0 +msgid "April" +msgstr "Abril" + +#. module: board +#: view:board.board:0 +#: field:board.board,name:0 +#: field:board.board.line,board_id:0 +#: model:ir.ui.menu,name:board.admin_menu_dasboard +#: model:ir.ui.menu,name:board.menu_dasboard +msgid "Dashboard" +msgstr "Tablero" + +#. module: board +#: model:ir.module.module,description:board.module_meta_information +msgid "Base module for all dashboards." +msgstr "Módulo base para todos los tableros." + +#. module: board +#: field:board.board.line,action_id:0 +msgid "Action" +msgstr "Acción." + +#. module: board +#: field:board.board.line,position:0 +msgid "Position" +msgstr "Posición" + +#. module: board +#: view:res.log.report:0 +msgid "Model" +msgstr "Modelo" + +#. module: board +#: field:board.menu.create,menu_name:0 +msgid "Menu Name" +msgstr "Nombre del menú" + +#. module: board +#: view:board.board:0 +#: model:ir.actions.act_window,name:board.action_latest_activities_tree +msgid "Latest Activities" +msgstr "Últimas actividades" + +#. module: board +#: selection:board.board.line,position:0 +msgid "Left" +msgstr "Izquieda" + +#. module: board +#: field:board.board,view_id:0 +msgid "Board View" +msgstr "Vista de tablero" + +#. module: board +#: selection:board.board.line,position:0 +msgid "Right" +msgstr "Derecha" + +#. module: board +#: field:board.board.line,width:0 +msgid "Width" +msgstr "Ancho" + +#. module: board +#: view:res.log.report:0 +msgid " Month " +msgstr " Mes " + +#. module: board +#: field:board.board.line,sequence:0 +msgid "Sequence" +msgstr "Secuencia" + +#. module: board +#: selection:res.log.report,month:0 +msgid "September" +msgstr "Septiembre" + +#. module: board +#: selection:res.log.report,month:0 +msgid "December" +msgstr "Diciembre" + +#. module: board +#: view:board.board:0 +#: view:board.menu.create:0 +msgid "Create Menu" +msgstr "Crear Menú" + +#. module: board +#: field:board.board.line,height:0 +msgid "Height" +msgstr "Alto" + +#. module: board +#: model:ir.actions.act_window,name:board.action_board_menu_create +msgid "Create Board Menu" +msgstr "Crear menú del tablero" + +#. module: board +#: selection:res.log.report,month:0 +msgid "May" +msgstr "Mayo" + +#. module: board +#: field:res.log.report,res_model:0 +msgid "Object" +msgstr "Objeto" + +#. module: board +#: view:res.log.report:0 +#: field:res.log.report,name:0 +msgid "Year" +msgstr "Año" + +#. module: board +#: view:board.menu.create:0 +msgid "Cancel" +msgstr "Cancelar" + +#. module: board +#: view:board.board:0 +msgid "Dashboard View" +msgstr "Vista de tablero" + +#. module: board +#: code:addons/board/wizard/board_menu_create.py:46 +#, python-format +msgid "Please Insert Dashboard View(s) !" +msgstr "¡Por favor inserte alguna vista de tablero!" + +#. module: board +#: view:board.note:0 +#: field:board.note,name:0 +msgid "Subject" +msgstr "Asunto" diff --git a/addons/stock/i18n/es_CL.po b/addons/stock/i18n/es_CL.po index ff898a8d261..2a33cd0c47f 100644 --- a/addons/stock/i18n/es_CL.po +++ b/addons/stock/i18n/es_CL.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2011-01-11 11:16+0000\n" -"PO-Revision-Date: 2011-01-14 04:42+0000\n" -"Last-Translator: Borja López Soilán (NeoPolus) \n" +"PO-Revision-Date: 2011-10-03 18:53+0000\n" +"Last-Translator: David Acevedo Toledo \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-09-05 04:57+0000\n" -"X-Generator: Launchpad (build 13830)\n" +"X-Launchpad-Export-Date: 2011-10-04 05:01+0000\n" +"X-Generator: Launchpad (build 14071)\n" #. module: stock #: field:product.product,track_outgoing:0 @@ -37,7 +37,7 @@ msgstr "" #. module: stock #: field:stock.location,chained_location_id:0 msgid "Chained Location If Fixed" -msgstr "Ubicación encadenada si fija" +msgstr "Encadenar Ubicación si es Fija" #. module: stock #: view:stock.inventory:0 @@ -70,20 +70,20 @@ msgstr "Hoy" #. module: stock #: field:stock.production.lot.revision,indice:0 msgid "Revision Number" -msgstr "Número revisión" +msgstr "Número de revisión" #. module: stock #: view:stock.move.memory.in:0 #: view:stock.move.memory.out:0 msgid "Product Moves" -msgstr "Movimientos productos" +msgstr "Movimientos de producto" #. module: stock #: model:ir.actions.act_window,name:stock.action_stock_move_report #: model:ir.ui.menu,name:stock.menu_action_stock_move_report #: view:report.stock.move:0 msgid "Moves Analysis" -msgstr "Análisis movimientos" +msgstr "Análisis de movimientos" #. module: stock #: help:stock.production.lot,ref:0 @@ -194,8 +194,8 @@ msgstr "" "de esta categoría, también puede indicarse directamente en cada producto." #. module: stock -#: code:addons/stock/stock.py:1170 -#: code:addons/stock/stock.py:2415 +#: code:addons/stock/stock.py:1190 +#: code:addons/stock/stock.py:2436 #, python-format msgid "Missing partial picking data for move #%s" msgstr "Faltan datos del albarán parcial para el movimiento #%s" @@ -260,8 +260,8 @@ msgid "Reference" msgstr "Referencia" #. module: stock -#: code:addons/stock/stock.py:666 -#: code:addons/stock/stock.py:1472 +#: code:addons/stock/stock.py:679 +#: code:addons/stock/stock.py:1492 #, python-format msgid "Products to Process" msgstr "Productos a procesar" @@ -286,7 +286,7 @@ msgid "Split lines" msgstr "Dividir líneas" #. module: stock -#: code:addons/stock/stock.py:1120 +#: code:addons/stock/stock.py:1139 #, python-format msgid "You cannot cancel picking because stock move is in done state !" msgstr "" @@ -294,9 +294,9 @@ msgstr "" "realizado!" #. module: stock -#: code:addons/stock/stock.py:2233 -#: code:addons/stock/stock.py:2274 -#: code:addons/stock/stock.py:2334 +#: code:addons/stock/stock.py:2253 +#: code:addons/stock/stock.py:2295 +#: code:addons/stock/stock.py:2355 #: code:addons/stock/wizard/stock_fill_inventory.py:53 #, python-format msgid "Warning!" @@ -506,7 +506,6 @@ msgstr "Filtro lotes de producto" #: report:lot.stock.overview:0 #: report:lot.stock.overview_all:0 #: report:stock.inventory.move:0 -#: report:stock.picking.list:0 msgid "[" msgstr "[" @@ -525,7 +524,7 @@ msgid "Move History (child moves)" msgstr "Historial movimientos (movimientos hijos)" #. module: stock -#: code:addons/stock/stock.py:2015 +#: code:addons/stock/stock.py:2033 #, python-format msgid "" "There is no stock output account defined for this product or its category: " @@ -547,7 +546,7 @@ msgid "Destination Location" msgstr "Ubicación destino" #. module: stock -#: code:addons/stock/stock.py:754 +#: code:addons/stock/stock.py:769 #, python-format msgid "You can not process picking without stock moves" msgstr "No puede procesar un albarán sin movimientos de stock" @@ -601,7 +600,7 @@ msgid "Location / Product" msgstr "Ubicación / Producto" #. module: stock -#: code:addons/stock/stock.py:1303 +#: code:addons/stock/stock.py:1323 #, python-format msgid "Reception" msgstr "Recepción" @@ -633,6 +632,11 @@ msgid "" "worker/team that should perform the operation. Examples of stock journals " "may be: quality control, pick lists, packing, etc." msgstr "" +"El sistema de existencias diarias le permite asignar cada operación de " +"existencias a un diario específico según el tipo de operación a realizar o " +"el trabajador / equipo que debe realizar la operación. Algunos ejemplos de " +"diarios de existencias pueden ser: control de calidad, listas de selección, " +"embalaje, etc." #. module: stock #: field:stock.location,complete_name:0 @@ -1094,7 +1098,7 @@ msgid "Author" msgstr "Autor" #. module: stock -#: code:addons/stock/stock.py:1302 +#: code:addons/stock/stock.py:1322 #, python-format msgid "Delivery Order" msgstr "Orden de entrega" @@ -1102,7 +1106,7 @@ msgstr "Orden de entrega" #. module: stock #: model:ir.model,name:stock.model_stock_move_memory_in msgid "stock.move.memory.in" -msgstr "" +msgstr "stock.move.memory.in" #. module: stock #: selection:stock.location,chained_auto_packing:0 @@ -1246,7 +1250,7 @@ msgstr "" "stock generados por las órdenes de producción." #. module: stock -#: code:addons/stock/stock.py:2006 +#: code:addons/stock/stock.py:2024 #, python-format msgid "" "Can not create Journal Entry, Output Account defined on this product and " @@ -1256,7 +1260,7 @@ msgstr "" "y la cuenta variante de la categoría de producto son la misma." #. module: stock -#: code:addons/stock/stock.py:1319 +#: code:addons/stock/stock.py:1339 #, python-format msgid "is in draft state." msgstr "está en estado borrador." @@ -1322,7 +1326,7 @@ msgid "Additional Info" msgstr "Información adicional" #. module: stock -#: code:addons/stock/stock.py:1619 +#: code:addons/stock/stock.py:1639 #, python-format msgid "Operation forbidden" msgstr "Operación prohibida" @@ -1469,7 +1473,7 @@ msgid "Downstream traceability" msgstr "Trazabilidad hacia abajo" #. module: stock -#: code:addons/stock/wizard/stock_invoice_onshipping.py:101 +#: code:addons/stock/wizard/stock_invoice_onshipping.py:106 #, python-format msgid "No Invoices were created" msgstr "No se han creado facturas" @@ -1533,7 +1537,7 @@ msgstr "" "compañías." #. module: stock -#: code:addons/stock/stock.py:2233 +#: code:addons/stock/stock.py:2253 #, python-format msgid "Please provide a positive quantity to scrap!" msgstr "¡Introduzca una cantidad positiva a desechar!" @@ -1564,6 +1568,13 @@ msgid "" " When the picking is done the state is 'Done'. \n" "The state is 'Waiting' if the move is waiting for another one." msgstr "" +"Cuando un movimiento de almacén es creado queda en estado 'Borrador'.\n" +" Luego, se establece en estado 'No Disponible' si el ... no ha encontrado " +"los productos.\n" +" Cuando los productos se han reservado es puesto en 'Disponible'.\n" +" Cuando el retiro es realizado el estado es puesto en 'Realizado'. " +" \n" +"El estado es puesto 'En Espera' si se está esperando por otro movimiento." #. module: stock #: selection:report.stock.inventory,location_type:0 @@ -1573,7 +1584,7 @@ msgid "Supplier Location" msgstr "Ubicación del proveedor" #. module: stock -#: code:addons/stock/stock.py:2254 +#: code:addons/stock/stock.py:2275 #, python-format msgid "were scrapped" msgstr "estaban desechados" @@ -1765,7 +1776,7 @@ msgid "Warehouse Dashboard" msgstr "Tablero almacén" #. module: stock -#: code:addons/stock/stock.py:512 +#: code:addons/stock/stock.py:525 #, python-format msgid "You can not remove a lot line !" msgstr "¡No puede eliminar una línea de lote!" @@ -1779,7 +1790,7 @@ msgid "Scrap Products" msgstr "Productos de desecho" #. module: stock -#: code:addons/stock/stock.py:1128 +#: code:addons/stock/stock.py:1147 #, python-format msgid "You cannot remove the picking which is in %s state !" msgstr "¡No puede eliminar el albarán que está en estado %s!" @@ -1842,7 +1853,7 @@ msgid "Provide the quantities of the returned products." msgstr "Indique las cantidades de los productos devueltos." #. module: stock -#: code:addons/stock/stock.py:2009 +#: code:addons/stock/stock.py:2027 #, python-format msgid "" "Can not create Journal Entry, Input Account defined on this product and " @@ -1931,7 +1942,7 @@ msgid "Quantity per lot" msgstr "Cantidad por lote" #. module: stock -#: code:addons/stock/stock.py:2012 +#: code:addons/stock/stock.py:2030 #, python-format msgid "" "There is no stock input account defined for this product or its category: " @@ -2040,7 +2051,6 @@ msgstr "_Aplicar" #: report:lot.stock.overview:0 #: report:lot.stock.overview_all:0 #: report:stock.inventory.move:0 -#: report:stock.picking.list:0 msgid "]" msgstr "]" @@ -2146,7 +2156,7 @@ msgid "Inventory loss" msgstr "Pérdidas de inventario" #. module: stock -#: code:addons/stock/stock.py:1311 +#: code:addons/stock/stock.py:1331 #, python-format msgid "Document" msgstr "Documento" @@ -2271,13 +2281,13 @@ msgid "Shipping Type" msgstr "Tipo de envío" #. module: stock -#: code:addons/stock/stock.py:2210 +#: code:addons/stock/stock.py:2230 #, python-format msgid "You can only delete draft moves." msgstr "Sólo puede eliminar movimientos borrador." #. module: stock -#: code:addons/stock/wizard/stock_partial_picking.py:97 +#: code:addons/stock/wizard/stock_partial_picking.py:98 #: model:ir.actions.act_window,name:stock.act_product_location_open #: model:ir.ui.menu,name:stock.menu_stock_products_menu #: view:stock.inventory:0 @@ -2448,7 +2458,7 @@ msgid "Source" msgstr "Origen" #. module: stock -#: code:addons/stock/stock.py:2589 +#: code:addons/stock/stock.py:2610 #: model:ir.model,name:stock.model_stock_inventory #: selection:report.stock.inventory,location_type:0 #: field:stock.inventory.line,inventory_id:0 @@ -2489,7 +2499,7 @@ msgstr "Seguimiento nº serie" #. module: stock #: code:addons/stock/report/report_stock.py:78 #: code:addons/stock/report/report_stock.py:135 -#: code:addons/stock/stock.py:754 +#: code:addons/stock/stock.py:769 #, python-format msgid "Error !" msgstr "¡Error!" @@ -2673,7 +2683,7 @@ msgstr "" "(id: %d)" #. module: stock -#: code:addons/stock/stock.py:2339 +#: code:addons/stock/stock.py:2361 #, python-format msgid "Can not consume a move with negative or zero quantity !" msgstr "" @@ -2736,7 +2746,7 @@ msgid "Quantity (UOS)" msgstr "Cantidad (UdV)" #. module: stock -#: code:addons/stock/stock.py:1664 +#: code:addons/stock/stock.py:1684 #, python-format msgid "" "You are moving %.2f %s products but only %.2f %s available in this lot." @@ -2777,7 +2787,7 @@ msgid "Properties" msgstr "Propiedades" #. module: stock -#: code:addons/stock/stock.py:974 +#: code:addons/stock/stock.py:994 #, python-format msgid "Error, no partner !" msgstr "¡Error, sin empresa!" @@ -2828,7 +2838,7 @@ msgid "Inventory Reference" msgstr "Referencia inventario" #. module: stock -#: code:addons/stock/stock.py:1304 +#: code:addons/stock/stock.py:1324 #, python-format msgid "Internal picking" msgstr "Albarán interno" @@ -2917,7 +2927,7 @@ msgid "Date done" msgstr "Fecha realización" #. module: stock -#: code:addons/stock/stock.py:975 +#: code:addons/stock/stock.py:995 #, python-format msgid "" "Please put a partner on the picking list if you want to generate invoice." @@ -3002,8 +3012,8 @@ msgid "Invoicing" msgstr "Facturación" #. module: stock -#: code:addons/stock/stock.py:2274 -#: code:addons/stock/stock.py:2334 +#: code:addons/stock/stock.py:2295 +#: code:addons/stock/stock.py:2355 #, python-format msgid "Please provide Proper Quantity !" msgstr "¡Indique cantidad correcta!" @@ -3030,7 +3040,7 @@ msgid "Consume Products" msgstr "Consumir productos" #. module: stock -#: code:addons/stock/stock.py:1663 +#: code:addons/stock/stock.py:1683 #, python-format msgid "Insufficient Stock in Lot !" msgstr "¡Stock insuficiente en el lote!" @@ -3130,7 +3140,9 @@ msgstr "Referencia paquete" #. module: stock #: view:report.stock.move:0 #: field:report.stock.move,location_id:0 +#: field:stock.inventory.line.split,location_id:0 #: field:stock.move,location_id:0 +#: field:stock.move.split,location_id:0 msgid "Source Location" msgstr "Ubicación origen" @@ -3183,7 +3195,7 @@ msgid "All at once" msgstr "Todo junto" #. module: stock -#: code:addons/stock/stock.py:1620 +#: code:addons/stock/stock.py:1640 #, python-format msgid "" "Quantities, UoMs, Products and Locations cannot be modified on stock moves " @@ -3234,7 +3246,7 @@ msgstr "Movimientos para este lote de producción" #. module: stock #: model:ir.model,name:stock.model_stock_move_memory_out msgid "stock.move.memory.out" -msgstr "" +msgstr "stock.move.memory.out" #. module: stock #: code:addons/stock/wizard/stock_fill_inventory.py:115 @@ -3286,8 +3298,8 @@ msgid "Icon" msgstr "Icono" #. module: stock -#: code:addons/stock/stock.py:2209 -#: code:addons/stock/stock.py:2617 +#: code:addons/stock/stock.py:2229 +#: code:addons/stock/stock.py:2638 #, python-format msgid "UserError" msgstr "Error de usuario" @@ -3314,13 +3326,13 @@ msgstr "Clientes no europeos" #: code:addons/stock/product.py:100 #: code:addons/stock/product.py:121 #: code:addons/stock/product.py:147 -#: code:addons/stock/stock.py:2006 -#: code:addons/stock/stock.py:2009 -#: code:addons/stock/stock.py:2012 -#: code:addons/stock/stock.py:2015 -#: code:addons/stock/stock.py:2018 -#: code:addons/stock/stock.py:2021 -#: code:addons/stock/stock.py:2339 +#: code:addons/stock/stock.py:2024 +#: code:addons/stock/stock.py:2027 +#: code:addons/stock/stock.py:2030 +#: code:addons/stock/stock.py:2033 +#: code:addons/stock/stock.py:2036 +#: code:addons/stock/stock.py:2039 +#: code:addons/stock/stock.py:2361 #: code:addons/stock/wizard/stock_fill_inventory.py:47 #: code:addons/stock/wizard/stock_splitinto.py:49 #: code:addons/stock/wizard/stock_splitinto.py:53 @@ -3329,7 +3341,7 @@ msgid "Error!" msgstr "¡Error!" #. module: stock -#: code:addons/stock/stock.py:2021 +#: code:addons/stock/stock.py:2039 #, python-format msgid "" "There is no inventory variation account defined on the product category: " @@ -3363,7 +3375,7 @@ msgid "It specifies goods to be delivered all at once or by direct delivery" msgstr "Indica si las mercancías se enviarán todas a la vez o directamente." #. module: stock -#: code:addons/stock/wizard/stock_invoice_onshipping.py:85 +#: code:addons/stock/wizard/stock_invoice_onshipping.py:90 #, python-format msgid "This picking list does not require invoicing." msgstr "Este albarán no requiere facturación." @@ -3389,6 +3401,16 @@ msgid "" "* Fixed Location: The chained location is taken from the next field: Chained " "Location if Fixed." msgstr "" +"Determina si esta ubicación está encadenada a otra, por ejemplo cualquier " +"producto que entre en esta ubicación \n" +"debe ir a la siguiente ubicación encadenada. La ubicación encadenada se " +"determina en función del tipo: \n" +"* Ninguna: No se encadena con ninguna.\n" +"* Cliente: Se utiliza la ubicación encadenada definida en el campo Ubicación " +"del Cliente del Formulario del Cliente especificado en la Guía de Despacho " +"(albaran) de los productos entrantes.\n" +"* Ubicación fija: Se utiliza la ubicación encadenada del campo: Ubicación " +"Encadenada si es Fija." #. module: stock #: code:addons/stock/wizard/stock_inventory_merge.py:43 @@ -3398,8 +3420,8 @@ msgid "Warning" msgstr "Advertencia" #. module: stock -#: code:addons/stock/stock.py:1318 -#: code:addons/stock/stock.py:2589 +#: code:addons/stock/stock.py:1338 +#: code:addons/stock/stock.py:2610 #, python-format msgid "is done." msgstr "está realizado." @@ -3505,7 +3527,7 @@ msgid "Reporting" msgstr "Informe" #. module: stock -#: code:addons/stock/stock.py:1313 +#: code:addons/stock/stock.py:1333 #, python-format msgid " for the " msgstr " para el " @@ -3559,7 +3581,7 @@ msgid "Chaining Journal" msgstr "Diario encadenamiento" #. module: stock -#: code:addons/stock/stock.py:732 +#: code:addons/stock/stock.py:746 #, python-format msgid "Not enough stock, unable to reserve the products." msgstr "No suficiente stock, no ha sido posible reservar los productos." @@ -3570,7 +3592,7 @@ msgid "Customers" msgstr "Clientes" #. module: stock -#: code:addons/stock/stock.py:1317 +#: code:addons/stock/stock.py:1337 #, python-format msgid "is cancelled." msgstr "está cancelado." @@ -3581,7 +3603,7 @@ msgid "Stock Inventory Lines" msgstr "Líneas regularización de inventario" #. module: stock -#: code:addons/stock/wizard/stock_partial_picking.py:97 +#: code:addons/stock/wizard/stock_partial_picking.py:98 #, python-format msgid "Process Document" msgstr "Procesar documento" @@ -3775,7 +3797,7 @@ msgstr "" "generados por los abastecimientos." #. module: stock -#: code:addons/stock/stock.py:1316 +#: code:addons/stock/stock.py:1336 #, python-format msgid "is ready to process." msgstr "preparado para procesar." @@ -3791,7 +3813,7 @@ msgid "Set to zero" msgstr "Inicializar a cero" #. module: stock -#: code:addons/stock/wizard/stock_invoice_onshipping.py:87 +#: code:addons/stock/wizard/stock_invoice_onshipping.py:92 #, python-format msgid "None of these picking lists require invoicing." msgstr "Ninguno de estos albaranes requiere facturación." @@ -3803,7 +3825,7 @@ msgstr "Noviembre" #. module: stock #: code:addons/stock/product.py:101 -#: code:addons/stock/stock.py:2018 +#: code:addons/stock/stock.py:2036 #, python-format msgid "There is no journal defined on the product category: \"%s\" (id: %d)" msgstr "" @@ -3856,10 +3878,10 @@ msgid "Future Stock" msgstr "Stock futuro" #. module: stock -#: code:addons/stock/stock.py:512 -#: code:addons/stock/stock.py:1120 -#: code:addons/stock/stock.py:1128 -#: code:addons/stock/wizard/stock_invoice_onshipping.py:101 +#: code:addons/stock/stock.py:525 +#: code:addons/stock/stock.py:1139 +#: code:addons/stock/stock.py:1147 +#: code:addons/stock/wizard/stock_invoice_onshipping.py:106 #, python-format msgid "Error" msgstr "Error" @@ -4124,10 +4146,11 @@ msgid "Invoiced date" msgstr "Fecha facturado" #. module: stock -#: code:addons/stock/stock.py:732 +#: code:addons/stock/stock.py:746 #: code:addons/stock/wizard/stock_fill_inventory.py:106 -#: code:addons/stock/wizard/stock_invoice_onshipping.py:85 -#: code:addons/stock/wizard/stock_invoice_onshipping.py:87 +#: code:addons/stock/wizard/stock_invoice_onshipping.py:65 +#: code:addons/stock/wizard/stock_invoice_onshipping.py:90 +#: code:addons/stock/wizard/stock_invoice_onshipping.py:92 #: code:addons/stock/wizard/stock_return_picking.py:77 #: code:addons/stock/wizard/stock_return_picking.py:92 #: code:addons/stock/wizard/stock_return_picking.py:194 diff --git a/debian/po/es_CL.po b/debian/po/es_CL.po index 454fa9705eb..9da94b6ce29 100644 --- a/debian/po/es_CL.po +++ b/debian/po/es_CL.po @@ -8,20 +8,20 @@ msgstr "" "Project-Id-Version: openobject-server\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2009-08-24 22:41+0300\n" -"PO-Revision-Date: 2011-01-19 14:33+0000\n" -"Last-Translator: Juan Pizarro \n" +"PO-Revision-Date: 2011-10-03 16:05+0000\n" +"Last-Translator: doingit.cl \n" "Language-Team: Spanish (Chile) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-09-01 04:46+0000\n" -"X-Generator: Launchpad (build 13827)\n" +"X-Launchpad-Export-Date: 2011-10-04 05:01+0000\n" +"X-Generator: Launchpad (build 14071)\n" #. Type: string #. Description #: ../openerp-server.templates:1001 msgid "Dedicated system account for the Open ERP server:" -msgstr "" +msgstr "Cuenta del sistema dedicada para el servidor OpenERP" #. Type: string #. Description @@ -31,9 +31,12 @@ msgid "" "the system's security is not compromised by running it with superuser " "privileges." msgstr "" +"El servidor OpenERP debe utilizar una cuenta dedicada para su " +"funcionamiento, de tal modo que la seguridad del sistema no se vea " +"comprometida por su utilización con privilegios de administración." #. Type: string #. Description #: ../openerp-server.templates:1001 msgid "Please choose that account's username." -msgstr "" +msgstr "Elija un Nombre de Usuario para la cuenta" diff --git a/openerp/addons/base/i18n/es_CL.po b/openerp/addons/base/i18n/es_CL.po index 9a5dd8a395a..812eaf625e3 100644 --- a/openerp/addons/base/i18n/es_CL.po +++ b/openerp/addons/base/i18n/es_CL.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2011-01-11 11:14+0000\n" -"PO-Revision-Date: 2011-07-26 01:09+0000\n" -"Last-Translator: Juano \n" +"PO-Revision-Date: 2011-10-03 16:01+0000\n" +"Last-Translator: Francisco Reyes Acuña \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-10-01 05:07+0000\n" +"X-Launchpad-Export-Date: 2011-10-04 05:01+0000\n" "X-Generator: Launchpad (build 14071)\n" #. module: base @@ -5726,7 +5726,7 @@ msgstr "Traducciones" #. module: base #: field:ir.sequence,padding:0 msgid "Number Padding" -msgstr "" +msgstr "Número(s) de relleno" #. module: base #: view:ir.actions.report.xml:0 From ad8ee9601c992f63230072fc4519721363e8d8da Mon Sep 17 00:00:00 2001 From: "ARA (OpenERP)" Date: Tue, 4 Oct 2011 10:38:00 +0530 Subject: [PATCH 360/896] form view is not able to open of product'Take money out ' and 'Take money in' lp bug: https://launchpad.net/bugs/865296 fixed bzr revid: ara@tinyerp.com-20111004050800-e0fqsi2mkhtv527e --- addons/point_of_sale/point_of_sale_view.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml index ba2d2c0214f..f5f03ebfa76 100644 --- a/addons/point_of_sale/point_of_sale_view.xml +++ b/addons/point_of_sale/point_of_sale_view.xml @@ -624,7 +624,6 @@ -
From 6ea640a433cfa588068914e3b9167ae453ef78b1 Mon Sep 17 00:00:00 2001 From: "Bharat (OpenERP)" Date: Tue, 4 Oct 2011 11:08:37 +0530 Subject: [PATCH 361/896] account : account_payment payment mode demo data lp bug: https://launchpad.net/bugs/865227 fixed bzr revid: bde@tinyerp.com-20111004053837-dhrsxe9u6qnd9tpg --- addons/account_payment/account_payment_demo.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account_payment/account_payment_demo.xml b/addons/account_payment/account_payment_demo.xml index 65a8d341da4..02170c38fea 100644 --- a/addons/account_payment/account_payment_demo.xml +++ b/addons/account_payment/account_payment_demo.xml @@ -10,7 +10,7 @@
Direct Payment - + From ed023b8f81baf3cbcbdff358723f56699580af4d Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Tue, 4 Oct 2011 11:31:17 +0530 Subject: [PATCH 362/896] [FIX] show all configuration wizard in administrator dashboard. lp bug: https://launchpad.net/bugs/865162 fixed bzr revid: ysa@tinyerp.com-20111004060117-bylyhk4ye781q33r --- addons/web_dashboard/static/src/js/dashboard.js | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/web_dashboard/static/src/js/dashboard.js b/addons/web_dashboard/static/src/js/dashboard.js index 4672492821d..808fe64e5c7 100644 --- a/addons/web_dashboard/static/src/js/dashboard.js +++ b/addons/web_dashboard/static/src/js/dashboard.js @@ -292,7 +292,6 @@ openerp.web_dashboard.ConfigOverview = openerp.web.View.extend({ this._super(parent); this.dataset = new openerp.web.DataSetSearch( this, 'ir.actions.todo'); - this.dataset.domain = [['type', '=', 'manual']]; }, start: function () { this._super(); From be5bef72fa85d76d463c18d03d05635e0c25fb07 Mon Sep 17 00:00:00 2001 From: "Vaibhav (OpenERP)" Date: Tue, 4 Oct 2011 12:49:45 +0530 Subject: [PATCH 363/896] [FIX] Need to Update Pager after delete record. lp bug: https://launchpad.net/bugs/849020 fixed bzr revid: vda@tinyerp.com-20111004071945-2fixgi12ozr6wm6g --- addons/web/static/src/js/view_list.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index c78adf769bd..cb03258081c 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -493,6 +493,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# _(ids).each(function (id) { self.records.remove(self.records.get(id)); }); + self.configure_pager(self.dataset); self.compute_aggregates(); }); }, From 8c1e052007456ae34d2b0e7fbe0887ada7100fef Mon Sep 17 00:00:00 2001 From: Valentin Lab Date: Tue, 4 Oct 2011 09:24:24 +0200 Subject: [PATCH 364/896] [fix] use a more solid selector to get $element bzr revid: valentin.lab@kalysto.org-20111004072424-uwx50578hhafe2xm --- addons/web/static/src/js/view_form.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 11ebad8d093..773cd882405 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -688,7 +688,10 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form. this.width = this.node.attrs.width; }, start: function() { - this.$element = this.view.$element.find('.' + this.element_class); + // avoiding find('.' + this.element_class) form because it doesn't work + // if class contains some dots. + this.$element = this.view.$element + .find('[class~="' + this.element_class + '"]'); }, process_modifiers: function() { var compute_domain = openerp.web.form.compute_domain; From 2bb8cadb4dcf7311df7f056bda77332fe9d7955b Mon Sep 17 00:00:00 2001 From: Valentin Lab Date: Tue, 4 Oct 2011 09:43:21 +0200 Subject: [PATCH 365/896] [imp] notebookpage is now in registry also. bzr revid: valentin.lab@kalysto.org-20111004074321-b2z121y64yynt948 --- addons/web/static/src/js/view_form.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 11ebad8d093..19d9a5ee839 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -792,8 +792,7 @@ openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({ for (var i = 0; i < node.children.length; i++) { var n = node.children[i]; if (n.tag == "page") { - var page = new openerp.web.form.WidgetNotebookPage( - this.view, n, this, this.pages.length); + var page = new (this.view.registry.get_object('notebookpage'))(this.view, n, this, this.pages.length); this.pages.push(page); } } @@ -2781,6 +2780,7 @@ openerp.web.form.widgets = new openerp.web.Registry({ 'frame' : 'openerp.web.form.WidgetFrame', 'group' : 'openerp.web.form.WidgetFrame', 'notebook' : 'openerp.web.form.WidgetNotebook', + 'notebookpage' : 'openerp.web.form.WidgetNotebookPage', 'separator' : 'openerp.web.form.WidgetSeparator', 'label' : 'openerp.web.form.WidgetLabel', 'button' : 'openerp.web.form.WidgetButton', From e2b6e03c3c6ffe3c6e2ed34166b66f953ba35495 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 4 Oct 2011 09:50:39 +0200 Subject: [PATCH 366/896] [FIX] Save last search before triggering do_search bzr revid: fme@openerp.com-20111004075039-n8a30wnuueiwnbwt --- addons/web/static/src/js/views.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index a38825a35a5..28e9772da77 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -288,8 +288,8 @@ db.web.ViewManager = db.web.Widget.extend(/** @lends db.web.ViewManager# */{ }, function (results) { self.dataset.context = results.context; self.dataset.domain = results.domain; - controller.do_search(results.domain, results.context, results.group_by); self.last_search = [results.domain, results.context, results.group_by]; + controller.do_search(results.domain, results.context, results.group_by); }); } else if (this.last_search) { controller.do_search.apply(controller, this.last_search); From e6dd80d6b5f8fce6b62b5b9a1517e0239726cba0 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Tue, 4 Oct 2011 10:04:55 +0200 Subject: [PATCH 367/896] [fix] forgot some things bzr revid: nicolas.vanhoren@openerp.com-20111004080455-qyd2z0rfdxbi4ecf --- .../static/lib/backbone/backbone-0.5.3.js | 1158 +++++++++++++++++ addons/point_of_sale/static/src/js/pos.js | 1 - 2 files changed, 1158 insertions(+), 1 deletion(-) create mode 100644 addons/point_of_sale/static/lib/backbone/backbone-0.5.3.js diff --git a/addons/point_of_sale/static/lib/backbone/backbone-0.5.3.js b/addons/point_of_sale/static/lib/backbone/backbone-0.5.3.js new file mode 100644 index 00000000000..8958c798466 --- /dev/null +++ b/addons/point_of_sale/static/lib/backbone/backbone-0.5.3.js @@ -0,0 +1,1158 @@ +// Backbone.js 0.5.3 +// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc. +// Backbone may be freely distributed under the MIT license. +// For all details and documentation: +// http://documentcloud.github.com/backbone + +(function(){ + + // Initial Setup + // ------------- + + // Save a reference to the global object. + var root = this; + + // Save the previous value of the `Backbone` variable. + var previousBackbone = root.Backbone; + + // The top-level namespace. All public Backbone classes and modules will + // be attached to this. Exported for both CommonJS and the browser. + var Backbone; + if (typeof exports !== 'undefined') { + Backbone = exports; + } else { + Backbone = root.Backbone = {}; + } + + // Current version of the library. Keep in sync with `package.json`. + Backbone.VERSION = '0.5.3'; + + // Require Underscore, if we're on the server, and it's not already present. + var _ = root._; + if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._; + + // For Backbone's purposes, jQuery or Zepto owns the `$` variable. + var $ = root.jQuery || root.Zepto; + + // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable + // to its previous owner. Returns a reference to this Backbone object. + Backbone.noConflict = function() { + root.Backbone = previousBackbone; + return this; + }; + + // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option will + // fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and set a + // `X-Http-Method-Override` header. + Backbone.emulateHTTP = false; + + // Turn on `emulateJSON` to support legacy servers that can't deal with direct + // `application/json` requests ... will encode the body as + // `application/x-www-form-urlencoded` instead and will send the model in a + // form param named `model`. + Backbone.emulateJSON = false; + + // Backbone.Events + // ----------------- + + // A module that can be mixed in to *any object* in order to provide it with + // custom events. You may `bind` or `unbind` a callback function to an event; + // `trigger`-ing an event fires all callbacks in succession. + // + // var object = {}; + // _.extend(object, Backbone.Events); + // object.bind('expand', function(){ alert('expanded'); }); + // object.trigger('expand'); + // + Backbone.Events = { + + // Bind an event, specified by a string name, `ev`, to a `callback` function. + // Passing `"all"` will bind the callback to all events fired. + bind : function(ev, callback, context) { + var calls = this._callbacks || (this._callbacks = {}); + var list = calls[ev] || (calls[ev] = []); + list.push([callback, context]); + return this; + }, + + // Remove one or many callbacks. If `callback` is null, removes all + // callbacks for the event. If `ev` is null, removes all bound callbacks + // for all events. + unbind : function(ev, callback) { + var calls; + if (!ev) { + this._callbacks = {}; + } else if (calls = this._callbacks) { + if (!callback) { + calls[ev] = []; + } else { + var list = calls[ev]; + if (!list) return this; + for (var i = 0, l = list.length; i < l; i++) { + if (list[i] && callback === list[i][0]) { + list[i] = null; + break; + } + } + } + } + return this; + }, + + // Trigger an event, firing all bound callbacks. Callbacks are passed the + // same arguments as `trigger` is, apart from the event name. + // Listening for `"all"` passes the true event name as the first argument. + trigger : function(eventName) { + var list, calls, ev, callback, args; + var both = 2; + if (!(calls = this._callbacks)) return this; + while (both--) { + ev = both ? eventName : 'all'; + if (list = calls[ev]) { + for (var i = 0, l = list.length; i < l; i++) { + if (!(callback = list[i])) { + list.splice(i, 1); i--; l--; + } else { + args = both ? Array.prototype.slice.call(arguments, 1) : arguments; + callback[0].apply(callback[1] || this, args); + } + } + } + } + return this; + } + + }; + + // Backbone.Model + // -------------- + + // Create a new model, with defined attributes. A client id (`cid`) + // is automatically generated and assigned for you. + Backbone.Model = function(attributes, options) { + var defaults; + attributes || (attributes = {}); + if (defaults = this.defaults) { + if (_.isFunction(defaults)) defaults = defaults.call(this); + attributes = _.extend({}, defaults, attributes); + } + this.attributes = {}; + this._escapedAttributes = {}; + this.cid = _.uniqueId('c'); + this.set(attributes, {silent : true}); + this._changed = false; + this._previousAttributes = _.clone(this.attributes); + if (options && options.collection) this.collection = options.collection; + this.initialize(attributes, options); + }; + + // Attach all inheritable methods to the Model prototype. + _.extend(Backbone.Model.prototype, Backbone.Events, { + + // A snapshot of the model's previous attributes, taken immediately + // after the last `"change"` event was fired. + _previousAttributes : null, + + // Has the item been changed since the last `"change"` event? + _changed : false, + + // The default name for the JSON `id` attribute is `"id"`. MongoDB and + // CouchDB users may want to set this to `"_id"`. + idAttribute : 'id', + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize : function(){}, + + // Return a copy of the model's `attributes` object. + toJSON : function() { + return _.clone(this.attributes); + }, + + // Get the value of an attribute. + get : function(attr) { + return this.attributes[attr]; + }, + + // Get the HTML-escaped value of an attribute. + escape : function(attr) { + var html; + if (html = this._escapedAttributes[attr]) return html; + var val = this.attributes[attr]; + return this._escapedAttributes[attr] = escapeHTML(val == null ? '' : '' + val); + }, + + // Returns `true` if the attribute contains a value that is not null + // or undefined. + has : function(attr) { + return this.attributes[attr] != null; + }, + + // Set a hash of model attributes on the object, firing `"change"` unless you + // choose to silence it. + set : function(attrs, options) { + + // Extract attributes and options. + options || (options = {}); + if (!attrs) return this; + if (attrs.attributes) attrs = attrs.attributes; + var now = this.attributes, escaped = this._escapedAttributes; + + // Run validation. + if (!options.silent && this.validate && !this._performValidation(attrs, options)) return false; + + // Check for changes of `id`. + if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; + + // We're about to start triggering change events. + var alreadyChanging = this._changing; + this._changing = true; + + // Update attributes. + for (var attr in attrs) { + var val = attrs[attr]; + if (!_.isEqual(now[attr], val)) { + now[attr] = val; + delete escaped[attr]; + this._changed = true; + if (!options.silent) this.trigger('change:' + attr, this, val, options); + } + } + + // Fire the `"change"` event, if the model has been changed. + if (!alreadyChanging && !options.silent && this._changed) this.change(options); + this._changing = false; + return this; + }, + + // Remove an attribute from the model, firing `"change"` unless you choose + // to silence it. `unset` is a noop if the attribute doesn't exist. + unset : function(attr, options) { + if (!(attr in this.attributes)) return this; + options || (options = {}); + var value = this.attributes[attr]; + + // Run validation. + var validObj = {}; + validObj[attr] = void 0; + if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false; + + // Remove the attribute. + delete this.attributes[attr]; + delete this._escapedAttributes[attr]; + if (attr == this.idAttribute) delete this.id; + this._changed = true; + if (!options.silent) { + this.trigger('change:' + attr, this, void 0, options); + this.change(options); + } + return this; + }, + + // Clear all attributes on the model, firing `"change"` unless you choose + // to silence it. + clear : function(options) { + options || (options = {}); + var attr; + var old = this.attributes; + + // Run validation. + var validObj = {}; + for (attr in old) validObj[attr] = void 0; + if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false; + + this.attributes = {}; + this._escapedAttributes = {}; + this._changed = true; + if (!options.silent) { + for (attr in old) { + this.trigger('change:' + attr, this, void 0, options); + } + this.change(options); + } + return this; + }, + + // Fetch the model from the server. If the server's representation of the + // model differs from its current attributes, they will be overriden, + // triggering a `"change"` event. + fetch : function(options) { + options || (options = {}); + var model = this; + var success = options.success; + options.success = function(resp, status, xhr) { + if (!model.set(model.parse(resp, xhr), options)) return false; + if (success) success(model, resp); + }; + options.error = wrapError(options.error, model, options); + return (this.sync || Backbone.sync).call(this, 'read', this, options); + }, + + // Set a hash of model attributes, and sync the model to the server. + // If the server returns an attributes hash that differs, the model's + // state will be `set` again. + save : function(attrs, options) { + options || (options = {}); + if (attrs && !this.set(attrs, options)) return false; + var model = this; + var success = options.success; + options.success = function(resp, status, xhr) { + if (!model.set(model.parse(resp, xhr), options)) return false; + if (success) success(model, resp, xhr); + }; + options.error = wrapError(options.error, model, options); + var method = this.isNew() ? 'create' : 'update'; + return (this.sync || Backbone.sync).call(this, method, this, options); + }, + + // Destroy this model on the server if it was already persisted. Upon success, the model is removed + // from its collection, if it has one. + destroy : function(options) { + options || (options = {}); + if (this.isNew()) return this.trigger('destroy', this, this.collection, options); + var model = this; + var success = options.success; + options.success = function(resp) { + model.trigger('destroy', model, model.collection, options); + if (success) success(model, resp); + }; + options.error = wrapError(options.error, model, options); + return (this.sync || Backbone.sync).call(this, 'delete', this, options); + }, + + // Default URL for the model's representation on the server -- if you're + // using Backbone's restful methods, override this to change the endpoint + // that will be called. + url : function() { + var base = getUrl(this.collection) || this.urlRoot || urlError(); + if (this.isNew()) return base; + return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id); + }, + + // **parse** converts a response into the hash of attributes to be `set` on + // the model. The default implementation is just to pass the response along. + parse : function(resp, xhr) { + return resp; + }, + + // Create a new model with identical attributes to this one. + clone : function() { + return new this.constructor(this); + }, + + // A model is new if it has never been saved to the server, and lacks an id. + isNew : function() { + return this.id == null; + }, + + // Call this method to manually fire a `change` event for this model. + // Calling this will cause all objects observing the model to update. + change : function(options) { + this.trigger('change', this, options); + this._previousAttributes = _.clone(this.attributes); + this._changed = false; + }, + + // Determine if the model has changed since the last `"change"` event. + // If you specify an attribute name, determine if that attribute has changed. + hasChanged : function(attr) { + if (attr) return this._previousAttributes[attr] != this.attributes[attr]; + return this._changed; + }, + + // Return an object containing all the attributes that have changed, or false + // if there are no changed attributes. Useful for determining what parts of a + // view need to be updated and/or what attributes need to be persisted to + // the server. + changedAttributes : function(now) { + now || (now = this.attributes); + var old = this._previousAttributes; + var changed = false; + for (var attr in now) { + if (!_.isEqual(old[attr], now[attr])) { + changed = changed || {}; + changed[attr] = now[attr]; + } + } + return changed; + }, + + // Get the previous value of an attribute, recorded at the time the last + // `"change"` event was fired. + previous : function(attr) { + if (!attr || !this._previousAttributes) return null; + return this._previousAttributes[attr]; + }, + + // Get all of the attributes of the model at the time of the previous + // `"change"` event. + previousAttributes : function() { + return _.clone(this._previousAttributes); + }, + + // Run validation against a set of incoming attributes, returning `true` + // if all is well. If a specific `error` callback has been passed, + // call that instead of firing the general `"error"` event. + _performValidation : function(attrs, options) { + var error = this.validate(attrs); + if (error) { + if (options.error) { + options.error(this, error, options); + } else { + this.trigger('error', this, error, options); + } + return false; + } + return true; + } + + }); + + // Backbone.Collection + // ------------------- + + // Provides a standard collection class for our sets of models, ordered + // or unordered. If a `comparator` is specified, the Collection will maintain + // its models in sort order, as they're added and removed. + Backbone.Collection = function(models, options) { + options || (options = {}); + if (options.comparator) this.comparator = options.comparator; + _.bindAll(this, '_onModelEvent', '_removeReference'); + this._reset(); + if (models) this.reset(models, {silent: true}); + this.initialize.apply(this, arguments); + }; + + // Define the Collection's inheritable methods. + _.extend(Backbone.Collection.prototype, Backbone.Events, { + + // The default model for a collection is just a **Backbone.Model**. + // This should be overridden in most cases. + model : Backbone.Model, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize : function(){}, + + // The JSON representation of a Collection is an array of the + // models' attributes. + toJSON : function() { + return this.map(function(model){ return model.toJSON(); }); + }, + + // Add a model, or list of models to the set. Pass **silent** to avoid + // firing the `added` event for every new model. + add : function(models, options) { + if (_.isArray(models)) { + for (var i = 0, l = models.length; i < l; i++) { + this._add(models[i], options); + } + } else { + this._add(models, options); + } + return this; + }, + + // Remove a model, or a list of models from the set. Pass silent to avoid + // firing the `removed` event for every model removed. + remove : function(models, options) { + if (_.isArray(models)) { + for (var i = 0, l = models.length; i < l; i++) { + this._remove(models[i], options); + } + } else { + this._remove(models, options); + } + return this; + }, + + // Get a model from the set by id. + get : function(id) { + if (id == null) return null; + return this._byId[id.id != null ? id.id : id]; + }, + + // Get a model from the set by client id. + getByCid : function(cid) { + return cid && this._byCid[cid.cid || cid]; + }, + + // Get the model at the given index. + at: function(index) { + return this.models[index]; + }, + + // Force the collection to re-sort itself. You don't need to call this under normal + // circumstances, as the set will maintain sort order as each item is added. + sort : function(options) { + options || (options = {}); + if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); + this.models = this.sortBy(this.comparator); + if (!options.silent) this.trigger('reset', this, options); + return this; + }, + + // Pluck an attribute from each model in the collection. + pluck : function(attr) { + return _.map(this.models, function(model){ return model.get(attr); }); + }, + + // When you have more items than you want to add or remove individually, + // you can reset the entire set with a new list of models, without firing + // any `added` or `removed` events. Fires `reset` when finished. + reset : function(models, options) { + models || (models = []); + options || (options = {}); + this.each(this._removeReference); + this._reset(); + this.add(models, {silent: true}); + if (!options.silent) this.trigger('reset', this, options); + return this; + }, + + // Fetch the default set of models for this collection, resetting the + // collection when they arrive. If `add: true` is passed, appends the + // models to the collection instead of resetting. + fetch : function(options) { + options || (options = {}); + var collection = this; + var success = options.success; + options.success = function(resp, status, xhr) { + collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options); + if (success) success(collection, resp); + }; + options.error = wrapError(options.error, collection, options); + return (this.sync || Backbone.sync).call(this, 'read', this, options); + }, + + // Create a new instance of a model in this collection. After the model + // has been created on the server, it will be added to the collection. + // Returns the model, or 'false' if validation on a new model fails. + create : function(model, options) { + var coll = this; + options || (options = {}); + model = this._prepareModel(model, options); + if (!model) return false; + var success = options.success; + options.success = function(nextModel, resp, xhr) { + coll.add(nextModel, options); + if (success) success(nextModel, resp, xhr); + }; + model.save(null, options); + return model; + }, + + // **parse** converts a response into a list of models to be added to the + // collection. The default implementation is just to pass it through. + parse : function(resp, xhr) { + return resp; + }, + + // Proxy to _'s chain. Can't be proxied the same way the rest of the + // underscore methods are proxied because it relies on the underscore + // constructor. + chain: function () { + return _(this.models).chain(); + }, + + // Reset all internal state. Called when the collection is reset. + _reset : function(options) { + this.length = 0; + this.models = []; + this._byId = {}; + this._byCid = {}; + }, + + // Prepare a model to be added to this collection + _prepareModel: function(model, options) { + if (!(model instanceof Backbone.Model)) { + var attrs = model; + model = new this.model(attrs, {collection: this}); + if (model.validate && !model._performValidation(attrs, options)) model = false; + } else if (!model.collection) { + model.collection = this; + } + return model; + }, + + // Internal implementation of adding a single model to the set, updating + // hash indexes for `id` and `cid` lookups. + // Returns the model, or 'false' if validation on a new model fails. + _add : function(model, options) { + options || (options = {}); + model = this._prepareModel(model, options); + if (!model) return false; + var already = this.getByCid(model); + if (already) throw new Error(["Can't add the same model to a set twice", already.id]); + this._byId[model.id] = model; + this._byCid[model.cid] = model; + var index = options.at != null ? options.at : + this.comparator ? this.sortedIndex(model, this.comparator) : + this.length; + this.models.splice(index, 0, model); + model.bind('all', this._onModelEvent); + this.length++; + if (!options.silent) model.trigger('add', model, this, options); + return model; + }, + + // Internal implementation of removing a single model from the set, updating + // hash indexes for `id` and `cid` lookups. + _remove : function(model, options) { + options || (options = {}); + model = this.getByCid(model) || this.get(model); + if (!model) return null; + delete this._byId[model.id]; + delete this._byCid[model.cid]; + this.models.splice(this.indexOf(model), 1); + this.length--; + if (!options.silent) model.trigger('remove', model, this, options); + this._removeReference(model); + return model; + }, + + // Internal method to remove a model's ties to a collection. + _removeReference : function(model) { + if (this == model.collection) { + delete model.collection; + } + model.unbind('all', this._onModelEvent); + }, + + // Internal method called every time a model in the set fires an event. + // Sets need to update their indexes when models change ids. All other + // events simply proxy through. "add" and "remove" events that originate + // in other collections are ignored. + _onModelEvent : function(ev, model, collection, options) { + if ((ev == 'add' || ev == 'remove') && collection != this) return; + if (ev == 'destroy') { + this._remove(model, options); + } + if (model && ev === 'change:' + model.idAttribute) { + delete this._byId[model.previous(model.idAttribute)]; + this._byId[model.id] = model; + } + this.trigger.apply(this, arguments); + } + + }); + + // Underscore methods that we want to implement on the Collection. + var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', 'detect', + 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include', + 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', 'toArray', 'size', + 'first', 'rest', 'last', 'without', 'indexOf', 'lastIndexOf', 'isEmpty', 'groupBy']; + + // Mix in each Underscore method as a proxy to `Collection#models`. + _.each(methods, function(method) { + Backbone.Collection.prototype[method] = function() { + return _[method].apply(_, [this.models].concat(_.toArray(arguments))); + }; + }); + + // Backbone.Router + // ------------------- + + // Routers map faux-URLs to actions, and fire events when routes are + // matched. Creating a new one sets its `routes` hash, if not set statically. + Backbone.Router = function(options) { + options || (options = {}); + if (options.routes) this.routes = options.routes; + this._bindRoutes(); + this.initialize.apply(this, arguments); + }; + + // Cached regular expressions for matching named param parts and splatted + // parts of route strings. + var namedParam = /:([\w\d]+)/g; + var splatParam = /\*([\w\d]+)/g; + var escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g; + + // Set up all inheritable **Backbone.Router** properties and methods. + _.extend(Backbone.Router.prototype, Backbone.Events, { + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize : function(){}, + + // Manually bind a single named route to a callback. For example: + // + // this.route('search/:query/p:num', 'search', function(query, num) { + // ... + // }); + // + route : function(route, name, callback) { + Backbone.history || (Backbone.history = new Backbone.History); + if (!_.isRegExp(route)) route = this._routeToRegExp(route); + Backbone.history.route(route, _.bind(function(fragment) { + var args = this._extractParameters(route, fragment); + callback.apply(this, args); + this.trigger.apply(this, ['route:' + name].concat(args)); + }, this)); + }, + + // Simple proxy to `Backbone.history` to save a fragment into the history. + navigate : function(fragment, triggerRoute) { + Backbone.history.navigate(fragment, triggerRoute); + }, + + // Bind all defined routes to `Backbone.history`. We have to reverse the + // order of the routes here to support behavior where the most general + // routes can be defined at the bottom of the route map. + _bindRoutes : function() { + if (!this.routes) return; + var routes = []; + for (var route in this.routes) { + routes.unshift([route, this.routes[route]]); + } + for (var i = 0, l = routes.length; i < l; i++) { + this.route(routes[i][0], routes[i][1], this[routes[i][1]]); + } + }, + + // Convert a route string into a regular expression, suitable for matching + // against the current location hash. + _routeToRegExp : function(route) { + route = route.replace(escapeRegExp, "\\$&") + .replace(namedParam, "([^\/]*)") + .replace(splatParam, "(.*?)"); + return new RegExp('^' + route + '$'); + }, + + // Given a route, and a URL fragment that it matches, return the array of + // extracted parameters. + _extractParameters : function(route, fragment) { + return route.exec(fragment).slice(1); + } + + }); + + // Backbone.History + // ---------------- + + // Handles cross-browser history management, based on URL fragments. If the + // browser does not support `onhashchange`, falls back to polling. + Backbone.History = function() { + this.handlers = []; + _.bindAll(this, 'checkUrl'); + }; + + // Cached regex for cleaning hashes. + var hashStrip = /^#*/; + + // Cached regex for detecting MSIE. + var isExplorer = /msie [\w.]+/; + + // Has the history handling already been started? + var historyStarted = false; + + // Set up all inheritable **Backbone.History** properties and methods. + _.extend(Backbone.History.prototype, { + + // The default interval to poll for hash changes, if necessary, is + // twenty times a second. + interval: 50, + + // Get the cross-browser normalized URL fragment, either from the URL, + // the hash, or the override. + getFragment : function(fragment, forcePushState) { + if (fragment == null) { + if (this._hasPushState || forcePushState) { + fragment = window.location.pathname; + var search = window.location.search; + if (search) fragment += search; + if (fragment.indexOf(this.options.root) == 0) fragment = fragment.substr(this.options.root.length); + } else { + fragment = window.location.hash; + } + } + return decodeURIComponent(fragment.replace(hashStrip, '')); + }, + + // Start the hash change handling, returning `true` if the current URL matches + // an existing route, and `false` otherwise. + start : function(options) { + + // Figure out the initial configuration. Do we need an iframe? + // Is pushState desired ... is it available? + if (historyStarted) throw new Error("Backbone.history has already been started"); + this.options = _.extend({}, {root: '/'}, this.options, options); + this._wantsPushState = !!this.options.pushState; + this._hasPushState = !!(this.options.pushState && window.history && window.history.pushState); + var fragment = this.getFragment(); + var docMode = document.documentMode; + var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); + if (oldIE) { + this.iframe = $('