From d196118cdee85f246d2ddd19e39b29137bb288dc Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Wed, 27 Jul 2011 12:13:25 +0530 Subject: [PATCH 001/128] [IMP]Implement Import data Functionality. bzr revid: kch@tinyerp.com-20110727064325-zh8j9w7bvcdckv7v --- addons/base/__openerp__.py | 1 + addons/base/controllers/main.py | 86 ++++++++++++++++++++++----- addons/base/static/src/js/base.js | 3 + addons/base/static/src/js/import.js | 35 +++++++++++ addons/base/static/src/js/views.js | 17 +++++- addons/base/static/src/xml/base.xml | 91 +++++++++++++++++++++++++++-- 6 files changed, 214 insertions(+), 19 deletions(-) create mode 100644 addons/base/static/src/js/import.js diff --git a/addons/base/__openerp__.py b/addons/base/__openerp__.py index a983a4227ce..b4ff7052242 100644 --- a/addons/base/__openerp__.py +++ b/addons/base/__openerp__.py @@ -28,6 +28,7 @@ "static/src/js/list-editable.js", "static/src/js/search.js", "static/src/js/view_tree.js", + "static/src/js/import.js", ], 'css' : [ "static/lib/jquery.superfish/css/superfish.css", diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 628a086e3d6..16b4613df5e 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -10,7 +10,7 @@ import openerpweb.ast import openerpweb.nonliterals import cherrypy - +import csv # Should move to openerpweb.Xml2Json class Xml2Json: # xml2json-direct @@ -119,8 +119,8 @@ class WebClient(openerpweb.Controller): %s @@ -222,7 +222,7 @@ class Session(openerpweb.Controller): context, domain = eval_context_and_domain(req.session, openerpweb.nonliterals.CompoundContext(*(contexts or [])), openerpweb.nonliterals.CompoundDomain(*(domains or []))) - + group_by_sequence = [] for candidate in (group_by_seq or []): ctx = req.session.eval_context(candidate, context) @@ -233,7 +233,7 @@ class Session(openerpweb.Controller): group_by_sequence.append(group_by) else: group_by_sequence.extend(group_by) - + return { 'context': context, 'domain': domain, @@ -246,7 +246,7 @@ class Session(openerpweb.Controller): This method store an action object in the session object and returns an integer identifying that action. The method get_session_action() can be used to get back the action. - + :param the_action: The action to save in the session. :type the_action: anything :return: A key identifying the saved action. @@ -269,7 +269,7 @@ class Session(openerpweb.Controller): """ Gets back a previously saved action. This method can return None if the action was saved since too much time (this case should be handled in a smart way). - + :param key: The key given by save_session_action() :type key: integer :return: The saved action or None. @@ -404,7 +404,7 @@ class Menu(openerpweb.Controller): menu_items = Menus.read(menu_ids, ['name', 'sequence', 'parent_id'], context) menu_root = {'id': False, 'name': 'root', 'parent_id': [-1, '']} menu_items.append(menu_root) - + # make a tree using parent_id menu_items_map = dict((menu_item["id"], menu_item) for menu_item in menu_items) for menu_item in menu_items: @@ -511,7 +511,7 @@ class DataSet(openerpweb.Controller): record_map = dict((record['id'], record) for record in records) return [record_map[id] for id in ids if record_map.get(id)] - + @openerpweb.jsonrequest def load(self, req, model, id, fields): m = req.session.model(model) @@ -674,7 +674,7 @@ class View(openerpweb.Controller): except ValueError: # not a literal return openerpweb.nonliterals.Domain(session, domain) - + def parse_context(self, context, session): """ Parses an arbitrary string containing a context, transforms it to either a literal context or a :class:`openerpweb.nonliterals.Context` @@ -764,7 +764,7 @@ class SearchView(View): if field.get('context'): field["context"] = self.parse_domain(field["context"], req.session) return {'fields': fields} - + @openerpweb.jsonrequest def get_filters(self, req, model): Model = req.session.model("ir.filters") @@ -773,7 +773,7 @@ class SearchView(View): filter["context"] = req.session.eval_context(self.parse_context(filter["context"], req.session)) filter["domain"] = req.session.eval_domain(self.parse_domain(filter["domain"], req.session)) return filters - + @openerpweb.jsonrequest def save_filter(self, req, model, name, context_to_save, domain): Model = req.session.model("ir.filters") @@ -903,4 +903,64 @@ class Action(openerpweb.Controller): return clean_action(req.session.model('ir.actions.server').run( [action_id], req.session.eval_context(req.context)), req.session) -# +class Import(View): + _cp_path = "/base/import" + + @openerpweb.httprequest + def import_data(self, req, session_id, callback, model, id, csvfile, csvsep, csvdel, csvcode, csvskip, fields=[]): + import StringIO + context = req.session.eval_context(req.context) + modle_obj = req.session.model(model) + + res = None + content = csvfile.file.read() + print "\n content++++++++++++++++++++++++++++",content + input=StringIO.StringIO(content) + limit = 0 + data = [] + + if not (csvdel and len(csvdel) == 1): + return "The CSV delimiter must be a single character" + + try: + for j, line in enumerate(csv.reader(input, quotechar=str(csvdel), delimiter=str(csvsep))): + # If the line contains no data, we should skip it. + if not line: + continue + if j == limit: + fields = line + else: + data.append(line) + except: + return "CSV Error" + + datas = [] + ctx = context + + if not isinstance(fields, list): + fields = [fields] + + for line in data: + try: + datas.append(map(lambda x:x.decode(csvcode).encode('utf-8'), line)) + except: + datas.append(map(lambda x:x.decode('latin').encode('utf-8'), line)) + + # If the file contains nothing, + if not datas: + return "The file is empty." + + #Inverting the header into column names + try: + res = modle_obj.import_data(fields, datas, 'init', '', False, ctx) + except: + return "Error in Importing Data." + + if res[0]>=0: + return "Successfully Imported %d objects." % (res[0],) + + d = '' + for key,val in res[1].items(): + d+= ('%s: %s' % (key,val)) + + return "Error trying to import this record :%s. ErrorMessage:%s %s" % (d,res[2],res[3]) diff --git a/addons/base/static/src/js/base.js b/addons/base/static/src/js/base.js index 28022f8b764..d5f23209723 100644 --- a/addons/base/static/src/js/base.js +++ b/addons/base/static/src/js/base.js @@ -87,6 +87,9 @@ openerp.base = function(instance) { if (openerp.base.view_tree) { openerp.base.view_tree(instance); } + if (openerp.base.import){ + openerp.base.import(instance); + } }; // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/base/static/src/js/import.js b/addons/base/static/src/js/import.js new file mode 100644 index 00000000000..5b6affe27c7 --- /dev/null +++ b/addons/base/static/src/js/import.js @@ -0,0 +1,35 @@ +openerp.base.import = function(openerp) { +openerp.base.Import = openerp.base.Dialog.extend({ + init: function(parent, dataset, views){ + this._super(parent); + this.dataset = dataset; + this.views = views; + this.views_id = {}; + for (var key in this.views) { + this.views_id[key] = this.views[key].view_id + } + }, + start: function() { + var self = this + self._super(false); + self.template = 'ImportDataView'; + self.dialog_title = "Import Data" + self.open({ + modal: true, + width: '70%', + height: 'auto', + position: 'top', + buttons : { + "Close" : function() { + self.stop(); + }, + "Import File" : function() { + $("#import_data").submit(); + //self.do_import(); + } + }, + close: function(event, ui){ self.close();} + }); + }, +}); +} \ No newline at end of file diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index b98b0bc637f..1d44fb3a0c0 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -212,11 +212,24 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ } } } + if(this.flags && this.flags.sidebar) { + if(this.$element.find('#importview')){ + this.$element.find('#importview').remove() + } + if(this.active_view == 'list' || this.active_view == 'form') { + this.views[this.active_view].controller.$element.after(QWeb.render('ImportView')) + this.$element.find('#importview').click(function(ev) { + var import_view = new openerp.base.Import(self, self.dataset, self.views); + import_view.start(false); + ev.preventDefault(); + }); + } + } return view_promise; }, /** * Event launched when a controller has been inited. - * + * * @param {String} view_type type of view * @param {String} view the inited controller */ @@ -522,7 +535,7 @@ openerp.base.View = openerp.base.Controller.extend({ * Directly set a view to use instead of calling fields_view_get. This method must * be called before start(). When an embedded view is set, underlying implementations * of openerp.base.View must use the provided view instead of any other one. - * + * * @param embedded_view A view. */ set_embedded_view: function(embedded_view) { diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 10d1539cde1..f7c2cf53054 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -103,7 +103,7 @@

We think that daily job activities can be more intuitive, efficient, automated, .. and even fun.

OpenERP's vision to be:

- + @@ -135,7 +135,7 @@
- +
@@ -333,7 +333,7 @@ t-att-data-index="row_index"> - + @@ -821,7 +821,7 @@ -
@@ -944,4 +944,87 @@ .unwrap();
+ + Import + + +
+ + + + + + + + + + + + + + + + + +
+ + + + + + + +
+

1. Import a .CSV file

+
+ Select a .CSV file to import. If you need a sample of file to import, + you should use the export tool with the "Import Compatible" option. +
+
+ + + + + +
+ +
+
+
+
+ + + + +
+

2. Check your file format

+
+ +
+
+ CSV Options + + + + + + + + + + + + + +
+ +
+
+
+
+
+
From 0a25bfc2ede24275c694fc5305fdcd47ebd436d0 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Wed, 27 Jul 2011 17:23:09 +0530 Subject: [PATCH 002/128] [FIX/BUG] Increased the Ldap Filter Size bzr revid: jam@tinyerp.com-20110727115309-awso1wew48qtisdt --- addons/users_ldap/users_ldap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/users_ldap/users_ldap.py b/addons/users_ldap/users_ldap.py index bddaaeed6ae..7c3bd0d9c17 100644 --- a/addons/users_ldap/users_ldap.py +++ b/addons/users_ldap/users_ldap.py @@ -43,7 +43,7 @@ class CompanyLDAP(osv.osv): 'ldap_password': fields.char('LDAP password', size=64, help=("The password of the user account on the LDAP server that is " "used to query the directory.")), - 'ldap_filter': fields.char('LDAP filter', size=64, required=True), + 'ldap_filter': fields.char('LDAP filter', size=256, required=True), 'ldap_base': fields.char('LDAP base', size=64, required=True), 'user': fields.many2one('res.users', 'Model User', help="Model used for user creation"), From c82c9ce5af3b7ac12274f5cdda312d1c43851e0e Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Wed, 3 Aug 2011 18:02:42 +0530 Subject: [PATCH 003/128] [IMP]Improve code for import data and preview of three records. bzr revid: kch@tinyerp.com-20110803123242-bpyq87hw0bfm0iiv --- addons/base/__openerp__.py | 1 + addons/base/controllers/main.py | 105 ++++++++++++++++-- addons/base/static/src/css/data_import.css | 23 ++++ addons/base/static/src/img/gradientlinebg.gif | Bin 0 -> 182 bytes addons/base/static/src/js/import.js | 78 ++++++++++++- addons/base/static/src/xml/base.xml | 28 ++++- 6 files changed, 217 insertions(+), 18 deletions(-) create mode 100644 addons/base/static/src/css/data_import.css create mode 100644 addons/base/static/src/img/gradientlinebg.gif diff --git a/addons/base/__openerp__.py b/addons/base/__openerp__.py index 715358713d8..28b92809b33 100644 --- a/addons/base/__openerp__.py +++ b/addons/base/__openerp__.py @@ -40,5 +40,6 @@ "static/lib/jquery.ui.notify/css/ui.notify.css", "static/src/css/base.css", "static/src/css/data_export.css", + "static/src/css/data_import.css", ], } diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index a2e6cc0dbed..777fd5685d1 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -1204,8 +1204,86 @@ class Export(View): class Import(View): _cp_path = "/base/import" + def fields_get(self, req, model): + Model = req.session.model(model) + fields = Model.fields_get(False, req.session.eval_context(req.context)) + return fields + @openerpweb.httprequest - def import_data(self, req, session_id, callback, model, id, csvfile, csvsep, csvdel, csvcode, csvskip, fields=[]): + def detect_data(self, req, session_id, model, id, csvfile, csvsep, csvdel, csvcode, csvskip): + import StringIO + _fields = {} + _fields_invert = {} + error = None + + fields = dict(req.session.model(model).fields_get(False, req.session.eval_context(req.context))) + fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}}) + + def model_populate(fields, prefix_node='', prefix=None, prefix_value='', level=2): + def str_comp(x,y): + if xy: return -1 + else: return 0 + + fields_order = fields.keys() + fields_order.sort(lambda x,y: str_comp(fields[x].get('string', ''), fields[y].get('string', ''))) + for field in fields_order: + if (fields[field].get('type','') not in ('reference',))\ + and (not fields[field].get('readonly')\ + or not dict(fields[field].get('states', {}).get( + 'draft', [('readonly', True)])).get('readonly',True)): + + st_name = prefix_value+fields[field]['string'] or field + _fields[prefix_node+field] = st_name + _fields_invert[st_name] = prefix_node+field + + if fields[field].get('type','')=='one2many' and level>0: + fields2 = self.fields_get(req, fields[field]['relation']) + model_populate(fields2, prefix_node+field+'/', None, st_name+'/', level-1) + + if fields[field].get('relation',False) and level>0: + model_populate({'/id': {'type': 'char', 'string': 'ID'}, '.id': {'type': 'char', 'string': 'Database ID'}}, + prefix_node+field, None, st_name+'/', level-1) + fields.update({'id':{'string':'ID'},'.id':{'string':'Database ID'}}) + model_populate(fields) + + try: + data = csv.reader(csvfile.file, quotechar=str(csvdel), delimiter=str(csvsep)) + except: + raise 'Error opening .CSV file', 'Input Error.' + + records = [] + fields = [] + word='' + limit = 3 + + try: + for i, row in enumerate(data): + records.append(row) + if i == limit: + break + for line in records: + for word in line: + word = str(word.decode(csvcode)) + if word in _fields: + fields.append((word, _fields[word])) + elif word in _fields_invert.keys(): + fields.append((_fields_invert[word], word)) + else: + error = {'message':("You cannot import the field '%s', because we cannot auto-detect it" % (word,))} + break + except: + error = {'message':('Error processing the first line of the file. Field "%s" is unknown') % (word,)} + + if error: + csvfile.file.seek(0) + error=dict(error, preview=csvfile.file.read(200)) + return simplejson.dumps({'error':error}) + + return simplejson.dumps({'records':records}) + + @openerpweb.httprequest + def import_data(self, req, session_id, model, id, csvfile, csvsep, csvdel, csvcode, csvskip, fields=[]): import StringIO context = req.session.eval_context(req.context) @@ -1217,7 +1295,8 @@ class Import(View): data = [] if not (csvdel and len(csvdel) == 1): - return "The CSV delimiter must be a single character" + error={'message': "The CSV delimiter must be a single character"} + return simplejson.dumps({'error':error}) try: for j, line in enumerate(csv.reader(input, quotechar=str(csvdel), delimiter=str(csvsep))): @@ -1228,8 +1307,9 @@ class Import(View): fields = line else: data.append(line) - except: - return "CSV Error" + except csv.Error, e: + error={'message': str(e),'title': 'File Format Error'} + return simplejson.dumps({'error':error}) datas = [] ctx = context @@ -1245,19 +1325,24 @@ class Import(View): # If the file contains nothing, if not datas: - return "The file is empty." + error = {'message': 'The file is empty !', 'title': 'Importation !'} + return simplejson.dumps({'error':error}) #Inverting the header into column names try: res = modle_obj.import_data(fields, datas, 'init', '', False, ctx) - except: - return "Error in Importing Data." + except Exception, e: + error = {'message':str(e), 'title':'XML-RPC error'} + return simplejson.dumps({'error':error}) if res[0]>=0: - return "Successfully Imported %d objects." % (res[0],) + success={'message':'Imported %d objects' % (res[0],)} + return simplejson.dumps({'success':success}) d = '' for key,val in res[1].items(): - d+= ('%s: %s' % (key,val)) + d+= ('%s: %s' % (str(key),str(val))) + msg = 'Error trying to import this record:%s. ErrorMessage:%s %s' % (d,res[2],res[3]) + error = {'message':str(msg), 'title':'ImportationError'} - return "Error trying to import this record :%s. ErrorMessage:%s %s" % (d,res[2],res[3]) + return simplejson.dumps({'error':error}) diff --git a/addons/base/static/src/css/data_import.css b/addons/base/static/src/css/data_import.css new file mode 100644 index 00000000000..a0d63a2d99a --- /dev/null +++ b/addons/base/static/src/css/data_import.css @@ -0,0 +1,23 @@ + +.grid { + border: none; + border-collapse: collapse; +} + +.grid-header .grid-cell { + background: url(../img/gradientlinebg.gif); + border-bottom: 1px solid #E3E3E3; + font-weight: bold; + text-align: left; +} + +.grid-row .grid-cell { + border-bottom: 1px solid #E3E3E3; +} + +.openerp .separator.horizontal { + font-weight: bold; + border-bottom-width: 1px; + margin: 6px 4px 6px 1px; + height: 20px; +} \ No newline at end of file diff --git a/addons/base/static/src/img/gradientlinebg.gif b/addons/base/static/src/img/gradientlinebg.gif new file mode 100644 index 0000000000000000000000000000000000000000..f40bd9c0f6a1a85e5e4d51d16cfb693d3f1cec31 GIT binary patch literal 182 zcmV;n07?HxNk%w1VFCa=0E7Vm00030|Ns8}{{8*^{QUg-`uh3#`SER?(XjE>+9+1>F4L?<>lq$z>(a_M) z&d$!w&CSfr%>V!YEC2ui00IC!000F$U=RqxFc3sh7{+lRNRlYZvM@~3IL`AxkVr%# knM^2@O2uNiTrikSMx)tmIGj$$!|i&%;IMd14u=2$JFIAN1ONa4 literal 0 HcmV?d00001 diff --git a/addons/base/static/src/js/import.js b/addons/base/static/src/js/import.js index e13e4f04046..a41fade9c03 100644 --- a/addons/base/static/src/js/import.js +++ b/addons/base/static/src/js/import.js @@ -24,12 +24,86 @@ openerp.base.Import = openerp.base.Dialog.extend({ self.stop(); }, "Import File" : function() { - $("#import_data").submit(); - //self.do_import(); + //$("#import_data").submit(); + self.do_import(); } }, close: function(event, ui){ self.stop();} }); + this.$element.find('#csvfile').change(this.on_autodetect_data); + this.$element.find('fieldset legend').click(function () { + $(this).next().toggle(); + }); + }, + do_import: function() { + var self = this; + if(!this.$element.find('#csvfile').val()) { return; } + this.$element.find('#import_data').attr({ + 'action': '/base/import/import_data' + }).ajaxSubmit({ + success: this.import_results + }); + }, + on_autodetect_data: function() { + var self = this; + if(this.$element.find("#res td")){ + this.$element.find("#res td").remove(); + this.$element.find("#imported_success").css('display','none'); + } + if(!this.$element.find('#csvfile').val()) { return; } + this.$element.find('#import_data').attr({ + 'action': '/base/import/detect_data' + }).ajaxSubmit({ + success: this.import_results + }); + }, + import_results:function(res){ + var self = this; + var results = $.parseJSON(res); + + if (results['records']){ + var result = results['records']; + if ($('#error').find('table')){ + $("#error table").remove(); + } + if ($('#records_data').find('tr')){ + $("#records_data tr").remove(); + } + for (i in result) { + if (i == 0){ + $('#records_data').append(''); + for (m in result[i]){ + $('.grid-header').append(''+result[i][m]+''); + } + }else{ + $('#records_data tr:last').after(''); + for (n in result[i]){ + $("tr[id="+i+"]").append(''+result[i][n]+''); + } + } + } + }else if(results['error']){ + var result = results['error']; + if ($('#records_data').find('tr')){ + $("#records_data tr").remove(); + } + if ($('#error').find('table')){ + $("#error table").remove(); + } + $("#error").append('The import failed due to:'+result['message']+'
'); + if (result['preview']){ + $("#error_tbl tr:last").after('Here is a preview of the file we could not import:'); + $("#error_tbl tr:last").after('
'+result['preview']+'
'); + } + }else if(results['success']){ + var result = results['success']; + $("#imported_success").css('display','block'); + $("#res").append(''+result['message']+'') + } + }, + stop: function() { + $(this.$dialog).remove(); + this._super(); }, }); } \ No newline at end of file diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 9f76bf12afe..563c5bab43a 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -1280,9 +1280,8 @@ Import -
+ - @@ -1291,7 +1290,7 @@
@@ -1325,15 +1324,17 @@
-

1. Import a .CSV file

+

1. Import a .CSV file

-

2. Check your file format

+

2. Check your file format

- +
+
+
CSV Options - +
@@ -1356,6 +1357,21 @@ + + +
+ +
From 2075da0365c070f2e706e27c2df043ebd3a788e0 Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Wed, 3 Aug 2011 18:31:46 +0530 Subject: [PATCH 004/128] [FIX]Renamed import module to data_import. bzr revid: kch@tinyerp.com-20110803130146-ur8a91zoe6w7qjml --- addons/base/__openerp__.py | 2 +- addons/base/static/src/js/boot.js | 4 ++-- .../static/src/js/{import.js => data_import.js} | 11 +++-------- addons/base/static/src/js/views.js | 15 ++------------- 4 files changed, 8 insertions(+), 24 deletions(-) rename addons/base/static/src/js/{import.js => data_import.js} (92%) diff --git a/addons/base/__openerp__.py b/addons/base/__openerp__.py index 28b92809b33..658ebe5c6ba 100644 --- a/addons/base/__openerp__.py +++ b/addons/base/__openerp__.py @@ -27,12 +27,12 @@ "static/src/js/views.js", "static/src/js/data.js", "static/src/js/data_export.js", + "static/src/js/data_import.js", "static/src/js/form.js", "static/src/js/list.js", "static/src/js/list-editable.js", "static/src/js/search.js", "static/src/js/view_tree.js", - "static/src/js/import.js", ], 'css' : [ "static/lib/jquery.superfish/css/superfish.css", diff --git a/addons/base/static/src/js/boot.js b/addons/base/static/src/js/boot.js index dc0d514177b..a49ee13e2ba 100644 --- a/addons/base/static/src/js/boot.js +++ b/addons/base/static/src/js/boot.js @@ -89,8 +89,8 @@ openerp.base = function(instance) { if (openerp.base.data_export) { openerp.base.data_export(instance); } - if (openerp.base.import){ - openerp.base.import(instance); + if (openerp.base.data_import){ + openerp.base.data_import(instance); } }; diff --git a/addons/base/static/src/js/import.js b/addons/base/static/src/js/data_import.js similarity index 92% rename from addons/base/static/src/js/import.js rename to addons/base/static/src/js/data_import.js index a41fade9c03..0a31237c9b1 100644 --- a/addons/base/static/src/js/import.js +++ b/addons/base/static/src/js/data_import.js @@ -1,13 +1,8 @@ -openerp.base.import = function(openerp) { -openerp.base.Import = openerp.base.Dialog.extend({ - init: function(parent, dataset, views){ +openerp.base.data_import = function(openerp) { +openerp.base.DataImport = openerp.base.Dialog.extend({ + init: function(parent, dataset){ this._super(parent); this.dataset = dataset; - this.views = views; - this.views_id = {}; - for (var key in this.views) { - this.views_id[key] = this.views[key].view_id - } }, start: function() { var self = this diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index e957505ae37..c0f98eb552c 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -216,19 +216,6 @@ openerp.base.ViewManager = openerp.base.Widget.extend({ } } } - if(this.flags && this.flags.sidebar) { - if(this.$element.find('#importview')){ - this.$element.find('#importview').remove() - } - if(this.active_view == 'list' || this.active_view == 'form') { - this.views[this.active_view].controller.$element.after(QWeb.render('ImportView')) - this.$element.find('#importview').click(function(ev) { - var import_view = new openerp.base.Import(self, self.dataset, self.views); - import_view.start(false); - ev.preventDefault(); - }); - } - } return view_promise; }, /** @@ -575,6 +562,8 @@ openerp.base.View = openerp.base.Widget.extend({ console.log('Todo'); }, on_sidebar_import: function() { + var import_view = new openerp.base.DataImport(this, this.dataset); + import_view.start(false); }, on_sidebar_export: function() { var export_view = new openerp.base.DataExport(this, this.dataset); From dd2e69fcc16daec9141935c9d3bdf650280012cd Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Mon, 8 Aug 2011 11:57:03 +0530 Subject: [PATCH 005/128] [IMP]Improve code for preview records. bzr revid: kch@tinyerp.com-20110808062703-sv394tf2rr79na34 --- addons/base/controllers/main.py | 3 +- addons/base/static/src/js/data_import.js | 45 +++-------------- addons/base/static/src/xml/base.xml | 62 ++++++++++++++++++------ 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 777fd5685d1..5636be13701 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -1256,7 +1256,6 @@ class Import(View): fields = [] word='' limit = 3 - try: for i, row in enumerate(data): records.append(row) @@ -1280,7 +1279,7 @@ class Import(View): error=dict(error, preview=csvfile.file.read(200)) return simplejson.dumps({'error':error}) - return simplejson.dumps({'records':records}) + return simplejson.dumps({'records':records[1:],'fields':fields}) @openerpweb.httprequest def import_data(self, req, session_id, model, id, csvfile, csvsep, csvdel, csvcode, csvskip, fields=[]): diff --git a/addons/base/static/src/js/data_import.js b/addons/base/static/src/js/data_import.js index 0a31237c9b1..3b51f75c198 100644 --- a/addons/base/static/src/js/data_import.js +++ b/addons/base/static/src/js/data_import.js @@ -19,7 +19,6 @@ openerp.base.DataImport = openerp.base.Dialog.extend({ self.stop(); }, "Import File" : function() { - //$("#import_data").submit(); self.do_import(); } }, @@ -53,47 +52,17 @@ openerp.base.DataImport = openerp.base.Dialog.extend({ }); }, import_results:function(res){ - var self = this; var results = $.parseJSON(res); - + $('#result, #success').empty(); + var result_node = $("#result"); if (results['records']){ - var result = results['records']; - if ($('#error').find('table')){ - $("#error table").remove(); - } - if ($('#records_data').find('tr')){ - $("#records_data tr").remove(); - } - for (i in result) { - if (i == 0){ - $('#records_data').append(''); - for (m in result[i]){ - $('.grid-header').append(''+result[i][m]+''); - } - }else{ - $('#records_data tr:last').after(''); - for (n in result[i]){ - $("tr[id="+i+"]").append(''+result[i][n]+''); - } - } - } + records = {'header':results['fields'],'row':results['records']}; + result_node.append(QWeb.render('ImportView-result',{'records':records})); }else if(results['error']){ - var result = results['error']; - if ($('#records_data').find('tr')){ - $("#records_data tr").remove(); - } - if ($('#error').find('table')){ - $("#error table").remove(); - } - $("#error").append('The import failed due to:'+result['message']+'
'); - if (result['preview']){ - $("#error_tbl tr:last").after('Here is a preview of the file we could not import:'); - $("#error_tbl tr:last").after('
'+result['preview']+'
'); - } + result_node.append(QWeb.render('ImportView-result',{'error': results['error']})); }else if(results['success']){ - var result = results['success']; - $("#imported_success").css('display','block'); - $("#res").append(''+result['message']+'') + var success_node = $("#success"); + success_node.append(QWeb.render('ImportView-result',{'success': results['success']})); } }, stop: function() { diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 563c5bab43a..11d4e905f39 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -1328,10 +1328,7 @@ -
-
- -
+
CSV Options @@ -1359,20 +1356,55 @@
- +
+ + + + The import failed due to: + + Here is a preview of the file we could not import: +
+ +
+
+ + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + +
+

3. File imported

+
+
+
From 805acc602d3228670c2ddf3b1e73db8dc42ef0bb Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Tue, 9 Aug 2011 12:14:41 +0530 Subject: [PATCH 006/128] [IMP]Improve code for passing parameter. bzr revid: kch@tinyerp.com-20110809064441-edw62zxu7c0r8uvv --- addons/base/controllers/main.py | 24 +++++++++++------------ addons/base/static/src/js/chrome.js | 30 ++++++++++++++--------------- addons/base/static/src/xml/base.xml | 1 - 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 1a216c22bc9..214062493f3 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -1225,13 +1225,13 @@ class Import(View): return fields @openerpweb.httprequest - def detect_data(self, req, session_id, model, id, csvfile, csvsep, csvdel, csvcode, csvskip): + def detect_data(self, req, **params): import StringIO _fields = {} _fields_invert = {} error = None - fields = dict(req.session.model(model).fields_get(False, req.session.eval_context(req.context))) + fields = dict(req.session.model(params.get('model')).fields_get(False, req.session.eval_context(req.context))) fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}}) def model_populate(fields, prefix_node='', prefix=None, prefix_value='', level=2): @@ -1263,7 +1263,7 @@ class Import(View): model_populate(fields) try: - data = csv.reader(csvfile.file, quotechar=str(csvdel), delimiter=str(csvsep)) + data = csv.reader(params.get('csvfile').file, quotechar=str(params.get('csvdel')), delimiter=str(params.get('csvsep'))) except: raise 'Error opening .CSV file', 'Input Error.' @@ -1278,7 +1278,7 @@ class Import(View): break for line in records: for word in line: - word = str(word.decode(csvcode)) + word = str(word.decode(params.get('csvcode'))) if word in _fields: fields.append((word, _fields[word])) elif word in _fields_invert.keys(): @@ -1290,30 +1290,30 @@ class Import(View): error = {'message':('Error processing the first line of the file. Field "%s" is unknown') % (word,)} if error: - csvfile.file.seek(0) - error=dict(error, preview=csvfile.file.read(200)) + params.get('csvfile').file.seek(0) + error=dict(error, preview=params.get('csvfile').file.read(200)) return simplejson.dumps({'error':error}) return simplejson.dumps({'records':records[1:],'fields':fields}) @openerpweb.httprequest - def import_data(self, req, session_id, model, id, csvfile, csvsep, csvdel, csvcode, csvskip, fields=[]): + def import_data(self, req, **params): import StringIO context = req.session.eval_context(req.context) - modle_obj = req.session.model(model) + modle_obj = req.session.model(params.get('model')) res = None - content = csvfile.file.read() + content = params.get('csvfile').file.read() input=StringIO.StringIO(content) limit = 0 data = [] - if not (csvdel and len(csvdel) == 1): + if not (params.get('csvdel') and len(params.get('csvdel')) == 1): error={'message': "The CSV delimiter must be a single character"} return simplejson.dumps({'error':error}) try: - for j, line in enumerate(csv.reader(input, quotechar=str(csvdel), delimiter=str(csvsep))): + for j, line in enumerate(csv.reader(input, quotechar=str(params.get('csvdel')), delimiter=str(params.get('csvsep')))): # If the line contains no data, we should skip it. if not line: continue @@ -1333,7 +1333,7 @@ class Import(View): for line in data: try: - datas.append(map(lambda x:x.decode(csvcode).encode('utf-8'), line)) + datas.append(map(lambda x:x.decode(params.get('csvcode')).encode('utf-8'), line)) except: datas.append(map(lambda x:x.decode('latin').encode('utf-8'), line)) diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index e44e25b3c50..3ac161f70de 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -333,12 +333,12 @@ openerp.base.Dialog = openerp.base.OldWidget.extend({ }, set_options: function(options) { options = options || {}; - options.width = this.get_width(options.width || this.options.width); - options.min_width = this.get_width(options.min_width || this.options.min_width); - options.max_width = this.get_width(options.max_width || this.options.max_width); - options.height = this.get_height(options.height || this.options.height); - options.min_height = this.get_height(options.min_height || this.options.min_height); - options.max_height = this.get_height(options.max_height || this.options.max_width); + options.width = this.get_width(options.width || this.dialog_options.width); + options.min_width = this.get_width(options.min_width || this.dialog_options.min_width); + options.max_width = this.get_width(options.max_width || this.dialog_options.max_width); + options.height = this.get_height(options.height || this.dialog_options.height); + options.min_height = this.get_height(options.min_height || this.dialog_options.min_height); + options.max_height = this.get_height(options.max_height || this.dialog_options.max_width); if (options.width !== 'auto') { if (options.width > options.max_width) options.width = options.max_width; @@ -461,9 +461,9 @@ openerp.base.Database = openerp.base.Widget.extend({ this.$element.closest(".openerp") .removeClass("login-mode") .addClass("database_block"); - + var self = this; - + var fetch_db = this.rpc("/base/database/get_list", {}, function(result) { self.db_list = result.db_list; }); @@ -475,7 +475,7 @@ openerp.base.Database = openerp.base.Widget.extend({ self.lang_list = result.lang_list; }); $.when(fetch_db, fetch_langs).then(function () {self.do_create();}); - + this.$element.find('#db-create').click(this.do_create); this.$element.find('#db-drop').click(this.do_drop); this.$element.find('#db-backup').click(this.do_backup); @@ -594,11 +594,11 @@ openerp.base.Database = openerp.base.Widget.extend({ } }); }, - + do_drop: function() { var self = this; self.$option_id.html(QWeb.render("DropDB", self)); - + self.$option_id.find("form[name=drop_db_form]").validate({ submitHandler: function (form) { var $form = $(form), @@ -682,11 +682,11 @@ openerp.base.Database = openerp.base.Widget.extend({ } }); }, - + do_restore: function() { var self = this; self.$option_id.html(QWeb.render("RestoreDB", self)); - + self.$option_id.find("form[name=restore_db_form]").validate({ submitHandler: function (form) { $.blockUI(); @@ -752,7 +752,7 @@ openerp.base.Database = openerp.base.Widget.extend({ openerp.base.Login = openerp.base.Widget.extend({ remember_creditentials: true, - + init: function(parent, element_id) { this._super(parent, element_id); this.has_local_storage = typeof(localStorage) != 'undefined'; @@ -981,7 +981,7 @@ openerp.base.WebClient = openerp.base.Widget.extend({ this.menu = new openerp.base.Menu(this, "oe_menu", "oe_secondary_menu"); this.menu.on_action.add(this.on_menu_action); - + }, start: function() { this.session.start(); diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index a9ea7e557a1..fc02bd7e33d 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -1282,7 +1282,6 @@
- - From 27c2db6a61d623ccce4e94c890dc0cf5972cdaca Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Mon, 26 Sep 2011 17:44:53 +0200 Subject: [PATCH 116/128] [IMP] Added 'Expected Revenues' aggregate in opportunities kanban view bzr revid: fme@openerp.com-20110926154453-cz72hniz203hnj4q --- addons/crm/crm_lead_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index d631e371948..12a3428236d 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -302,7 +302,7 @@ - + From 78cdb8d1ab6081a7c9ce3bb63ff1560e67270918 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Mon, 26 Sep 2011 18:00:57 +0200 Subject: [PATCH 117/128] [fix] problem in o2m form view with read only fields, now saves result of default_get bzr revid: nicolas.vanhoren@openerp.com-20110926160057-zss7934ji5hhtq44 --- addons/web/static/src/js/data.js | 23 ++++++++++++++++++----- addons/web/static/src/js/view_form.js | 9 ++++++--- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index e5d323c8a96..00281628e75 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -608,15 +608,23 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({ init: function() { this._super.apply(this, arguments); this.reset_ids([]); + this.last_default_get = {}; + }, + default_get: function(fields, callback) { + return this._super(fields).then(this.on_default_get).then(callback); + }, + on_default_get: function(res) { + this.last_default_get = res; }, create: function(data, callback, error_callback) { - var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data}; + var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data, + defaults: this.last_default_get}; this.to_create.push(cached); this.cache.push(cached); this.on_change(); - var to_return = $.Deferred().then(callback); - to_return.resolve({result: cached.id}); - return to_return.promise(); + var prom = $.Deferred().then(callback); + setTimeout(function() {prom.resolve({result: cached.id});}, 0); + return prom.promise(); }, write: function (id, data, options, callback) { var self = this; @@ -676,7 +684,8 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({ var cached = _.detect(self.cache, function(x) {return x.id === id;}); var created = _.detect(self.to_create, function(x) {return x.id === id;}); if (created) { - _.each(fields, function(x) {if (cached.values[x] === undefined) cached.values[x] = false;}); + _.each(fields, function(x) {if (cached.values[x] === undefined) + cached.values[x] = created.defaults[x] || false;}); } else { if (!cached || !_.all(fields, function(x) {return cached.values[x] !== undefined})) to_get.push(id); @@ -716,6 +725,10 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({ } }); openerp.web.ReadOnlyDataSetSearch = openerp.web.DataSetSearch.extend({ + default_get: function(fields, callback) { + return this._super(fields, callback).then(this.on_default_get); + }, + on_default_get: function(result) {}, create: function(data, callback, error_callback) { this.on_create(data); var to_return = $.Deferred().then(callback); diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 806c0f0b319..c7ffb896186 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -474,8 +474,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# recursive_save: function() { var self = this; return $.when(this.do_save()).pipe(function(res) { - if (self.parent_form_view) - return self.parent_form_view.recursive_save(); + if (self.dataset.parent_view) + return self.dataset.parent_view.recursive_save(); }); } }); @@ -2004,6 +2004,7 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({ } else { var self = this; var pop = new openerp.web.form.SelectCreatePopup(this); + pop.on_default_get.add(self.dataset.on_default_get); pop.select_element(self.o2m.field.relation,{ initial_view: "form", alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined, @@ -2165,6 +2166,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope this.dataset = new openerp.web.ReadOnlyDataSetSearch(this, this.model, this.context); this.dataset.parent_view = this.options.parent_view; + this.dataset.on_default_get.add(this.on_default_get); if (this.options.initial_view == "search") { this.setup_search_view(); } else { // "form" @@ -2287,7 +2289,8 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope this.on_select_elements(this.created_elements); } this.stop(); - } + }, + on_default_get: function(res) {} }); openerp.web.form.SelectCreateListView = openerp.web.ListView.extend({ From 83df79527e2e1cdf843f3638c32a5756a1d2bed5 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Mon, 26 Sep 2011 19:24:13 +0200 Subject: [PATCH 118/128] [IMP] default sequences are shared in multicompany bzr revid: al@openerp.com-20110926172413-5idcn0u7d3cppihn --- addons/purchase/purchase_sequence.xml | 2 +- addons/sale/sale_sequence.xml | 1 + addons/stock/stock_sequence.xml | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/addons/purchase/purchase_sequence.xml b/addons/purchase/purchase_sequence.xml index e8a7f43c503..d5b4fc4bd3b 100644 --- a/addons/purchase/purchase_sequence.xml +++ b/addons/purchase/purchase_sequence.xml @@ -11,7 +11,7 @@ purchase.order PO 5 - + diff --git a/addons/sale/sale_sequence.xml b/addons/sale/sale_sequence.xml index dadd1942940..666f7b49720 100644 --- a/addons/sale/sale_sequence.xml +++ b/addons/sale/sale_sequence.xml @@ -13,6 +13,7 @@ sale.order SO 3 + diff --git a/addons/stock/stock_sequence.xml b/addons/stock/stock_sequence.xml index cabf24dda5b..a8515c2f0df 100644 --- a/addons/stock/stock_sequence.xml +++ b/addons/stock/stock_sequence.xml @@ -29,6 +29,7 @@ stock.picking.in IN/ 5 + @@ -36,6 +37,7 @@ stock.picking.out OUT/ 5 + @@ -43,6 +45,7 @@ stock.picking.internal INT/ 5 +
From a95630f17b890dfa153b0f82d777ec9d020486b8 Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Tue, 16 Aug 2011 11:38:35 +0530 Subject: [PATCH 007/128] [IMP]Improved code for csv options and error message. bzr revid: kch@tinyerp.com-20110816060835-f6ub35t0p2ye2yn0 --- addons/base/controllers/main.py | 7 ++++--- addons/base/static/src/js/data_import.js | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 08ef89b2cdf..f6c473e813a 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -1290,7 +1290,8 @@ class Import(View): try: data = csv.reader(params.get('csvfile').file, quotechar=str(params.get('csvdel')), delimiter=str(params.get('csvsep'))) except: - raise 'Error opening .CSV file', 'Input Error.' + error={'message': 'error opening .CSV file. Input Error.'} + return simplejson.dumps({'error':error}) records = [] fields = [] @@ -1370,8 +1371,8 @@ class Import(View): #Inverting the header into column names try: res = modle_obj.import_data(fields, datas, 'init', '', False, ctx) - except Exception, e: - error = {'message':str(e), 'title':'XML-RPC error'} + except xmlrpclib.Fault, e: + error = {"message":e.faultCode} return simplejson.dumps({'error':error}) if res[0]>=0: diff --git a/addons/base/static/src/js/data_import.js b/addons/base/static/src/js/data_import.js index 3b51f75c198..27291c0770a 100644 --- a/addons/base/static/src/js/data_import.js +++ b/addons/base/static/src/js/data_import.js @@ -25,6 +25,7 @@ openerp.base.DataImport = openerp.base.Dialog.extend({ close: function(event, ui){ self.stop();} }); this.$element.find('#csvfile').change(this.on_autodetect_data); + this.$element.find('fieldset').change(this.on_autodetect_data); this.$element.find('fieldset legend').click(function () { $(this).next().toggle(); }); @@ -52,8 +53,8 @@ openerp.base.DataImport = openerp.base.Dialog.extend({ }); }, import_results:function(res){ - var results = $.parseJSON(res); $('#result, #success').empty(); + var results = $.parseJSON(res); var result_node = $("#result"); if (results['records']){ records = {'header':results['fields'],'row':results['records']}; From c9dcb822c48b28ddb77e41e437d2b0d05b75fd48 Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Fri, 19 Aug 2011 12:42:16 +0530 Subject: [PATCH 008/128] [FIX]Missing semicolon and modifiy css. bzr revid: kch@tinyerp.com-20110819071216-lih8yiclgfn6w5dl --- addons/base/static/src/css/data_import.css | 12 ++++-------- addons/base/static/src/js/data_import.js | 2 +- addons/base/static/src/xml/base.xml | 14 +++++++------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/addons/base/static/src/css/data_import.css b/addons/base/static/src/css/data_import.css index a0d63a2d99a..65f2e0b258b 100644 --- a/addons/base/static/src/css/data_import.css +++ b/addons/base/static/src/css/data_import.css @@ -1,20 +1,16 @@ - -.grid { +.openerp .oe_import_grid { border: none; border-collapse: collapse; } - -.grid-header .grid-cell { - background: url(../img/gradientlinebg.gif); +.openerp .oe_import_grid-header .oe_import_grid-cell { + background: url(../img/gradientlinebg.gif) repeat-x #CCCCCC; border-bottom: 1px solid #E3E3E3; font-weight: bold; text-align: left; } - -.grid-row .grid-cell { +.openerp .oe_import_grid-row .oe_import_grid-cell { border-bottom: 1px solid #E3E3E3; } - .openerp .separator.horizontal { font-weight: bold; border-bottom-width: 1px; diff --git a/addons/base/static/src/js/data_import.js b/addons/base/static/src/js/data_import.js index 27291c0770a..6450f8709a3 100644 --- a/addons/base/static/src/js/data_import.js +++ b/addons/base/static/src/js/data_import.js @@ -71,4 +71,4 @@ openerp.base.DataImport = openerp.base.Dialog.extend({ this._super(); }, }); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 2f94947f6af..1ee1579f5d4 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -1363,9 +1363,9 @@ CSV Options - + - + @@ -1403,20 +1403,20 @@
- +
- + - + - + - + From b8cc16cc5dfb6faf0ddb1f65bc197b60c554cb07 Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Thu, 25 Aug 2011 11:19:30 +0530 Subject: [PATCH 009/128] [IMP]Improved code in boot.js. bzr revid: kch@tinyerp.com-20110825054930-z9p9zj276e6haj1l --- addons/base/controllers/main.py | 6 +++--- addons/base/static/src/js/boot.js | 5 +---- addons/base/static/src/js/form.js | 1 + 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index f04e59641f8..91819b1b173 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -168,14 +168,14 @@ class WebClient(openerpweb.Controller): "grouping", "decimal_point", "thousands_sep"]) else: lang_obj = None - + if lang.count("_") > 0: separator = "_" else: separator = "@" langs = lang.split(separator) langs = [separator.join(langs[:x]) for x in range(1, len(langs) + 1)] - + transs = {} for addon_name in mods: transl = {"messages":[]} @@ -1331,7 +1331,7 @@ class Import(View): records = [] fields = [] word='' - limit = 3 + limit = 4 try: for i, row in enumerate(data): records.append(row) diff --git a/addons/base/static/src/js/boot.js b/addons/base/static/src/js/boot.js index dd451efb0c3..5ae30611195 100644 --- a/addons/base/static/src/js/boot.js +++ b/addons/base/static/src/js/boot.js @@ -54,15 +54,12 @@ openerp.base = function(instance) { openerp.base.formats(instance); openerp.base.chrome(instance); openerp.base.data(instance); - files = ["views","search","list","form","list_editable","web_mobile","view_tree","data_export"]; + files = ["views","search","list","form","list_editable","web_mobile","view_tree","data_export","data_import"]; for(i=0; i Date: Mon, 29 Aug 2011 17:24:03 +0530 Subject: [PATCH 010/128] [FIX] account_sequence: View definition error for inherited view lp bug: https://launchpad.net/bugs/828603 fixed bzr revid: ara@tinyerp.com-20110829115403-g7vs4608eac2okyb --- addons/account_sequence/account_sequence.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account_sequence/account_sequence.xml b/addons/account_sequence/account_sequence.xml index 6b9e2c94402..ba97025bae7 100644 --- a/addons/account_sequence/account_sequence.xml +++ b/addons/account_sequence/account_sequence.xml @@ -70,7 +70,7 @@ - + From 4e8923ec8c50d455437c80fbcb5d4b8f9542b1d6 Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Mon, 29 Aug 2011 18:17:43 +0530 Subject: [PATCH 011/128] [IMP]Implement selection box of list of fields under the header. bzr revid: kch@tinyerp.com-20110829124743-0pcuv3e0u37cbpjf --- addons/base/controllers/main.py | 12 +++++++----- addons/base/static/src/js/data_import.js | 9 ++++++++- addons/base/static/src/xml/base.xml | 18 +++++++++++++++++- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index ca4b8c76f7a..c56a66342f0 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -436,7 +436,7 @@ def clean_action(req, action): context = req.session.eval_context(req.context) eval_ctx = req.session.evaluation_context(context) action.setdefault('flags', {}) - + # values come from the server, we can just eval them if isinstance(action.get('context'), basestring): action['context'] = eval( action['context'], eval_ctx ) or {} @@ -1289,7 +1289,7 @@ class Import(View): _fields = {} _fields_invert = {} error = None - + all_fields = [] fields = dict(req.session.model(params.get('model')).fields_get(False, req.session.eval_context(req.context))) fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}}) @@ -1320,7 +1320,7 @@ class Import(View): prefix_node+field, None, st_name+'/', level-1) fields.update({'id':{'string':'ID'},'.id':{'string':'Database ID'}}) model_populate(fields) - + all_fields = fields.keys() try: data = csv.reader(params.get('csvfile').file, quotechar=str(params.get('csvdel')), delimiter=str(params.get('csvsep'))) except: @@ -1336,6 +1336,7 @@ class Import(View): records.append(row) if i == limit: break + for line in records: for word in line: word = str(word.decode(params.get('csvcode'))) @@ -1344,7 +1345,8 @@ class Import(View): elif word in _fields_invert.keys(): fields.append((_fields_invert[word], word)) else: - error = {'message':("You cannot import the field '%s', because we cannot auto-detect it" % (word,))} + fields.append((word, word)) +# error = {'message':("You cannot import the field '%s', because we cannot auto-detect it" % (word,))} break except: error = {'message':('Error processing the first line of the file. Field "%s" is unknown') % (word,)} @@ -1354,7 +1356,7 @@ class Import(View): error=dict(error, preview=params.get('csvfile').file.read(200)) return simplejson.dumps({'error':error}) - return simplejson.dumps({'records':records[1:],'fields':fields}) + return simplejson.dumps({'records':records[1:],'fields':fields,'all_fields':all_fields}) @openerpweb.httprequest def import_data(self, req, **params): diff --git a/addons/base/static/src/js/data_import.js b/addons/base/static/src/js/data_import.js index 6450f8709a3..a98f387eeaf 100644 --- a/addons/base/static/src/js/data_import.js +++ b/addons/base/static/src/js/data_import.js @@ -54,10 +54,11 @@ openerp.base.DataImport = openerp.base.Dialog.extend({ }, import_results:function(res){ $('#result, #success').empty(); + $(".ui-button-text:contains('Import File')").parent().attr("disabled",false); var results = $.parseJSON(res); var result_node = $("#result"); if (results['records']){ - records = {'header':results['fields'],'row':results['records']}; + records = {'header':results['fields'],'sel':results['all_fields'],'row':results['records']}; result_node.append(QWeb.render('ImportView-result',{'records':records})); }else if(results['error']){ result_node.append(QWeb.render('ImportView-result',{'error': results['error']})); @@ -65,6 +66,12 @@ openerp.base.DataImport = openerp.base.Dialog.extend({ var success_node = $("#success"); success_node.append(QWeb.render('ImportView-result',{'success': results['success']})); } + $('td #header').each(function(){ + var head = $(this).text(); + if (jQuery.inArray(head,results['all_fields']) <= -1){ + $(".ui-button-text:contains('Import File')").parent().attr("disabled",true); + } + }); }, stop: function() { $(this.$dialog).remove(); diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 60b9c4c4c08..7fd999d00d5 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -1459,7 +1459,23 @@ - + + + + + + + + From 573e20d7e6fe6f0a1ab1ce90a0c9c4516228d566 Mon Sep 17 00:00:00 2001 From: "Mayur Maheshwari (OpenERP)" Date: Tue, 30 Aug 2011 14:37:37 +0530 Subject: [PATCH 012/128] [FIX]purchase:now action_picking_create method returns true lp bug: https://launchpad.net/bugs/788789 fixed bzr revid: mma@tinyerp.com-20110830090737-gf4uetngbaet1ku9 --- 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 c756bf05128..e5a6f003e63 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -474,7 +474,7 @@ class purchase_order(osv.osv): self.pool.get('stock.move').force_assign(cr, uid, todo_moves) wf_service = netsvc.LocalService("workflow") wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) - return picking_id + return True def copy(self, cr, uid, id, default=None, context=None): if not default: From b3bb9d330651a9ae50b79e4cffda508289db716d Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Wed, 31 Aug 2011 16:22:32 +0530 Subject: [PATCH 013/128] [FIX]Fix header problem in preview table and selection of column if duplicate it will highlight. bzr revid: kch@tinyerp.com-20110831105232-2gra1irdwv61aj02 --- addons/base/controllers/main.py | 14 ++++++++++---- addons/base/static/src/js/data_import.js | 24 +++++++++++++++++------- addons/base/static/src/xml/base.xml | 4 ++-- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 4b07cac867b..f01bf1867f6 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -1304,7 +1304,6 @@ class Export(View): #TODO: ok now we've got the report, and so what? return False - class Import(View): _cp_path = "/base/import" @@ -1351,6 +1350,7 @@ class Import(View): fields.update({'id':{'string':'ID'},'.id':{'string':'Database ID'}}) model_populate(fields) all_fields = fields.keys() + try: data = csv.reader(params.get('csvfile').file, quotechar=str(params.get('csvdel')), delimiter=str(params.get('csvsep'))) except: @@ -1361,13 +1361,16 @@ class Import(View): fields = [] word='' limit = 4 + count = 0 try: for i, row in enumerate(data): records.append(row) if i == limit: break - for line in records: + for j, line in enumerate(records): + if j == 1: + break for word in line: word = str(word.decode(params.get('csvcode'))) if word in _fields: @@ -1375,8 +1378,12 @@ class Import(View): elif word in _fields_invert.keys(): fields.append((_fields_invert[word], word)) else: + count = count + 1 fields.append((word, word)) # error = {'message':("You cannot import the field '%s', because we cannot auto-detect it" % (word,))} + + if len(line) == count: + error = {'message':"File has not any column header."} except: error = {'message':('Error processing the first line of the file. Field "%s" is unknown') % (word,)} @@ -1385,7 +1392,7 @@ class Import(View): error=dict(error, preview=params.get('csvfile').file.read(200)) return simplejson.dumps({'error':error}) - return simplejson.dumps({'records':records[1:],'fields':fields,'all_fields':all_fields}) + return simplejson.dumps({'records':records[1:],'header':fields,'all_fields':all_fields}) @openerpweb.httprequest def import_data(self, req, **params): @@ -1449,5 +1456,4 @@ class Import(View): d+= ('%s: %s' % (str(key),str(val))) msg = 'Error trying to import this record:%s. ErrorMessage:%s %s' % (d,res[2],res[3]) error = {'message':str(msg), 'title':'ImportationError'} - return simplejson.dumps({'error':error}) diff --git a/addons/base/static/src/js/data_import.js b/addons/base/static/src/js/data_import.js index a98f387eeaf..e09b4dc743f 100644 --- a/addons/base/static/src/js/data_import.js +++ b/addons/base/static/src/js/data_import.js @@ -54,11 +54,11 @@ openerp.base.DataImport = openerp.base.Dialog.extend({ }, import_results:function(res){ $('#result, #success').empty(); - $(".ui-button-text:contains('Import File')").parent().attr("disabled",false); + var results = $.parseJSON(res); var result_node = $("#result"); if (results['records']){ - records = {'header':results['fields'],'sel':results['all_fields'],'row':results['records']}; + records = {'header':results['header'],'sel':results['all_fields'],'row':results['records']}; result_node.append(QWeb.render('ImportView-result',{'records':records})); }else if(results['error']){ result_node.append(QWeb.render('ImportView-result',{'error': results['error']})); @@ -66,16 +66,26 @@ openerp.base.DataImport = openerp.base.Dialog.extend({ var success_node = $("#success"); success_node.append(QWeb.render('ImportView-result',{'success': results['success']})); } - $('td #header').each(function(){ - var head = $(this).text(); - if (jQuery.inArray(head,results['all_fields']) <= -1){ - $(".ui-button-text:contains('Import File')").parent().attr("disabled",true); + var mm = []; + $("td #sel_field").click(function(){ + mm = []; + $("td #sel_field option:selected").each(function(){ + mm.push($(this).index()); + }); + }); + $("td #sel_field").change(function(){ + $(this).css('background-color',''); + var kk = this.selectedIndex; + if ($.inArray(kk,mm) > -1){ + $(this).css('background-color','#FF6666'); + }else{ + $(this).css('background-color',''); } }); }, stop: function() { $(this.$dialog).remove(); this._super(); - }, + } }); }; \ No newline at end of file diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 491495c4696..2bcef18a876 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -1471,7 +1471,7 @@ From 25c38ca653ff24773f68e2dbd90310a13c247786 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 23 Sep 2011 14:22:02 +0200 Subject: [PATCH 081/128] [IMP] move more import logic to javascript bzr revid: xmo@openerp.com-20110923122202-hnp6o1ybwgugive4 --- addons/web/controllers/main.py | 92 +++++++++++++------------ addons/web/static/src/js/data_import.js | 35 ++++++++-- addons/web/static/src/xml/base.xml | 1 - 3 files changed, 79 insertions(+), 49 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 4786d2193d5..f6fda7f7e32 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1419,7 +1419,7 @@ class Import(View): return fields @openerpweb.httprequest - def detect_data(self, req, model, csvfile, csvsep, csvdel, csvcode, jsonp): + def detect_data(self, req, csvfile, csvsep, csvdel, csvcode, jsonp): try: data = list(csv.reader( csvfile, quotechar=str(csvdel), delimiter=str(csvsep))) @@ -1447,65 +1447,69 @@ class Import(View): })) @openerpweb.httprequest - def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, jsonp): - # TODO: reintroduce CSVSKIP param - context = req.session.eval_context(req.context) + def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, jsonp, + meta): modle_obj = req.session.model(model) - res = None - data = [] + skip, indices, fields = operator.itemgetter('skip', 'indices', 'fields')( + simplejson.loads(meta)) + error = None if not (csvdel and len(csvdel) == 1): - error={'message': "The CSV delimiter must be a single character"} - return '' % ( - jsonp, simplejson.dumps({'error':error})) + error = u"The CSV delimiter must be a single character" + if not indices and fields: + error = u"You must select at least one field to import" + + if error: + return '' % ( + jsonp, simplejson.dumps({'error': {'message': error}})) + + # skip ignored records + data_record = itertools.islice( + csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep)), + skip, None) + + # if only one index, itemgetter will return an atom rather than a tuple + if len(indices) == 1: mapper = lambda row: [row[indices[0]]] + else: mapper = operator.itemgetter(*indices) + + data = None + error = None try: - data_record = csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep)) - for rec in itertools.islice(data_record,0,None): - data.append(rec) - - headers = itertools.islice(data,1) - fields = headers.next() - + # decode each data row + data = [ + [record.decode(csvcode) for record in row] + for row in itertools.imap(mapper, data_record) + # don't insert completely empty rows (can happen due to fields + # filtering in case of e.g. o2m content rows) + if any(row) + ] + except UnicodeDecodeError: + error = u"Failed to decode CSV file using encoding %s" % csvcode except csv.Error, e: - error={'message': str(e),'title': 'File Format Error'} - return '' % ( - jsonp, simplejson.dumps({'error':error})) - - datas = [] - ctx = context - - if not isinstance(fields, list): - fields = [fields] - - for line in data[1:]: - try: - datas.append(map(lambda x:x.decode(csvcode).encode('utf-8'), line)) - except: - datas.append(map(lambda x:x.decode('latin').encode('utf-8'), line)) + error = u"Could not process CSV file: %s" % e # If the file contains nothing, - if not datas: - error = {'message': 'The file is empty !'} + if not data: + error = u"File to import is empty" + if error: return '' % ( - jsonp, simplejson.dumps({'error':error})) + jsonp, simplejson.dumps({'error': {'message': error}})) - #Inverting the header into column names try: - res = modle_obj.import_data(fields, datas, 'init', '', False, ctx) + (code, record, message, _nope) = modle_obj.import_data( + fields, data, 'init', '', False, + req.session.eval_context(req.context)) except xmlrpclib.Fault, e: - error = {"message":e.faultCode} + error = {"message": u"%s, %s" % (e.faultCode, e.faultString)} return '' % ( jsonp, simplejson.dumps({'error':error})) - if res[0]>=0: + if code != -1: return '' % ( jsonp, simplejson.dumps({'success':True})) - d = '' - for key,val in res[1].items(): - d+= ('%s: %s' % (str(key),str(val))) - msg = 'Error trying to import this record:%s. ErrorMessage:%s %s' % (d,res[2],res[3]) - error = {'message':str(msg)} + msg = u"Error during import: %s\n\nTrying to import record %r" % ( + message, record) return '' % ( - jsonp, simplejson.dumps({'error':error})) + jsonp, simplejson.dumps({'error': {'message':msg}})) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index c62901e4795..70cb9ef9755 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -14,14 +14,18 @@ var QWeb = openerp.web.qweb; * @param {Function} callback function to call with the returned data */ function jsonp(form, attributes, callback) { + attributes = attributes || {}; var options = {jsonp: _.uniqueId('import_callback_')}; window[options.jsonp] = function () { delete window[options.jsonp]; callback.apply(null, arguments); }; - $(form).ajaxSubmit(_.extend({ - data: options - }, attributes)); + if ('data' in attributes) { + _.extend(attributes.data, options); + } else { + _.extend(attributes, {data: options}); + } + $(form).ajaxSubmit(attributes); } openerp.web.DataImport = openerp.web.Dialog.extend({ @@ -113,8 +117,31 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ }, do_import: function() { if(!this.$element.find('#csvfile').val()) { return; } + var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10); + var with_headers = this.$element.find('#file_has_headers').prop('checked'); + if (!lines_to_skip && with_headers) { + lines_to_skip = 1; + } + var indices = [], fields = []; + this.$element.find(".sel_fields").each(function(index, element) { + var val = element.value; + if (!val) { + return; + } + indices.push(index); + fields.push(val); + }); + jsonp(this.$element.find('#import_data'), { - url: '/web/import/import_data' + url: '/web/import/import_data', + data: { + model: this.model, + meta: JSON.stringify({ + skip: lines_to_skip, + indices: indices, + fields: fields + }) + } }, this.on_import_results); }, on_autodetect_data: function() { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index c735a6fa815..78fc4c4eb21 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1389,7 +1389,6 @@
-

1. Import a .CSV file

Select a .CSV file to import. If you need a sample of file to import, you should use the export tool with the "Import Compatible" option. From fc83119e6075533bfd21703e9b7543598b88a89c Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 23 Sep 2011 14:46:34 +0200 Subject: [PATCH 082/128] [IMP] fixup field names since we only ever export xmlids anymore might have to change that, or something bzr revid: xmo@openerp.com-20110923124634-sk9ka293ugc159gu --- addons/web/static/src/js/data_import.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 70cb9ef9755..741a19dd2b4 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -42,10 +42,9 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ var convert_fields = function (root, prefix) { prefix = prefix || ''; _(root.fields).each(function (f) { - var name = prefix + f.name; - self.all_fields.push(name); + self.all_fields.push(prefix + f.name); if (f.fields) { - convert_fields(f, name + '/'); + convert_fields(f, prefix + f.id + '/'); } }); }; @@ -92,12 +91,19 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ var self = this; _(fields).each(function (field, field_name) { var f = { + id: field_name, name: field_name, string: field.string, required: field.required }; - if (field.type === 'one2many') { + switch (field.type) { + case 'many2many': + case 'many2one': + f.name += '/id'; + break; + case 'one2many': + f.name += '/id'; f.fields = []; // only fetch sub-fields to a depth of 2 levels if (level < 2) { @@ -106,6 +112,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ self.graft_fields(fields, f, level+1); })); } + break; } parent.fields.push(f); }); From 6fb2e928ac38bc3a387c589e52ee33faa38961cd Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 23 Sep 2011 15:09:43 +0200 Subject: [PATCH 083/128] [FIX] moved a self around so import window did not close correctly bzr revid: xmo@openerp.com-20110923130943-293wxucafwjloxwh --- addons/web/static/src/js/data_import.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 741a19dd2b4..88f5375ac98 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -174,10 +174,11 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ result_node.append(QWeb.render('ImportView.error', { 'error': results['error']})); } else if (results['success']) { - self.stop(); if (this.widget_parent.widget_parent.active_view == "list") { this.widget_parent.reload_content(); } + this.stop(); + return; } var self = this; From e3fb2ccea19a13284104445191f0f274976f065e Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 25 Sep 2011 14:28:22 +0200 Subject: [PATCH 084/128] [IMP] setup.py remove python2.5 compatibility bzr revid: al@openerp.com-20110925122822-njsjvs8d0qlwf4l5 --- setup.py | 44 +------------------------------------------- 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/setup.py b/setup.py index 58424dbad12..94c3cf50a92 100755 --- a/setup.py +++ b/setup.py @@ -20,55 +20,13 @@ # ############################################################################## -# setup from TinERP -# taken from straw http://www.nongnu.org/straw/index.html -# taken from gnomolicious http://www.nongnu.org/gnomolicious/ -# adapted by Nicolas Évrard -# -# doc/migrate is not included since about 6.1-dev -# doc/tests is not included -# python25-compat/*py should be in the openerp (and imported appropriately) - -import sys +import os, sys import os from os.path import join, isfile import glob from setuptools import setup, find_packages -# Backports os.walk with followlinks from python 2.6. -# Needed to add all addons files to data_files for Windows packaging. -def walk_followlinks(top, topdown=True, onerror=None, followlinks=False): - from os.path import join, isdir, islink - from os import listdir, error - - try: - names = listdir(top) - except error, err: - if onerror is not None: - onerror(err) - return - - dirs, nondirs = [], [] - for name in names: - if isdir(join(top, name)): - dirs.append(name) - else: - nondirs.append(name) - - if topdown: - yield top, dirs, nondirs - for name in dirs: - path = join(top, name) - if followlinks or not islink(path): - for x in walk_followlinks(path, topdown, onerror, followlinks): - yield x - if not topdown: - yield top, dirs, nondirs - -if sys.version_info < (2, 6): - os.walk = walk_followlinks - py2exe_keywords = {} py2exe_data_files = [] if os.name == 'nt': From dee06af4b1954e9f3a11838a13aa03011a36967e Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Sun, 25 Sep 2011 16:08:02 +0200 Subject: [PATCH 085/128] [FIX] YAML empty records bzr revid: fp@tinyerp.com-20110925140802-gnyr92iepj73ceg0 --- openerp/tools/yaml_import.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openerp/tools/yaml_import.py b/openerp/tools/yaml_import.py index 81fec089fe1..ce4354d2b67 100644 --- a/openerp/tools/yaml_import.py +++ b/openerp/tools/yaml_import.py @@ -333,6 +333,7 @@ class YamlInterpreter(object): def _create_record(self, model, fields): record_dict = {} + fields = fields or {} for field_name, expression in fields.items(): field_value = self._eval_field(model, field_name, expression) record_dict[field_name] = field_value From d4febfe788a819dbdf749e022bbac3181a2b7167 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Sun, 25 Sep 2011 16:09:30 +0200 Subject: [PATCH 086/128] [IMP+FIX] Complete cleaning of point_of_sale module - refactorisation of the code: 2000 lines of code removed - fixed all the bugs - support for tax included/excluded prices - fixed rounding troubles - don't print the ticket if partial payment - cleaning features: - workflow reviewed: New -> Done -> Posted / Invoiced - generation of journal entries/invoice is now correct - only one picking per PoS order - wizard to apply global discount (on each lines) - useability: - reviewed the main point_of_sale screen - statements are open by default - removed non-useful features - advances (you use a special product for this) - contracts numbers - refund with exchange (you just create a new sale with negatives qty) - removed validation steps - scan product wizard - clean and complete YAML tests (work in progress) bzr revid: fp@tinyerp.com-20110925140930-s6zibvn51k3914in --- addons/point_of_sale/__openerp__.py | 11 +- addons/point_of_sale/point_of_sale.py | 937 ++++-------------- addons/point_of_sale/point_of_sale_demo.xml | 316 +++--- .../point_of_sale/point_of_sale_sequence.xml | 8 +- addons/point_of_sale/point_of_sale_view.xml | 152 +-- .../point_of_sale/point_of_sale_workflow.xml | 2 +- addons/point_of_sale/report/__init__.py | 4 +- addons/point_of_sale/report/pos_details.py | 4 +- addons/point_of_sale/report/pos_invoice.py | 2 +- .../point_of_sale/report/pos_order_report.py | 21 +- .../report/pos_order_report_view.xml | 134 ++- addons/point_of_sale/report/pos_receipt.rml | 4 - .../report/pos_receipt_with_remboursment.py | 83 -- .../report/pos_receipt_with_remboursment.rml | 131 --- .../pos_receipt_without_remboursment.py | 83 -- .../pos_receipt_without_remboursment.rml | 138 --- addons/point_of_sale/report/pos_report.py | 4 +- .../test/point_of_sale_report.yml | 68 -- .../point_of_sale/test/point_of_sale_test.yml | 578 +++-------- addons/point_of_sale/wizard/__init__.py | 3 - .../point_of_sale/wizard/pos_add_product.py | 98 -- .../point_of_sale/wizard/pos_add_product.xml | 41 - addons/point_of_sale/wizard/pos_box_out.py | 11 - addons/point_of_sale/wizard/pos_discount.py | 65 +- addons/point_of_sale/wizard/pos_discount.xml | 27 +- addons/point_of_sale/wizard/pos_payment.py | 205 +--- addons/point_of_sale/wizard/pos_payment.xml | 58 +- addons/point_of_sale/wizard/pos_return.py | 15 - .../point_of_sale/wizard/pos_return_view.xml | 9 +- .../point_of_sale/wizard/pos_scan_product.py | 51 - .../wizard/pos_scan_product_view.xml | 34 - 31 files changed, 692 insertions(+), 2605 deletions(-) delete mode 100644 addons/point_of_sale/report/pos_receipt_with_remboursment.py delete mode 100644 addons/point_of_sale/report/pos_receipt_with_remboursment.rml delete mode 100644 addons/point_of_sale/report/pos_receipt_without_remboursment.py delete mode 100644 addons/point_of_sale/report/pos_receipt_without_remboursment.rml delete mode 100644 addons/point_of_sale/wizard/pos_add_product.py delete mode 100644 addons/point_of_sale/wizard/pos_add_product.xml delete mode 100644 addons/point_of_sale/wizard/pos_scan_product.py delete mode 100644 addons/point_of_sale/wizard/pos_scan_product_view.xml diff --git a/addons/point_of_sale/__openerp__.py b/addons/point_of_sale/__openerp__.py index 3bd0f188581..75d23c2ca06 100644 --- a/addons/point_of_sale/__openerp__.py +++ b/addons/point_of_sale/__openerp__.py @@ -39,14 +39,13 @@ Main features : """, 'author': 'OpenERP SA', 'images': ['images/cash_registers.jpeg', 'images/pos_analysis.jpeg','images/register_analysis.jpeg','images/sale_order_pos.jpeg','images/product_pos.jpeg'], - 'depends': ['sale', 'delivery'], + 'depends': ['sale'], 'init_xml': [], 'update_xml': [ 'security/point_of_sale_security.xml', 'security/ir.model.access.csv', 'wizard/pos_details.xml', - 'wizard/pos_add_product.xml', 'wizard/pos_confirm.xml', 'wizard/pos_discount.xml', 'wizard/pos_get_sale.xml', @@ -64,8 +63,6 @@ Main features : 'wizard/pos_payment_report_user.xml', 'wizard/pos_payment_report.xml', 'wizard/pos_payment.xml', - 'wizard/pos_scan_product_view.xml', - 'wizard/pos_return_view.xml', 'point_of_sale_report.xml', 'point_of_sale_view.xml', 'report/pos_order_report_view.xml', @@ -75,7 +72,11 @@ Main features : 'account_statement_view.xml', 'account_statement_report.xml', ], - 'demo_xml': ['point_of_sale_demo.xml','account_statement_demo.xml'], + 'demo_xml': [ + 'point_of_sale_demo.xml', + 'account_statement_demo.xml', + 'test/00_register_open.yml' + ], 'test': ['test/point_of_sale_test.yml', 'test/point_of_sale_report.yml', ], diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index c90e325c48e..b23ba71d065 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -42,125 +42,23 @@ class pos_config_journal(osv.osv): pos_config_journal() - -class pos_company_discount(osv.osv): - """ Company Discount and Cashboxes """ - _inherit = 'res.company' - - _columns = { - 'company_discount': fields.float('Max Discount(%)', digits_compute=dp.get_precision('Point Of Sale')), - 'max_diff': fields.float('Max Difference for Cashboxes', digits_compute=dp.get_precision('Point Of Sale Discount')), - } - -pos_company_discount() - class pos_order(osv.osv): - """ Point of sale gives business owners a convenient way of checking out customers - and of recording sales """ - _name = "pos.order" _description = "Point of Sale" - _order = "date_order, create_date desc" + _order = "id desc" def unlink(self, cr, uid, ids, context=None): for rec in self.browse(cr, uid, ids, context=context): - for rec_statement in rec.statement_ids: - if (rec_statement.statement_id and rec_statement.statement_id.state == 'confirm') or rec.state == 'done': - raise osv.except_osv(_('Invalid action !'), _('Cannot delete a point of sale which is closed or contains confirmed cashboxes!')) + if rec.state not in ('draft','cancel'): + raise osv.except_osv(_('Unable to Delete !'), _('In order to delete a sale, it must be new or cancelled.')) return super(pos_order, self).unlink(cr, uid, ids, context=context) - def onchange_partner_pricelist(self, cr, uid, ids, part=False, context=None): - - """ Changed price list on_change of partner_id""" + def onchange_partner_id(self, cr, uid, ids, part=False, context=None): if not part: return {'value': {}} pricelist = self.pool.get('res.partner').browse(cr, uid, part, context=context).property_product_pricelist.id return {'value': {'pricelist_id': pricelist}} - def _amount_total(self, cr, uid, ids, field_name, arg, context=None): - """ Calculates amount_tax of order line - @param field_names: Names of fields. - @return: Dictionary of values """ - cr.execute(""" - SELECT - p.id, - COALESCE(SUM( - l.price_unit*l.qty*(1-(l.discount/100.0)))::decimal(16,2), 0 - ) AS amount - FROM pos_order p - LEFT OUTER JOIN pos_order_line l ON (p.id = l.order_id) - WHERE p.id IN %s GROUP BY p.id """,(tuple(ids),)) - res = dict(cr.fetchall()) - for rec in self.browse(cr, uid, ids, context=context): - if rec.partner_id \ - and rec.partner_id.property_account_position \ - and rec.partner_id.property_account_position.tax_ids: - res[rec.id] = res[rec.id] - rec.amount_tax - else : - res[rec.id] = res[rec.id] + rec.amount_tax - return res - - def _get_date_payment2(self, cr, uid, ids, context=None, *a): - # Todo need to check this function - """ Find payment Date - @param field_names: Names of fields. - @return: Dictionary of values """ - res = {} - val = None - for order in self.browse(cr, uid, ids, context=context): - cr.execute("SELECT date_payment FROM pos_order WHERE id = %s", (order.id,)) - date_p = cr.fetchone() - date_p = date_p and date_p[0] or None - if date_p: - res[order.id] = date_p - return res - if order.invoice_id: - cr.execute(" SELECT MAX(l.date) " - " FROM account_move_line l, account_move m, account_invoice i, account_move_reconcile r, pos_order o " - " WHERE i.move_id = m.id AND l.move_id = m.id AND l.reconcile_id = r.id AND o.id = %s AND o.invoice_id = i.id", - (order.id,)) - else: - cr.execute("SELECT MAX(l.date) from account_move_line l " - "left join account_bank_statement abs on (l.statement_id=abs.id)" - "left join account_bank_statement_line absl on (absl.statement_id=abs.id) " - "left join pos_order p on (p.id=absl.pos_statement_id) " - "left join account_account a on (a.id=absl.account_id) " - "where p.id=%s and l.reconcile_id is not NULL and a.reconcile=True", (order.id,)) - val = cr.fetchone() - val = val and val[0] or None - if val: - res[order.id] = val - return res - - def _get_date_payment(self, cr, uid, ids, context, *a): - """ Find Validation Date - @return: Dictionary of values """ - res = {} - val = None - for order in self.browse(cr, uid, ids): - cr.execute("SELECT date_validation FROM pos_order WHERE id = %s", (order.id,)) - date_p = cr.fetchone() - date_p = date_p and date_p[0] or None - if date_p: - res[order.id] = date_p - return res - discount_allowed = order.company_id.company_discount - for line in order.lines: - if line.discount > discount_allowed: - return {order.id: None } - if order.amount_paid == order.amount_total and not date_p: - cr.execute("SELECT MAX(date) FROM account_bank_statement_line WHERE pos_statement_id = %s", (order.id,)) - val = cr.fetchone() - val = val and val[0] or None - if order.invoice_id and order.invoice_id.move_id and not date_p and not val: - for o in order.invoice_id.move_id.line_id: - if o.balance == 0: - if val < o.date_created: - val = o.date_created - if val: - res[order.id] = val - return res - def _amount_all(self, cr, uid, ids, name, args, context=None): tax_obj = self.pool.get('account.tax') cur_obj = self.pool.get('res.currency') @@ -171,45 +69,30 @@ class pos_order(osv.osv): 'amount_return':0.0, 'amount_tax':0.0, } - val = val1 = 0.0 + val1 = val2 = 0.0 cur = order.pricelist_id.currency_id for payment in order.statement_ids: res[order.id]['amount_paid'] += payment.amount res[order.id]['amount_return'] += (payment.amount < 0 and payment.amount or 0) for line in order.lines: val1 += line.price_subtotal_incl - if order.price_type != 'tax_excluded': - res[order.id]['amount_tax'] = reduce(lambda x, y: x+round(y['amount'], 2), - tax_obj.compute_inv(cr, uid, line.product_id.taxes_id, - line.price_unit * \ - (1-(line.discount or 0.0)/100.0), line.qty), - res[order.id]['amount_tax']) - elif line.qty != 0.0: - for c in tax_obj.compute_all(cr, uid, line.product_id.taxes_id, \ - line.price_unit * (1-(line.discount or 0.0)/100.0), \ - line.qty, line.product_id, line.order_id.partner_id)['taxes']: - val += c.get('amount', 0.0) - res[order.id]['amount_tax'] = cur_obj.round(cr, uid, cur, val) - res[order.id]['amount_total'] = res[order.id]['amount_tax'] + cur_obj.round(cr, uid, cur, val1) + val2 += line.price_subtotal + res[order.id]['amount_tax'] = cur_obj.round(cr, uid, cur, val1-val2) + res[order.id]['amount_total'] = cur_obj.round(cr, uid, cur, val1) return res - def _sale_journal_get(self, cr, uid, context=None): - """ To get sale journal for this order - @return: journal """ - journal_obj = self.pool.get('account.journal') - res = journal_obj.search(cr, uid, [('type', '=', 'sale')], limit=1) + def _default_sale_journal(self, cr, uid, context=None): + res = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'sale')], limit=1) return res and res[0] or False - def _shop_get(self, cr, uid, context=None): - """ To get Shop for this order - @return: Shop id """ + def _default_shop(self, cr, uid, context=None): res = self.pool.get('sale.shop').search(cr, uid, []) return res and res[0] or False def copy(self, cr, uid, id, default=None, context=None): if not default: default = {} - default.update({ + d = { 'state': 'draft', 'partner_id': False, 'invoice_id': False, @@ -217,279 +100,117 @@ class pos_order(osv.osv): 'picking_id': False, 'statement_ids': [], 'nb_print': 0, - 'pickings': [], 'name': self.pool.get('ir.sequence').get(cr, uid, 'pos.order'), - }) - return super(pos_order, self).copy(cr, uid, id, default, context=context) - - def _get_v( self, cr, uid, ids, *args): - """ Changed the Validation state of order - @return: State """ - res_obj = self.pool.get('res.users') - company_disc = self.browse(cr, uid, ids) - list_jrnl = [] - r = {} - if not company_disc: - comp = res_obj.browse(cr, uid, uid).company_id.company_discount or 0.0 - else: - comp = company_disc[0] and company_disc[0].company_id and company_disc[0].company_id.company_discount or 0.0 - cr.execute("SELECT discount FROM pos_order_line WHERE order_id = %s AND discount <= %s", (ids[0], comp)) - res = cr.fetchone() - cr.execute("SELECT discount FROM pos_order_line WHERE order_id = %s AND discount > %s", (ids[0], comp)) - res2 = cr.fetchone() - cr.execute("SELECT journal_id FROM account_bank_statement_line WHERE pos_statement_id = %s ", (ids[0], )) - res3 = cr.fetchall() - for r in res3: - cr.execute("SELECT id FROM account_journal WHERE name = '%s' AND special_journal = 't'", (r[0], )) - res3 = cr.fetchone() - is_special = res3 and res3[0] or None - if is_special: - list_jrnl.append(is_special) - for order in self.browse(cr, uid, ids): - if order.state in ('paid', 'done', 'invoiced') and res and not res2 and not len(list_jrnl): - r[order.id] = 'accepted' - return r + } + d.update(default) + return super(pos_order, self).copy(cr, uid, id, d, context=context) _columns = { - 'name': fields.char('Order Description', size=64, required=True, + 'name': fields.char('Order Ref', size=64, required=True, states={'draft': [('readonly', False)]}, readonly=True), 'company_id':fields.many2one('res.company', 'Company', required=True, readonly=True), - 'num_sale': fields.char('Internal Note', size=64), 'shop_id': fields.many2one('sale.shop', 'Shop', required=True, states={'draft': [('readonly', False)]}, readonly=True), 'date_order': fields.datetime('Date Ordered', readonly=True, select=True), - 'date_validation': fields.function(_get_date_payment, - string='Validation Date', - type='date', select=True, store=True), - 'date_payment': fields.function(_get_date_payment2, - string='Payment Date', - type='date', select=True, store=True), - 'date_validity': fields.date('Validity Date', required=True), 'user_id': fields.many2one('res.users', 'Connected Salesman', help="Person who uses the the cash register. It could be a reliever, a student or an interim employee."), - 'user_salesman_id': fields.many2one('res.users', 'Cashier', required=True, help="User who is logged into the system."), - 'sale_manager': fields.many2one('res.users', 'Salesman Manager'), 'amount_tax': fields.function(_amount_all, string='Taxes', digits_compute=dp.get_precision('Point Of Sale'), multi='all'), 'amount_total': fields.function(_amount_all, string='Total', multi='all'), 'amount_paid': fields.function(_amount_all, string='Paid', states={'draft': [('readonly', False)]}, readonly=True, digits_compute=dp.get_precision('Point Of Sale'), multi='all'), 'amount_return': fields.function(_amount_all, 'Returned', digits_compute=dp.get_precision('Point Of Sale'), multi='all'), 'lines': fields.one2many('pos.order.line', 'order_id', 'Order Lines', states={'draft': [('readonly', False)]}, readonly=True), - 'price_type': fields.selection([ - ('tax_excluded','Tax excluded')], - 'Price Method', required=True), 'statement_ids': fields.one2many('account.bank.statement.line', 'pos_statement_id', 'Payments', states={'draft': [('readonly', False)]}, readonly=True), 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True, states={'draft': [('readonly', False)]}, readonly=True), 'partner_id': fields.many2one('res.partner', 'Customer', change_default=True, select=1, states={'draft': [('readonly', False)], 'paid': [('readonly', False)]}), - 'state': fields.selection([('draft', 'Quotation'), - ('payment', 'Payment'), - ('advance','Advance'), - ('paid', 'Paid'), - ('done', 'Done'), + + 'state': fields.selection([('draft', 'New'), + ('paid', 'Done'), + ('done', 'Posted'), ('invoiced', 'Invoiced'), - ('cancel', 'Cancel')], + ('cancel', 'Cancelled')], 'State', readonly=True), + 'invoice_id': fields.many2one('account.invoice', 'Invoice'), - 'account_move': fields.many2one('account.move', 'Account Entry', readonly=True), - 'pickings': fields.one2many('stock.picking', 'pos_order', 'Picking', readonly=True), - 'picking_id': fields.many2one('stock.picking', 'Last Output Picking', readonly=True), - 'first_name': fields.char('First Name', size=64), + 'account_move': fields.many2one('account.move', 'Journal Entry', readonly=True), + 'picking_id': fields.many2one('stock.picking', 'Picking', readonly=True), 'note': fields.text('Internal Notes'), 'nb_print': fields.integer('Number of Print', readonly=True), 'sale_journal': fields.many2one('account.journal', 'Journal', required=True, states={'draft': [('readonly', False)]}, readonly=True), - 'invoice_wanted': fields.boolean('Create Invoice'), - 'note_2': fields.char('Customer Note', size=64), - 'type_rec': fields.char('Type of Receipt', size=64), - 'remboursed': fields.boolean('Remboursed'), - 'contract_number': fields.char('Contract Number', size=512, select=1), - 'journal_entry': fields.boolean('Journal Entry'), } - def _select_pricelist(self, cr, uid, context=None): - """ To get default pricelist for the order - @param name: Names of fields. - @return: pricelist ID - """ + def _default_pricelist(self, cr, uid, context=None): res = self.pool.get('sale.shop').search(cr, uid, [], context=context) if res: shop = self.pool.get('sale.shop').browse(cr, uid, res[0], context=context) return shop.pricelist_id and shop.pricelist_id.id or False return False - def _journal_default(self, cr, uid, context=None): - """ To get default pricelist for the order - @param name: Names of fields. - @return: journal ID - """ - journal_list = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'cash')]) - return journal_list and journal_list[0] or False - _defaults = { - 'user_salesman_id':lambda self, cr, uid, context: uid, 'user_id': lambda self, cr, uid, context: uid, - 'sale_manager': lambda self, cr, uid, context: uid, 'state': 'draft', - 'price_type': 'tax_excluded', 'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'pos.order'), 'date_order': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - 'date_validity': lambda *a: (datetime.today() + relativedelta(months=+6)).strftime('%Y-%m-%d'), 'nb_print': 0, 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, - 'sale_journal': _sale_journal_get, - 'invoice_wanted': False, - 'shop_id': _shop_get, - 'pricelist_id': _select_pricelist, + 'sale_journal': _default_sale_journal, + 'shop_id': _default_shop, + 'pricelist_id': _default_pricelist, } - - def test_order_lines(self, cr, uid, order, context=None): - """ Test order line is created or not for the order - @param name: Names of fields. - @return: True - """ - if not order.lines: - raise osv.except_osv(_('Error'), _('No order lines defined for this sale.')) - - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'pos.order', order.id, 'paid', cr) - return True - - def dummy_button(self, cr, uid, order, context=None): - return True - def test_paid(self, cr, uid, ids, context=None): - """ Test all amount is paid for this order + """A Point of Sale is paid when the sum @return: True """ for order in self.browse(cr, uid, ids, context=context): if order.lines and not order.amount_total: return True if (not order.lines) or (not order.statement_ids) or \ - Decimal(str(order.amount_total)) != Decimal(str(order.amount_paid)): + (abs(order.amount_total-order.amount_paid) > 0.00001): return False return True - def _get_qty_differences(self, orders, old_picking): - """check if the customer changed the product quantity """ - order_dict = {} - for order in orders: - for line in order.lines: - order_dict[line.product_id.id] = line - - # check the quantity differences: - diff_dict = {} - for line in old_picking.move_lines: - order_line = order_dict.get(line.product_id.id) - if not order_line: - deleted = True - qty_to_delete_from_original_picking = line.product_qty - diff_dict[line.product_id.id] = (deleted, qty_to_delete_from_original_picking) - elif line.product_qty != order_line.qty: - deleted = False - qty_to_delete_from_original_picking = line.product_qty - order_line.qty - diff_dict[line.product_id.id] = (deleted, qty_to_delete_from_original_picking) - - return diff_dict - - def _split_picking(self, cr, uid, ids, context, old_picking, diff_dict): - """if the customer changes the product quantity, split the picking in two""" - # create a copy of the original picking and adjust the product qty: - picking_model = self.pool.get('stock.picking') - defaults = { - 'note': "Partial picking from customer", # add a note to tell why we create a new picking - 'name': self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out'), # increment the sequence - } - - new_picking_id = picking_model.copy(cr, uid, old_picking.id, defaults) # state = 'draft' - new_picking = picking_model.browse(cr, uid, new_picking_id, context=context) - - for line in new_picking.move_lines: - p_id = line.product_id.id - if p_id in diff_dict: - diff = diff_dict[p_id] - deleted = diff[0] - qty_to_del = diff[1] - if deleted: # product has been deleted (customer didn't took it): - # delete this product from old picking: - for old_line in old_picking.move_lines: - if old_line.product_id.id == p_id: - old_line.write({'state': 'draft'}, context=context) # cannot delete if not draft - old_line.unlink(context=context) - elif qty_to_del > 0: # product qty has been modified (customer took less than the ordered quantity): - # subtract qty from old picking: - for old_line in old_picking.move_lines: - if old_line.product_id.id == p_id: - old_line.write({'product_qty': old_line.product_qty - qty_to_del}, context=context) - # add qty to new picking: - line.write({'product_qty': qty_to_del}, context=context) - else: # product hasn't changed (customer took it without any change): - # delete this product from new picking: - line.unlink(context=context) - else: - # delete it in the new picking: - line.unlink(context=context) - def create_picking(self, cr, uid, ids, context=None): """Create a picking for each order and validate it.""" picking_obj = self.pool.get('stock.picking') - property_obj = self.pool.get("ir.property") - 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: + move_obj = self.pool.get('stock.move') + + for order in self.browse(cr, uid, ids, context=context): 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', + picking_id = picking_obj.create(cr, uid, { + 'origin': order.name, + 'address_id': addr.get('delivery',False), + 'type': 'out', + 'company_id': order.company_id.id, + 'move_type': 'direct', + 'note': order.note or "", + 'invoice_state': 'none', + 'auto_picking': True, + }, context=context) + self.write(cr, uid, [order.id], {'picking_id': picking_id}, context=context) + location_id = order.shop_id.warehouse_id.lot_stock_id.id + output_id = order.shop_id.warehouse_id.lot_output_id.id + + for line in order.lines: + if line.product_id and line.product_id.type == 'service': + continue + if line.qty < 0: + location_id, output_id = output_id, location_id + + move_obj.create(cr, uid, { + 'name': line.name, + 'product_uom': line.product_id.uom_id.id, + 'product_uos': line.product_id.uom_id.id, + 'picking_id': picking_id, + 'product_id': line.product_id.id, + 'product_uos_qty': abs(line.qty), + 'product_qty': abs(line.qty), + 'tracking_id': False, 'state': 'draft', - 'move_type': 'direct', - 'note': 'POS notes ' + (order.note or ""), - 'invoice_state': 'none', - 'auto_picking': True, - 'pos_order': order.id, + 'location_id': location_id, + 'location_dest_id': output_id, }, context=context) - self.write(cr, uid, [order.id], {'picking_id': picking_id}, context=context) - else: - picking_id = order.picking_id.id - picking_obj.write(cr, uid, [picking_id], {'auto_picking': True}, context=context) - picking = picking_obj.browse(cr, uid, [picking_id], context=context)[0] - new = False - - # split the picking (if product quantity has changed): - diff_dict = self._get_qty_differences(orders, picking) - if diff_dict: - self._split_picking(cr, uid, ids, context, picking, diff_dict) - - if new: - for line in order.lines: - if line.product_id and line.product_id.type == 'service': - continue - prop_ids = property_obj.search(cr, uid, [('name', '=', 'property_stock_customer')], context=context) - val = property_obj.browse(cr, uid, prop_ids[0], context=context).value_reference - cr.execute("SELECT s.id FROM stock_location s, stock_warehouse w WHERE w.lot_stock_id = s.id AND w.id = %s", (order.shop_id.warehouse_id.id, )) - res = cr.fetchone() - location_id = res and res[0] or None - stock_dest_id = val.id - if line.qty < 0: - location_id, stock_dest_id = stock_dest_id, location_id - move_obj.create(cr, uid, { - 'name': '(POS %d)' % (order.id, ), - 'product_uom': line.product_id.uom_id.id, - 'product_uos': line.product_id.uom_id.id, - 'picking_id': picking_id, - 'product_id': line.product_id.id, - 'product_uos_qty': abs(line.qty), - 'product_qty': abs(line.qty), - 'tracking_id': False, - 'pos_line_id': line.id, - '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) + if line.qty < 0: + location_id, output_id = output_id, location_id wf_service = netsvc.LocalService("workflow") wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) @@ -497,36 +218,27 @@ class pos_order(osv.osv): return True def set_to_draft(self, cr, uid, ids, *args): - """ Changes order state to draft - @return: True - """ if not len(ids): return False + for order in self.browse(cr, uid, ids, context=context): + if order.state<>'cancel': + raise osv.except_osv(_('Error!'), _('In order to set to draft a sale, it must be cancelled.')) self.write(cr, uid, ids, {'state': 'draft'}) wf_service = netsvc.LocalService("workflow") for i in ids: wf_service.trg_create(uid, 'pos.order', i, cr) return True - def button_invalidate(self, cr, uid, ids, *args): - """ Check the access for the sale order - @return: True - """ - res_obj = self.pool.get('res.company') - try: - part_company = res_obj.browse(cr, uid, uid) and res_obj.browse(cr, uid, uid).parent_id and res_obj.browse(cr, uid, uid).parent_id.id or None - except Exception: - raise osv.except_osv(_('Error'), _('You don\'t have enough access to validate this sale!')) - if part_company: - raise osv.except_osv(_('Error'), _('You don\'t have enough access to validate this sale!')) - return True - def cancel_order(self, cr, uid, ids, context=None): """ Changes order state to cancel @return: True """ + stock_picking_obj = self.pool.get('stock.picking') + for order in self.browse(cr, uid, ids, context=context): + wf_service.trg_validate(uid, 'stock.picking', order.picking_id.id, 'button_cancel', cr) + if stock_picking_obj.browse(cr, uid, order.picking_id.id, context=context).state <> 'cancel': + raise osv.except_osv(_('Error!'), _('Unable to cancel the picking.')) self.write(cr, uid, ids, {'state': 'cancel'}, context=context) - self.cancel_picking(cr, uid, ids, context=context) return True def add_payment(self, cr, uid, order_id, data, context=None): @@ -538,8 +250,6 @@ class pos_order(osv.osv): curr_c = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id curr_company = curr_c.id order = self.browse(cr, uid, order_id, context=context) - if not order.num_sale and data['num_sale']: - self.write(cr, uid, order_id, {'num_sale': data['num_sale']}, context=context) ids_new = [] args = { 'amount': data['amount'], @@ -551,15 +261,7 @@ class pos_order(osv.osv): account_def = property_obj.get(cr, uid, 'property_account_receivable', 'res.partner', context=context) args['account_id'] = order.partner_id and order.partner_id.property_account_receivable \ and order.partner_id.property_account_receivable.id or account_def.id or curr_c.account_receivable.id - if data.get('is_acc', False): - args['is_acc'] = data['is_acc'] - args['account_id'] = prod_obj.browse(cr, uid, data['product_id'], context=context).property_account_income \ - and prod_obj.browse(cr, uid, data['product_id'], context=context).property_account_income.id - if not args['account_id']: - raise osv.except_osv(_('Error'), _('Please provide an account for the product: %s')% \ - (prod_obj.browse(cr, uid, data['product_id'], context=context).name)) args['partner_id'] = order.partner_id and order.partner_id.id or None - args['ref'] = order.contract_number or None statement_id = statement_obj.search(cr,uid, [ ('journal_id', '=', data['journal']), @@ -584,58 +286,39 @@ class pos_order(osv.osv): return statement_id - def add_product(self, cr, uid, order_id, product_id, qty, context=None): - - """Create a new order line the order""" - - line_obj = self.pool.get('pos.order.line') - values = self.read(cr, uid, order_id, ['partner_id', 'pricelist_id']) - - pricelist = values['pricelist_id'] and values['pricelist_id'][0] - product = values['partner_id'] and values['partner_id'][0] - - price = line_obj.price_by_product(cr, uid, [], - pricelist, product_id, qty, product) - - order_line_id = line_obj.create(cr, uid, { - 'order_id': order_id, - 'product_id': product_id, - 'qty': qty, - 'price_unit': price, - }, context=context) - return order_line_id, price - def refund(self, cr, uid, ids, context=None): - """Create a copy of order for refund order""" - clone_list = [] line_obj = self.pool.get('pos.order.line') - for order in self.browse(cr, uid, ids, context=context): clone_id = self.copy(cr, uid, order.id, { 'name': order.name + ' REFUND', - 'date_order': time.strftime('%Y-%m-%d'), - 'state': 'draft', - 'note': 'REFUND\n'+ (order.note or ''), - 'invoice_id': False, - 'nb_print': 0, - 'statement_ids': False, - }, context=context) + }, context=context) clone_list.append(clone_id) - for clone in self.browse(cr, uid, clone_list, context=context): for order_line in clone.lines: line_obj.write(cr, uid, [order_line.id], { 'qty': -order_line.qty - }, context=context) - return clone_list + }, context=context) + + new_order = ','.join(map(str,clone_list)) + abs = { + #'domain': "[('id', 'in', ["+new_order+"])]", + 'name': _('Return Products'), + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'pos.order', + 'res_id':clone_list[0], + 'view_id': False, + 'context':context, + 'type': 'ir.actions.act_window', + 'nodestroy': True, + 'target': 'current', + } + return abs def action_invoice(self, cr, uid, ids, context=None): - - """Create a invoice of order """ - inv_ref = self.pool.get('account.invoice') inv_line_ref = self.pool.get('account.invoice.line') product_obj = self.pool.get('product.product') @@ -689,22 +372,36 @@ class pos_order(osv.osv): and [(6, 0, inv_line['invoice_line_tax_id'])] or [] inv_line_ref.create(cr, uid, inv_line, context=context) - for i in inv_ids: - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'account.invoice', i, 'invoice_open', cr) - return inv_ids + if not inv_ids: return {} + mod_obj = self.pool.get('ir.model.data') + res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_form') + res_id = res and res[1] or False, + return { + 'name': _('PoS Invoices'), + 'view_type': 'form', + 'view_mode': 'form', + 'view_id': [res_id], + 'res_model': 'account.invoice', + 'context': "{'type':'out_invoice'}", + 'type': 'ir.actions.act_window', + 'nodestroy': True, + 'target': 'current', + 'res_id': inv_ids and inv_ids[0] or False, + } def create_account_move(self, cr, uid, ids, context=None): - """Create a account move line of order """ + """Create a account move line of order grouped by products or not.""" account_move_obj = self.pool.get('account.move') account_move_line_obj = self.pool.get('account.move.line') account_period_obj = self.pool.get('account.period') + period = account_period_obj.find(cr, uid, context=context)[0] account_tax_obj = self.pool.get('account.tax') res_obj=self.pool.get('res.users') property_obj=self.pool.get('ir.property') - period = account_period_obj.find(cr, uid, context=context)[0] for order in self.browse(cr, uid, ids, context=context): + if order.state<>'paid': continue + curr_c = res_obj.browse(cr, uid, uid).company_id comp_id = res_obj.browse(cr, order.user_id.id, order.user_id.id).company_id comp_id = comp_id and comp_id.id or False @@ -717,19 +414,14 @@ class pos_order(osv.osv): # Create an entry for the sale move_id = account_move_obj.create(cr, uid, { 'journal_id': order.sale_journal.id, - 'period_id': period, - }, context=context) + }, context=context) # Create an move for each order line for line in order.lines: tax_amount = 0 taxes = [t for t in line.product_id.taxes_id] - if order.price_type == 'tax_excluded': - computed_taxes = account_tax_obj.compute_all( - cr, uid, taxes, line.price_unit, line.qty)['taxes'] - else: - computed_taxes = account_tax_obj.compute_inv( - cr, uid, taxes, line.price_unit, line.qty) + computed = account_tax_obj.compute_all(cr, uid, taxes, line.price_unit * (100.0-line.discount) / 100.0, line.qty) + computed_taxes = computed['taxes'] for tax in computed_taxes: tax_amount += round(tax['amount'], 2) @@ -741,10 +433,7 @@ class pos_order(osv.osv): group_tax[group_key] += round(tax['amount'], 2) else: group_tax[group_key] = round(tax['amount'], 2) - if order.price_type != 'tax_excluded': - amount = line.price_subtotal - tax_amount - else: - amount = line.price_subtotal + amount = line.price_subtotal # Search for the income account if line.product_id.property_account_income.id: @@ -774,11 +463,12 @@ class pos_order(osv.osv): if tax_code_id: break + # Create a move for the line account_move_line_obj.create(cr, uid, { - 'name': "aa"+order.name, + 'name': line.name, 'date': order.date_order[:10], - 'ref': order.contract_number or order.name, + 'ref': order.name, 'quantity': line.qty, 'product_id': line.product_id.id, 'move_id': move_id, @@ -805,9 +495,9 @@ class pos_order(osv.osv): continue account_move_line_obj.create(cr, uid, { - 'name': "bb" + order.name, + 'name': "Tax" + line.name, 'date': order.date_order[:10], - 'ref': order.contract_number or order.name, + 'ref': order.name, 'product_id':line.product_id.id, 'quantity': line.qty, 'move_id': move_id, @@ -826,9 +516,9 @@ class pos_order(osv.osv): (tax_code_pos, base_code_pos, account_pos)= (0, 1, 2) for key, amount in group_tax.items(): account_move_line_obj.create(cr, uid, { - 'name': "cc" + order.name, + 'name': 'Tax', 'date': order.date_order[:10], - 'ref': order.contract_number or order.name, + 'ref': order.name, 'move_id': move_id, 'company_id': comp_id, 'quantity': line.qty, @@ -844,9 +534,9 @@ class pos_order(osv.osv): # counterpart to_reconcile.append(account_move_line_obj.create(cr, uid, { - 'name': "dd" + order.name, + 'name': order.name, 'date': order.date_order[:10], - 'ref': order.contract_number or order.name, + 'ref': order.name, 'move_id': move_id, 'company_id': comp_id, 'account_id': order_account, @@ -858,71 +548,16 @@ class pos_order(osv.osv): 'period_id': period, 'partner_id': order.partner_id and order.partner_id.id or False }, context=context)) - - - # search the account receivable for the payments: - account_receivable = order.sale_journal.default_credit_account_id.id - if not account_receivable: - raise osv.except_osv(_('Error !'), - _('There is no receivable account defined for this journal:'\ - ' "%s" (id:%d)') % (order.sale_journal.name, order.sale_journal.id, )) - for payment in order.statement_ids: - # Create one entry for the payment - if payment.is_acc: - continue - account_move_obj.create(cr, uid, { - 'journal_id': payment.statement_id.journal_id.id, - 'period_id': period, - }, context=context) - - for stat_l in order.statement_ids: - if stat_l.is_acc and len(stat_l.move_ids): - for st in stat_l.move_ids: - for s in st.line_id: - if s.credit: - account_move_line_obj.copy(cr, uid, s.id, { - 'debit': s.credit, - 'statement_id': False, - 'credit': s.debit - }) - account_move_line_obj.copy(cr, uid, s.id, { - 'statement_id': False, - 'account_id': order_account - }) - - self.write(cr, uid, order.id, {'state':'done'}, context=context) + self.write(cr, uid, order.id, {'state':'done', 'account_move': move_id}, context=context) return True - def cancel_picking(self, cr, uid, ids, context=None): - stock_picking_obj = self.pool.get('stock.picking') - for order in self.browse(cr, uid, ids, context=context): - for picking in order.pickings: - stock_picking_obj.unlink(cr, uid, [picking.id], context=context) - return True - - def action_payment(self, cr, uid, ids, context=None): - vals = {'state': 'payment'} - sequence_obj = self.pool.get('ir.sequence') - 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': - 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, {'state': 'payment'}, context=context) def action_paid(self, cr, uid, ids, context=None): - if context is None: - context = {} - if context.get('flag', False): - self.create_picking(cr, uid, ids, context=None) - self.write(cr, uid, ids, {'state': 'paid'}, context=context) - else: - context['flag'] = True + context = context or {} + self.create_picking(cr, uid, ids, context=None) + self.write(cr, uid, ids, {'state': 'paid'}, context=context) return True def action_cancel(self, cr, uid, ids, context=None): @@ -930,43 +565,25 @@ class pos_order(osv.osv): return True def action_done(self, cr, uid, ids, context=None): - for order in self.browse(cr, uid, ids, context=context): - if not order.journal_entry: - self.create_account_move(cr, uid, ids, context=None) + self.create_account_move(cr, uid, ids, context=context) return True - def compute_state(self, cr, uid, id): - cr.execute("SELECT act.id, act.name FROM wkf_activity act " - "INNER JOIN wkf_workitem item ON act.id = item.act_id " - "INNER JOIN wkf_instance inst ON item.inst_id = inst.id " - "INNER JOIN wkf ON inst.wkf_id = wkf.id " - "WHERE wkf.osv = 'pos.order' AND inst.res_id = %s " - "ORDER BY act.name", (id, )) - return [name for id, name in cr.fetchall()] - pos_order() class account_bank_statement(osv.osv): _inherit = 'account.bank.statement' _columns= { - 'user_id': fields.many2one('res.users', ondelete='cascade', string='User', readonly=True), + 'user_id': fields.many2one('res.users', 'User', readonly=True), } _defaults = { - 'user_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).id + 'user_id': lambda self,cr,uid,c={}: uid } account_bank_statement() class account_bank_statement_line(osv.osv): _inherit = 'account.bank.statement.line' - def _get_statement_journal(self, cr, uid, ids, context, *a): - res = {} - for line in self.browse(cr, uid, ids): - res[line.id] = line.statement_id and line.statement_id.journal_id and line.statement_id.journal_id.name or None - return res _columns= { - 'journal_id': fields.function(_get_statement_journal,store=True, string='Journal', type='char', size=64), - 'am_out': fields.boolean("To count"), - 'is_acc': fields.boolean("Is accompte"), + 'journal_id': fields.related('statement_id','journal_id','name', store=True, string='Journal', type='char', size=64), 'pos_statement_id': fields.many2one('pos.order', ondelete='cascade'), } account_bank_statement_line() @@ -974,275 +591,85 @@ account_bank_statement_line() class pos_order_line(osv.osv): _name = "pos.order.line" _description = "Lines of Point of Sale" - - def _get_amount(self, cr, uid, ids, field_name, arg, context=None): - res = {} - for line in self.browse(cr, uid, ids, context=context): - price = self.price_by_product(cr, uid, ids, line.order_id.pricelist_id.id, line.product_id.id, line.qty, line.order_id.partner_id.id) - res[line.id] = price - return res - - def _amount_line_ttc(self, cr, uid, ids, field_name, arg, context=None): - res = dict.fromkeys(ids, 0.0) - account_tax_obj = self.pool.get('account.tax') - self.price_by_product_multi(cr, uid, ids) - for line in self.browse(cr, uid, ids, context=context): - tax_amount = 0.0 - taxes = [t for t in line.product_id.taxes_id] - if line.qty == 0.0: - continue - computed_taxes = account_tax_obj.compute_all(cr, uid, taxes, line.price_unit, line.qty)['taxes'] - for tax in computed_taxes: - tax_amount += tax['amount'] - if line.discount != 0.0: - res[line.id] = line.price_unit * line.qty * (1 - (line.discount or 0.0) / 100.0) - else: - res[line.id] = line.price_unit*line.qty - res[line.id] = res[line.id] + tax_amount - return res - - def _amount_line(self, cr, uid, ids, field_name, arg, context=None): - res = {} - self.price_by_product_multi(cr, uid, ids) - for line in self.browse(cr, uid, ids, context=context): - if line.discount!=0.0: - res[line.id] = line.price_unit * line.qty * (1 - (line.discount or 0.0) / 100.0) - else: - res[line.id] = line.price_unit * line.qty - return res + _rec_name = "product_id" def _amount_line_all(self, cr, uid, ids, field_names, arg, context=None): res = dict([(i, {}) for i in ids]) account_tax_obj = self.pool.get('account.tax') - - self.price_by_product_multi(cr, uid, ids) + cur_obj = self.pool.get('res.currency') for line in self.browse(cr, uid, ids, context=context): - for f in field_names: - if f == 'price_subtotal': - if line.discount != 0.0: - res[line.id][f] = line.price_unit * line.qty * (1 - (line.discount or 0.0) / 100.0) - else: - res[line.id][f] = line.price_unit * line.qty - elif f == 'price_subtotal_incl': - taxes = [t for t in line.product_id.taxes_id] - if line.qty == 0.0: - res[line.id][f] = 0.0 - continue - price = line.price_unit * (1 - (line.discount or 0.0) / 100.0) - computed_taxes = account_tax_obj.compute_all(cr, uid, taxes, price, line.qty) - cur = line.order_id.pricelist_id.currency_id - res[line.id][f] = self.pool.get('res.currency').round(cr, uid, cur, computed_taxes['total']) + taxes = line.product_id.taxes_id + price = line.price_unit * (1 - (line.discount or 0.0) / 100.0) + taxes = account_tax_obj.compute_all(cr, uid, line.product_id.taxes_id, price, line.qty, product=line.product_id, partner=line.order_id.partner_id or False) + + cur = line.order_id.pricelist_id.currency_id + res[line.id]['price_subtotal'] = cur_obj.round(cr, uid, cur, taxes['total']) + res[line.id]['price_subtotal_incl'] = cur_obj.round(cr, uid, cur, taxes['total_included']) return res - def price_by_product_multi(self, cr, uid, ids, context=None): - if context is None: - context = {} - res = {}.fromkeys(ids, 0.0) - lines = self.browse(cr, uid, ids, context=context) + def onchange_product_id(self, cr, uid, ids, pricelist, product_id, qty=0, partner_id=False, context=None): + context = context or {} + if not product_id: + return {} + if not pricelist: + raise osv.except_osv(_('No Pricelist !'), + _('You have to select a pricelist in the sale form !\n' \ + 'Please set one before choosing a product.')) - pricelist_ids = [line.order_id.pricelist_id.id for line in lines] - products_by_qty_by_partner = [(line.product_id.id, line.qty, line.order_id.partner_id.id) for line in lines] + price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist], + product_id, qty or 1.0, partner_id)[pricelist] - price_get_multi_res = self.pool.get('product.pricelist').price_get_multi(cr, uid, pricelist_ids, products_by_qty_by_partner, context=context) + result = self.onchange_qty(cr, uid, ids, product_id, 0.0, qty, price, context=context) + result['value']['price_unit'] = price + return result - for line in lines: - pricelist = line.order_id.pricelist_id.id - product_id = line.product_id + def onchange_qty(self, cr, uid, ids, product, discount, qty, price_unit, context=None): + result = {} + if not product: + return result + account_tax_obj = self.pool.get('account.tax') + cur_obj = self.pool.get('res.currency') - if not product_id: - res[line.id] = 0.0 - continue - if not pricelist: - raise osv.except_osv(_('No Pricelist !'), - _('You have to select a pricelist in the sale form !\n' \ - 'Please set one before choosing a product.')) + prod = self.pool.get('product.product').browse(cr, uid, product, context=context) - #old_price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist], product_id.id, qty or 1.0, partner_id, {'uom': uom_id})[pricelist] - #print "prod_id: %s, pricelist: %s, price: %s" % (product_id.id, pricelist, price) - price = price_get_multi_res[line.product_id.id][pricelist] - #print "prod_id: %s, pricelist: %s, price2: %s" % (product_id.id, pricelist, price2) + taxes = prod.taxes_id + price = price_unit * (1 - (discount or 0.0) / 100.0) + taxes = account_tax_obj.compute_all(cr, uid, prod.taxes_id, price, qty, product=prod, partner=False) - #if old_price != price: - # raise Exception('old_price != price') - - unit_price = price or product_id.list_price - res[line.id] = unit_price - if unit_price is False: - raise osv.except_osv(_('No valid pricelist line found !'), - _("Couldn't find a pricelist line matching this product" \ - " and quantity.\nYou have to change either the product," \ - " the quantity or the pricelist.")) - return res - - def price_by_product(self, cr, uid, ids, pricelist, product_id, qty=0, partner_id=False): - if not product_id: - return 0.0 - if not pricelist: - raise osv.except_osv(_('No Pricelist !'), - _('You have to select a pricelist in the sale form !\n' \ - 'Please set one before choosing a product.')) - p_obj = self.pool.get('product.product').browse(cr, uid, [product_id])[0] - uom_id = p_obj.uom_po_id.id - price = self.pool.get('product.pricelist').price_get(cr, uid, - [pricelist], product_id, qty or 1.0, partner_id, {'uom': uom_id})[pricelist] - unit_price=price or p_obj.list_price - if unit_price is False: - raise osv.except_osv(_('No valid pricelist line found !'), - _("Couldn't find a pricelist line matching this product" \ - " and quantity.\nYou have to change either the product," \ - " the quantity or the pricelist.")) - return unit_price - - def onchange_product_id(self, cr, uid, ids, pricelist, product_id, qty=0, partner_id=False): - price = self.price_by_product(cr, uid, ids, pricelist, product_id, qty, partner_id) - self.write(cr, uid, ids, {'price_unit':price}) - pos_stot = (price * qty) - return {'value': {'price_unit': price, 'price_subtotal_incl': pos_stot}} - - def onchange_subtotal(self, cr, uid, ids, discount, price, pricelist, qty,partner_id, product_id, *a): - prod_obj = self.pool.get('product.product') - price_f = self.price_by_product(cr, uid, ids, pricelist, product_id, qty, partner_id) - prod_id = '' - if product_id: - prod_id = prod_obj.browse(cr, uid, product_id).disc_controle - disc = 0.0 - if (disc != 0.0 or prod_id) and price_f > 0: - disc = 100 - (price/price_f*100) - return {'value': {'discount': disc, 'price_unit': price_f}} - return {} - - def onchange_ded(self, cr, uid, ids, val_ded, price_u, *a): - res_obj = self.pool.get('res.users') - comp = res_obj.browse(cr, uid, uid).company_id.company_discount or 0.0 - val = 0.0 - if val_ded and price_u: - val=100.0 * val_ded / price_u - if val > comp: - return {'value': {'discount': val, 'notice': '' }} - return {'value': {'discount': val}} - - def onchange_discount(self, cr, uid, ids, discount, price, *a): - pos_order = self.pool.get('pos.order.line') - res_obj = self.pool.get('res.users') - company_disc = pos_order.browse(cr,uid,ids) - if discount: - if not company_disc: - comp=res_obj.browse(cr,uid,uid).company_id.company_discount or 0.0 - else: - comp= company_disc[0] and company_disc[0].order_id.company_id and company_disc[0].order_id.company_id.company_discount or 0.0 - - if discount > comp : - return {'value': {'notice': '', 'price_ded': price * discount * 0.01 or 0.0 }} - else: - return {'value': {'notice': 'Minimum Discount', 'price_ded': price * discount * 0.01 or 0.0 }} - else : - return {'value': {'notice': 'No Discount', 'price_ded': price * discount * 0.01 or 0.0}} - - def onchange_qty(self, cr, uid, ids, discount, qty, price, context=None): - subtotal = qty * price - if discount: - subtotal = subtotal - (subtotal * discount / 100) - return {'value': {'price_subtotal_incl': subtotal}} + result['price_subtotal'] = taxes['total'] + result['price_subtotal_incl'] = taxes['total_included'] + return {'value': result} _columns = { - 'name': fields.char('Line Description', size=512), 'company_id': fields.many2one('res.company', 'Company', required=True), + 'name': fields.char('Line No', size=32, required=True), 'notice': fields.char('Discount Notice', size=128, required=True), - 'serial_number': fields.char('Serial Number', size=128), 'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], required=True, change_default=True), - 'price_unit': fields.function(_get_amount, string='Unit Price', store=True), - 'price_ded': fields.float('Discount(Amount)', digits_compute=dp.get_precision('Point Of Sale')), - 'qty': fields.float('Quantity'), - 'qty_rfd': fields.float('Refunded Quantity'), - 'price_subtotal': fields.function(_amount_line_all, multi='pos_order_line_amount', string='Subtotal w/o Tax'), - 'price_subtotal_incl': fields.function(_amount_line_all, multi='pos_order_line_amount', string='Subtotal'), + 'price_unit': fields.float(string='Unit Price', digits=(16, 2)), + 'qty': fields.float('Quantity', digits=(16, 2)), + 'price_subtotal': fields.function(_amount_line_all, multi='pos_order_line_amount', string='Subtotal w/o Tax', store=True), + 'price_subtotal_incl': fields.function(_amount_line_all, multi='pos_order_line_amount', string='Subtotal', store=True), '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 = { 'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'pos.order.line'), 'qty': lambda *a: 1, 'discount': lambda *a: 0.0, - 'price_ded': lambda *a: 0.0, 'notice': lambda *a: 'No Discount', 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, - } - - def create(self, cr, user, vals, context=None): - if vals.get('product_id'): - return super(pos_order_line, self).create(cr, user, vals, context=context) - return False - - def write(self, cr, user, ids, values, context=None): - if 'product_id' in values and not values['product_id']: - return False - return super(pos_order_line, self).write(cr, user, ids, values, context=context) + } def copy_data(self, cr, uid, id, default=None, context=None): if not default: default = {} default.update({ - 'prodlot_id': False, 'name': self.pool.get('ir.sequence').get(cr, uid, 'pos.order.line') }) return super(pos_order_line, self).copy_data(cr, uid, id, default, context=context) - def _scan_product(self, cr, uid, ean, qty, order): - # search pricelist_id - product_obj = self.pool.get('product.product') - pricelist_id = self.pool.get('pos.order').read(cr, uid, [order], ['pricelist_id'] ) - if not pricelist_id: - return False - - new_line = True - - product_id = product_obj.search(cr, uid, [('ean13','=', ean)]) - if not product_id: - return False - - # search price product - product = product_obj.read(cr, uid, product_id) - product_name = product[0]['name'] - price = self.price_by_product(cr, uid, 0, pricelist_id[0]['pricelist_id'][0], product_id[0], 1) - - order_line_ids = self.search(cr, uid, [('name', '=', product_name), ('order_id', '=' ,order)]) - if order_line_ids: - new_line = False - order_line_id = order_line_ids[0] - qty += self.read(cr, uid, order_line_ids[0], ['qty'])['qty'] - - if new_line: - vals = {'product_id': product_id[0], - 'price_unit': price, - 'qty': qty, - 'name': product_name, - 'order_id': order, - } - line_id = self.create(cr, uid, vals) - if not line_id: - raise osv.except_osv(_('Error'), _('Create line failed !')) - else: - vals = { - 'qty': qty, - 'price_unit': price - } - line_id = self.write(cr, uid, order_line_id, vals) - if not line_id: - raise osv.except_osv(_('Error'), _('Modify line failed !')) - line_id = order_line_id - - price_line = float(qty) * float(price) - return { - 'name': product_name, - 'product_id': product_id[0], - 'price': price, - 'price_line': price_line , - 'qty': qty - } - pos_order_line() class product_product(osv.osv): @@ -1250,20 +677,6 @@ class product_product(osv.osv): _columns = { 'income_pdt': fields.boolean('Product for Input'), 'expense_pdt': fields.boolean('Product for Output'), - 'am_out': fields.boolean('Control for Output Operations'), - 'disc_controle': fields.boolean('Discount Control'), - } - _defaults = { - 'disc_controle': True, } product_product() -class stock_picking(osv.osv): - _inherit = 'stock.picking' - _columns = { - 'pos_order': fields.many2one('pos.order', 'Pos order'), - } - -stock_picking() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/point_of_sale_demo.xml b/addons/point_of_sale/point_of_sale_demo.xml index e95ce05256f..c6fbefb1f6b 100644 --- a/addons/point_of_sale/point_of_sale_demo.xml +++ b/addons/point_of_sale/point_of_sale_demo.xml @@ -4,185 +4,175 @@ - + - buy - - Cash In - - Cash In - - - - - buy - - Cash Out - - Cash Out - - + buy + + Cash In + + Cash In + + + + + buy + + Cash Out + + Cash Out + + - - POS/019 - POS/019 - SAJ/2010/010 - - - - - - none - - open - out_invoice - SAJ/2010/010 - - - - / - - - - - - - Invoice from POS: POS/019 - - - - - [PC1] Basic PC - - - - - - - - - - - - - 2011-04-07 - - - - - - - - - paid - - - - POS/019 - tax_excluded - - - - - - - 10 - open - - - - - - - - - general - - - - Cash Journal - (test) - - - - - - Payment POS/019 - - - - - POS/019 - - - - direct - - POS notes - done - out - - - OUT/00019 - none - - - - POS/019 - - - - - - 1 - - - done - - - - Stock move (POS 12) - - - - - - - - No Discount - - - - - - Order Line/07 - - + + POS/019 + POS/019 + SAJ/2010/010 + + + + + + none + + open + out_invoice + SAJ/2010/010 + + + + / + + + + + + + Invoice from POS: POS/019 + + + + + [PC1] Basic PC + + + + + + + + + + + + + + + + + + + paid + + POS/019 + tax_excluded + + + + + + 10 + open + + + + + + + + + general + + + + Cash Journal - (test) + + + + Payment POS/019 + + + + + POS/019 + + + + direct + + POS notes + done + out + + + OUT/00019 + none + + + + POS/019 + + + + + + 1 + + + done + + + + Stock move (POS 12) + + + + + + + + No Discount + + + + + + Order Line/07 + + - + - tax_excluded - - - + + + - tax_excluded - - - - - + + + + + [PC3] Medium PC @@ -218,6 +208,6 @@ 0.10 Discount Applied - + diff --git a/addons/point_of_sale/point_of_sale_sequence.xml b/addons/point_of_sale/point_of_sale_sequence.xml index 399dccc634f..d2d4bcc313c 100644 --- a/addons/point_of_sale/point_of_sale_sequence.xml +++ b/addons/point_of_sale/point_of_sale_sequence.xml @@ -13,8 +13,8 @@ POS Order pos.order - POS/ - 3 + POS + 6 @@ -24,8 +24,8 @@ POS order line pos.order.line - Order Line/ - 2 + POSL + 4 diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml index a8552cddc15..0f81d4a24f5 100644 --- a/addons/point_of_sale/point_of_sale_view.xml +++ b/addons/point_of_sale/point_of_sale_view.xml @@ -13,51 +13,47 @@ form - + - - - + +

- [[o.note_2]] diff --git a/addons/point_of_sale/report/pos_receipt_with_remboursment.py b/addons/point_of_sale/report/pos_receipt_with_remboursment.py deleted file mode 100644 index c600a9e2754..00000000000 --- a/addons/point_of_sale/report/pos_receipt_with_remboursment.py +++ /dev/null @@ -1,83 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 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 . -# -############################################################################## - -import time -from report import report_sxw -import pooler - -class order(report_sxw.rml_parse): - - def __init__(self, cr, uid, name, context): - super(order, self).__init__(cr, uid, name, context=context) - - user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid) - partner = user.company_id.partner_id - - self.localcontext.update({ - 'time': time, - 'disc': self.discount, - 'net': self.netamount, - 'get_journal_amt': self._get_journal_amt, - 'address': partner.address and partner.address[0] or False, - }) - - def netamount(self, order_line_id): - sql = 'select (qty*price_unit) as net_price from pos_order_line where id = %s' - self.cr.execute(sql, (order_line_id,)) - res = self.cr.fetchone() - return res[0] - - def discount(self, order_id): - sql = 'select discount, price_unit, qty from pos_order_line where order_id = %s ' - self.cr.execute(sql, (order_id,)) - res = self.cr.fetchall() - dsum = 0 - for line in res: - if line[0] != 0: - dsum = dsum +(line[2] * (line[0]*line[1]/100)) - return dsum - - def _get_journal_amt(self, order_id): - lst=[] - sql = """ select aj.name from account_bank_statement as abs - LEFT JOIN account_bank_statement_line as absl ON abs.id = absl.statement_id - LEFT JOIN account_journal as aj ON aj.id = abs.journal_id - WHERE absl.pos_statement_id = %d"""%(order_id.id) - self.cr.execute(sql) - res = self.cr.fetchone() - if not res: - return ['', 0.0] - lst.append(res[0]) # todo: improve - sql2 = """ select sum(absl.amount) as amt from account_bank_statement as abs - LEFT JOIN account_bank_statement_line as absl ON abs.id = absl.statement_id - LEFT JOIN account_journal as aj ON aj.id = abs.journal_id - where aj.name = '%s' """%(res[0]) - self.cr.execute(sql2) - res1 = self.cr.fetchone() - if res1: - lst.append(res1[0]) - else: - lst.append(0.0) - return lst - -report_sxw.report_sxw('report.pos.receipt.with.remboursment', 'pos.order', 'addons/point_of_sale/report/pos_receipt_with_remboursment.rml', parser=order, header=False) - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/addons/point_of_sale/report/pos_receipt_with_remboursment.rml b/addons/point_of_sale/report/pos_receipt_with_remboursment.rml deleted file mode 100644 index 9c25d5f4a89..00000000000 --- a/addons/point_of_sale/report/pos_receipt_with_remboursment.rml +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - [[ repeatIn(objects,'o') ]] - [[ repeatIn(objects,'o') ]] - [[o.user_id.company_id.name]] - [[ address and address.street ]], [[ address and address.zip ]] [[ address and address.city ]] - [[ address and address.country_id.name ]] - Tel : [[ address and address.phone ]] - E-mail : [[ address and address.email ]] - Salesman : [[ o.user_id.name ]] - Sale Date : [[ o.date_order ]] - Refund Receipt: [[o.name]] - - - - - - - - - - - -
- [[ repeatIn(o.lines,'line') ]] - - -
- - - - - - - - - - - - - - - - - [[ get_journal_amt(o)[1] ]] - - - diff --git a/addons/point_of_sale/report/pos_receipt_without_remboursment.py b/addons/point_of_sale/report/pos_receipt_without_remboursment.py deleted file mode 100644 index d2fe0f1d542..00000000000 --- a/addons/point_of_sale/report/pos_receipt_without_remboursment.py +++ /dev/null @@ -1,83 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 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 . -# -############################################################################## - -import time -from report import report_sxw -import pooler - -class order(report_sxw.rml_parse): - - def __init__(self, cr, uid, name, context): - super(order, self).__init__(cr, uid, name, context=context) - - user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid) - partner = user.company_id.partner_id - - self.localcontext.update({ - 'time': time, - 'disc': self.discount, - 'net': self.netamount, - 'get_journal_amt': self._get_journal_amt, - 'address': partner.address and partner.address[0] or False, - }) - - def netamount(self, order_line_id): - sql = 'select (qty*price_unit) as net_price from pos_order_line where id = %s' - self.cr.execute(sql, (order_line_id,)) - res = self.cr.fetchone() - return res[0] - - def discount(self, order_id): - sql = 'select discount, price_unit, qty from pos_order_line where order_id = %s ' - self.cr.execute(sql, (order_id,)) - res = self.cr.fetchall() - dsum = 0 - for line in res: - if line[0] != 0: - dsum = dsum +(line[2] * (line[0]*line[1]/100)) - return dsum - - def _get_journal_amt(self, order_id): - lst=[] - sql = """ select aj.name from account_bank_statement as abs - LEFT JOIN account_bank_statement_line as absl ON abs.id = absl.statement_id - LEFT JOIN account_journal as aj ON aj.id = abs.journal_id - WHERE absl.pos_statement_id = %d"""%(order_id.id) - self.cr.execute(sql) - res = self.cr.fetchone() - if not res: - return ['', 0.0] - lst.append(res[0]) # todo: improve - sql2 = """ select sum(absl.amount) as amt from account_bank_statement as abs - LEFT JOIN account_bank_statement_line as absl ON abs.id = absl.statement_id - LEFT JOIN account_journal as aj ON aj.id = abs.journal_id - where aj.name = '%s' """%(res[0]) - self.cr.execute(sql2) - res1 = self.cr.fetchone() - if res1: - lst.append(res1[0]) - else: - lst.append(0.0) - return lst - -report_sxw.report_sxw('report.pos.receipt.without.remboursment', 'pos.order', 'addons/point_of_sale/report/pos_receipt_without_remboursment.rml', parser=order, header=False) - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/addons/point_of_sale/report/pos_receipt_without_remboursment.rml b/addons/point_of_sale/report/pos_receipt_without_remboursment.rml deleted file mode 100644 index ff6ebc3132c..00000000000 --- a/addons/point_of_sale/report/pos_receipt_without_remboursment.rml +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - [[ repeatIn(objects,'o') ]] - [[ repeatIn(objects,'o') ]] - [[o.user_id.company_id.name]] - [[ address and address.street ]], [[ address and address.zip ]] [[ address and address.city ]] - [[ address and address.country_id.name ]] - Tel : [[ address and address.phone ]] - E-mail : [[ address and address.email ]] - Salesman : [[ o.user_id.name ]] - Sale Date : [[ str(o.date_order) ]] - Purchase Voucher : [[o.name]] - - - - - - - - - - - -
- [[ repeatIn(o.lines,'line') ]] - - -
- - - - - - - - - - - - - - - - - [[ get_journal_amt(o)[0] ]] : [[ get_journal_amt(o)[1] ]] - - - - - - - - - - diff --git a/addons/point_of_sale/report/pos_report.py b/addons/point_of_sale/report/pos_report.py index cd41eb92944..1ecf80ea519 100644 --- a/addons/point_of_sale/report/pos_report.py +++ b/addons/point_of_sale/report/pos_report.py @@ -49,7 +49,7 @@ class report_transaction_pos(osv.osv): min(absl.id) as id, count(absl.id) as no_trans, sum(absl.amount) as amount, - sum(line.price_ded) as disc, + sum((100.0-line.discount) * line.price_unit * line.qty / 100.0) as disc, to_char(date_trunc('day',absl.create_date),'YYYY-MM-DD')::text as date_create, po.user_id as user_id, po.sale_journal as journal_id, @@ -236,4 +236,4 @@ class report_sales_by_margin_pos_month(osv.osv): """) report_sales_by_margin_pos_month() -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/test/point_of_sale_report.yml b/addons/point_of_sale/test/point_of_sale_report.yml index b821dc899df..14a4b334800 100644 --- a/addons/point_of_sale/test/point_of_sale_report.yml +++ b/addons/point_of_sale/test/point_of_sale_report.yml @@ -34,24 +34,6 @@ if tools.config['test_report_directory']: file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-receipt report'+format), 'wb+').write(data) -- - In order to test the PDF reports defined on a Point of Sale, we will print a POS Receipt With Reimbursement Report -- - !python {model: pos.order}: | - import netsvc, tools, os - (data, format) = netsvc.LocalService('report.pos.receipt.with.remboursment').create(cr, uid, [ref('point_of_sale.order_pos1'), ref('point_of_sale.order_pos2')], {}, {}) - if tools.config['test_report_directory']: - file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-receipt with reimbursement report'+format), 'wb+').write(data) - -- - In order to test the PDF reports defined on a point of sale, we will print a POS Receipt Without Reimbursement Report -- - !python {model: pos.order}: | - import netsvc, tools, os - (data, format) = netsvc.LocalService('report.pos.receipt.without.remboursment').create(cr, uid, [ref('point_of_sale.order_pos1'), ref('point_of_sale.order_pos2')], {}, {}) - if tools.config['test_report_directory']: - file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-receipt without reimbursement report'+format), 'wb+').write(data) - - Print the POS Payment Report through the wizard - @@ -74,56 +56,6 @@ from tools import test_reports test_reports.try_report_action(cr, uid, 'action_report_pos_details',wiz_data=data_dict, context=ctx, our_module='point_of_sale') -- - I create a cash journal. -- - !record {model: account.journal, id: account_journal_cash0}: - name: 'Cash Journal' - code: 'CSJ' - type: 'cash' - view_id: account.account_journal_bank_view - sequence_id: account.sequence_journal -- - I create a record for Bank Statement. -- - !record {model: account.bank.statement, id: account_bank_statement_st0}: - name: St.05/19 - balance_end_real: 0.0 - date: !eval time.strftime('%Y-%m-%d') - journal_id: account_journal_cash0 - line_ids: - - name: statement - date: !eval "(datetime.now() + timedelta(5*31)).strftime('%Y-%m-%d')" - type: customer - account_id: account_pos_account_sales - amount: 100 - partner_id: base.res_partner_agrolait - period_id: account.period_5 - state: 'draft' -- - In order to test the PDF reports defined on a Point Of Sale, we will print a Account Statement Report -- - !python {model: account.bank.statement}: | - import netsvc, tools, os, time - (data, format) = netsvc.LocalService('report.account.statement').create(cr, uid, [ref('point_of_sale.account_bank_statement_st0')], {}, {}) - if tools.config['test_report_directory']: - file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-account_statement_report'+format), 'wb+').write(data) -- - In order to test the PDF reports defined on a Point Of Sale, we will print a POS User Product Report -- - !python {model: account.bank.statement}: | - import netsvc, tools, os, time - (data, format) = netsvc.LocalService('report.pos.user.product').create(cr, uid, [ref('point_of_sale.account_bank_statement_st0')], {}, {}) - if tools.config['test_report_directory']: - file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-user_product_report'+format), 'wb+').write(data) -- - In order to test the PDF reports defined on a Point Of Sale, we will print a POS All Close Cashbox Of the Day Report -- - !python {model: account.bank.statement}: | - import netsvc, tools, os, time - (data, format) = netsvc.LocalService('report.all.closed.cashbox.of.the.day').create(cr, uid, [ref('point_of_sale.account_bank_statement_st0')], {}, {}) - if tools.config['test_report_directory']: - file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-all_closed_cash_box_of_the_day_report'+format), 'wb+').write(data) - In order to test the PDF reports defined on a Point of Sale, we will print a POS Sales User Report - diff --git a/addons/point_of_sale/test/point_of_sale_test.yml b/addons/point_of_sale/test/point_of_sale_test.yml index 6165e993240..f565741a8a1 100644 --- a/addons/point_of_sale/test/point_of_sale_test.yml +++ b/addons/point_of_sale/test/point_of_sale_test.yml @@ -1,437 +1,173 @@ - - In order to test the POS in module, To give business owners a convenient way of checking out customers and of recording sales. - + In order to test the Point of Sale in module, I will do a full flow from the sale to the paiement and invoicing. - - I configure all details for the Customer, Salesman and Product, Account. -- - I create View Account Type. -- - !record {model: account.account.type, id: account_account_type_view0}: - close_method: none - code: View - name: View - sign: 1 -- - I create Income Account Type. -- - !record {model: account.account.type, id: account_account_type_income0}: - close_method: unreconciled - code: Income - name: Income - sign: 1 -- - I create Expense Account Type. -- - !record {model: account.account.type, id: account_account_type_expense0}: - close_method: unreconciled - code: Expense - name: Expense - sign: 1 -- - I create Cash Account Type. -- - !record {model: account.account.type, id: account_account_type_cash0}: - close_method: balance - code: Cash - name: Cash - sign: 1 -- - I create Minimal Chart Account. -- - !record {model: account.account, id: account_pos_account_minimalchart}: - code: 'x_0' - company_id: base.main_company - currency_mode: current - name: Minimal Chart - parent_left: 1 - parent_right: 12 - type: view - user_type: account_account_type_view0 -- - I create Payable Account. -- - !record {model: account.account, id: account_pos_account_payable}: - code: AP - company_id: base.main_company - currency_mode: current - name: Payable - parent_id: account_pos_account_minimalchart - parent_left: 2 - parent_right: 3 - reconcile: true - type: payable - user_type: account_account_type_expense0 -- - I create Receivable Account. -- - !record {model: account.account, id: account_pos_account_receivable}: - code: AR - company_id: base.main_company - currency_mode: current - name: Receivable - parent_id: account_pos_account_minimalchart - parent_left: 4 - parent_right: 5 - reconcile: true - type: receivable - user_type: account_account_type_income0 -- - I create Cash Account. -- - !record {model: account.account, id: account_pos_account_cash}: - code: C - company_id: base.main_company - currency_mode: current - name: Cash - parent_id: account_pos_account_minimalchart - parent_left: 6 - parent_right: 7 - type: other - user_type: account_account_type_cash0 -- - I create Purchases Account. -- - !record {model: account.account, id: account_pos_account_purchases}: - code: P - company_id: base.main_company - currency_mode: current - name: Purchases - parent_id: account_pos_account_minimalchart - parent_left: 8 - parent_right: 9 - type: other - user_type: account_account_type_expense0 -- - I create Sales Account. -- - !record {model: account.account, id: account_pos_account_sales}: - code: Sale - company_id: base.main_company - currency_mode: current - name: Sales - parent_id: account_pos_account_minimalchart - parent_left: 10 - parent_right: 11 - type: other - user_type: account_account_type_income0 -- - I create Purchase Journal - (test). -- - !record {model: account.journal, id: account_pos_account_journal_purchasejournal}: - code: PUJ - company_id: base.main_company - default_credit_account_id: account_pos_account_purchases - default_debit_account_id: account_pos_account_purchases - name: Purchase Journal - (test) - sequence_id: account.sequence_purchase_journal - type: purchase - view_id: account.account_journal_view -- - I create Sale Journal. -- - !record {model: account.journal, id: account_pos_account_journal_salejournal}: - code: SJ - company_id: base.main_company - default_credit_account_id: account_pos_account_sales - default_debit_account_id: account_pos_account_sales - name: Sale Journal - sequence_id: account.sequence_sale_journal - type: sale - view_id: account.account_journal_view -- - I create Bank Journal. -- - !record {model: account.journal, id: account_pos_account_journal_bankjournal}: - code: BNK - company_id: base.main_company - default_credit_account_id: account_pos_account_cash - default_debit_account_id: account_pos_account_cash - name: Bank Journal - sequence_id: account.sequence_journal - type: cash - view_id: account.account_journal_bank_view -- - I create property for account payable. -- - !record {model: ir.property, id: ir_property_propertyaccountexpensecateg0}: - company_id: base.main_company - fields_id: account.field_res_partner_property_account_payable - name: property_account_expense_categ - value_reference: account.account,5 -- - I create property for account receivable. -- - !record {model: ir.property, id: ir_property_propertyaccountincomecateg0}: - company_id: base.main_company - fields_id: account.field_res_partner_property_account_receivable - name: property_account_income_categ - value_reference: account.account,6 -- - I create Partner category Customers. -- - !record {model: res.partner.category, id: res_partner_category_customers0}: - name: Customers -- - I create partner -- - !record {model: res.partner, id: res_partner_cleartrail0}: - category_id: - - res_partner_category_customers0 - name: Cleartrail -- - I create partner address. -- - !record {model: res.partner.address, id: res_partner_address_1}: - partner_id: res_partner_cleartrail0 - street: onam plaza, 14 B palasia A B Road - type: contact -- - I create partner invoice address. -- - !record {model: res.partner.address, id: res_partner_address_2}: - partner_id: res_partner_cleartrail0 - type: invoice -- - I create partner delivery address. -- - !record {model: res.partner.address, id: res_partner_address_3}: - partner_id: res_partner_cleartrail0 - street: sangam house 15 B palasia, A B Road - type: delivery -- - I create product category. -- - !record {model: product.category, id: product_category_allproductssellable0}: - name: Mobile Products Sellable -- - I create partner. -- - !record {model: res.partner, id: res_partner_microlinktechnologies0}: - address: - - street: Kailash Vaibhav, Parksite - name: Micro Link Technologies - property_account_payable: account_pos_account_payable - property_account_receivable: account_pos_account_receivable - supplier: true -- - I create partner address . -- - !record {model: res.partner.address, id: res_partner_address_0}: - country_id: base.in - partner_id: res_partner_microlinktechnologies0 - street: Ash House, Ash Road - title: base.res_partner_title_miss -- - I create product category . -- - !record {model: product.category, id: product_category_services0}: - name: Mobile Services -- - I create product Samsung Mobile. -- - !record {model: product.product, id: product_product_samsungmobile0}: - categ_id: product_category_allproductssellable0 - cost_method: standard - list_price: 170.0 - mes_type: fixed - name: Samsung Mobile - procure_method: make_to_order - property_account_expense: account_pos_account_payable - property_account_income: account_pos_account_receivable - seller_delay: '1' - standard_price: 160.0 - supply_method: produce - type: product - uom_id: product.product_uom_unit - uom_po_id: product.product_uom_unit -- - I create product category . -- - !record {model: product.category, id: product_category_computer0}: - name: Computer -- - I create product HP Pavilion Desktop PCs -- - !record {model: product.product, id: product_product_hppaviliondesktoppcs0}: - categ_id: product_category_computer0 - cost_method: standard - mes_type: fixed - list_price: 1000.0 - name: HP Pavilion Desktop PCs - procure_method: make_to_stock - seller_ids: - - delay: 1 - name: res_partner_microlinktechnologies0 - min_qty: 5.0 - supply_method: buy - type: product - uom_id: product.product_uom_unit - uom_po_id: product.product_uom_unit - property_account_expense: account_pos_account_payable - property_account_income: account_pos_account_receivable -- - I create New Order by adding the details of Salesman and "Product" as Samsung Mobile Quantity as 10. + I create a PoS with 2 PC1 and 3 PC2. - !record {model: pos.order, id: pos_order_pos0}: company_id: base.main_company - date_order: !eval time.strftime('%Y-%m-%d %H:%M:%S') - date_validity: !eval "(datetime.now() + timedelta(6*1)).strftime('%Y-%m-%d')" lines: - - company_id: base.main_company - name: Order Line/01 - notice: No Discount - product_id: product_product_samsungmobile0 - qty: 10.0 - qty_rfd: 0.0 - partner_id: base.res_partner_agrolait - name: POS/001 - price_type: tax_excluded - pricelist_id: product.list0 - sale_journal: account.sales_journal - shop_id: sale.shop - user_salesman_id: base.user_root + - name: OL/0001 + product_id: product.product_product_pc1 + price_unit: 450 + discount: 0.0 + qty: 2.0 + - name: OL/0002 + product_id: product.product_product_pc2 + price_unit: 300 + discount: 0.0 + qty: 3.0 - - I add discount. + I check that the total of the order is equal to 450 * 2 / 300 * 3 +- + !python {model: pos.order}: | + order = self.browse(cr, uid, ref('pos_order_pos0')) + assert(abs(order.amount_total - 1800.0) < 0.01), "The order has a wrong amount" +- + I want to add a global discount of 5 percent using the wizard - !record {model: pos.discount, id: pos_discount_0}: discount: 5.0 - discount_notes: More then 5 product - - I Apply the discount. + I click the apply button to set the discount on all lines - !python {model: pos.discount}: | - self.apply_discount(cr, uid, [ref("pos_discount_0")], {"lang": "en_US", "active_model": - "pos.order", "active_ids": [ref("pos_order_pos0")], "tz": False, "active_id": - ref("pos_order_pos0"), }) + self.apply_discount(cr, uid, [ref("pos_discount_0")], {"active_model": "pos.order", + "active_ids": [ref("pos_order_pos0")], "active_id": ref("pos_order_pos0"), }) - - I open the register. -- - !record {model: pos.open.statement, id: pos_open_statement_0}: - {} -- - I open the statement. -- - !python {model: pos.open.statement}: | - jou_obj = self.pool.get('account.journal') - statement_obj = self.pool.get('account.bank.statement') - cr.execute("SELECT DISTINCT journal_id FROM pos_journal_users " - "WHERE user_id = %s ORDER BY journal_id"% (uid, )) - jou_ids = map(lambda x1: x1[0], cr.fetchall()) - journal_ids = jou_obj.search(cr, uid, [('auto_cash', '=', True), ('type', '=', 'cash'), ('id', 'in', jou_ids)], context=context) - ids = statement_obj.search(cr, uid, [('state', '=', 'open'), ('user_id', '=', uid), ('journal_id', 'in', journal_ids)], context=context) - if not ids: - self.open_statement(cr, uid, [ref("pos_open_statement_0")], {"lang": "en_US", "active_model": - "ir.ui.menu", "active_ids": [ref("point_of_sale.menu_open_statement")], "tz": - False, "active_id": ref("point_of_sale.menu_open_statement"), }) -- - I click on the "Make Payment" wizard. -- - !record {model: pos.make.payment, id: pos_make_payment_0}: - amount: 1615.0 - journal: 7 - payment_date: !eval time.strftime("%Y-%m-%d") - payment_name: Payment - product_id: product_product_hppaviliondesktoppcs0 - pricelist_id: product.list0 - partner_id: base.res_partner_agrolait -- - I make the payment. -- - !python {model: pos.make.payment}: | - self.check(cr, uid, [ref("pos_make_payment_0")], {"lang": "en_US", "active_model": - "pos.order", "active_ids": [ref("point_of_sale.pos_order_pos0")], "tz": - False, "active_id": ref("point_of_sale.pos_order_pos0"), }) -- - I check the Statement lines are created automatically when payment is done. -- - !python {model: pos.order}: | - order=self.browse(cr,uid,ref("pos_order_pos0")) - assert(order.statement_ids!=[]), "Statement lines not created" -- - When I click on Return picking button , I get three option. -- - 1. Cancel 2.Return Goods and Exchange 3.Return without Refund -- - When I click on Return Goods and Exchange. -- - Then it allows me to define the quantity of products, which will return to the stock. -- - I click on Return Picking button. -- - !record {model: pos.return, id: pos_return_0}: - pos_moves_ids: - - product_id: product_product_hppaviliondesktoppcs0 - quantity: 5.0 -- - I Return the product. -- - !python {model: pos.return}: | - self.create_returns(cr, uid, [ref("pos_return_0")], {"lang": "en_US", "active_model": - "pos.order", "active_ids": [ref("point_of_sale.pos_order_pos0")], "tz": - False, "active_id": ref("point_of_sale.pos_order_pos0"), }) -- - Then it allows me to define the quantity of products, which will return to the stock. -- - I select the HP Pavilion Desktop PCs for exchange. -- - !record {model: pos.add.product, id: pos_add_product_0}: - product_id: product_product_hppaviliondesktoppcs0 - quantity: 5.0 -- - I click on close button. -- - !python {model: pos.add.product}: | - return_obj = self.pool.get('pos.return') - context['active_ids'] = [ref("pos_order_pos0")] - return_obj.view_init(cr, uid, [],context=context) - self.close_action(cr, uid, [ref("pos_add_product_0")], {"lang": "en_US", "active_model": - "pos.order", "active_ids": [ref("pos_order_pos0")], "tz": False, "active_id": ref("pos_order_pos0"), - }) -- - I create payment. -- - !record {model: pos.make.payment, id: pos_make_payment_1}: - amount: 680.0 - journal: 7 - payment_date: !eval time.strftime("%Y-%m-%d") - payment_name: Payment - product_id: product_product_hppaviliondesktoppcs0 - pricelist_id: product.list0 - partner_id: base.res_partner_agrolait -- - For payment, I click on Make Payment. -- - !python {model: pos.make.payment}: | - self.check(cr, uid, [ref("pos_make_payment_1")], {"lang": "en_US", "active_model": - "pos.order", "active_ids": [ref("pos_order_pos0")], "tz": - False, "active_id": ref("pos_order_pos0"), }) -- - To check the Return without Refund . I click button "Return without Refund ". -- - !record {model: pos.return, id: pos_return_0}: - {} -- - The quantity which is selected in Return lines wizard is refunded. -- - !python {model: pos.return}: | - self.create_returns2(cr, uid, [ref("pos_return_0")], {"lang": "en_US", "active_model": - "pos.order", "active_ids": [ref("pos_order_pos0")], "tz": - False, "active_id": ref("pos_order_pos0"), }) -- - To Close order, I use the wizard "Sale Confirm". -- - !record {model: pos.confirm, id: pos_confirm_0}: - {} -- - I close this order. -- - !python {model: pos.confirm}: | - self.action_confirm(cr, uid, [ref("pos_confirm_0")], {"lang": "en_US", "active_model": - "pos.order", "active_ids": [ref("pos_order_pos0")], "tz": False, "active_id": ref("pos_order_pos0"), - }) -- - I check order state is done. + I check that the total of the order is not equal to (450 * 2 / 300 * 3)*0.95 - !python {model: pos.order}: | - order=self.browse(cr,uid,ref("pos_order_pos0")) - assert(order.state=='done'), "Order is not done" + order = self.browse(cr, uid, ref('pos_order_pos0')) + assert(abs(order.amount_total - (450*2+300*3)*0.95) < 0.01), "The order has a wrong amount" +#- +# I open the register. +#- +# !record {model: pos.open.statement, id: pos_open_statement_0}: +# {} +#- +# I open the statement. +#- +# !python {model: pos.open.statement}: | +# jou_obj = self.pool.get('account.journal') +# statement_obj = self.pool.get('account.bank.statement') +# cr.execute("SELECT DISTINCT journal_id FROM pos_journal_users " +# "WHERE user_id = %s ORDER BY journal_id"% (uid, )) +# jou_ids = map(lambda x1: x1[0], cr.fetchall()) +# journal_ids = jou_obj.search(cr, uid, [('auto_cash', '=', True), ('type', '=', 'cash'), ('id', 'in', jou_ids)], context=context) +# ids = statement_obj.search(cr, uid, [('state', '=', 'open'), ('user_id', '=', uid), ('journal_id', 'in', journal_ids)], context=context) +# if not ids: +# self.open_statement(cr, uid, [ref("pos_open_statement_0")], {"lang": "en_US", "active_model": +# "ir.ui.menu", "active_ids": [ref("point_of_sale.menu_open_statement")], "tz": +# False, "active_id": ref("point_of_sale.menu_open_statement"), }) +#- +# I click on the "Make Payment" wizard. +#- +# !record {model: pos.make.payment, id: pos_make_payment_0}: +# amount: 1615.0 +# journal: 7 +# payment_date: !eval time.strftime("%Y-%m-%d") +# payment_name: Payment +# product_id: product_product_hppaviliondesktoppcs0 +# pricelist_id: product.list0 +# partner_id: base.res_partner_agrolait +#- +# I make the payment. +#- +# !python {model: pos.make.payment}: | +# self.check(cr, uid, [ref("pos_make_payment_0")], {"lang": "en_US", "active_model": +# "pos.order", "active_ids": [ref("point_of_sale.pos_order_pos0")], "tz": +# False, "active_id": ref("point_of_sale.pos_order_pos0"), }) +#- +# I check the Statement lines are created automatically when payment is done. +#- +# !python {model: pos.order}: | +# order=self.browse(cr,uid,ref("pos_order_pos0")) +# assert(order.statement_ids!=[]), "Statement lines not created" +#- +# When I click on Return picking button , I get three option. +#- +# 1. Cancel 2.Return Goods and Exchange 3.Return without Refund +#- +# When I click on Return Goods and Exchange. +#- +# Then it allows me to define the quantity of products, which will return to the stock. +#- +# I click on Return Picking button. +#- +# !record {model: pos.return, id: pos_return_0}: +# pos_moves_ids: +# - product_id: product_product_hppaviliondesktoppcs0 +# quantity: 5.0 +#- +# I Return the product. +#- +# !python {model: pos.return}: | +# self.create_returns(cr, uid, [ref("pos_return_0")], {"lang": "en_US", "active_model": +# "pos.order", "active_ids": [ref("point_of_sale.pos_order_pos0")], "tz": +# False, "active_id": ref("point_of_sale.pos_order_pos0"), }) +#- +# Then it allows me to define the quantity of products, which will return to the stock. +#- +# I select the HP Pavilion Desktop PCs for exchange. +#- +# !record {model: pos.add.product, id: pos_add_product_0}: +# product_id: product_product_hppaviliondesktoppcs0 +# quantity: 5.0 +#- +# I click on close button. +#- +# !python {model: pos.add.product}: | +# return_obj = self.pool.get('pos.return') +# context['active_ids'] = [ref("pos_order_pos0")] +# return_obj.view_init(cr, uid, [],context=context) +# self.close_action(cr, uid, [ref("pos_add_product_0")], {"lang": "en_US", "active_model": +# "pos.order", "active_ids": [ref("pos_order_pos0")], "tz": False, "active_id": ref("pos_order_pos0"), +# }) +#- +# I create payment. +#- +# !record {model: pos.make.payment, id: pos_make_payment_1}: +# amount: 680.0 +# journal: 7 +# payment_date: !eval time.strftime("%Y-%m-%d") +# payment_name: Payment +# product_id: product_product_hppaviliondesktoppcs0 +# pricelist_id: product.list0 +# partner_id: base.res_partner_agrolait +#- +# For payment, I click on Make Payment. +#- +# !python {model: pos.make.payment}: | +# self.check(cr, uid, [ref("pos_make_payment_1")], {"lang": "en_US", "active_model": +# "pos.order", "active_ids": [ref("pos_order_pos0")], "tz": +# False, "active_id": ref("pos_order_pos0"), }) +#- +# To check the Return without Refund . I click button "Return without Refund ". +#- +# !record {model: pos.return, id: pos_return_0}: +# {} +#- +# The quantity which is selected in Return lines wizard is refunded. +#- +# !python {model: pos.return}: | +# self.create_returns2(cr, uid, [ref("pos_return_0")], {"lang": "en_US", "active_model": +# "pos.order", "active_ids": [ref("pos_order_pos0")], "tz": +# False, "active_id": ref("pos_order_pos0"), }) +#- +# To Close order, I use the wizard "Sale Confirm". +#- +# !record {model: pos.confirm, id: pos_confirm_0}: +# {} +#- +# I close this order. +#- +# !python {model: pos.confirm}: | +# self.action_confirm(cr, uid, [ref("pos_confirm_0")], {"lang": "en_US", "active_model": +# "pos.order", "active_ids": [ref("pos_order_pos0")], "tz": False, "active_id": ref("pos_order_pos0"), +# }) +#- +# I check order state is done. +#- +# !python {model: pos.order}: | +# order=self.browse(cr,uid,ref("pos_order_pos0")) +# assert(order.state=='done'), "Order is not done" diff --git a/addons/point_of_sale/wizard/__init__.py b/addons/point_of_sale/wizard/__init__.py index 4a131d5894b..1c95aa17ab0 100644 --- a/addons/point_of_sale/wizard/__init__.py +++ b/addons/point_of_sale/wizard/__init__.py @@ -19,7 +19,6 @@ # ############################################################################## -import pos_add_product import pos_confirm import pos_discount import pos_get_sale @@ -37,8 +36,6 @@ import pos_payment_report_user import pos_payment_report_date import pos_payment_report import pos_payment -import pos_scan_product -import pos_return # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_add_product.py b/addons/point_of_sale/wizard/pos_add_product.py deleted file mode 100644 index dfbb96a4514..00000000000 --- a/addons/point_of_sale/wizard/pos_add_product.py +++ /dev/null @@ -1,98 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 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 -from tools.translate import _ - - -class add_product(osv.osv_memory): - _name = 'pos.add.product' - _description = 'Add Product' - - _columns = { - 'product_id': fields.many2one('product.product', 'Product', required=True), - 'quantity': fields.float('Quantity', required=True), - } - _defaults = { - 'quantity': 1, - } - - def select_product(self, cr, uid, ids, context=None): - """ - To get the product and quantity and add in order . - @param self: The object pointer. - @param cr: A database cursor - @param uid: ID of the user currently logged in - @param context: A standard dictionary - @return : Return the add product form again for adding more product - """ - if context is None: - context = {} - this = self.browse(cr, uid, ids[0], context=context) - record_id = context and context.get('active_id', False) - assert record_id, _('Active ID is not found') - if record_id: - order_obj = self.pool.get('pos.order') - order_obj.add_product(cr, uid, record_id, this.product_id.id, this.quantity, context=context) - return { - 'name': _('Add Product'), - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'pos.add.product', - 'view_id': False, - 'target': 'new', - 'views': False, - 'type': 'ir.actions.act_window', - } - - def close_action(self, cr, uid, ids, context=None): - """ - To get the product and Make the payment . - @param self: The object pointer. - @param cr: A database cursor - @param uid: ID of the user currently logged in - @param context: A standard dictionary - @return : Return the Make Payment - """ - if context is None: - context = {} - record_id = context and context.get('active_id', False) - order_obj= self.pool.get('pos.order') - this = self.browse(cr, uid, ids[0], context) - order_obj.add_product(cr, uid, record_id, this.product_id.id, this.quantity, context=context) - - order_obj.write(cr, uid, [record_id], {'state': 'done'}, context=context) - return { - 'name': _('Make Payment'), - 'context': context and context.get('active_id', False), - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'pos.make.payment', - 'view_id': False, - 'target': 'new', - 'views': False, - 'type': 'ir.actions.act_window', - } - -add_product() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - diff --git a/addons/point_of_sale/wizard/pos_add_product.xml b/addons/point_of_sale/wizard/pos_add_product.xml deleted file mode 100644 index ac5fd1c6f65..00000000000 --- a/addons/point_of_sale/wizard/pos_add_product.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - Add Product - pos.add.product - form - -
- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
- [[ repeatIn(get_data_current_user(user), 'line_ids')]] - -
- - - - - - - - - - - - - - - - - - - - diff --git a/addons/point_of_sale/test/point_of_sale_report.yml b/addons/point_of_sale/test/point_of_sale_report.yml index 14a4b334800..f31f9ec0f49 100644 --- a/addons/point_of_sale/test/point_of_sale_report.yml +++ b/addons/point_of_sale/test/point_of_sale_report.yml @@ -65,15 +65,6 @@ (data, format) = netsvc.LocalService('report.pos.sales.user').create(cr, uid, [ref('point_of_sale.order_pos1'), ref('point_of_sale.order_pos2')], data_dict, {}) if tools.config['test_report_directory']: file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-sales_user_report'+format), 'wb+').write(data) -- - In order to test the PDF reports defined on a Point of Sale, we will print a POS Sales User Today Report -- - !python {model: pos.order}: | - import netsvc, tools, os, time - data_dict = {'model':'ir.ui.menu', 'form':{'user_id':[ref('base.user_root')] }} - (data, format) = netsvc.LocalService('report.pos.sales.user.today').create(cr, uid, [ref('point_of_sale.order_pos1'), ref('point_of_sale.order_pos2')], data_dict, {}) - if tools.config['test_report_directory']: - file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-sales_user_today_report'+format), 'wb+').write(data) - In order to test the PDF reports defined on a Point of Sale, we will print a POS Payment Report - @@ -91,11 +82,3 @@ (data, format) = netsvc.LocalService('report.pos.payment.report.user').create(cr, uid, [ref('point_of_sale.order_pos1'), ref('point_of_sale.order_pos2')], data_dict, {}) if tools.config['test_report_directory']: file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-payment_user_report'+format), 'wb+').write(data) -- - In order to test the PDF reports defined on a Point of Sale, we will print a POS Today's Sales of Current User Report -- - !python {model: pos.order}: | - import netsvc, tools, os, time - (data, format) = netsvc.LocalService('report.pos.sales.user.today.current.user').create(cr, uid, [ref('point_of_sale.order_pos1'), ref('point_of_sale.order_pos2')], {}, {}) - if tools.config['test_report_directory']: - file(os.path.join(tools.config['test_report_directory'], 'point_of_sale-sales_today_current_user_report'+format), 'wb+').write(data) diff --git a/addons/point_of_sale/wizard/__init__.py b/addons/point_of_sale/wizard/__init__.py index 1c95aa17ab0..0985ae906be 100644 --- a/addons/point_of_sale/wizard/__init__.py +++ b/addons/point_of_sale/wizard/__init__.py @@ -21,7 +21,6 @@ import pos_confirm import pos_discount -import pos_get_sale import pos_open_statement import pos_close_statement import pos_box_entries @@ -29,8 +28,6 @@ import pos_box_out import pos_details import pos_sales_user import pos_sales_user_today -import pos_sales_user_current_user -import all_closed_cashbox_of_the_day import pos_receipt import pos_payment_report_user import pos_payment_report_date diff --git a/addons/point_of_sale/wizard/pos_confirm.py b/addons/point_of_sale/wizard/pos_confirm.py index c55209650ca..c80f3ed1dd6 100644 --- a/addons/point_of_sale/wizard/pos_confirm.py +++ b/addons/point_of_sale/wizard/pos_confirm.py @@ -25,36 +25,21 @@ from osv import osv class pos_confirm(osv.osv_memory): _name = 'pos.confirm' - _description = 'Point of Sale Confirm' + _description = 'Post POS Journal Entries' def action_confirm(self, cr, uid, ids, context=None): - """ - Confirm the order and close the sales. - @param self: The object pointer. - @param cr: A database cursor - @param uid: ID of the user currently logged in - @param context: A standard dictionary - @return :Blank dictionary - """ - if context is None: - context = {} - record_id = context and context.get('active_id', False) - if record_id: - if isinstance(record_id, (int, long)): - record_id = [record_id] - if record_id: - order_obj = self.pool.get('pos.order') - - for order_id in order_obj.browse(cr, uid, record_id, context=context): - if order_id.state == 'paid': - order_obj.write(cr, uid, [order_id.id], {'journal_entry': True}, context=context) - order_obj.create_account_move(cr, uid, [order_id.id], context=context) - - wf_service = netsvc.LocalService("workflow") - for i in record_id: - wf_service.trg_validate(uid, 'pos.order', i, 'done', cr) + wf_service = netsvc.LocalService("workflow") + order_obj = self.pool.get('pos.order') + ids = order_obj.search(cr, uid, [('state','=','paid')], context=context) + for order in order_obj.browse(cr, uid, ids, context=context): + todo = True + for line in order.statement_ids: + if line.statement_id.state <> 'confirm': + todo = False + break + if todo: + wf_service.trg_validate(uid, 'pos.order', order.id, 'done', cr) return {} - pos_confirm() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_confirm.xml b/addons/point_of_sale/wizard/pos_confirm.xml index 1ea769cae49..85b43bd115d 100644 --- a/addons/point_of_sale/wizard/pos_confirm.xml +++ b/addons/point_of_sale/wizard/pos_confirm.xml @@ -1,32 +1,37 @@ - + - - - Sale Confirm + + Post Journal Entries pos.confirm form -
-
@@ -39,4 +39,4 @@
+ +
- - + \ No newline at end of file From 95f0fa26eea3ace29f787209cb7cee82984bda96 Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Fri, 2 Sep 2011 17:46:42 +0530 Subject: [PATCH 014/128] [IMP]Improved code for show message if selection of same column and can not match file field with object field. bzr revid: kch@tinyerp.com-20110902121642-qv4ike63cuqaa9vk --- addons/base/controllers/main.py | 21 +++++++++++++++++++++ addons/base/static/src/js/data_import.js | 16 ++++++++++------ addons/base/static/src/xml/base.xml | 3 ++- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 3a1d84b3b14..23ec289517a 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -1411,6 +1411,10 @@ class Import(View): @openerpweb.httprequest def import_data(self, req, **params): import StringIO + _fields = {} + _fields_invert = {} + prefix_node='' + prefix_value = '' context = req.session.eval_context(req.context) modle_obj = req.session.model(params.get('model')) @@ -1443,6 +1447,23 @@ class Import(View): if not isinstance(fields, list): fields = [fields] + flds = dict(req.session.model(params.get('model')).fields_get(False, req.session.eval_context(req.context))) + flds.update({'id':{'string':'ID'},'.id':{'string':'Database ID'}}) + fields_order = flds.keys() + for field in fields_order: + st_name = prefix_value+flds[field]['string'] or field + _fields[prefix_node+field] = st_name + _fields_invert[st_name] = prefix_node+field + + unmatch_field = [] + for fld in fields: + if ((fld not in _fields.keys()) and (fld not in _fields_invert.keys())): + unmatch_field.append(fld) + + if unmatch_field: + error = {'message':("You cannot import the fields '%s',because we cannot auto-detect it." % (unmatch_field))} + return simplejson.dumps({'error':error}) + for line in data: try: datas.append(map(lambda x:x.decode(params.get('csvcode')).encode('utf-8'), line)) diff --git a/addons/base/static/src/js/data_import.js b/addons/base/static/src/js/data_import.js index e09b4dc743f..6329f0910d7 100644 --- a/addons/base/static/src/js/data_import.js +++ b/addons/base/static/src/js/data_import.js @@ -66,18 +66,22 @@ openerp.base.DataImport = openerp.base.Dialog.extend({ var success_node = $("#success"); success_node.append(QWeb.render('ImportView-result',{'success': results['success']})); } - var mm = []; + var selected_fields = []; $("td #sel_field").click(function(){ - mm = []; + selected_fields = []; $("td #sel_field option:selected").each(function(){ - mm.push($(this).index()); + selected_fields.push($(this).index()); }); }); $("td #sel_field").change(function(){ - $(this).css('background-color',''); - var kk = this.selectedIndex; - if ($.inArray(kk,mm) > -1){ + $("#message").empty(); + $("td #sel_field").css('background-color',''); + $(".ui-button-text:contains('Import File')").parent().attr("disabled",false); + var curr_selected = this.selectedIndex; + if ($.inArray(curr_selected,selected_fields) > -1){ $(this).css('background-color','#FF6666'); + $("#message").append("Selected column should not be same."); + $(".ui-button-text:contains('Import File')").parent().attr("disabled",true); }else{ $(this).css('background-color',''); } diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 42c64a34429..7d8dc7782e4 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -1415,7 +1415,7 @@
- +
@@ -1439,6 +1439,7 @@
+
CSV Options From 1127d2b3e33e012a681b93aaf506af0c698a6ad0 Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Tue, 6 Sep 2011 17:41:46 +0530 Subject: [PATCH 015/128] [IMP]Improved code for required field should be selected and reload list view after successfully import data. bzr revid: kch@tinyerp.com-20110906121146-p7z0gpa4bofneg2j --- addons/base/controllers/main.py | 9 ++++-- addons/base/static/src/js/data_import.js | 40 +++++++++++++++++++----- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 23ec289517a..643ec1e8c48 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -1331,11 +1331,17 @@ class Import(View): import StringIO _fields = {} _fields_invert = {} + req_field = [] error = None all_fields = [] fields = dict(req.session.model(params.get('model')).fields_get(False, req.session.eval_context(req.context))) fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}}) + for field in fields: + value = fields[field] + if (value.get('required',False) == True): + req_field.append(field) + def model_populate(fields, prefix_node='', prefix=None, prefix_value='', level=2): def str_comp(x,y): if x -1){ $(this).css('background-color','#FF6666'); - $("#message").append("Selected column should not be same."); + $("#message").append("*Selected column should not be same."); $(".ui-button-text:contains('Import File')").parent().attr("disabled",true); }else{ $(this).css('background-color',''); } }); }, + do_check_req_field: function(req_fld){ + if (req_fld.length){ + var sel_fields =[]; + var required_fields = []; + $("td #sel_field option:selected").each(function(){ + sel_fields.push($(this).val()); + }); + _.each(req_fld,function(fld){ + if ($.inArray(fld,sel_fields) <= -1){ + required_fields.push(fld); + } + }); + if (required_fields.length){ + $("#message").append("*Required Fields are not selected which is "+required_fields+". "); + $(".ui-button-text:contains('Import File')").parent().attr("disabled",true); + } + } + }, stop: function() { $(this.$dialog).remove(); this._super(); From 4c31251a43e6c45c502c51abf49fa59961971593 Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Tue, 6 Sep 2011 18:35:25 +0530 Subject: [PATCH 016/128] [FIX]Fixed code as par rename web from base and read file data problem. bzr revid: kch@tinyerp.com-20110906130525-axb8e40gc1zya9a3 --- addons/web/controllers/main.py | 10 +++++----- addons/web/static/src/js/boot.js | 2 +- addons/web/static/src/js/data_import.js | 8 ++++---- addons/web/static/src/js/views.js | 10 +++++----- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 790e6034218..e8b7ef23f8b 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1361,7 +1361,7 @@ class Reports(View): return False class Import(View): - _cp_path = "/base/import" + _cp_path = "/web/import" def fields_get(self, req, model): Model = req.session.model(model) @@ -1414,7 +1414,7 @@ class Import(View): all_fields = fields.keys() try: - data = csv.reader(params.get('csvfile').file, quotechar=str(params.get('csvdel')), delimiter=str(params.get('csvsep'))) + data = csv.reader(params.get('csvfile'), quotechar=str(params.get('csvdel')), delimiter=str(params.get('csvsep'))) except: error={'message': 'error opening .CSV file. Input Error.'} return simplejson.dumps({'error':error}) @@ -1449,8 +1449,8 @@ class Import(View): error = {'message':('Error processing the first line of the file. Field "%s" is unknown') % (word,)} if error: - params.get('csvfile').file.seek(0) - error=dict(error, preview=params.get('csvfile').file.read(200)) + params.get('csvfile').seek(0) + error=dict(error, preview=params.get('csvfile').read(200)) return simplejson.dumps({'error':error}) return simplejson.dumps({'records':records[1:],'header':fields,'all_fields':all_fields,'req_field':req_field}) @@ -1466,7 +1466,7 @@ class Import(View): context = req.session.eval_context(req.context) modle_obj = req.session.model(params.get('model')) res = None - content = params.get('csvfile').file.read() + content = params.get('csvfile').read() input=StringIO.StringIO(content) limit = 0 data = [] diff --git a/addons/web/static/src/js/boot.js b/addons/web/static/src/js/boot.js index 2c00eb3d5a5..1846a2727ae 100644 --- a/addons/web/static/src/js/boot.js +++ b/addons/web/static/src/js/boot.js @@ -56,7 +56,7 @@ openerp.web = function(instance) { openerp.web.formats(instance); openerp.web.chrome(instance); openerp.web.data(instance); - var files = ["views","search","list","form","list_editable","web_mobile","view_tree","data_export"]; + var files = ["views","search","list","form","list_editable","web_mobile","view_tree","data_export","data_import"]; for(var i=0; i Date: Thu, 8 Sep 2011 12:01:05 +0530 Subject: [PATCH 017/128] [FIX]product:Now UoM rounding precision is working lp bug: https://launchpad.net/bugs/842758 fixed bzr revid: mma@tinyerp.com-20110908063105-96bqho40ok3tkblt --- addons/product/product.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/product/product.py b/addons/product/product.py index b81fdc0bad1..7d4ff35d530 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -75,7 +75,7 @@ class product_uom(osv.osv): _description = 'Product Unit of Measure' def _compute_factor_inv(self, factor): - return factor and round(1.0 / factor, 6) or 0.0 + return factor and (1.0 / factor) or 0.0 def _factor_inv(self, cursor, user, ids, name, arg, context=None): res = {} From b84248c04cf86c8bfa56e275181bf7b9dce8723d Mon Sep 17 00:00:00 2001 From: Ferdinand <> Date: Thu, 8 Sep 2011 14:52:29 +0530 Subject: [PATCH 018/128] [FIX] hr_timesheet_invoice : Added Configuration Error when no income account is set for service product lp bug: https://launchpad.net/bugs/838887 fixed bzr revid: bde@tinyerp.com-20110908092229-2fg0xixgpjgsdjwm --- .../wizard/hr_timesheet_final_invoice_create.py | 4 ++++ .../wizard/hr_timesheet_invoice_create.py | 2 ++ 2 files changed, 6 insertions(+) diff --git a/addons/hr_timesheet_invoice/wizard/hr_timesheet_final_invoice_create.py b/addons/hr_timesheet_invoice/wizard/hr_timesheet_final_invoice_create.py index 66975695814..0e056b81e61 100644 --- a/addons/hr_timesheet_invoice/wizard/hr_timesheet_final_invoice_create.py +++ b/addons/hr_timesheet_invoice/wizard/hr_timesheet_final_invoice_create.py @@ -126,6 +126,8 @@ class final_invoice_create(osv.osv_memory): taxes = [] tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes) + if not account_id: + raise osv.except_osv(_("Configuration Error"), _("No income account defined for product '%s'") % product.name) curr_line = { 'price_unit': -amount, 'quantity': 1.0, @@ -146,6 +148,8 @@ class final_invoice_create(osv.osv_memory): taxes = product.taxes_id tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes) account_id = product.product_tmpl_id.property_account_income.id or product.categ_id.property_account_income_categ.id + if not account_id: + raise osv.except_osv(_("Configuration Error"), _("No income account defined for product '%s'") % product.name) curr_line = { 'price_unit': account.amount_max - amount_total, 'quantity': 1.0, diff --git a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py index 4372a19a89a..502d35ff2d1 100644 --- a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py +++ b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py @@ -155,6 +155,8 @@ class hr_timesheet_invoice_create(osv.osv_memory): taxes = product.taxes_id tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes) account_id = product.product_tmpl_id.property_account_income.id or product.categ_id.property_account_income_categ.id + if not account_id: + raise osv.except_osv(_("Configuration Error"), _("No income account defined for product '%s'") % product.name) curr_line = { 'price_unit': price, 'quantity': qty, From 783904920a5804b61af90f335d1a1a3963e1ff70 Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Thu, 8 Sep 2011 15:54:00 +0530 Subject: [PATCH 019/128] [FIX]Fixed code for python related problems as par review. bzr revid: kch@tinyerp.com-20110908102400-7udk4wzl0blt7jgg --- addons/web/controllers/main.py | 90 +++++++++++++++---------------- addons/web/static/src/js/views.js | 2 +- 2 files changed, 44 insertions(+), 48 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index e8b7ef23f8b..8b61484d794 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1369,19 +1369,18 @@ class Import(View): return fields @openerpweb.httprequest - def detect_data(self, req, **params): - import StringIO + def detect_data(self, req, csvfile, csvsep, csvdel, csvcode, csvskip, **params): + _fields = {} _fields_invert = {} req_field = [] error = None - all_fields = [] - fields = dict(req.session.model(params.get('model')).fields_get(False, req.session.eval_context(req.context))) + fields = req.session.model(params.get('model')).fields_get(False, req.session.eval_context(req.context)) fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}}) for field in fields: value = fields[field] - if (value.get('required',False) == True): + if value.get('required'): req_field.append(field) def model_populate(fields, prefix_node='', prefix=None, prefix_value='', level=2): @@ -1402,7 +1401,7 @@ class Import(View): _fields[prefix_node+field] = st_name _fields_invert[st_name] = prefix_node+field - if fields[field].get('type','')=='one2many' and level>0: + if fields[field].get('type')=='one2many' and level>0: fields2 = self.fields_get(req, fields[field]['relation']) model_populate(fields2, prefix_node+field+'/', None, st_name+'/', level-1) @@ -1412,36 +1411,35 @@ class Import(View): fields.update({'id':{'string':'ID'},'.id':{'string':'Database ID'}}) model_populate(fields) all_fields = fields.keys() + all_fields.sort() try: - data = csv.reader(params.get('csvfile'), quotechar=str(params.get('csvdel')), delimiter=str(params.get('csvsep'))) + data = csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep)) except: error={'message': 'error opening .CSV file. Input Error.'} return simplejson.dumps({'error':error}) records = [] - fields = [] - word='' - limit = 4 count = 0 - try: - for i, row in enumerate(data): - records.append(row) - if i == limit: - break + header_fields = [] + word='' - for j, line in enumerate(records): - if j == 1: - break - for word in line: - word = str(word.decode(params.get('csvcode'))) - if word in _fields: - fields.append((word, _fields[word])) - elif word in _fields_invert.keys(): - fields.append((_fields_invert[word], word)) - else: - count = count + 1 - fields.append((word, word)) + try: + for rec in itertools.islice(data,0,4): + records.append(rec) + + headers = itertools.islice(records,1) + line = headers.next() + + for word in line: + word = str(word.decode(csvcode)) + if word in _fields: + header_fields.append((word, _fields[word])) + elif word in _fields_invert.keys(): + header_fields.append((_fields_invert[word], word)) + else: + count = count + 1 + header_fields.append((word, word)) if len(line) == count: error = {'message':"File has not any column header."} @@ -1449,15 +1447,15 @@ class Import(View): error = {'message':('Error processing the first line of the file. Field "%s" is unknown') % (word,)} if error: - params.get('csvfile').seek(0) - error=dict(error, preview=params.get('csvfile').read(200)) + csvfile.seek(0) + error=dict(error, preview=csvfile.read(200)) return simplejson.dumps({'error':error}) - return simplejson.dumps({'records':records[1:],'header':fields,'all_fields':all_fields,'req_field':req_field}) + return simplejson.dumps({'records':records[1:],'header':header_fields,'all_fields':all_fields,'req_field':req_field}) @openerpweb.httprequest - def import_data(self, req, **params): - import StringIO + def import_data(self, req, csvfile, csvsep, csvdel, csvcode, csvskip, **params): + _fields = {} _fields_invert = {} prefix_node='' @@ -1466,24 +1464,22 @@ class Import(View): context = req.session.eval_context(req.context) modle_obj = req.session.model(params.get('model')) res = None - content = params.get('csvfile').read() - input=StringIO.StringIO(content) + limit = 0 data = [] - if not (params.get('csvdel') and len(params.get('csvdel')) == 1): + if not (csvdel and len(csvdel) == 1): error={'message': "The CSV delimiter must be a single character"} return simplejson.dumps({'error':error}) try: - for j, line in enumerate(csv.reader(input, quotechar=str(params.get('csvdel')), delimiter=str(params.get('csvsep')))): - # If the line contains no data, we should skip it. - if not line: - continue - if j == limit: - fields = line - else: - data.append(line) + data_record = csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep)) + for rec in itertools.islice(data_record,0,None): + data.append(rec) + + headers = itertools.islice(data,1) + fields = headers.next() + except csv.Error, e: error={'message': str(e),'title': 'File Format Error'} return simplejson.dumps({'error':error}) @@ -1494,7 +1490,7 @@ class Import(View): if not isinstance(fields, list): fields = [fields] - flds = dict(req.session.model(params.get('model')).fields_get(False, req.session.eval_context(req.context))) + flds = modle_obj.fields_get(False, req.session.eval_context(req.context)) flds.update({'id':{'string':'ID'},'.id':{'string':'Database ID'}}) fields_order = flds.keys() for field in fields_order: @@ -1504,16 +1500,16 @@ class Import(View): unmatch_field = [] for fld in fields: - if ((fld not in _fields.keys()) and (fld not in _fields_invert.keys())): + if ((fld not in _fields) and (fld not in _fields_invert)): unmatch_field.append(fld) if unmatch_field: error = {'message':("You cannot import the fields '%s',because we cannot auto-detect it." % (unmatch_field))} return simplejson.dumps({'error':error}) - for line in data: + for line in data[1:]: try: - datas.append(map(lambda x:x.decode(params.get('csvcode')).encode('utf-8'), line)) + datas.append(map(lambda x:x.decode(csvcode).encode('utf-8'), line)) except: datas.append(map(lambda x:x.decode('latin').encode('utf-8'), line)) diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 530e9fc68a3..f8382cb362b 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -733,7 +733,7 @@ openerp.web.View = openerp.web.Widget.extend({ }, on_sidebar_import: function() { var import_view = new openerp.web.DataImport(this, this.dataset); - import_view.start(false); + import_view.start(); }, on_sidebar_export: function() { var export_view = new openerp.web.DataExport(this, this.dataset); From ec6f367388fb41c494d4e115f7131938550d18cd Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Thu, 8 Sep 2011 16:18:56 +0530 Subject: [PATCH 020/128] [FIX]Fixed code for merge related problem(instanciate a QWeb2.Engine for each session). bzr revid: kch@tinyerp.com-20110908104856-qemlzgbc6zo5ar5k --- addons/web/static/src/js/data_import.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 37682f9e243..a0b0ca34430 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -1,4 +1,5 @@ openerp.web.data_import = function(openerp) { +var QWeb = openerp.web.qweb; openerp.web.DataImport = openerp.web.Dialog.extend({ init: function(parent, dataset){ this.parent = parent; From ea82b312865db0bac4f6a951a0d023483dd5c0dd Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 9 Sep 2011 12:53:07 +0200 Subject: [PATCH 021/128] [FIX] Safari seems to have issue with loading json data in iframes, implement a jsonp-ish system to correctly handle retrieval of import test return data bzr revid: xmo@openerp.com-20110909105307-2463mzr6micqdvbm --- addons/web/controllers/main.py | 40 ++++++++++++++-------- addons/web/static/src/js/data_import.js | 44 +++++++++++++++++-------- 2 files changed, 57 insertions(+), 27 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index c79bf4552b1..9d71451a344 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1402,13 +1402,14 @@ class Import(View): return fields @openerpweb.httprequest - def detect_data(self, req, csvfile, csvsep, csvdel, csvcode, csvskip, **params): + def detect_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip, + jsonp): _fields = {} _fields_invert = {} req_field = [] error = None - fields = req.session.model(params.get('model')).fields_get(False, req.session.eval_context(req.context)) + fields = req.session.model(model).fields_get(False, req.session.eval_context(req.context)) fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}}) for field in fields: @@ -1450,7 +1451,8 @@ class Import(View): data = csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep)) except: error={'message': 'error opening .CSV file. Input Error.'} - return simplejson.dumps({'error':error}) + return '' % ( + jsonp, simplejson.dumps({'error':error})) records = [] count = 0 @@ -1482,12 +1484,15 @@ class Import(View): if error: csvfile.seek(0) error=dict(error, preview=csvfile.read(200)) - return simplejson.dumps({'error':error}) + return '' % ( + jsonp, simplejson.dumps({'error':error})) - return simplejson.dumps({'records':records[1:],'header':header_fields,'all_fields':all_fields,'req_field':req_field}) + return '' % ( + jsonp, simplejson.dumps({'records':records[1:],'header':header_fields,'all_fields':all_fields,'req_field':req_field})) @openerpweb.httprequest - def import_data(self, req, csvfile, csvsep, csvdel, csvcode, csvskip, **params): + def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip, + jsonp): _fields = {} _fields_invert = {} @@ -1495,7 +1500,7 @@ class Import(View): prefix_value = '' context = req.session.eval_context(req.context) - modle_obj = req.session.model(params.get('model')) + modle_obj = req.session.model(model) res = None limit = 0 @@ -1503,7 +1508,8 @@ class Import(View): if not (csvdel and len(csvdel) == 1): error={'message': "The CSV delimiter must be a single character"} - return simplejson.dumps({'error':error}) + return '' % ( + jsonp, simplejson.dumps({'error':error})) try: data_record = csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep)) @@ -1515,7 +1521,8 @@ class Import(View): except csv.Error, e: error={'message': str(e),'title': 'File Format Error'} - return simplejson.dumps({'error':error}) + return '' % ( + jsonp, simplejson.dumps({'error':error})) datas = [] ctx = context @@ -1538,7 +1545,8 @@ class Import(View): if unmatch_field: error = {'message':("You cannot import the fields '%s',because we cannot auto-detect it." % (unmatch_field))} - return simplejson.dumps({'error':error}) + return '' % ( + jsonp, simplejson.dumps({'error':error})) for line in data[1:]: try: @@ -1549,22 +1557,26 @@ class Import(View): # If the file contains nothing, if not datas: error = {'message': 'The file is empty !', 'title': 'Importation !'} - return simplejson.dumps({'error':error}) + return '' % ( + jsonp, simplejson.dumps({'error':error})) #Inverting the header into column names try: res = modle_obj.import_data(fields, datas, 'init', '', False, ctx) except xmlrpclib.Fault, e: error = {"message":e.faultCode} - return simplejson.dumps({'error':error}) + return '' % ( + jsonp, simplejson.dumps({'error':error})) if res[0]>=0: success={'message':'Imported %d objects' % (res[0],)} - return simplejson.dumps({'success':success}) + return '' % ( + jsonp, simplejson.dumps({'error':success})) d = '' for key,val in res[1].items(): d+= ('%s: %s' % (str(key),str(val))) msg = 'Error trying to import this record:%s. ErrorMessage:%s %s' % (d,res[2],res[3]) error = {'message':str(msg), 'title':'ImportationError'} - return simplejson.dumps({'error':error}) \ No newline at end of file + return '' % ( + jsonp, simplejson.dumps({'error':error})) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index a0b0ca34430..ae22d2226c9 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -1,5 +1,28 @@ openerp.web.data_import = function(openerp) { var QWeb = openerp.web.qweb; +/** + * Safari does not deal well at all with raw JSON data being returned. As a + * result, we're going to cheat by using a pseudo-jsonp: instead of getting + * JSON data in the iframe, we're getting a ``script`` tag which consists of a + * function call and the returned data (the json dump). + * + * The function is an auto-generated name bound to ``window``, which calls + * back into the callback provided here. + * + * @param {Object} form the form element (DOM or jQuery) to use in the call + * @param {Object} attributes jquery.form attributes object + * @param {Function} callback function to call with the returned data + */ +function jsonp(form, attributes, callback) { + var options = {jsonp: _.uniqueId('import_callback_')}; + window[options.jsonp] = function () { + delete window[options.jsonp]; + callback.apply(null, arguments); + }; + $(form).ajaxSubmit(_.extend({ + data: options + }, attributes)); +} openerp.web.DataImport = openerp.web.Dialog.extend({ init: function(parent, dataset){ this.parent = parent; @@ -35,11 +58,9 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ do_import: function() { var self = this; if(!this.$element.find('#csvfile').val()) { return; } - this.$element.find('#import_data').attr({ - 'action': '/web/import/import_data' - }).ajaxSubmit({ - success: this.on_import_results - }); + jsonp(this.$element.find('#import_data'), { + url: '/web/import/import_data' + }, this.on_import_results); }, on_autodetect_data: function() { var self = this; @@ -48,17 +69,14 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ this.$element.find("#imported_success").css('display','none'); } if(!this.$element.find('#csvfile').val()) { return; } - this.$element.find('#import_data').attr({ - 'action': '/web/import/detect_data' - }).ajaxSubmit({ - success: this.on_import_results - }); + jsonp(this.$element.find('#import_data'), { + url: '/web/import/detect_data' + }, this.on_import_results); }, - on_import_results:function(res){ + on_import_results:function(results){ var self = this; this.$element.find('#result, #success , #message').empty(); - var results = $.parseJSON(res); var result_node = $("#result"); var records = {}; @@ -117,4 +135,4 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ this._super(); } }); -}; \ No newline at end of file +}; From 555610e41bdbb4904a2a042d916bedf122be2abc Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Fri, 9 Sep 2011 17:19:33 +0530 Subject: [PATCH 022/128] [FIX]Fixed code for javascript problems as par review. bzr revid: kch@tinyerp.com-20110909114933-4f0zgiedo9dp865h --- addons/web/controllers/main.py | 2 +- addons/web/static/src/js/data_import.js | 44 ++++++++++++++----------- addons/web/static/src/xml/base.xml | 2 +- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 9d71451a344..016a1596098 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1571,7 +1571,7 @@ class Import(View): if res[0]>=0: success={'message':'Imported %d objects' % (res[0],)} return '' % ( - jsonp, simplejson.dumps({'error':success})) + jsonp, simplejson.dumps({'success':success})) d = '' for key,val in res[1].items(): diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index ae22d2226c9..f31c694312c 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -24,8 +24,9 @@ function jsonp(form, attributes, callback) { }, attributes)); } openerp.web.DataImport = openerp.web.Dialog.extend({ + template: 'ImportDataView', + dialog_title: "Import Data", init: function(parent, dataset){ - this.parent = parent; this._super(parent); this.dataset = dataset; }, @@ -87,23 +88,25 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ result_node.append(QWeb.render('ImportView-result',{'error': results['error']})); }else if(results['success']){ self.stop(); - this.parent.reload_content(); + if (((this.widget_parent['fields_view']['type']) == "tree") || ((this.widget_parent['fields_view']['type']) == "list")){ + this.widget_parent.reload_content(); + } } this.do_check_req_field(results['req_field']); var selected_fields = []; - $("td #sel_field").click(function(){ + this.$element.find("td #sel_field").click(function(){ selected_fields = []; - $("td #sel_field option:selected").each(function(){ + self.$element.find("td #sel_field option:selected").each(function(){ selected_fields.push($(this).index()); }); }); - $("td #sel_field").change(function(){ - $("#message").empty(); - $("td #sel_field").css('background-color',''); - $(".ui-button-text:contains('Import File')").parent().attr("disabled",false); + this.$element.find("td #sel_field").change(function(){ + self.$element.find("#message").empty(); + self.$element.find("td #sel_field").css('background-color',''); + self.$element.find(".ui-button-text:contains('Import File')").parent().attr("disabled",false); self.do_check_req_field(results['req_field']); var curr_selected = this.selectedIndex; - if ($.inArray(curr_selected,selected_fields) > -1){ + if ((curr_selected != 0) && _.contains(selected_fields, curr_selected)){ $(this).css('background-color','#FF6666'); $("#message").append("*Selected column should not be same."); $(".ui-button-text:contains('Import File')").parent().attr("disabled",true); @@ -114,19 +117,22 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ }, do_check_req_field: function(req_fld){ if (req_fld.length){ - var sel_fields =[]; - var required_fields = []; - $("td #sel_field option:selected").each(function(){ - sel_fields.push($(this).val()); - }); - _.each(req_fld,function(fld){ - if ($.inArray(fld,sel_fields) <= -1){ - required_fields.push(fld); - } - }); + var required_fields = ""; + var sel_fields = _.map(this.$element.find("td #sel_field option:selected"), function(fld){ + return fld['text'] + }); + + required_fields = required_fields + _.filter(req_fld, function (fld){ + if (!_.contains(sel_fields,fld)){ + return fld + "," ; + } + }); + if (required_fields.length){ $("#message").append("*Required Fields are not selected which is "+required_fields+". "); $(".ui-button-text:contains('Import File')").parent().attr("disabled",true); + }else{ + $(".ui-button-text:contains('Import File')").parent().attr("disabled",false); } } }, diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index a523b49a863..67729c262ac 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1468,7 +1468,7 @@
- From d7c967689a7372772f1fe67415e2149a1d3f1aae Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Tue, 13 Sep 2011 15:07:56 +0530 Subject: [PATCH 024/128] [FIX]Fixed code for removing unnecessary t-attributes and tabels. bzr revid: kch@tinyerp.com-20110913093756-2z6g8p059lgjxpxq --- addons/web/static/src/js/data_import.js | 1 + addons/web/static/src/xml/base.xml | 169 +++++++++--------------- 2 files changed, 66 insertions(+), 104 deletions(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 6d8ed93aafe..11e62983c47 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -111,6 +111,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ $(".ui-button-text:contains('Import File')").parent().attr("disabled",true); }else{ $(this).css('background-color',''); + $(".ui-button-text:contains('Import File')").parent().attr("disabled",false); } }); }, diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index ce9f7aad562..e103ea0dd43 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1371,80 +1371,62 @@ - +
- + + + - + + + - @@ -1447,7 +1446,7 @@ - From acdb9b1ec4bd0bb6815eb5c4e930d811ffa95759 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Tue, 20 Sep 2011 17:34:00 +0530 Subject: [PATCH 043/128] [ADD] kanban :- add icon of fold and unfold columns. bzr revid: ysa@tinyerp.com-20110920120400-nzybn0awsspkrsli --- addons/web_kanban/static/src/img/minus-icon.png | Bin 0 -> 1390 bytes addons/web_kanban/static/src/img/plus-icon.png | Bin 0 -> 761 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 addons/web_kanban/static/src/img/minus-icon.png create mode 100644 addons/web_kanban/static/src/img/plus-icon.png diff --git a/addons/web_kanban/static/src/img/minus-icon.png b/addons/web_kanban/static/src/img/minus-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cb655d1e5032501ad68d7312725d9cb609c5d157 GIT binary patch literal 1390 zcmcIkO-vI(6douf6$7GCgGS?Ii3tbW+1<93ZrUPH5DY~^n~25(%XTO$?e1oGNDCVL zi5FwM8Dorza5Emfm>9e$a^OIqA&udJ1PvyVh>-&)gWzmiN+Cv1Om_ZW-uJyX-@Kjs zogEj-%TAU70Lt5116_2kG`rP8#~RzA5S=VWV_Vp2wJvX1~7rWI4UbX213X{IpSk3yMk;`Yr_3< z>#&YP!yRF1ctG+*n0kMiHz`nX91{^H<1xh$l0GKGDbTfPMhwijkO3dlUKHohoK0dKPUx()^KRbbu001sB7ql01@dmlvn(BWf#U=xN0Z4k#$QHfuO8_Wx&ke^ zm~`i3`U%klgc6B_J>jscdK7UUj|Z_l;&~hOuo*)N5tBB>sLqmX^8~OV>9R&-Re>h2 z*sBf_AH$e|@-25bQOgHXjGTB>MwApa#MxPtXQn_eH405S7Ky5dRW;_%9=}D_aYEGx z4A>NMcunLkr=6%fcpMN3_ESCTY%Fi%++mg%9JPYeb+rJWb=m_Dsu8(wr~vM;@vaQq zB{;Yoyuc|3_aak>0%0<$n`hc%|DRwHDX*6{sK+wb5|I#&;W$=^K{excYZj4&K2?v4 z#4pFiC`OtR^`af)Kl96GP0NCoCCb<3cIx@7+?U0U?)yTYCEd>;j$^dAe zCf-j?O-)Zv&(6-IQmJ%0JwHFcu&}VSw6wguyt1;gy1Kfywzj^$PJxeaZA?-g3+d_z z0mP)K16MJ5zOsbbWclW8C_=Zb~GH-P80Ef&5mFX!vysop{R z^^U%jo0#tI?95(hZAGzA0EpnaE`DroVs&jTU#XP)J}oVM`3G>LtE;1_srmD*?ru9` zS=ig%!Pw|10AONr5)IibwANTzT{+m^{dMQ#!uv%4U;yap=}BeMnZ;}E?Y8GtuwU9o z+4DkYp68)l+K2CXxO(}Dold2vd!O{mFb6DUe@jbq%CGte0)Uv|`^Ny^_fW1>K*V4s zG&MG6^~S*vfL9g(L}Rf5As9Q`Kf%l(7VzsF6pKY@ZNLnG81XYP#NzRLAs{07;>P+q z-o1GXtu<=38no6S;N|3tuy0^6@^l2Tc+3s2pd48U{2)t25JEr*aiX-245d@~w#z|0WL zD3>eYiHSr4$z%e|d?Ld@7%m8WtegilFP(74yjQR5yu~*prBQPW_00000NkvXXu0mjfdy-ED literal 0 HcmV?d00001 From 87a55e927e7a177b21607275256614251abd5a60 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Tue, 20 Sep 2011 18:42:36 +0530 Subject: [PATCH 044/128] [IMP] kanban :- improve css of fold and unfold columns. bzr revid: ysa@tinyerp.com-20110920131236-hrwinziygoh9j9az --- addons/web_kanban/static/src/css/kanban.css | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index 35b8c1cb16b..c7a1b3f27a9 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -4,9 +4,15 @@ } .oe_vertical_text { - -moz-transform: rotate(-90deg); - -o-transform: rotate(-90deg); - -webkit-transform: rotate(-90deg); + writing-mode:tb-rl; + -webkit-transform:rotate(90deg); + -moz-transform:rotate(90deg); + -o-transform: rotate(90deg); + display:block; + width:30px; + height:20px; + align:center; + font-size:24px; } .openerp .oe_kanban_view .ui-sortable-placeholder { From 01106829e939f30487235481a3365d8cc78da69d Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Tue, 20 Sep 2011 19:01:36 +0530 Subject: [PATCH 045/128] [IMP] kanban :- improve css bzr revid: ysa@tinyerp.com-20110920133136-oida5na52ola5ygl --- addons/web_kanban/static/src/css/kanban.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index c7a1b3f27a9..6224723147f 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -3,11 +3,13 @@ width: 100%; } -.oe_vertical_text { +.openerp .oe_vertical_text { writing-mode:tb-rl; -webkit-transform:rotate(90deg); -moz-transform:rotate(90deg); -o-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); display:block; width:30px; height:20px; From 01eff42ca26c312c75a1eee8d86a986a21c7d9a8 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 20 Sep 2011 17:43:58 +0200 Subject: [PATCH 046/128] [ADD] key in manifest allowing auto loading of modules bzr revid: chs@openerp.com-20110920154358-gpv2ahph6gbtlmiy --- addons/web/__openerp__.py | 1 + addons/web/controllers/main.py | 20 ++++++++++++------- addons/web/static/src/js/boot.js | 10 +++++----- addons/web_hello/__openerp__.py | 1 + addons/web_hello/static/openerp/base_hello.js | 7 +++++++ 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index 74633fa4969..e4a6f235836 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -2,6 +2,7 @@ "name" : "web", "depends" : [], 'active': True, + 'web_auto_load': True, 'js' : [ "static/lib/datejs/globalization/en-US.js", "static/lib/datejs/core.js", diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index d0c8950f9e1..729829b592c 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -104,7 +104,7 @@ home_template = textwrap.dedent(""" %(javascript)s '%i for i in jslist]) # css tags - csslist = ['/web/webclient/css'] + csslist = ['/web/webclient/css?mods='+cs_mods] if req.debug: - csslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(req.config.addons_path, ['web'], 'css')] + csslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(req.config.addons_path, modules, 'css')] css = "\n ".join([''%i for i in csslist]) + r = home_template % { 'javascript': js, - 'css': css + 'css': css, + 'modules': repr(modules), # XXX good js-ification ? } return r @@ -342,7 +348,7 @@ class Session(openerpweb.Controller): # TODO query server for installed web modules mods = [] for name, manifest in openerpweb.addons_manifest.items(): - if name != 'web' and manifest.get('active', True): + if not manifest.get('web_auto_load') and manifest.get('active', True): mods.append(name) return mods diff --git a/addons/web/static/src/js/boot.js b/addons/web/static/src/js/boot.js index b9d5180b5c4..27dc4dcda5e 100644 --- a/addons/web/static/src/js/boot.js +++ b/addons/web/static/src/js/boot.js @@ -19,9 +19,9 @@ /** * OpenERP instance constructor * - * @param {Boolean} skip_init if true, skips the built-in initialization + * @param {Array} modules list of modules to initialize */ - init: function(skip_init) { + init: function(modules) { var new_instance = { // links to the global openerp _openerp: openerp, @@ -35,9 +35,9 @@ web_mobile: {} }; openerp.sessions[new_instance._session_id] = new_instance; - if (!skip_init){ - openerp.web(new_instance); - } + _.each(modules, function (element, index, list) { + openerp[element](new_instance); + }); return new_instance; } }; diff --git a/addons/web_hello/__openerp__.py b/addons/web_hello/__openerp__.py index 7a80645f3b8..f67ff4b23d5 100644 --- a/addons/web_hello/__openerp__.py +++ b/addons/web_hello/__openerp__.py @@ -5,4 +5,5 @@ "js": ["static/*/*.js", "static/*/js/*.js"], "css": [], 'active': False, + 'web_auto_load': True, } diff --git a/addons/web_hello/static/openerp/base_hello.js b/addons/web_hello/static/openerp/base_hello.js index 99c97006eac..91caf263196 100644 --- a/addons/web_hello/static/openerp/base_hello.js +++ b/addons/web_hello/static/openerp/base_hello.js @@ -13,6 +13,13 @@ openerp.web.SearchView = openerp.web.SearchView.extend({ // here you may tweak globals object, if any, and play with on_* or do_* callbacks on them +openerp.web.Login = openerp.web.Login.extend({ + start: function() { + console.log('Hello there'); + this._super.apply(this,arguments); + } +}); + }; // vim:et fdc=0 fdl=0: From e583cf5f0dc5af07b1386661207729b04695f615 Mon Sep 17 00:00:00 2001 From: "Vaibhav (OpenERP)" Date: Wed, 21 Sep 2011 10:57:17 +0530 Subject: [PATCH 047/128] [FIX] web 6.1 does not accept None and integer dict key. bzr revid: vda@tinyerp.com-20110921052717-12cxwnz04jeiddx6 --- addons/process/process.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/addons/process/process.py b/addons/process/process.py index 72c8bce10b4..b4b82c13fac 100644 --- a/addons/process/process.py +++ b/addons/process/process.py @@ -84,12 +84,12 @@ class process_process(osv.osv): process = pool.get('process.process').browse(cr, uid, id, context=context) name = process.name - resource = None + resource = False state = 'N/A' expr_context = {} states = {} - perm = None + perm = False if res_model: states = dict(pool.get(res_model).fields_get(cr, uid, context=context).get('state', {}).get('selection', {})) @@ -242,7 +242,12 @@ class process_process(osv.osv): for k, v in nodes.items(): y = v['y'] v['y'] = min(y - miny + 10, y) - + for k,v in nodes.items(): + k = str(k) + for t,i in transitions.items(): + t = str(t) + nodes = dict([str(n_key), n_val] for n_key, n_val in nodes.iteritems()) + transitions = dict([str(t_key), t_val] for t_key, t_val in transitions.iteritems()) return dict(name=name, resource=resource, state=state, perm=perm, notes=notes, nodes=nodes, transitions=transitions) def copy(self, cr, uid, id, default=None, context=None): From 9862fa094e8979a0566a2391f03a990da44093f8 Mon Sep 17 00:00:00 2001 From: "Vaibhav (OpenERP)" Date: Wed, 21 Sep 2011 13:35:00 +0530 Subject: [PATCH 048/128] [FIX] clean up. bzr revid: vda@tinyerp.com-20110921080500-0dljjjesk3umhwl8 --- addons/process/process.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/addons/process/process.py b/addons/process/process.py index b4b82c13fac..524a3236e7b 100644 --- a/addons/process/process.py +++ b/addons/process/process.py @@ -242,10 +242,7 @@ class process_process(osv.osv): for k, v in nodes.items(): y = v['y'] v['y'] = min(y - miny + 10, y) - for k,v in nodes.items(): - k = str(k) - for t,i in transitions.items(): - t = str(t) + nodes = dict([str(n_key), n_val] for n_key, n_val in nodes.iteritems()) transitions = dict([str(t_key), t_val] for t_key, t_val in transitions.iteritems()) return dict(name=name, resource=resource, state=state, perm=perm, notes=notes, nodes=nodes, transitions=transitions) From 9936f7276006887839998a3dba348d49bc66b9d2 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Wed, 21 Sep 2011 15:22:45 +0530 Subject: [PATCH 049/128] [IMP] kanban :- if drag any record and no record in this group then empty group display and again drop any record in empty group. bzr revid: ysa@tinyerp.com-20110921095245-e2hucliw1zo5uzbj --- addons/web_kanban/static/src/css/kanban.css | 1 - addons/web_kanban/static/src/js/kanban.js | 5 +---- addons/web_kanban/static/src/xml/web_kanban.xml | 8 +++----- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index 6224723147f..0aca55430b5 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -1,5 +1,4 @@ .openerp .oe_kanban_view .oe_column { - float: left; width: 100%; } diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 62f7dd3052a..6c2a06b982c 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -173,7 +173,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ }, do_fold_unfold_columns: function(event, element_id) { var column_id = "column_" + element_id; - var column_element = this.$element.find("#" + column_id + ".oe_fold_column"); + var column_element = this.$element.find("#" + column_id + " .oe_fold_column"); if (column_element.is(":hidden")) { this.$element.find("#" + column_id).find("img.fold-columns-icon").attr('src', '/web_kanban/static/src/img/minus-icon.png'); column_element.show(); @@ -399,9 +399,6 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ kanban_gravatar: self.kanban_gravatar })); }); - } else { - self.$element.find("#column_" + data.value).remove(); - self.all_display_data.splice(index, 1); } }); this.$element.find('.oe_kanban_action').click(this.on_action_clicked); diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index 599c81a8797..352ccf66b19 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -11,7 +11,7 @@ -
- - - - - - - -
-

1. Import a .CSV file

-
- Select a .CSV file to import. If you need a sample of file to import, - you should use the export tool with the "Import Compatible" option. -
+
+

1. Import a .CSV file

+
+ Select a .CSV file to import. If you need a sample of file to import, + you should use the export tool with the "Import Compatible" option.
- - - - - -
- -
+ +
+
+

2. Check your file format

-
- + + + + - - -
+
+
+
+
+ CSV Options + - + + + + + + + + +
-

2. Check your file format

+
+
-
-
-
- CSV Options - - - - - - - - - - - - - -
- -
-
- -
-
+
@@ -1452,52 +1434,31 @@ - - The import failed due to: - - Here is a preview of the file we could not import: -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
+

The import failed due to:

+ +

Here is a preview of the file we could not import:

+
+
+ + + + + + + + + + + + +
+ +
From 804755f9a8c133e4393ecc8bb44550d43e8502f7 Mon Sep 17 00:00:00 2001 From: "Bharat (OpenERP)" Date: Tue, 13 Sep 2011 15:19:42 +0530 Subject: [PATCH 025/128] [FIX] account : Changing date on register in draft state lp bug: https://launchpad.net/bugs/847581 fixed bzr revid: bde@tinyerp.com-20110913094942-930lf6c9641lv38p --- addons/account/account_cash_statement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/account_cash_statement.py b/addons/account/account_cash_statement.py index 982383ea2a7..169c7c0494a 100644 --- a/addons/account/account_cash_statement.py +++ b/addons/account/account_cash_statement.py @@ -340,7 +340,7 @@ class account_cash_statement(osv.osv): }) vals.update({ - 'date': time.strftime("%Y-%m-%d %H:%M:%S"), + 'date': statement.date, 'state': 'open', }) self.write(cr, uid, [statement.id], vals, context=context) From 1f611417f52852b6f22b0e113535f3ce871c6467 Mon Sep 17 00:00:00 2001 From: "Bharat (OpenERP)" Date: Tue, 13 Sep 2011 16:04:58 +0530 Subject: [PATCH 026/128] [FIX] account : removed the date which was getting updated in button_open method bzr revid: bde@tinyerp.com-20110913103458-xqclq7dbwk6bxjrh --- addons/account/account_cash_statement.py | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/account/account_cash_statement.py b/addons/account/account_cash_statement.py index 169c7c0494a..f26fd46c142 100644 --- a/addons/account/account_cash_statement.py +++ b/addons/account/account_cash_statement.py @@ -340,7 +340,6 @@ class account_cash_statement(osv.osv): }) vals.update({ - 'date': statement.date, 'state': 'open', }) self.write(cr, uid, [statement.id], vals, context=context) From 4eb0672324b905d4e6f43eb194b7c85e8043d856 Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Thu, 15 Sep 2011 12:17:53 +0530 Subject: [PATCH 027/128] [FIX]Fixed code for select same column or imported file has same column its shows message and field will red coloration. bzr revid: kch@tinyerp.com-20110915064753-mij2danikyv9iouh --- addons/web/static/src/css/data_import.css | 6 +++ addons/web/static/src/js/data_import.js | 56 +++++++++++++++-------- addons/web/static/src/xml/base.xml | 3 +- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/addons/web/static/src/css/data_import.css b/addons/web/static/src/css/data_import.css index 65f2e0b258b..7ae13fcf5ee 100644 --- a/addons/web/static/src/css/data_import.css +++ b/addons/web/static/src/css/data_import.css @@ -16,4 +16,10 @@ border-bottom-width: 1px; margin: 6px 4px 6px 1px; height: 20px; +} +.openerp .duplicate_fld{ + background-color:#FF6666; +} +.openerp .select_fld{ + background: none repeat scroll 0 0 white; } \ No newline at end of file diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 11e62983c47..a9daca20373 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -75,8 +75,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ }, on_import_results:function(results){ var self = this; - this.$element.find('#result, #success , #message').empty(); - + this.$element.find('#result, #success').empty(); var result_node = $("#result"); var records = {}; @@ -92,44 +91,63 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ } } this.do_check_req_field(results['req_field']); - var selected_fields = []; + var selected_fields; this.$element.find("td #sel_field").click(function(){ - selected_fields = []; - self.$element.find("td #sel_field option:selected").each(function(){ - selected_fields.push($(this).index()); + selected_fields = _.map(self.$element.find("td #sel_field"), function(field){ + return field.selectedIndex }); }); + this.on_change_check(selected_fields,results['req_field']); this.$element.find("td #sel_field").change(function(){ - self.$element.find("#message").empty(); - self.$element.find("td #sel_field").css('background-color',''); - self.$element.find(".ui-button-text:contains('Import File')").parent().attr("disabled",false); - self.do_check_req_field(results['req_field']); - var curr_selected = this.selectedIndex; - if ((curr_selected != 0) && _.contains(selected_fields, curr_selected)){ - $(this).css('background-color','#FF6666'); - $("#message").append("*Selected column should not be same."); + self.on_change_check(selected_fields,results['req_field']); + }); + }, + on_change_check:function(selected_fields,req_field){ + var self = this; + self.$element.find("#message, #msg").remove(); + var duplicate_flds = self.$element.find("td #sel_field option:selected"); + _.each(duplicate_flds,function(fld){ + if (fld.index != 0){ + var res = self.$element.find("td #sel_field option:selected[value='"+ fld.value +"']"); + if (res.length == 1){ + res.parent().removeClass("duplicate_fld"); + res.parent().addClass("select_fld"); + }else if(res.length > 1){ + res.parent().removeClass("select_fld"); + res.parent().addClass("duplicate_fld"); + res.parent().focus(); + } + }else{ + var elem = self.$element.find("td #sel_field option:selected[value='"+ fld.value +"']"); + elem.parent().removeClass("duplicate_fld"); + elem.parent().addClass("select_fld"); + } + }); + if(self.$element.find(".duplicate_fld").length){ + $("#result").before('
*Selected column should not be same.
'); $(".ui-button-text:contains('Import File')").parent().attr("disabled",true); }else{ - $(this).css('background-color',''); + self.$element.find("#msg").remove(); $(".ui-button-text:contains('Import File')").parent().attr("disabled",false); } - }); + self.do_check_req_field(req_field); + }, do_check_req_field: function(req_fld){ var self = this; if (req_fld.length){ + self.$element.find("#message").remove(); var sel_fields = _.map(this.$element.find("td #sel_field option:selected"), function(fld){ return fld['text'] }); - var required_fields = _.filter(req_fld, function(fld){ return !_.contains(sel_fields, fld) }); - if (required_fields.length){ - $("#message").append("*Required Fields are not selected which is "+required_fields+". "); + $("#result").before('
*Required Fields are not selected which is '+required_fields+'.
'); $(".ui-button-text:contains('Import File')").parent().attr("disabled",true); }else{ + self.$element.find("#message").remove(); $(".ui-button-text:contains('Import File')").parent().attr("disabled",false); } } diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index e103ea0dd43..238edbedb62 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1399,7 +1399,6 @@
-
- From b8a01047aeaa9e9f7fe1db94fa3cf2376893e040 Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Thu, 15 Sep 2011 12:37:30 +0530 Subject: [PATCH 028/128] [IMP]rename openerp to db. bzr revid: kch@tinyerp.com-20110915070730-8jira5yy107s99vf --- 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 8d07d4faca0..f3e44467152 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -853,7 +853,7 @@ db.web.View = db.web.Widget.extend(/** @lends db.web.View# */{ console.log('Todo'); }, on_sidebar_import: function() { - var import_view = new openerp.web.DataImport(this, this.dataset); + var import_view = new db.web.DataImport(this, this.dataset); import_view.start(); }, on_sidebar_export: function() { From ccd10f58225fb6b4c14ca0800429c207d4cd455c Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Thu, 15 Sep 2011 13:00:58 +0530 Subject: [PATCH 029/128] [IMP] kanban :- if no groupby on field then display records in multiple columns. bzr revid: ysa@tinyerp.com-20110915073058-w8c9bwjp1otfweoz --- addons/web_kanban/static/src/js/kanban.js | 36 ++++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index dfaf7769df5..1d4de3d8398 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -18,6 +18,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.all_display_data = false; this.groups = []; this.qweb = new QWeb2.Engine(); + this.NO_OF_COLUMNS = 2; if (this.options.action_views_ids.form) { this.form_dialog = new openerp.web.FormDialog(this, {}, this.options.action_views_ids.form, dataset).start(); this.form_dialog.on_form_dialog_saved.add_last(this.on_record_saved); @@ -117,9 +118,12 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ } } }, - on_show_data: function(data) { + on_show_data: function() { var self = this; - this.$element.html(QWeb.render("KanbanView", {"data": data})); + if (!this.group_by.length) { + this.do_record_group(); + } + this.$element.html(QWeb.render("KanbanView", {"data": self.all_display_data})); this.on_reload_kanban(); var drag_handel = false; if (this.$element.find(".oe_kanban_draghandle").length > 0) { @@ -137,6 +141,17 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.$element.find(".oe_column").disableSelection() this.$element.find('button.oe_kanban_button_new').click(this.do_add_record); }, + do_record_group: function() { + if (this.NO_OF_COLUMNS) { + var records = this.all_display_data[0].records; + var record_per_group = Math.round((records).length / this.NO_OF_COLUMNS); + this.all_display_data = []; + for (var i=0, ii=this.NO_OF_COLUMNS; i < ii; i++) { + this.all_display_data.push({'records': records.slice(0,record_per_group), 'value':false, 'header' : false, 'ids':[]}); + records.splice(0,record_per_group); + } + } + }, on_button_click: function (button_attrs, record_id) { var self = this; if (this.groups.length) { @@ -391,6 +406,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ group_by = self.group_by; if (!group_by.length && this.fields_view.arch.attrs.default_group_by) { group_by = [this.fields_view.arch.attrs.default_group_by]; + self.group_by = group_by; } self.datagroup = new openerp.web.DataGroup(self, self.model, self.domain, self.context, group_by); self.dataset.context = self.context; @@ -407,7 +423,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ self.dataset.read_slice([], {}, function(records) { self.all_display_data = [{'records': records, 'value':false, 'header' : false, 'ids': self.dataset.ids}]; self.$element.find(".oe_kanban_view").remove(); - self.on_show_data(self.all_display_data); + self.on_show_data(); }); } ); @@ -431,7 +447,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ self.all_display_data.push({"value" : group_value, "records": records, 'header':group_name, 'ids': self.dataset.ids}); if (datagroups.length == self.all_display_data.length) { self.$element.find(".oe_kanban_view").remove(); - self.on_show_data(self.all_display_data); + self.on_show_data(); } }); }); @@ -443,6 +459,18 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.$element.hide(); } }); + +/*openerp.web_kanban.KanbanRecord = openerp.web.Class.extend({ + init: function (dataset, record_id, options) { + this.dataset = dataset; + this.record_id = record_id; + this.options = options; + }, + + on_render: function () { + + }, +});*/ }; // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: From 7b500f02ad31a6be0337faa3b2d503ef5fdbf93c Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Thu, 15 Sep 2011 13:06:03 +0530 Subject: [PATCH 030/128] [IMP] kanban :- if no group by on field then drag and drop functionality disable. bzr revid: ysa@tinyerp.com-20110915073603-5yvddorwgu0xwyt6 --- addons/web_kanban/static/src/js/kanban.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 1d4de3d8398..3cc2e6a8beb 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -129,6 +129,9 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ if (this.$element.find(".oe_kanban_draghandle").length > 0) { drag_handel = ".oe_kanban_draghandle"; } + if (!this.group_by.length) { + drag_handel = true; + } this.$element.find(".oe_column").sortable({ connectWith: ".oe_column", handle : drag_handel, From 681a5bd001871c68730636ef6ac27c01f79615b1 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Thu, 15 Sep 2011 15:42:09 +0530 Subject: [PATCH 031/128] [IMP] kanban :- column group pass in sort order. bzr revid: ysa@tinyerp.com-20110915101209-29ez6zvd1q8s6asg --- addons/web_kanban/static/src/js/kanban.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 3bbaaf6c430..424518b92d8 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -118,11 +118,19 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ } } }, + sort_group: function (first, second) { + first = first.header.toLowerCase(); + second = second.header.toLowerCase(); + if (first > second) return 1; + else if (first < second) return -1; + else return 0; + }, on_show_data: function() { var self = this; if (!this.group_by.length) { this.do_record_group(); } + self.all_display_data.sort(this.sort_group); this.$element.html(QWeb.render("KanbanView", {"data": self.all_display_data})); this.on_reload_kanban(); var drag_handel = false; From 8511d9986b627ae926b54fe0c3a5b492fd64472e Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Thu, 15 Sep 2011 17:54:20 +0530 Subject: [PATCH 032/128] [FIX]Remove unnecessary code which affects in importing data. bzr revid: kch@tinyerp.com-20110915122420-90lur8b4mdjw5bpx --- addons/web/controllers/main.py | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 09df2b5276f..7f8e91f8695 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1381,7 +1381,7 @@ class Reports(View): report_data['form'] = action['datas']['form'] if 'ids' in action['datas']: report_ids = action['datas']['ids'] - + report_id = report_srv.report( req.session._db, req.session._uid, req.session._password, action["report_name"], report_ids, @@ -1461,7 +1461,7 @@ class Import(View): fields.update({'id':{'string':'ID'},'.id':{'string':'Database ID'}}) model_populate(fields) - all_fields = fields.keys() + all_fields = _fields.keys() all_fields.sort() try: @@ -1511,16 +1511,9 @@ class Import(View): def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip, jsonp): - _fields = {} - _fields_invert = {} - prefix_node='' - prefix_value = '' - context = req.session.eval_context(req.context) modle_obj = req.session.model(model) res = None - - limit = 0 data = [] if not (csvdel and len(csvdel) == 1): @@ -1547,24 +1540,6 @@ class Import(View): if not isinstance(fields, list): fields = [fields] - flds = modle_obj.fields_get(False, req.session.eval_context(req.context)) - flds.update({'id':{'string':'ID'},'.id':{'string':'Database ID'}}) - fields_order = flds.keys() - for field in fields_order: - st_name = prefix_value+flds[field]['string'] or field - _fields[prefix_node+field] = st_name - _fields_invert[st_name] = prefix_node+field - - unmatch_field = [] - for fld in fields: - if ((fld not in _fields) and (fld not in _fields_invert)): - unmatch_field.append(fld) - - if unmatch_field: - error = {'message':("You cannot import the fields '%s',because we cannot auto-detect it." % (unmatch_field))} - return '' % ( - jsonp, simplejson.dumps({'error':error})) - for line in data[1:]: try: datas.append(map(lambda x:x.decode(csvcode).encode('utf-8'), line)) @@ -1573,7 +1548,7 @@ class Import(View): # If the file contains nothing, if not datas: - error = {'message': 'The file is empty !', 'title': 'Importation !'} + error = {'message': 'The file is empty !'} return '' % ( jsonp, simplejson.dumps({'error':error})) @@ -1593,6 +1568,6 @@ class Import(View): for key,val in res[1].items(): d+= ('%s: %s' % (str(key),str(val))) msg = 'Error trying to import this record:%s. ErrorMessage:%s %s' % (d,res[2],res[3]) - error = {'message':str(msg), 'title':'ImportationError'} + error = {'message':str(msg)} return '' % ( jsonp, simplejson.dumps({'error':error})) From 903ae74c11998eafc34ef5612877dd965cb5d7c5 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Fri, 16 Sep 2011 18:30:49 +0530 Subject: [PATCH 033/128] [IMP] kanban :- add basic struncture of folding and unfolding columns. bzr revid: ysa@tinyerp.com-20110916130049-ppachcw4xne1rzjw --- addons/web_kanban/static/src/css/kanban.css | 11 ++++-- addons/web_kanban/static/src/js/kanban.js | 13 +++++++ .../web_kanban/static/src/xml/web_kanban.xml | 36 ++++++++++--------- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index afac6cdef47..1497044b807 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -2,18 +2,25 @@ float: left; width: 100%; } - .openerp .oe_kanban_view .ui-sortable-placeholder { border: 1px dotted black; visibility: visible !important; height: 60px !important; } - +.oe_kanban_row { + overflow:hidden; +} +.oe_table_column { + float:left; +} .openerp .oe_kanban_view .oe_column_heading { color: #000000; font-size: 1.5em; font-weight: bold; } +.openerp .oe_kanban_view { + overflow:hidden; +} .openerp .oe_kanban_action_button { height: 22px; margin: 0; diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 424518b92d8..db9473b4ff5 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -19,6 +19,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.groups = []; this.qweb = new QWeb2.Engine(); this.NO_OF_COLUMNS = 2; + this.DISPLAY_COLUMNS = 3; if (this.options.action_views_ids.form) { this.form_dialog = new openerp.web.FormDialog(this, {}, this.options.action_views_ids.form, dataset).start(); this.form_dialog.on_form_dialog_saved.add_last(this.on_record_saved); @@ -151,6 +152,18 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ }); this.$element.find(".oe_column").disableSelection() this.$element.find('button.oe_kanban_button_new').click(this.do_add_record); + var column_width = 100 / (this.all_display_data).length; + var row_width = column_width * (this.all_display_data).length * this.DISPLAY_COLUMNS; + self.$element.find(".oe_table_column" ).css("width", column_width +"%"); + self.$element.find(".oe_kanban_row" ).css("width", row_width +"%"); + this.$element.find('#next-column').click(function(event) { + event.preventDefault(); + self.$element.find('.oe_kanban_view').animate({"scrollLeft" : "+=" + self.$element.find('.oe_column').width()}, 'slow'); + }); + this.$element.find('#previous-column').click(function(event) { + event.preventDefault(); + self.$element.find('.oe_kanban_view').animate({scrollLeft:'-=' + self.$element.find('.oe_column').width() }, 'slow'); + }); }, do_record_group: function() { if (this.NO_OF_COLUMNS) { diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index aa7bc73190d..b48333e6df4 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -1,26 +1,28 @@ \ No newline at end of file From 9fa1aaec6e9acec1aa1183dda1f4dea46a2218ca Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Tue, 20 Sep 2011 17:32:47 +0530 Subject: [PATCH 042/128] [IMP] kanban :- add basic structure of folding and unfolding columns. bzr revid: ysa@tinyerp.com-20110920120247-0f916npmmeu83qhu --- addons/web_kanban/static/src/css/kanban.css | 9 ++++++-- addons/web_kanban/static/src/js/kanban.js | 21 +++++++++++++++++++ .../web_kanban/static/src/xml/web_kanban.xml | 16 ++++++++------ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index afac6cdef47..35b8c1cb16b 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -3,6 +3,12 @@ width: 100%; } +.oe_vertical_text { + -moz-transform: rotate(-90deg); + -o-transform: rotate(-90deg); + -webkit-transform: rotate(-90deg); +} + .openerp .oe_kanban_view .ui-sortable-placeholder { border: 1px dotted black; visibility: visible !important; @@ -199,5 +205,4 @@ /* Alert color */ .openerp .oe_kanban_color_alert .oe_kanban_color_border { border-color: #c00 !important; -} - +} \ No newline at end of file diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index d11d2cfbcf4..62f7dd3052a 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -148,6 +148,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ self.all_display_data.sort(this.sort_group); this.$element.html(QWeb.render("KanbanView", {"data": self.all_display_data})); this.on_reload_kanban(); + this.$element.find(".oe_vertical_text").hide(); var drag_handel = false; if (this.$element.find(".oe_kanban_draghandle").length > 0) { drag_handel = ".oe_kanban_draghandle"; @@ -166,6 +167,26 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ }); this.$element.find(".oe_column").disableSelection() this.$element.find('button.oe_kanban_button_new').click(this.do_add_record); + this.$element.find(".fold-columns-icon").click(function(event) { + self.do_fold_unfold_columns(event, this.id); + }); + }, + do_fold_unfold_columns: function(event, element_id) { + var column_id = "column_" + element_id; + var column_element = this.$element.find("#" + column_id + ".oe_fold_column"); + if (column_element.is(":hidden")) { + this.$element.find("#" + column_id).find("img.fold-columns-icon").attr('src', '/web_kanban/static/src/img/minus-icon.png'); + column_element.show(); + this.$element.find("#" + column_id + ".oe_table_column").css("width",Math.round(99 / this.all_display_data.length) + "%"); + this.$element.find("#" + column_id + ".oe_vertical_text").hide(); + } + else{ + this.$element.find("#" + column_id).find("img.fold-columns-icon").attr('src', '/web_kanban/static/src/img/plus-icon.png'); + column_element.hide(); + this.$element.find("#" + column_id + ".oe_table_column").css("width","0.5%"); + this.$element.find("#" + column_id + ".oe_vertical_text").show(); + } + }, do_record_group: function() { if (this.NO_OF_COLUMNS) { diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index 04620abaeae..599c81a8797 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -10,15 +10,19 @@
- - -
- + +
+ + +
+ +
-
+
+

+
-
+

@@ -20,11 +20,9 @@
+

-
-
-
+
From db0cb54ec6dc0e5751eb50c90692b3fb6a0fb616 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 21 Sep 2011 12:09:00 +0200 Subject: [PATCH 050/128] [REM] redundant code bzr revid: xmo@openerp.com-20110921100900-e86bcmyi7fn3whiz --- addons/web/static/src/js/data_import.js | 38 ++++++++++++------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index fd1e6301eb9..f6008155803 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -27,28 +27,28 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ template: 'ImportDataView', dialog_title: "Import Data", init: function(parent, dataset){ - this._super(parent); + this._super(parent, {}); }, start: function() { var self = this; - self._super(false); - self.template = 'ImportDataView'; - self.dialog_title = "Import Data" - self.open({ - modal: true, - width: '70%', - height: 'auto', - position: 'top', - buttons : { - "Close" : function() { - self.stop(); - }, - "Import File" : function() { - self.do_import(); - } - }, - close: function(event, ui){ self.stop();} - }); + this._super(); + this.open({ + modal:true, + width:'70%', + height:'auto', + position:'top', + buttons:{ + "Close":function() { + self.stop(); + }, + "Import File":function() { + self.do_import(); + } + }, + close:function(event, ui) { + self.stop(); + } + }); this.$element.find('#csvfile').change(this.on_autodetect_data); this.$element.find('fieldset').change(this.on_autodetect_data); this.$element.find('fieldset legend').click(function () { From 09716cac9f249a3ad6c4f742d1890c34e399ec76 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 21 Sep 2011 12:13:20 +0200 Subject: [PATCH 051/128] [IMP] style fixes * White space between tokens * Remove unused variables bzr revid: xmo@openerp.com-20110921101320-7hr2nla3km3yhgfj --- addons/web/static/src/js/data_import.js | 139 ++++++++++++------------ 1 file changed, 68 insertions(+), 71 deletions(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index f6008155803..73d8001f9f6 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -33,113 +33,110 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ var self = this; this._super(); this.open({ - modal:true, - width:'70%', - height:'auto', - position:'top', - buttons:{ - "Close":function() { + modal: true, + width: '70%', + height: 'auto', + position: 'top', + buttons: { + "Close": function() { self.stop(); }, - "Import File":function() { + "Import File": function() { self.do_import(); } }, - close:function(event, ui) { + close: function(event, ui) { self.stop(); } }); this.$element.find('#csvfile').change(this.on_autodetect_data); this.$element.find('fieldset').change(this.on_autodetect_data); - this.$element.find('fieldset legend').click(function () { - $(this).next().toggle(); + this.$element.find('fieldset legend').click(function() { + $(this).next().toggle(); }); }, do_import: function() { - var self = this; - if(!this.$element.find('#csvfile').val()) { return; } - jsonp(this.$element.find('#import_data'), { - url: '/web/import/import_data' - }, this.on_import_results); + if(!this.$element.find('#csvfile').val()) { return; } + jsonp(this.$element.find('#import_data'), { + url: '/web/import/import_data' + }, this.on_import_results); }, on_autodetect_data: function() { - var self = this; - if(this.$element.find("#res td")){ - this.$element.find("#res td").remove(); - this.$element.find("#imported_success").css('display','none'); - } - if(!this.$element.find('#csvfile').val()) { return; } - jsonp(this.$element.find('#import_data'), { - url: '/web/import/detect_data' - }, this.on_import_results); + if(this.$element.find("#res td")){ + this.$element.find("#res td").remove(); + this.$element.find("#imported_success").css('display','none'); + } + if(!this.$element.find('#csvfile').val()) { return; } + jsonp(this.$element.find('#import_data'), { + url: '/web/import/detect_data' + }, this.on_import_results); }, - on_import_results:function(results){ - var self = this; + on_import_results: function(results) { this.$element.find('#result, #success').empty(); var result_node = $("#result"); var records = {}; - if (results['records']){ - records = {'header':results['header'],'sel':results['all_fields'],'row':results['records']}; - result_node.append(QWeb.render('ImportView-result',{'records':records})); - }else if(results['error']){ - result_node.append(QWeb.render('ImportView-result',{'error': results['error']})); - }else if(results['success']){ + if (results['records']) { + records = {'header': results['header'], 'sel': results['all_fields'], 'row': results['records']}; + result_node.append(QWeb.render('ImportView-result', {'records': records})); + } else if (results['error']) { + result_node.append(QWeb.render('ImportView-result', {'error': results['error']})); + } else if (results['success']) { self.stop(); - if (this.widget_parent.widget_parent.active_view == "list"){ + if (this.widget_parent.widget_parent.active_view == "list") { this.widget_parent.reload_content(); } } this.do_check_req_field(results['req_field']); this.on_change_check(results['req_field']); - this.$element.find("td #sel_field").change(function(){ + var self = this; + this.$element.find("td #sel_field").change(function() { self.on_change_check(results['req_field']); }); }, - on_change_check:function(req_field){ - var self = this; - self.$element.find("#message, #msg").remove(); - var selected_flds = self.$element.find("td #sel_field option:selected"); - _.each(selected_flds,function(fld){ - if (fld.index != 0){ - var res = self.$element.find("td #sel_field option:selected[value='"+ fld.value +"']"); - if (res.length == 1){ - res.parent().removeClass("duplicate_fld").addClass("select_fld"); - }else if(res.length > 1){ - res.parent().removeClass("select_fld").addClass("duplicate_fld"); - res.parent().focus(); - } - }else{ - var elem = self.$element.find("td #sel_field option:selected[value='"+ fld.value +"']"); - elem.parent().removeClass("duplicate_fld").addClass("select_fld"); - } - }); - if(self.$element.find(".duplicate_fld").length){ - $("#result").before('
*Selected column should not be same.
'); - $(".ui-button-text:contains('Import File')").parent().attr("disabled",true); - }else{ - self.$element.find("#msg").remove(); - $(".ui-button-text:contains('Import File')").parent().attr("disabled",false); - } - self.do_check_req_field(req_field); - - }, - do_check_req_field: function(req_fld){ + on_change_check: function (req_field){ var self = this; - if (req_fld.length){ + self.$element.find("#message, #msg").remove(); + var selected_flds = self.$element.find("td #sel_field option:selected"); + _.each(selected_flds, function(fld) { + if (fld.index != 0) { + var res = self.$element.find("td #sel_field option:selected[value='" + fld.value + "']"); + if (res.length == 1) { + res.parent().removeClass("duplicate_fld").addClass("select_fld"); + } else if (res.length > 1) { + res.parent().removeClass("select_fld").addClass("duplicate_fld"); + res.parent().focus(); + } + } else { + var elem = self.$element.find("td #sel_field option:selected[value='" + fld.value + "']"); + elem.parent().removeClass("duplicate_fld").addClass("select_fld"); + } + }); + if (self.$element.find(".duplicate_fld").length) { + $("#result").before('
*Selected column should not be same.
'); + $(".ui-button-text:contains('Import File')").parent().attr("disabled", true); + } else { + self.$element.find("#msg").remove(); + $(".ui-button-text:contains('Import File')").parent().attr("disabled", false); + } + self.do_check_req_field(req_field); + }, + do_check_req_field: function(req_fld) { + var self = this; + if (req_fld.length) { self.$element.find("#message").remove(); - var sel_fields = _.map(this.$element.find("td #sel_field option:selected"), function(fld){ + var sel_fields = _.map(this.$element.find("td #sel_field option:selected"), function(fld) { return fld['text'] }); - var required_fields = _.filter(req_fld, function(fld){ + var required_fields = _.filter(req_fld, function(fld) { return !_.contains(sel_fields, fld) }); - if (required_fields.length){ - $("#result").before('
*Required Fields are not selected : '+required_fields+'.
'); - $(".ui-button-text:contains('Import File')").parent().attr("disabled",true); - }else{ + if (required_fields.length) { + $("#result").before('
*Required Fields are not selected : ' + required_fields + '.
'); + $(".ui-button-text:contains('Import File')").parent().attr("disabled", true); + } else { self.$element.find("#message").remove(); - $(".ui-button-text:contains('Import File')").parent().attr("disabled",false); + $(".ui-button-text:contains('Import File')").parent().attr("disabled", false); } } }, From 519eed06a0c354a4fc27113dc2afb1dc05153c90 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 21 Sep 2011 12:14:36 +0200 Subject: [PATCH 052/128] [IMP] guard checks as early returns to reduce nesting bzr revid: xmo@openerp.com-20110921101436-m24gccn4isew8hwo --- addons/web/static/src/js/data_import.js | 31 ++++++++++++------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 73d8001f9f6..8c1ed3f8dcb 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -122,22 +122,21 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ self.do_check_req_field(req_field); }, do_check_req_field: function(req_fld) { - var self = this; - if (req_fld.length) { - self.$element.find("#message").remove(); - var sel_fields = _.map(this.$element.find("td #sel_field option:selected"), function(fld) { - return fld['text'] - }); - var required_fields = _.filter(req_fld, function(fld) { - return !_.contains(sel_fields, fld) - }); - if (required_fields.length) { - $("#result").before('
*Required Fields are not selected : ' + required_fields + '.
'); - $(".ui-button-text:contains('Import File')").parent().attr("disabled", true); - } else { - self.$element.find("#message").remove(); - $(".ui-button-text:contains('Import File')").parent().attr("disabled", false); - } + if (!req_fld.length) { return; } + + this.$element.find("#message").remove(); + var sel_fields = _.map(this.$element.find("td #sel_field option:selected"), function(fld) { + return fld['text'] + }); + var required_fields = _.filter(req_fld, function(fld) { + return !_.contains(sel_fields, fld) + }); + if (required_fields.length) { + $("#result").before('
*Required Fields are not selected : ' + required_fields + '.
'); + $(".ui-button-text:contains('Import File')").parent().attr("disabled", true); + } else { + this.$element.find("#message").remove(); + $(".ui-button-text:contains('Import File')").parent().attr("disabled", false); } }, stop: function() { From b4722d456464c658e69e0cdfddc29aff0bb7675a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 21 Sep 2011 13:25:56 +0200 Subject: [PATCH 053/128] [IMP] build a sequence of buttons to initialize dialog for localization (future), simplify enabling and disabling of import button bzr revid: xmo@openerp.com-20110921112556-0qv8h9jldlmd799a --- addons/web/static/src/js/data_import.js | 32 +++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 8c1ed3f8dcb..8b6632e93e2 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -37,24 +37,26 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ width: '70%', height: 'auto', position: 'top', - buttons: { - "Close": function() { - self.stop(); - }, - "Import File": function() { - self.do_import(); - } - }, + buttons: [ + {text: "Close", click: function() { self.stop(); }}, + {text: "Import File", click: function() { self.do_import(); }, 'class': 'oe-dialog-import-button'} + ], close: function(event, ui) { self.stop(); } }); + this.toggle_import_button(false); this.$element.find('#csvfile').change(this.on_autodetect_data); this.$element.find('fieldset').change(this.on_autodetect_data); this.$element.find('fieldset legend').click(function() { $(this).next().toggle(); }); }, + toggle_import_button: function (newstate) { + this.$dialog.dialog('widget') + .find('.oe-dialog-import-button') + .button('option', 'disabled', !newstate); + }, do_import: function() { if(!this.$element.find('#csvfile').val()) { return; } jsonp(this.$element.find('#import_data'), { @@ -73,7 +75,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ }, on_import_results: function(results) { this.$element.find('#result, #success').empty(); - var result_node = $("#result"); + var result_node = this.$element.find("#result"); var records = {}; if (results['records']) { @@ -113,11 +115,11 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ } }); if (self.$element.find(".duplicate_fld").length) { - $("#result").before('
*Selected column should not be same.
'); - $(".ui-button-text:contains('Import File')").parent().attr("disabled", true); + this.$element.find("#result").before('
*Selected column should not be same.
'); + this.toggle_import_button(false); } else { self.$element.find("#msg").remove(); - $(".ui-button-text:contains('Import File')").parent().attr("disabled", false); + this.toggle_import_button(true); } self.do_check_req_field(req_field); }, @@ -132,11 +134,11 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ return !_.contains(sel_fields, fld) }); if (required_fields.length) { - $("#result").before('
*Required Fields are not selected : ' + required_fields + '.
'); - $(".ui-button-text:contains('Import File')").parent().attr("disabled", true); + this.$element.find("#result").before('
*Required Fields are not selected : ' + required_fields + '.
'); + this.toggle_import_button(false); } else { this.$element.find("#message").remove(); - $(".ui-button-text:contains('Import File')").parent().attr("disabled", false); + this.toggle_import_button(true); } }, stop: function() { From f8b7d96e20a2a89d31d3d7e4e90e5a30920131ab Mon Sep 17 00:00:00 2001 From: kmo Date: Wed, 21 Sep 2011 17:07:43 +0530 Subject: [PATCH 054/128] [FIX]Remove Overlapping of graph in Dashboard. lp bug: https://launchpad.net/bugs/854692 fixed bzr revid: kgo@kgo-desktop-20110921113743-0p19ocxmf26n2jtl --- addons/web_graph/static/src/js/graph.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web_graph/static/src/js/graph.js b/addons/web_graph/static/src/js/graph.js index 2a493b89166..3ab6d4e20fc 100644 --- a/addons/web_graph/static/src/js/graph.js +++ b/addons/web_graph/static/src/js/graph.js @@ -49,7 +49,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ this.operator = []; this.group_field = []; this.orientation = this.fields_view.arch.attrs.orientation || ''; - this.elem_id = this.$element[0]['id']; + this.elem_id = this.view_manager.element_id; _.each(this.fields_view.arch.children, function (field) { if (field.attrs.operator) { From 8b9a3b31a8577d9ea91dfed88cb48f7777710a36 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Wed, 21 Sep 2011 17:09:37 +0530 Subject: [PATCH 055/128] [IMP] kanban :- default dataset domain problem fix. bzr revid: ysa@tinyerp.com-20110921113937-407vhkefp6olpb64 --- addons/web_kanban/static/src/js/kanban.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 6c2a06b982c..a9aecbb0e22 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -20,6 +20,8 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.qweb = new QWeb2.Engine(); this.aggregates = {}; this.NO_OF_COLUMNS = 3; + this.dataset_default_domain = dataset.domain; + this.dataset_default_context = dataset.context; if (this.options.action_views_ids.form) { this.form_dialog = new openerp.web.FormDialog(this, {}, this.options.action_views_ids.form, dataset).start(); this.form_dialog.on_form_dialog_saved.add_last(this.on_record_saved); @@ -434,9 +436,11 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ }, do_search: function (domains, contexts, group_by) { var self = this; + this.dataset.domain = this.dataset_default_domain; + this.dataset.context = this.dataset_default_context; this.rpc('/web/session/eval_domain_and_context', { - domains: domains, - contexts: contexts, + domains: [this.dataset.get_domain()].concat(domains), + contexts: [this.dataset.get_context()].concat(contexts), group_by_seq: group_by }, function (results) { self.domain = results.domain; @@ -461,8 +465,6 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ self.do_render_group(groups); }, function (dataset) { - self.domain = dataset.domain; - self.context = dataset.context; self.groups = []; self.dataset.read_slice([], {}, function(records) { self.all_display_data = [{'records': records, 'value':false, 'header' : false, 'ids': self.dataset.ids}]; From 5142f9a9aaf4108f03093e6efde118556dbe7532 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Thu, 22 Sep 2011 10:50:59 +0530 Subject: [PATCH 056/128] [IMP] kanban :- improve group title. bzr revid: ysa@tinyerp.com-20110922052059-fg7w253jxb6i8dxs --- addons/web_kanban/static/src/js/kanban.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index a9aecbb0e22..c30794fb6ed 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -491,9 +491,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ } var group_aggregates = ''; _.each(self.aggregates, function(value, key) { - if (group.aggregates[key]) { - group_aggregates += value + ": " + group.aggregates[key]; - } + group_aggregates += value + ": " + group.aggregates[key]; }); self.dataset.read_slice([], {}, function(records) { self.all_display_data.push({"value" : group_value, "records": records, 'header':group_name, 'ids': self.dataset.ids, 'aggregates': group_aggregates}); From 5ba40e563953dff00931403d9a03ce6807cc2603 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Thu, 22 Sep 2011 12:47:18 +0530 Subject: [PATCH 057/128] [IMP] kanban :- pass domain and context in read_slice method instead of dataset. bzr revid: ysa@tinyerp.com-20110922071718-jksxy7hx8dm0n8ce --- addons/web_kanban/static/src/js/kanban.js | 38 ++--------------------- 1 file changed, 3 insertions(+), 35 deletions(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index c30794fb6ed..1450e8c26fa 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -20,8 +20,6 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.qweb = new QWeb2.Engine(); this.aggregates = {}; this.NO_OF_COLUMNS = 3; - this.dataset_default_domain = dataset.domain; - this.dataset_default_context = dataset.context; if (this.options.action_views_ids.form) { this.form_dialog = new openerp.web.FormDialog(this, {}, this.options.action_views_ids.form, dataset).start(); this.form_dialog.on_form_dialog_saved.add_last(this.on_record_saved); @@ -202,31 +200,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ } }, on_button_click: function (button_attrs, record_id) { - var self = this; - if (this.groups.length) { - _.each(this.groups, function (group) { - group.list([], - function (groups) {}, - function (dataset) { - dataset.read_slice([], {}, function(records) { - var index = parseInt(_.indexOf(dataset.ids, record_id)); - if(index >= 0) { - self.on_confirm_click(dataset, button_attrs, index, record_id); - } - }); - } - ); - }); - } else { - var index = parseInt(_.indexOf(self.dataset.ids, record_id)); - if (index >= 0) { - _.extend(self.dataset, {domain: self.domain, context: self.context}); - self.on_confirm_click(self.dataset, button_attrs, index, record_id); - } - } - }, - on_confirm_click: function (dataset, button_attrs, index, record_id) { - this.on_execute_button_click(dataset, button_attrs, record_id); + this.on_execute_button_click(this.dataset, button_attrs, record_id); }, do_add_record: function() { this.dataset.index = null; @@ -436,8 +410,6 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ }, do_search: function (domains, contexts, group_by) { var self = this; - this.dataset.domain = this.dataset_default_domain; - this.dataset.context = this.dataset_default_context; this.rpc('/web/session/eval_domain_and_context', { domains: [this.dataset.get_domain()].concat(domains), contexts: [this.dataset.get_context()].concat(contexts), @@ -457,8 +429,6 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ self.group_by = group_by; } self.datagroup = new openerp.web.DataGroup(self, self.model, self.domain, self.context, group_by); - self.dataset.context = self.context; - self.dataset.domain = self.domain; self.datagroup.list([], function (groups) { self.groups = groups; @@ -466,7 +436,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ }, function (dataset) { self.groups = []; - self.dataset.read_slice([], {}, function(records) { + self.dataset.read_slice([], {'domain': self.domain, 'context': self.context}, function(records) { self.all_display_data = [{'records': records, 'value':false, 'header' : false, 'ids': self.dataset.ids}]; self.$element.find(".oe_kanban_view").remove(); self.on_show_data(); @@ -478,8 +448,6 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.all_display_data = []; var self = this; _.each(datagroups, function (group) { - self.dataset.context = group.context; - self.dataset.domain = group.domain; var group_name = group.value; var group_value = group.value; if (!group.value) { @@ -493,7 +461,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ _.each(self.aggregates, function(value, key) { group_aggregates += value + ": " + group.aggregates[key]; }); - self.dataset.read_slice([], {}, function(records) { + self.dataset.read_slice([], {'domain': group.domain, 'context': group.context}, function(records) { self.all_display_data.push({"value" : group_value, "records": records, 'header':group_name, 'ids': self.dataset.ids, 'aggregates': group_aggregates}); if (datagroups.length == self.all_display_data.length) { self.$element.find(".oe_kanban_view").remove(); From b20f60ba611bb3ba9e3a73579ea93b4fec298205 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Thu, 22 Sep 2011 12:50:01 +0530 Subject: [PATCH 058/128] [FIX] kanban :- if no record found then display as a blank page. bzr revid: ysa@tinyerp.com-20110922072001-u3rcsst3g0y9vwiz --- addons/web_kanban/static/src/js/kanban.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 1450e8c26fa..eec3daaba09 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -432,7 +432,13 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ self.datagroup.list([], function (groups) { self.groups = groups; - self.do_render_group(groups); + if (groups.length) { + self.do_render_group(groups); + } + else { + self.all_display_data = []; + self.on_show_data(); + } }, function (dataset) { self.groups = []; From 51dbb70b8d3894450684226cf786c76e98abc924 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 22 Sep 2011 10:58:12 +0200 Subject: [PATCH 059/128] [FIX] mitigate horrendous performance issues inserting options in Webkit In some cases, at least with complex-enough views, inserting many options in a document in a row will get progressively slower. In import, this issue is hit on trying to import partners: partners have a humongous number of fields (direct and on their o2m), ~940, which yields a correspondingly huge number of options in the selection. A basic partner export also has quite high a number of columns (~50 without exporting o2m fields), so this list of 940 options is inserted 50 times in a row (literally too, they're all in the same table row).. While not all that fast, Firefox 5/6 has no significant issue with this (~18ms/insertion, where an insertion is a full select with all its options). Webkit browsers (Chrome and Safari) on the other hand start out fair (~10ms/insertion), but get slower and slower until they end up at 3~5 *seconds* for each insertion (3s if inserting a DocumentFragment, 5s if inserting text via innerHTML). This means the preview table takes up to *two minutes* to display, even the best cases (pre-generating everything that can be and optimizing everything I could think of) take 75 *seconds* for the insertions (the pregeneration of a given select and its options is ~100ms, the base template rendering is ~20ms). rendering divs or inputs does not have this issue, I did not manage to reduce or fix the issue directly so I replaced the options by jQuery-ui's autocomplete widget. This is not issues-free: when tabbing through the fields lists, when reaching the edge of the popup the browser will automatically scroll the field back into view. However, this is done *after* autocomplete's popup has opened, and as a result the popup opens in the wrong place (at the popup's edge, instead of under the now-moved field). bzr revid: xmo@openerp.com-20110922085812-3u1esk6czraskm01 --- addons/web/static/src/css/data_import.css | 7 +++++- addons/web/static/src/js/data_import.js | 10 +++++++- addons/web/static/src/xml/base.xml | 30 +++++++++-------------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/addons/web/static/src/css/data_import.css b/addons/web/static/src/css/data_import.css index 7ae13fcf5ee..1fb3b22aa45 100644 --- a/addons/web/static/src/css/data_import.css +++ b/addons/web/static/src/css/data_import.css @@ -22,4 +22,9 @@ } .openerp .select_fld{ background: none repeat scroll 0 0 white; -} \ No newline at end of file +} +.openerp .ui-autocomplete { + max-height: 300px; + overflow-y: auto; + padding-right: 20px; +} diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 8b6632e93e2..2f0fea688cd 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -79,7 +79,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ var records = {}; if (results['records']) { - records = {'header': results['header'], 'sel': results['all_fields'], 'row': results['records']}; + records = {'header': results['header'], 'row': results['records']}; result_node.append(QWeb.render('ImportView-result', {'records': records})); } else if (results['error']) { result_node.append(QWeb.render('ImportView-result', {'error': results['error']})); @@ -89,6 +89,14 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ this.widget_parent.reload_content(); } } + + this.$element.find('.sel_fields').autocomplete({ + minLength: 0, + source: results.all_fields + }).focus(function () { + $(this).autocomplete('search'); + }); + this.do_check_req_field(results['req_field']); this.on_change_check(results['req_field']); var self = this; diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 09e71043e38..3245ec43e8f 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1459,23 +1459,17 @@
- - - - - - - - - - - + + + + + + + + +
- -
+ +
@@ -1522,4 +1516,4 @@
- \ No newline at end of file + From 132377f01252d1c6d738d42a842e41924cca8554 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Thu, 22 Sep 2011 14:33:14 +0530 Subject: [PATCH 060/128] [FIX] kanban :- fix problem on group by. bzr revid: ysa@tinyerp.com-20110922090314-o7cs05dkgptr8nkc --- addons/web_kanban/static/src/js/kanban.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index eec3daaba09..d1fa4bb92ba 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -189,7 +189,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ }, do_record_group: function() { - if (this.NO_OF_COLUMNS) { + if (this.NO_OF_COLUMNS && this.all_display_data.length > 0) { var records = this.all_display_data[0].records; var record_per_group = Math.round((records).length / this.NO_OF_COLUMNS); this.all_display_data = []; @@ -443,7 +443,8 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ function (dataset) { self.groups = []; self.dataset.read_slice([], {'domain': self.domain, 'context': self.context}, function(records) { - self.all_display_data = [{'records': records, 'value':false, 'header' : false, 'ids': self.dataset.ids}]; + if (records.length) self.all_display_data = [{'records': records, 'value':false, 'header' : false, 'ids': self.dataset.ids}]; + else self.all_display_data = []; self.$element.find(".oe_kanban_view").remove(); self.on_show_data(); }); From 099b18ace64c913c93f1c1c401dcd29c05ef0a0a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 22 Sep 2011 11:40:58 +0200 Subject: [PATCH 061/128] [REM] redundant call to do_check_req_field: it's already called from on_change_check bzr revid: xmo@openerp.com-20110922094058-yhy6re1l679fg42m --- addons/web/controllers/main.py | 11 +++++------ addons/web/static/src/js/data_import.js | 9 ++++----- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 676c7ae2572..fad6c474ec5 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1424,15 +1424,12 @@ class Import(View): _fields = {} _fields_invert = {} - req_field = [] error = None fields = req.session.model(model).fields_get(False, req.session.eval_context(req.context)) fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}}) - for field in fields: - value = fields[field] - if value.get('required'): - req_field.append(field) + required_fields = [field_name for field_name, field in fields.iteritems() + if field.get('required')] def model_populate(fields, prefix_node='', prefix=None, prefix_value='', level=2): def str_comp(x,y): @@ -1506,7 +1503,9 @@ class Import(View): jsonp, simplejson.dumps({'error':error})) return '' % ( - jsonp, simplejson.dumps({'records':records[1:],'header':header_fields,'all_fields':all_fields,'req_field':req_field})) + jsonp, simplejson.dumps({ + 'records':records[1:],'header':header_fields, + 'all_fields':all_fields,'required_fields':required_fields})) @openerpweb.httprequest def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip, diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 2f0fea688cd..42b17795bfe 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -97,14 +97,13 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ $(this).autocomplete('search'); }); - this.do_check_req_field(results['req_field']); - this.on_change_check(results['req_field']); + this.on_change_check(results['required_fields']); var self = this; this.$element.find("td #sel_field").change(function() { - self.on_change_check(results['req_field']); + self.on_change_check(results['required_fields']); }); }, - on_change_check: function (req_field){ + on_change_check: function (required_fields){ var self = this; self.$element.find("#message, #msg").remove(); var selected_flds = self.$element.find("td #sel_field option:selected"); @@ -129,7 +128,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ self.$element.find("#msg").remove(); this.toggle_import_button(true); } - self.do_check_req_field(req_field); + self.do_check_req_field(required_fields); }, do_check_req_field: function(req_fld) { if (!req_fld.length) { return; } From 01fd5a7fb60fafd2825a2d7164715b22a837f277 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 22 Sep 2011 11:54:09 +0200 Subject: [PATCH 062/128] [FIX] onchange call for data import fields bzr revid: xmo@openerp.com-20110922095409-xxqb0l3mirralwps --- addons/web/static/src/js/data_import.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 42b17795bfe..8ae909a7d47 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -90,18 +90,17 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ } } + var self = this; this.$element.find('.sel_fields').autocomplete({ minLength: 0, source: results.all_fields }).focus(function () { $(this).autocomplete('search'); + }).change(function() { + self.on_change_check(results['required_fields']); }); this.on_change_check(results['required_fields']); - var self = this; - this.$element.find("td #sel_field").change(function() { - self.on_change_check(results['required_fields']); - }); }, on_change_check: function (required_fields){ var self = this; From 948b1c0a57498a976d34e42963c5e9c3f100a5a1 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Thu, 22 Sep 2011 16:08:34 +0530 Subject: [PATCH 063/128] [IMP] kanban :- remove unused code. bzr revid: ysa@tinyerp.com-20110922103834-2h6hrp9ym8o872ah --- addons/web_kanban/static/src/js/kanban.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index d1fa4bb92ba..f278d304af3 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -265,16 +265,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.do_execute_action( button_attrs, dataset, record_id, function () { - var count = 1; - _.each(self.all_display_data, function(data, index) { - self.dataset.read_ids( data.ids, [], function(records){ - self.all_display_data[index].records = records; - if(self.all_display_data.length == count) { - self.do_actual_search(); - } - count++; - }); - }); + self.do_actual_search(); } ); }, From 0b8cfc4c26ca933e9fd8e185aa2dd50e7606c67f Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 22 Sep 2011 12:59:48 +0200 Subject: [PATCH 064/128] [IMP] attempt to slighly improve duplicate field match handling in import bzr revid: xmo@openerp.com-20110922105948-oms4h0t4uyl0qnm6 --- addons/web/static/src/js/data_import.js | 65 ++++++++++++++++--------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 8ae909a7d47..313b157f401 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -93,43 +93,60 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ var self = this; this.$element.find('.sel_fields').autocomplete({ minLength: 0, - source: results.all_fields + source: results.all_fields, + change: function () { + self.on_check_field_values(results['required_fields']); + } }).focus(function () { $(this).autocomplete('search'); - }).change(function() { - self.on_change_check(results['required_fields']); }); - this.on_change_check(results['required_fields']); + this.on_check_field_values(results['required_fields']); }, - on_change_check: function (required_fields){ - var self = this; - self.$element.find("#message, #msg").remove(); - var selected_flds = self.$element.find("td #sel_field option:selected"); - _.each(selected_flds, function(fld) { - if (fld.index != 0) { - var res = self.$element.find("td #sel_field option:selected[value='" + fld.value + "']"); - if (res.length == 1) { - res.parent().removeClass("duplicate_fld").addClass("select_fld"); - } else if (res.length > 1) { - res.parent().removeClass("select_fld").addClass("duplicate_fld"); - res.parent().focus(); - } + on_check_field_values: function (required_fields) { + this.$element.find("#message, #msg").remove(); + + // Maps values to DOM nodes, in order to discover duplicates + var values = {}, duplicates = {}; + this.$element.find(".sel_fields").each(function (index, element) { + var value = element.value; + var $element = $(element).removeClass('duplicate_fld'); + if (!value) { return; } + + if (!(value in values)) { + values[value] = element; } else { - var elem = self.$element.find("td #sel_field option:selected[value='" + fld.value + "']"); - elem.parent().removeClass("duplicate_fld").addClass("select_fld"); + var same_valued_field = values[value]; + if (value in duplicates) { + duplicates[value].push(element); + } else { + duplicates[value] = [same_valued_field, element]; + } + $element.add(same_valued_field).addClass('duplicate_fld'); } }); - if (self.$element.find(".duplicate_fld").length) { - this.$element.find("#result").before('
*Selected column should not be same.
'); + + if (!_.isEmpty(duplicates)) { + var $err = $('
Destination fields should only be selected once, some fields are selected more than once:
') + .insertBefore(this.$element.find('#result')); + var $dupes = $('
').appendTo($err); + _(duplicates).each(function (elements, value) { + $('
').text(value).appendTo($dupes); + _(elements).each(function (element) { + var cell = $(element).closest('td'); + $('
').text(cell.parent().children().index(cell)) + .appendTo($dupes); + }); + }); this.toggle_import_button(false); } else { - self.$element.find("#msg").remove(); + this.$element.find("#msg").remove(); this.toggle_import_button(true); } - self.do_check_req_field(required_fields); + + this.do_check_required(required_fields); }, - do_check_req_field: function(req_fld) { + do_check_required: function(req_fld) { if (!req_fld.length) { return; } this.$element.find("#message").remove(); From a250e39ad337aa3c02b6a2166237c04c125fd7f5 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 22 Sep 2011 14:03:28 +0200 Subject: [PATCH 065/128] [FIX] required fields check, maybe bzr revid: xmo@openerp.com-20110922120328-h9eh9gl6wf28xbzd --- addons/web/static/src/js/data_import.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 313b157f401..1032af66d03 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -140,27 +140,24 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ }); this.toggle_import_button(false); } else { - this.$element.find("#msg").remove(); this.toggle_import_button(true); } this.do_check_required(required_fields); }, - do_check_required: function(req_fld) { - if (!req_fld.length) { return; } + do_check_required: function(required_fields) { + if (!required_fields.length) { return; } - this.$element.find("#message").remove(); - var sel_fields = _.map(this.$element.find("td #sel_field option:selected"), function(fld) { - return fld['text'] - }); - var required_fields = _.filter(req_fld, function(fld) { - return !_.contains(sel_fields, fld) - }); - if (required_fields.length) { - this.$element.find("#result").before('
*Required Fields are not selected : ' + required_fields + '.
'); + var selected_fields = _(this.$element.find('.sel_fields').get()).chain() + .pluck('value') + .compact() + .value(); + + var missing_fields = _.difference(required_fields, selected_fields); + if (missing_fields.length) { + this.$element.find("#result").before('
*Required Fields are not selected : ' + missing_fields + '.
'); this.toggle_import_button(false); } else { - this.$element.find("#message").remove(); this.toggle_import_button(true); } }, From c3a0d98e1353c382df0e30a8afa2ba5ef15a641a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 22 Sep 2011 14:08:10 +0200 Subject: [PATCH 066/128] [FIX] don't declare the file is importable when there are duplicate columns selected bzr revid: xmo@openerp.com-20110922120810-2fcmntd4w6yh9zvy --- addons/web/static/src/js/data_import.js | 30 ++++++++++++------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 1032af66d03..b2675079ddd 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -106,9 +106,11 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ on_check_field_values: function (required_fields) { this.$element.find("#message, #msg").remove(); + var required_valid = this.check_required(required_fields); + // Maps values to DOM nodes, in order to discover duplicates var values = {}, duplicates = {}; - this.$element.find(".sel_fields").each(function (index, element) { + this.$element.find(".sel_fields").each(function(index, element) { var value = element.value; var $element = $(element).removeClass('duplicate_fld'); if (!value) { return; } @@ -126,27 +128,24 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ } }); - if (!_.isEmpty(duplicates)) { - var $err = $('
Destination fields should only be selected once, some fields are selected more than once:
') - .insertBefore(this.$element.find('#result')); + if (_.isEmpty(duplicates)) { + this.toggle_import_button(required_valid); + } else { + var $err = $('
Destination fields should only be selected once, some fields are selected more than once:
').insertBefore(this.$element.find('#result')); var $dupes = $('
').appendTo($err); - _(duplicates).each(function (elements, value) { + _(duplicates).each(function(elements, value) { $('
').text(value).appendTo($dupes); - _(elements).each(function (element) { + _(elements).each(function(element) { var cell = $(element).closest('td'); - $('
').text(cell.parent().children().index(cell)) - .appendTo($dupes); + $('
').text(cell.parent().children().index(cell)).appendTo($dupes); }); }); this.toggle_import_button(false); - } else { - this.toggle_import_button(true); } - this.do_check_required(required_fields); }, - do_check_required: function(required_fields) { - if (!required_fields.length) { return; } + check_required: function(required_fields) { + if (!required_fields.length) { return true; } var selected_fields = _(this.$element.find('.sel_fields').get()).chain() .pluck('value') @@ -156,10 +155,9 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ var missing_fields = _.difference(required_fields, selected_fields); if (missing_fields.length) { this.$element.find("#result").before('
*Required Fields are not selected : ' + missing_fields + '.
'); - this.toggle_import_button(false); - } else { - this.toggle_import_button(true); + return false; } + return true; }, stop: function() { $(this.$dialog).remove(); From 2187a3e3ddecbf2b95b9c29d68131472d6395e3a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 22 Sep 2011 14:13:27 +0200 Subject: [PATCH 067/128] [IMP] extract duplicate fields discovery routine to its own method, to make the validation process clearer bzr revid: xmo@openerp.com-20110922121327-zqenx9bisr8pe4kd --- addons/web/static/src/js/data_import.js | 26 ++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index b2675079ddd..dedcc97df17 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -103,11 +103,20 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ this.on_check_field_values(results['required_fields']); }, - on_check_field_values: function (required_fields) { - this.$element.find("#message, #msg").remove(); - - var required_valid = this.check_required(required_fields); - + /** + * Looks through all the field selections, and tries to find if two + * (or more) columns were matched to the same model field. + * + * Returns a map of the multiply-mapped fields to an array of offending + * columns (not actually columns, but the inputs containing the same field + * names). + * + * Also has the side-effect of marking the discovered inputs with the class + * ``duplicate_fld``. + * + * @returns {Object>} map of duplicate field matches to same-valued inputs + */ + find_duplicate_fields: function() { // Maps values to DOM nodes, in order to discover duplicates var values = {}, duplicates = {}; this.$element.find(".sel_fields").each(function(index, element) { @@ -127,7 +136,14 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ $element.add(same_valued_field).addClass('duplicate_fld'); } }); + return duplicates; + }, + on_check_field_values: function (required_fields) { + this.$element.find("#message, #msg").remove(); + var required_valid = this.check_required(required_fields); + + var duplicates = this.find_duplicate_fields(); if (_.isEmpty(duplicates)) { this.toggle_import_button(required_valid); } else { From 6a931ae10496fd969d12f4bc2e62cb713471c2fc Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 22 Sep 2011 17:27:04 +0200 Subject: [PATCH 068/128] [ADD] checkbox for the user to specify that his file does *not* have column headers bzr revid: xmo@openerp.com-20110922152704-lu36gl07ijnkd3sl --- addons/web/controllers/main.py | 4 ++-- addons/web/static/src/xml/base.xml | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index fad6c474ec5..6ded38b5f93 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1420,7 +1420,7 @@ class Import(View): @openerpweb.httprequest def detect_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip, - jsonp): + file_has_headers, jsonp): _fields = {} _fields_invert = {} @@ -1509,7 +1509,7 @@ class Import(View): @openerpweb.httprequest def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip, - jsonp): + file_has_headers, jsonp): context = req.session.eval_context(req.context) modle_obj = req.session.model(model) diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 3245ec43e8f..aa83ed4e5b2 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1426,6 +1426,14 @@
CSV Options + + + @@ -1441,7 +1449,7 @@ - +
+ + +
From 5b7ecef331d3030facf69306dfd68a516eacfd01 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 22 Sep 2011 17:30:26 +0200 Subject: [PATCH 069/128] [IMP] add @for to import options labels, some help on skip bzr revid: xmo@openerp.com-20110922153026-742c7cc76zv9f2t6 --- addons/web/static/src/xml/base.xml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index aa83ed4e5b2..8bd99687ced 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1424,7 +1424,7 @@
- CSV Options + Import Options - + - + - + - +
@@ -1435,20 +1435,21 @@
From 6bd754060ece5e84ab256dd3f3964e6d90e27598 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 22 Sep 2011 17:57:51 +0200 Subject: [PATCH 070/128] [REM] table of no use whatsoever bzr revid: xmo@openerp.com-20110922155751-rkch0vpmhpolihxa --- addons/web/static/src/js/data_import.js | 4 - addons/web/static/src/xml/base.xml | 108 +++++++++--------------- 2 files changed, 41 insertions(+), 71 deletions(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index dedcc97df17..e3624b9ec57 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -64,10 +64,6 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ }, this.on_import_results); }, on_autodetect_data: function() { - if(this.$element.find("#res td")){ - this.$element.find("#res td").remove(); - this.$element.find("#imported_success").css('display','none'); - } if(!this.$element.find('#csvfile').val()) { return; } jsonp(this.$element.find('#import_data'), { url: '/web/import/detect_data' diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 8bd99687ced..273c0b53d1c 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1390,73 +1390,47 @@ - - - - - - - - - - - - - - - - - - - - - - -
-

1. Import a .CSV file

-
- Select a .CSV file to import. If you need a sample of file to import, - you should use the export tool with the "Import Compatible" option. -
- - -
-

2. Check your file format

-
-
-
-
- Import Options - - - - - - - - - - - - - - - - -
- - -
- -
-
-
+

1. Import a .CSV file

+

Select a .CSV file to import. If you need a sample of file to import, + you should use the export tool with the "Import Compatible" option. +

+

+ + +

+

2. Check your file format

+
+
+ Import Options + + + + + + + + + + + + + + + + +
+ + +
+ +
+
From baf367d0e1945404fe20f250e8e25acea4cc40fe Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 23 Sep 2011 10:33:41 +0200 Subject: [PATCH 071/128] [IMP] move fetching of fields for import to javascript. Also extraction of required fields bzr revid: xmo@openerp.com-20110923083341-xa3sg2t053zonlsm --- addons/web/__openerp__.py | 1 + addons/web/controllers/main.py | 9 +-- .../static/lib/jquery.deferred-queue/LICENSE | 21 ++++++ .../static/lib/jquery.deferred-queue/README | 59 +++++++++++++++ .../jquery.deferred-queue.js | 34 +++++++++ addons/web/static/src/js/data_import.js | 72 ++++++++++++++++--- 6 files changed, 178 insertions(+), 18 deletions(-) create mode 100644 addons/web/static/lib/jquery.deferred-queue/LICENSE create mode 100644 addons/web/static/lib/jquery.deferred-queue/README create mode 100644 addons/web/static/lib/jquery.deferred-queue/jquery.deferred-queue.js diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index ff0a038aae2..06175ba6199 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -19,6 +19,7 @@ "static/lib/jquery.ui/js/jquery-ui-1.8.9.custom.min.js", "static/lib/jquery.ui/js/jquery-ui-timepicker-addon.js", "static/lib/jquery.ui.notify/js/jquery.notify.js", + "static/lib/jquery.deferred-queue/jquery.deferred-queue.js", "static/lib/json/json2.js", "static/lib/qweb/qweb2.js", "static/lib/underscore/underscore.js", diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 6ded38b5f93..85f13a9f9f1 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1428,9 +1428,6 @@ class Import(View): fields = req.session.model(model).fields_get(False, req.session.eval_context(req.context)) fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}}) - required_fields = [field_name for field_name, field in fields.iteritems() - if field.get('required')] - def model_populate(fields, prefix_node='', prefix=None, prefix_value='', level=2): def str_comp(x,y): if xwindow.top.%s(%s);' % ( jsonp, simplejson.dumps({ - 'records':records[1:],'header':header_fields, - 'all_fields':all_fields,'required_fields':required_fields})) + 'records':records[1:],'header':header_fields})) @openerpweb.httprequest def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip, diff --git a/addons/web/static/lib/jquery.deferred-queue/LICENSE b/addons/web/static/lib/jquery.deferred-queue/LICENSE new file mode 100644 index 00000000000..ee8cf44578c --- /dev/null +++ b/addons/web/static/lib/jquery.deferred-queue/LICENSE @@ -0,0 +1,21 @@ +Copyright 2011 Xavier Morel. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY XAVIER MOREL ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/addons/web/static/lib/jquery.deferred-queue/README b/addons/web/static/lib/jquery.deferred-queue/README new file mode 100644 index 00000000000..71d980199c9 --- /dev/null +++ b/addons/web/static/lib/jquery.deferred-queue/README @@ -0,0 +1,59 @@ +.. -*- restructuredtext -*- + +In jQuery 1.5, jQuery has introduced a Deferred object in order to +better handle callbacks. + +Along with Deferred, it introduced ``jQuery.when`` which allows, among +other things, for multiplexing deferreds (waiting on multiple +deferreds at the same time). + +While this is very nice if all deferreds are available at the same +point, it doesn't really work if the resolution of a deferred can +generate more deferreds, or for collections of deferreds coming from +multiple sources (which may not be aware of one another). + +Deferred.queue tries to be a solution to this. It is based on the +principle of FIFO multiple-producers multiple-consumers tasks queues, +such as Python's `queue.Queue`_. Any code with a reference to the +queue can add a promise to the queue, and the queue (which is a +promise itself) will only be resolved once all promises within are +resolved. + +Quickstart +---------- + +Deferred.queue has a very simple life cycle: it is dormant when +created, it starts working as soon as promises get added to it (via +``.push``, or by providing them directly to its constructor), and as +soon as the last promise in the queue is resolved it resolves itself. + +If any promise in the queue fails, the queue itself will be rejected +(without waiting for further promises). + +Once a queue has been resolved or rejected, adding new promises to it +results in an error. + +API +--- + +``jQuery.Deferred.queue([promises...])`` + Creates a new deferred queue. Can be primed with a series of promise + objects. + +``.push(promise...)`` + Adds promises to the queue. Returns the queue itself (can be + chained). + + If the queue has already been resolved or rejected, raises an error. + +``.then([doneCallbacks][, failCallbacks])`` + Promise/A ``then`` method. + +``.done(doneCallbacks)`` + jQuery ``done`` extension to promise objects + +``.fail(failCallbacks)`` + jQuery ``fail`` extension to promise objects + +.. _queue.Queue: + http://docs.python.org/dev/library/queue.html diff --git a/addons/web/static/lib/jquery.deferred-queue/jquery.deferred-queue.js b/addons/web/static/lib/jquery.deferred-queue/jquery.deferred-queue.js new file mode 100644 index 00000000000..7ef83342f77 --- /dev/null +++ b/addons/web/static/lib/jquery.deferred-queue/jquery.deferred-queue.js @@ -0,0 +1,34 @@ +(function ($) { + "use strict"; + $.extend($.Deferred, { + queue: function () { + var queueDeferred = $.Deferred(); + var promises = 0; + + function resolve() { + if (--promises > 0) { + return; + } + setTimeout($.proxy(queueDeferred, 'resolve'), 0); + } + + var promise = $.extend(queueDeferred.promise(), { + push: function () { + if (this.isResolved() || this.isRejected()) { + throw new Error("Can not add promises to a resolved " + + "or rejected promise queue"); + } + + promises += 1; + $.when.apply(null, arguments).then( + resolve, $.proxy(queueDeferred, 'reject')); + return this; + } + }); + if (arguments.length) { + promise.push.apply(promise, arguments); + } + return promise; + } + }); +})(jQuery) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index e3624b9ec57..1453c39fd17 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -23,11 +23,36 @@ function jsonp(form, attributes, callback) { data: options }, attributes)); } + openerp.web.DataImport = openerp.web.Dialog.extend({ template: 'ImportDataView', dialog_title: "Import Data", init: function(parent, dataset){ + var self = this; this._super(parent, {}); + this.model = parent.model; + this.fields = []; + this.all_fields = []; + this.required_fields; + + var convert_fields = function (root, prefix) { + prefix = prefix || ''; + _(root.fields).each(function (f) { + var name = prefix + f.name; + self.all_fields.push(name); + if (f.fields) { + convert_fields(f, name + '/'); + } + }); + }; + this.ready = $.Deferred.queue().then(function () { + self.required_fields = _(self.fields).chain() + .filter(function (field) { return field.required; }) + .pluck('name') + .value(); + convert_fields(self); + self.all_fields.sort(); + }); }, start: function() { var self = this; @@ -51,6 +76,35 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ this.$element.find('fieldset legend').click(function() { $(this).next().toggle(); }); + this.ready.push(new openerp.web.DataSet(this, this.model).call( + 'fields_get', [], function (fields) { + self.graft_fields(fields); + })); + }, + graft_fields: function (fields, parent, level) { + parent = parent || this; + level = level || 0; + + var self = this; + _(fields).each(function (field, field_name) { + var f = { + name: field_name, + string: field.string, + required: field.required + }; + + if (field.type === 'one2many') { + f.fields = []; + // only fetch sub-fields to a depth of 2 levels + if (level < 2) { + self.ready.push(new openerp.web.DataSet(self, field.relation).call( + 'fields_get', [], function (fields) { + self.graft_fields(fields, f, level+1); + })); + } + } + parent.fields.push(f); + }); }, toggle_import_button: function (newstate) { this.$dialog.dialog('widget') @@ -89,15 +143,13 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ var self = this; this.$element.find('.sel_fields').autocomplete({ minLength: 0, - source: results.all_fields, - change: function () { - self.on_check_field_values(results['required_fields']); - } + source: this.all_fields, + change: self.on_check_field_values }).focus(function () { $(this).autocomplete('search'); }); - this.on_check_field_values(results['required_fields']); + this.on_check_field_values(); }, /** * Looks through all the field selections, and tries to find if two @@ -134,10 +186,10 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ }); return duplicates; }, - on_check_field_values: function (required_fields) { + on_check_field_values: function () { this.$element.find("#message, #msg").remove(); - var required_valid = this.check_required(required_fields); + var required_valid = this.check_required(); var duplicates = this.find_duplicate_fields(); if (_.isEmpty(duplicates)) { @@ -156,15 +208,15 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ } }, - check_required: function(required_fields) { - if (!required_fields.length) { return true; } + check_required: function() { + if (!this.required_fields.length) { return true; } var selected_fields = _(this.$element.find('.sel_fields').get()).chain() .pluck('value') .compact() .value(); - var missing_fields = _.difference(required_fields, selected_fields); + var missing_fields = _.difference(this.required_fields, selected_fields); if (missing_fields.length) { this.$element.find("#result").before('
*Required Fields are not selected : ' + missing_fields + '.
'); return false; From 092190e8de399b7297905866b9e768b051051ff2 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Fri, 23 Sep 2011 14:18:38 +0530 Subject: [PATCH 072/128] [IMP] kanban :- add on_reload_record method for reload particular record. bzr revid: ysa@tinyerp.com-20110923084838-yynkktws7t1bpvbm --- addons/web_kanban/static/src/js/kanban.js | 28 ++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index f278d304af3..eb3810e3b5e 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -236,11 +236,37 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ data[$e.data('name')] = $(this).data('color'); self.dataset.write(id, data, {}, function() { // TODO fme: reload record instead of all. need refactoring - self.do_actual_search(); + self.on_reload_record(id, data); }); $cpicker.remove(); }); }, + /** + Reload one record in view. + record_id : reload record id. + data : change value in particular record. + */ + on_reload_record: function (record_id, data){ + var self = this; + for (var i=0, ii=this.all_display_data.length; i < ii; i++) { + for(j=0, jj=this.all_display_data[i].records.length; j < jj; j++) { + if (this.all_display_data[i].records[j].id == record_id) { + _.extend(this.all_display_data[i].records[j], data); + this.$element.find("#main_" + record_id).children().remove(); + this.$element.find("#main_" + record_id).append(this.qweb.render('kanban-box', { + record: this.do_transform_record(this.all_display_data[i].records[j]), + kanban_color: this.kanban_color, + kanban_gravatar: this.kanban_gravatar + })); + break; + } + } + } + this.$element.find("#main_" + record_id + " .oe_kanban_action").click(this.on_action_clicked); + this.$element.find("#main_" + record_id + " .oe_kanban_box_show_onclick_trigger").click(function() { + $(this).parent("#main_" + record_id + " .oe_kanban_box").find(".oe_kanban_box_show_onclick").toggle(); + }); + }, do_delete: function (id) { var self = this; return $.when(this.dataset.unlink([id])).then(function () { From 9adf06ef6f96acb7bac5ca3c3582c41b666d69ad Mon Sep 17 00:00:00 2001 From: Khushbu Date: Fri, 23 Sep 2011 14:25:57 +0530 Subject: [PATCH 073/128] [FIX] Used views own element id. bzr revid: kmo@tinyerp.com-20110923085557-t6alqpum8aa5ic4i --- addons/web_graph/static/src/js/graph.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/web_graph/static/src/js/graph.js b/addons/web_graph/static/src/js/graph.js index 3ab6d4e20fc..3052a7bb8c9 100644 --- a/addons/web_graph/static/src/js/graph.js +++ b/addons/web_graph/static/src/js/graph.js @@ -49,8 +49,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ this.operator = []; this.group_field = []; this.orientation = this.fields_view.arch.attrs.orientation || ''; - this.elem_id = this.view_manager.element_id; - + this.elem_id = this.element_id; _.each(this.fields_view.arch.children, function (field) { if (field.attrs.operator) { this.operator.push(field.attrs.name); From 99cd8a01a37f24445d618f30242eac0e21b572f5 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 23 Sep 2011 10:59:24 +0200 Subject: [PATCH 074/128] [REM] most of the fields-manipulation crapola of Import.detect_data, have headers be a simple list(str) bzr revid: xmo@openerp.com-20110923085924-4gq1g660mcmb8bus --- addons/web/controllers/main.py | 86 ++++++------------------------ addons/web/static/src/xml/base.xml | 3 +- 2 files changed, 17 insertions(+), 72 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 85f13a9f9f1..94fed949c72 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1421,80 +1421,24 @@ class Import(View): @openerpweb.httprequest def detect_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip, file_has_headers, jsonp): - - _fields = {} - _fields_invert = {} - error = None - fields = req.session.model(model).fields_get(False, req.session.eval_context(req.context)) - fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}}) - - def model_populate(fields, prefix_node='', prefix=None, prefix_value='', level=2): - def str_comp(x,y): - if xy: return -1 - else: return 0 - - fields_order = fields.keys() - fields_order.sort(lambda x,y: str_comp(fields[x].get('string', ''), fields[y].get('string', ''))) - for field in fields_order: - if (fields[field].get('type','') not in ('reference',))\ - and (not fields[field].get('readonly')\ - or not dict(fields[field].get('states', {}).get( - 'draft', [('readonly', True)])).get('readonly',True)): - - st_name = prefix_value+fields[field]['string'] or field - _fields[prefix_node+field] = st_name - _fields_invert[st_name] = prefix_node+field - - if fields[field].get('type')=='one2many' and level>0: - fields2 = self.fields_get(req, fields[field]['relation']) - model_populate(fields2, prefix_node+field+'/', None, st_name+'/', level-1) - - if fields[field].get('relation',False) and level>0: - model_populate({'/id': {'type': 'char', 'string': 'ID'}, '.id': {'type': 'char', 'string': 'Database ID'}}, - prefix_node+field, None, st_name+'/', level-1) - fields.update({'id':{'string':'ID'},'.id':{'string':'Database ID'}}) - model_populate(fields) - try: - data = csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep)) - except: - error={'message': 'error opening .CSV file. Input Error.'} - return '' % ( - jsonp, simplejson.dumps({'error':error})) - - records = [] - count = 0 - header_fields = [] - word='' - - try: - for rec in itertools.islice(data,0,4): - records.append(rec) - - headers = itertools.islice(records,1) - line = headers.next() - - for word in line: - word = str(word.decode(csvcode)) - if word in _fields: - header_fields.append((word, _fields[word])) - elif word in _fields_invert.keys(): - header_fields.append((_fields_invert[word], word)) - else: - count = count + 1 - header_fields.append((word, word)) - - if len(line) == count: - error = {'message':"File has not any column header."} - except: - error = {'message':('Error processing the first line of the file. Field "%s" is unknown') % (word,)} - - if error: + data = list(csv.reader( + csvfile, quotechar=str(csvdel), delimiter=str(csvsep))) + except csv.Error, e: csvfile.seek(0) - error=dict(error, preview=csvfile.read(200)) return '' % ( - jsonp, simplejson.dumps({'error':error})) + jsonp, simplejson.dumps({'error': { + 'message': 'Error parsing CSV file: %s' % e, + # decodes each byte to a unicode character, which may or + # may not be printable, but decoding will succeed. + # Otherwise simplejson will try to decode the `str` using + # utf-8, which is very likely to blow up on characters out + # of the ascii range (in range [128, 256)) + 'preview': csvfile.read(200).decode('iso-8859-1')}})) + + records = data[:5] + + header_fields = [word.decode(csvcode) for word in records[0]] return '' % ( jsonp, simplejson.dumps({ diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 273c0b53d1c..19f8078554f 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1443,7 +1443,8 @@
- + @@ -1435,12 +1434,12 @@
+
From a7a71dea83ac33d7f67e0068c5a5102f80e86856 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 23 Sep 2011 11:23:46 +0200 Subject: [PATCH 075/128] [IMP] split import check result in two different templates, wait before everything's ready to setup autocompletes and check required fields & al bzr revid: xmo@openerp.com-20110923092346-007j09xf5wfzq9zo --- addons/web/controllers/main.py | 21 +++++++------ addons/web/static/src/js/data_import.js | 27 +++++++++------- addons/web/static/src/xml/base.xml | 42 ++++++++++++------------- 3 files changed, 48 insertions(+), 42 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 94fed949c72..3839380f54f 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1419,8 +1419,8 @@ class Import(View): return fields @openerpweb.httprequest - def detect_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip, - file_has_headers, jsonp): + def detect_data(self, req, model, csvfile, csvsep, csvdel, csvcode, + csvskip, file_has_headers, jsonp): try: data = list(csv.reader( csvfile, quotechar=str(csvdel), delimiter=str(csvsep))) @@ -1436,13 +1436,16 @@ class Import(View): # of the ascii range (in range [128, 256)) 'preview': csvfile.read(200).decode('iso-8859-1')}})) - records = data[:5] - - header_fields = [word.decode(csvcode) for word in records[0]] - - return '' % ( - jsonp, simplejson.dumps({ - 'records':records[1:],'header':header_fields})) + try: + return '' % ( + jsonp, simplejson.dumps( + {'records': data[:10]}, encoding=csvcode)) + except UnicodeDecodeError: + return '' % ( + jsonp, simplejson.dumps({ + 'message': u"Failed to decode CSV file using encoding %s, " + u"try switching to a different encoding" % csvcode + })) @openerpweb.httprequest def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip, diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 1453c39fd17..9d77a68e54f 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -126,13 +126,15 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ on_import_results: function(results) { this.$element.find('#result, #success').empty(); var result_node = this.$element.find("#result"); - var records = {}; if (results['records']) { - records = {'header': results['header'], 'row': results['records']}; - result_node.append(QWeb.render('ImportView-result', {'records': records})); + result_node.append(QWeb.render('ImportView.result', { + 'headers': results.records[0], + 'records': results.records.slice(1) + })); } else if (results['error']) { - result_node.append(QWeb.render('ImportView-result', {'error': results['error']})); + result_node.append(QWeb.render('ImportView.error', { + 'error': results['error']})); } else if (results['success']) { self.stop(); if (this.widget_parent.widget_parent.active_view == "list") { @@ -141,15 +143,16 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ } var self = this; - this.$element.find('.sel_fields').autocomplete({ - minLength: 0, - source: this.all_fields, - change: self.on_check_field_values - }).focus(function () { - $(this).autocomplete('search'); + this.ready.then(function () { + self.$element.find('.sel_fields').autocomplete({ + minLength: 0, + source: self.all_fields, + change: self.on_check_field_values + }).focus(function () { + $(this).autocomplete('search'); + }); + self.on_check_field_values(); }); - - this.on_check_field_values(); }, /** * Looks through all the field selections, and tries to find if two diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 19f8078554f..db7663ed60d 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1433,28 +1433,28 @@ - - -

The import failed due to:

- -

Here is a preview of the file we could not import:

-
-
+ + + + + + + + + + +
+
+ +
+
+ +

The import failed due to:

+ +

Here is a preview of the file we could not import:

+
- - - - - - - - - - -
-
- -
From 895c2ab980b71f7a0f8785c67a4e3e846525ea71 Mon Sep 17 00:00:00 2001 From: Khushbu Date: Fri, 23 Sep 2011 15:07:53 +0530 Subject: [PATCH 076/128] [FIX] direct use of element_id bzr revid: kmo@tinyerp.com-20110923093753-8ghoyq5y24imet7y --- addons/web_graph/static/src/js/graph.js | 9 ++++----- addons/web_graph/static/src/xml/web_graph.xml | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/addons/web_graph/static/src/js/graph.js b/addons/web_graph/static/src/js/graph.js index 3052a7bb8c9..86c37b5508a 100644 --- a/addons/web_graph/static/src/js/graph.js +++ b/addons/web_graph/static/src/js/graph.js @@ -49,7 +49,6 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ this.operator = []; this.group_field = []; this.orientation = this.fields_view.arch.attrs.orientation || ''; - this.elem_id = this.element_id; _.each(this.fields_view.arch.children, function (field) { if (field.attrs.operator) { this.operator.push(field.attrs.name); @@ -97,7 +96,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ }, schedule_chart: function(results) { - this.$element.html(QWeb.render("GraphView", {"fields_view": this.fields_view, "chart": this.chart,'elem_id': this.elem_id})); + this.$element.html(QWeb.render("GraphView", {"fields_view": this.fields_view, "chart": this.chart,'element_id': this.element_id})); _.each(results, function (result) { _.each(result, function (field_value, field_name) { @@ -303,7 +302,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ var bar_chart = new dhtmlXChart({ view: view_chart, - container: self.elem_id+"-barchart", + container: self.element_id+"-barchart", value:"#"+group_list[0]+"#", gradient: "3d", border: false, @@ -373,7 +372,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ }); } bar_chart.parse(_.values(abscissa_data), "json"); - jQuery("#"+self.elem_id+"-barchart").height(jQuery("#"+self.elem_id+"-barchart").height()+50); + jQuery("#"+self.element_id+"-barchart").height(jQuery("#"+self.element_id+"-barchart").height()+50); bar_chart.attachEvent("onItemClick", function(id) { self.open_list_view(bar_chart.get(id)); }); @@ -382,7 +381,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ var self = this; var chart = new dhtmlXChart({ view:"pie3D", - container:self.elem_id+"-piechart", + container:self.element_id+"-piechart", value:"#"+self.operator_field+"#", pieInnerText:function(obj) { var sum = chart.sum("#"+self.operator_field+"#"); diff --git a/addons/web_graph/static/src/xml/web_graph.xml b/addons/web_graph/static/src/xml/web_graph.xml index 4320b9f156e..90349623946 100644 --- a/addons/web_graph/static/src/xml/web_graph.xml +++ b/addons/web_graph/static/src/xml/web_graph.xml @@ -1,4 +1,4 @@ \ No newline at end of file From 8d0a65ec94b99531168b46cbb4ac564c2c58dd5a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 23 Sep 2011 11:45:00 +0200 Subject: [PATCH 077/128] [IMP] handle switch indicating whether the CSV file being imported has headers bzr revid: xmo@openerp.com-20110923094500-0jn0tb3fgix9j8wg --- addons/web/controllers/main.py | 4 ++-- addons/web/static/src/js/data_import.js | 7 ++++--- addons/web/static/src/xml/base.xml | 7 +++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 3839380f54f..1e0bd87f383 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1420,7 +1420,7 @@ class Import(View): @openerpweb.httprequest def detect_data(self, req, model, csvfile, csvsep, csvdel, csvcode, - csvskip, file_has_headers, jsonp): + csvskip, jsonp): try: data = list(csv.reader( csvfile, quotechar=str(csvdel), delimiter=str(csvsep))) @@ -1449,7 +1449,7 @@ class Import(View): @openerpweb.httprequest def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip, - file_has_headers, jsonp): + jsonp): context = req.session.eval_context(req.context) modle_obj = req.session.model(model) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 9d77a68e54f..3998c437727 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -33,7 +33,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ this.model = parent.model; this.fields = []; this.all_fields = []; - this.required_fields; + this.required_fields = null; var convert_fields = function (root, prefix) { prefix = prefix || ''; @@ -128,9 +128,10 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ var result_node = this.$element.find("#result"); if (results['records']) { + var with_headers = this.$element.find('#file_has_headers').prop('checked'); result_node.append(QWeb.render('ImportView.result', { - 'headers': results.records[0], - 'records': results.records.slice(1) + 'headers': with_headers ? results.records[0] : null, + 'records': with_headers ? results.records.slice(1) : results.records })); } else if (results['error']) { result_node.append(QWeb.render('ImportView.error', { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index db7663ed60d..354189ac4a5 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1407,8 +1407,7 @@
+ id="file_has_headers"/>
- + - From f42cacc5c14b294578b2ab528da21ff81d5b1eab Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 23 Sep 2011 11:56:09 +0200 Subject: [PATCH 078/128] [IMP] handle lines to skip in import preview bzr revid: xmo@openerp.com-20110923095609-ogxc6q3wnd0tez8v --- addons/web/controllers/main.py | 8 +++----- addons/web/static/src/js/data_import.js | 5 ++++- addons/web/static/src/xml/base.xml | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 1e0bd87f383..4786d2193d5 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1419,8 +1419,7 @@ class Import(View): return fields @openerpweb.httprequest - def detect_data(self, req, model, csvfile, csvsep, csvdel, csvcode, - csvskip, jsonp): + def detect_data(self, req, model, csvfile, csvsep, csvdel, csvcode, jsonp): try: data = list(csv.reader( csvfile, quotechar=str(csvdel), delimiter=str(csvsep))) @@ -1448,9 +1447,8 @@ class Import(View): })) @openerpweb.httprequest - def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip, - jsonp): - + def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, jsonp): + # TODO: reintroduce CSVSKIP param context = req.session.eval_context(req.context) modle_obj = req.session.model(model) res = None diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 3998c437727..0e36c463738 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -128,10 +128,13 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ var result_node = this.$element.find("#result"); if (results['records']) { + var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10); var with_headers = this.$element.find('#file_has_headers').prop('checked'); result_node.append(QWeb.render('ImportView.result', { 'headers': with_headers ? results.records[0] : null, - 'records': with_headers ? results.records.slice(1) : results.records + 'records': lines_to_skip ? results.records.slice(lines_to_skip) + : with_headers ? results.records.slice(1) + : results.records })); } else if (results['error']) { result_node.append(QWeb.render('ImportView.error', { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 354189ac4a5..c735a6fa815 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1426,7 +1426,7 @@ - +
+
From 633c27ab79e5aa007da06f06c878cc5d99606e47 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 23 Sep 2011 13:06:05 +0200 Subject: [PATCH 079/128] [REM] selector on an element which does not exist bzr revid: xmo@openerp.com-20110923110605-vw82lm018q6b2grt --- addons/web/static/src/js/data_import.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 0e36c463738..c62901e4795 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -124,7 +124,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ }, this.on_import_results); }, on_import_results: function(results) { - this.$element.find('#result, #success').empty(); + this.$element.find('#result').empty(); var result_node = this.$element.find("#result"); if (results['records']) { From 04d35223741955b118529a22c23591c4f600b861 Mon Sep 17 00:00:00 2001 From: "Yogesh (OpenERP)" Date: Fri, 23 Sep 2011 17:24:22 +0530 Subject: [PATCH 080/128] [IMP] kanbna:- if no group by on field then folding and unfolding columns work. bzr revid: ysa@tinyerp.com-20110923115422-0oi0y9pcj3swxg82 --- addons/web_kanban/static/src/js/kanban.js | 2 +- addons/web_kanban/static/src/xml/web_kanban.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index eb3810e3b5e..fc286855c90 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -194,7 +194,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ var record_per_group = Math.round((records).length / this.NO_OF_COLUMNS); this.all_display_data = []; for (var i=0, ii=this.NO_OF_COLUMNS; i < ii; i++) { - this.all_display_data.push({'records': records.slice(0,record_per_group), 'value':false, 'header' : false, 'ids':[]}); + this.all_display_data.push({'records': records.slice(0,record_per_group), 'value':i, 'header' : false, 'ids':[]}); records.splice(0,record_per_group); } } diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index 352ccf66b19..9ca9913b795 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -12,7 +12,7 @@
- +
@@ -21,7 +21,7 @@
-

+

@@ -165,6 +162,5 @@
- Description - - Qty - - Price -
- [[ line.product_id.name ]] - - [[o.state=='cancel' and o.statement_ids and '-' or '']][['%.f' % line.qty ]] - - [[o.state=='cancel' and o.statement_ids and '-' or '']][[ '%.2f' % net(line.id) ]] [[ line and line.discount == 0.0 and removeParentNode('font') ]] ([[ '%.f' % line.discount ]]%) -
- Disc : - - [[o.state=='cancel' and o.statement_ids and '-' or '']][[ '%.2f' % (disc(o.id) or 0.0,) ]] -
- Total : - - [[o.state=='cancel' and o.statement_ids and '-' or '']][['%.2f' % o.amount_total ]] [[ o.pricelist_id.currency_id.name ]] -
- Description - - Qty - - Price -
- [[ line.product_id.name ]] - - [[o.state=='cancel' and o.statement_ids and '-' or '']][['%.f' % line.qty ]] - - [[o.state=='cancel' and o.statement_ids and '-' or '']][[ '%.2f' % net(line.id) ]] [[ line and line.discount == 0.0 and removeParentNode('font') ]] ([[ '%.f' % line.discount ]]%) -
- Disc : - - [[o.state=='cancel' and o.statement_ids and '-' or '']][[ '%.2f' % (disc(o.id) or 0.0,) ]] -
- Total : - - [[o.state=='cancel' and o.statement_ids and '-' or '']][['%.2f' % o.amount_total ]] [[ o.pricelist_id.currency_id.name ]] -
- Company - - Print Date - - User -
- [[ company.name ]] - - [[ formatLang(time.strftime('%Y-%m-%d'),date = True) ]] - - [[ (get_user(user)) ]] -
- Name - - State - - Qty - - Total -
- [[ line_ids['name'] ]] - - [[ line_ids['state'] ]] - - [[ formatLang(line_ids['qty']) ]] - - [[ formatLang(line_ids['total']) ]] [[company.currency_id.symbol]] -
- - - - - Total : - - [[ formatLang((get_data_current_user_qty(user))) ]] - - [[ formatLang((get_data_current_user_tot(user))) or removeParentNode('blockTable')]] [[company.currency_id.symbol]] -
- +
@@ -21,7 +21,7 @@
-

+

- \ No newline at end of file + From bd93d296afa53b32687838f648bcb73ba385d6a7 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Mon, 26 Sep 2011 17:04:24 +0200 Subject: [PATCH 115/128] [IMP] Improved aggregates bzr revid: fme@openerp.com-20110926150424-o6nsd1h26y8o945d --- addons/web_kanban/static/src/css/kanban.css | 6 +++++- addons/web_kanban/static/src/js/kanban.js | 4 ++-- addons/web_kanban/static/src/xml/web_kanban.xml | 10 +++++----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index 0aca55430b5..f92203589b2 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -27,6 +27,10 @@ font-size: 1.5em; font-weight: bold; } +.openerp .oe_kanban_view .fold-columns-icon { + float: left; + padding: 2px 2px 0 0; +} .openerp .oe_kanban_action_button { height: 22px; margin: 0; @@ -212,4 +216,4 @@ /* Alert color */ .openerp .oe_kanban_color_alert .oe_kanban_color_border { border-color: #c00 !important; -} \ No newline at end of file +} diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 0f449a51cfc..df194e9f844 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -482,9 +482,9 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ group_name = group.value[1]; group_value = group.value[0]; } - var group_aggregates = ''; + var group_aggregates = {}; _.each(self.aggregates, function(value, key) { - group_aggregates += value + ": " + group.aggregates[key]; + group_aggregates[value] = group.aggregates[key]; }); self.dataset.read_slice([], {'domain': group.domain, 'context': group.context}, function(records) { self.all_display_data.push({"value" : group_value, "records": records, 'header':group_name, 'ids': self.dataset.ids, 'aggregates': group_aggregates}); diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index 70843b8a0b8..4c98f42f8fe 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -9,13 +9,13 @@
+ -
+
- -
- +
+
+ :