From 66cadcdb18c9ce0f48a05cf7d13cb98ed2f171e5 Mon Sep 17 00:00:00 2001 From: Anael Closson Date: Fri, 10 May 2013 17:19:34 +0200 Subject: [PATCH 001/520] [FIX] OPW 591344 : internal reports don't show images Cleaner method, and shows images. bzr revid: acl@openerp.com-20130510151934-e2kvwiexdzp3zc12 --- openerp/report/render/rml2pdf/trml2pdf.py | 54 +++++++---------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/openerp/report/render/rml2pdf/trml2pdf.py b/openerp/report/render/rml2pdf/trml2pdf.py index e8d6375c98e..153eff14b65 100644 --- a/openerp/report/render/rml2pdf/trml2pdf.py +++ b/openerp/report/render/rml2pdf/trml2pdf.py @@ -82,53 +82,33 @@ def _open_image(filename, path=None): pass raise IOError("File %s cannot be found in image path" % filename) + class NumberedCanvas(canvas.Canvas): def __init__(self, *args, **kwargs): canvas.Canvas.__init__(self, *args, **kwargs) - self._codes = [] - self._flag=False - self._pageCount=0 - self._currentPage =0 - self._pageCounter=0 - self.pages={} + self._saved_page_states = [] def showPage(self): - self._currentPage +=1 - if not self._flag: - self._pageCount += 1 - else: - self.pages.update({self._currentPage:self._pageCount}) - self._codes.append({'code': self._code, 'stack': self._codeStack}) + self._saved_page_states.append(dict(self.__dict__)) self._startPage() - self._flag=False - - def pageCount(self): - if self.pages.get(self._pageCounter,False): - self._pageNumber=0 - self._pageCounter +=1 - key=self._pageCounter - if not self.pages.get(key,False): - while not self.pages.get(key,False): - key += 1 - self.setFont("Helvetica", 8) - self.drawRightString((self._pagesize[0]-30), (self._pagesize[1]-40), - " %(this)i / %(total)i" % { - 'this': self._pageNumber+1, - 'total': self.pages.get(key,False), - } - ) def save(self): """add page info to each page (page x of y)""" - # reset page counter - self._pageNumber = 0 - for code in self._codes: - self._code = code['code'] - self._codeStack = code['stack'] - self.pageCount() + num_pages = len(self._saved_page_states) + for state in self._saved_page_states: + self.__dict__.update(state) + self.draw_page_number(num_pages) canvas.Canvas.showPage(self) -# self.restoreState() - self._doc.SaveToFile(self._filename, self) + canvas.Canvas.save(self) + + def draw_page_number(self, page_count): + self.drawRightString((self._pagesize[0]-30), (self._pagesize[1]-40), + " %(this)i / %(total)i" % { + 'this': self._pageNumber, + 'total': page_count, + } + ) + class PageCount(platypus.Flowable): def __init__(self, story_count=0): From 46e89d6dd1427ac5c7fa144f3b36e6ea1779771d Mon Sep 17 00:00:00 2001 From: Mohammed Shekha Date: Thu, 20 Jun 2013 16:16:06 +0530 Subject: [PATCH 002/520] [FIX]Fixed the issue of recurrent rule, there is no need of passing 'Z' after end_date as end_date converted to UNTIL parameter in rrule, and date given in UNTIL is not converted to any other timezone until you specifically passed timezone difference or timezone name like 20130620T121012+3:30 or 20130620T121012TZOFFSET, currently Z creates issue when recurrent rule is created with end_date, TypeError: can't compare offset-naive and offset-aware datetimes. bzr revid: msh@openerp.com-20130620104606-g3dyr1mafzo8fpgg --- addons/base_calendar/base_calendar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 25bfcac448c..41a142cde68 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -1269,7 +1269,7 @@ rule or repeating pattern of time to exclude from the recurring rule."), def get_end_date(data): if data.get('end_date'): - data['end_date_new'] = ''.join((re.compile('\d')).findall(data.get('end_date'))) + 'T235959Z' + data['end_date_new'] = ''.join((re.compile('\d')).findall(data.get('end_date'))) + 'T235959' return (data.get('end_type') == 'count' and (';COUNT=' + str(data.get('count'))) or '') +\ ((data.get('end_date_new') and data.get('end_type') == 'end_date' and (';UNTIL=' + data.get('end_date_new'))) or '') From 3276861ff6125d02e08b5c5b12b75925e056c4bc Mon Sep 17 00:00:00 2001 From: "Xavier Fernandez http://www.smile.fr" <> Date: Wed, 28 Aug 2013 17:21:17 +0200 Subject: [PATCH 003/520] [FIX] port Xavier Fernandez's fix for Bug #971412: Translations export has no fixed order lp bug: https://launchpad.net/bugs/971412 fixed bzr revid: ls@numerigraphe.com-20130828152117-e1h0yljgbn538v9o --- openerp/tools/translate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openerp/tools/translate.py b/openerp/tools/translate.py index 41113b8ccd9..53762b87970 100644 --- a/openerp/tools/translate.py +++ b/openerp/tools/translate.py @@ -458,7 +458,8 @@ def trans_export(lang, modules, buffer, format, cr): row.setdefault('tnrs', []).append((type, name, res_id)) row.setdefault('comments', set()).update(comments) - for src, row in grouped_rows.items(): + for src in sorted(grouped_rows): + row = grouped_rows[src] if not lang: # translation template, so no translation value row['translation'] = '' From 1f3a482f4fc309262802b980e143234a56b9503d Mon Sep 17 00:00:00 2001 From: Lionel Sausin Date: Thu, 29 Aug 2013 09:50:35 +0200 Subject: [PATCH 004/520] [REF] avoid the lookup in sorted translation export as suggested by Guewen Baconnier @ Camptocamp bzr revid: ls@numerigraphe.com-20130829075035-lml81a7bbn66c8qu --- openerp/tools/translate.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openerp/tools/translate.py b/openerp/tools/translate.py index 53762b87970..6e9eb50f162 100644 --- a/openerp/tools/translate.py +++ b/openerp/tools/translate.py @@ -458,8 +458,7 @@ def trans_export(lang, modules, buffer, format, cr): row.setdefault('tnrs', []).append((type, name, res_id)) row.setdefault('comments', set()).update(comments) - for src in sorted(grouped_rows): - row = grouped_rows[src] + for src, row in sorted(grouped_rows.items()): if not lang: # translation template, so no translation value row['translation'] = '' From 0dcc62426c3689191798fbe33717eea5d4659a96 Mon Sep 17 00:00:00 2001 From: Yanina Aular Date: Fri, 15 Nov 2013 17:57:17 -0430 Subject: [PATCH 005/520] [FIX] function field with wrong type int instead of integer bzr revid: yanina.aular@vauxoo.com-20131115222717-12bplq7rnv3rorbm --- addons/project/project.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/project/project.py b/addons/project/project.py index 5bd2e604368..c7b394ad376 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -288,7 +288,8 @@ class project(osv.osv): help="The kind of document created when an email is received on this project's email alias"), 'privacy_visibility': fields.selection(_visibility_selection, 'Privacy / Visibility', required=True), 'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'), ('cancelled', 'Cancelled'),('pending','Pending'),('close','Closed')], 'Status', required=True,), - 'doc_count':fields.function(_get_attached_docs, string="Number of documents attached", type='int') + 'doc_count':fields.function(_get_attached_docs, string="Number of documents attached", + type='integer') } def _get_type_common(self, cr, uid, context): From 02224a3f112426c9e15a8af7cde2e7a5406d1885 Mon Sep 17 00:00:00 2001 From: "Dharti Ratani (Open ERP)" Date: Thu, 12 Dec 2013 10:55:44 +0530 Subject: [PATCH 006/520] [FIX]Added the refernce of the supplier in price_get method while creating purchase order from purchase requisition, as without supplier the price unit in PO was 0 when the pricelist item was based on Supplier Price in the product form bzr revid: dhr@tinyerp.com-20131212052544-skljxsrxea4o8efm --- addons/purchase_requisition/purchase_requisition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py index c1d80c7bf46..c31f8a69a37 100644 --- a/addons/purchase_requisition/purchase_requisition.py +++ b/addons/purchase_requisition/purchase_requisition.py @@ -109,7 +109,7 @@ class purchase_requisition(osv.osv): seller_delay = product_supplier.delay seller_qty = product_supplier.qty supplier_pricelist = supplier.property_product_pricelist_purchase or False - seller_price = pricelist.price_get(cr, uid, [supplier_pricelist.id], product.id, qty, False, {'uom': default_uom_po_id})[supplier_pricelist.id] + seller_price = pricelist.price_get(cr, uid, [supplier_pricelist.id], product.id, qty, supplier.id, {'uom': default_uom_po_id})[supplier_pricelist.id] if seller_qty: qty = max(qty,seller_qty) date_planned = self._planned_date(requisition_line.requisition_id, seller_delay) From f6349ffb7fd9c075573953be2ac0f0dc20520743 Mon Sep 17 00:00:00 2001 From: Leonardo Pistone Date: Wed, 18 Dec 2013 17:45:26 +0100 Subject: [PATCH 007/520] [merge] [fix] action should return True lp bug: https://launchpad.net/bugs/1262265 fixed bzr revid: leonardo.pistone@camptocamp.com-20131218164526-rgyo6p9hlpk28sxg --- addons/mrp_repair/mrp_repair.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py index 9c0ab0e2c18..9335d395714 100644 --- a/addons/mrp_repair/mrp_repair.py +++ b/addons/mrp_repair/mrp_repair.py @@ -350,7 +350,8 @@ class mrp_repair(osv.osv): return self.write(cr,uid,ids,{'state':'cancel'}) def wkf_invoice_create(self, cr, uid, ids, *args): - return self.action_invoice_create(cr, uid, ids) + self.action_invoice_create(cr, uid, ids) + return True def action_invoice_create(self, cr, uid, ids, group=False, context=None): """ Creates invoice(s) for repair order. From 262a405b0ab1018bde19e61d38a4525582831a16 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Fri, 3 Jan 2014 14:07:03 +0100 Subject: [PATCH 008/520] Remove old FR VAT taxes 19.6% and 7.0%. Remove the FR VAT tax 5.0% that has never seen the light ! bzr revid: alexis@via.ecp.fr-20140103130703-kpf5rojc4azwq8w1 --- addons/l10n_fr/fr_fiscal_templates.xml | 79 ----- addons/l10n_fr/fr_tax.xml | 387 ------------------------- 2 files changed, 466 deletions(-) diff --git a/addons/l10n_fr/fr_fiscal_templates.xml b/addons/l10n_fr/fr_fiscal_templates.xml index bf49d15e08c..cca96422c22 100644 --- a/addons/l10n_fr/fr_fiscal_templates.xml +++ b/addons/l10n_fr/fr_fiscal_templates.xml @@ -40,11 +40,6 @@ - - - - - @@ -57,22 +52,12 @@ - - - - - - - - - - @@ -91,17 +76,6 @@ - - - - - - - - - - - @@ -124,17 +98,6 @@ - - - - - - - - - - - @@ -146,17 +109,6 @@ - - - - - - - - - - - @@ -177,11 +129,6 @@ - - - - - @@ -194,22 +141,12 @@ - - - - - - - - - - @@ -224,11 +161,6 @@ - - - - - @@ -241,23 +173,12 @@ - - - - - - - - - - - diff --git a/addons/l10n_fr/fr_tax.xml b/addons/l10n_fr/fr_tax.xml index 8633cca7141..22e1e12f03c 100644 --- a/addons/l10n_fr/fr_tax.xml +++ b/addons/l10n_fr/fr_tax.xml @@ -29,28 +29,6 @@ sale - - - - TVA collectée (vente) 19,6% - 19.6 - - percent - - - - - - - - - - - - - - sale - @@ -96,51 +74,7 @@ sale - - - TVA collectée (vente) 7,0% - 7.0 - - percent - - - - - - - - - - - - - - sale - - - - TVA collectée (vente) 5,0% - 5.0 - - percent - - - - - - - - - - - - - - sale - - - TVA collectée (vente) 5,5% 5.5 @@ -208,28 +142,6 @@ purchase - - - TVA déductible (achat) 19,6% - ACH-19.6 - - percent - - - - - - - - - - - - - - purchase - - TVA déductible (achat) 8,5% @@ -274,51 +186,7 @@ purchase - - - TVA déductible (achat) 7,0% - ACH-7.0 - - percent - - - - - - - - - - - - - - purchase - - - - TVA déductible (achat) 5,0% - ACH-5.0 - - percent - - - - - - - - - - - - - - purchase - - - TVA déductible (achat) 5,5% ACH-5.5 @@ -387,29 +255,6 @@ purchase - - - TVA déductible (achat) 19,6% TTC - ACH-19.6-TTC - - - percent - - - - - - - - - - - - - - purchase - - TVA déductible (achat) 8,5% TTC @@ -456,53 +301,7 @@ purchase - - - TVA déductible (achat) 7,0% TTC - ACH-7.0-TTC - - - percent - - - - - - - - - - - - - - purchase - - - - TVA déductible (achat) 5,0% TTC - ACH-5.0-TTC - - - percent - - - - - - - - - - - - - - purchase - - - TVA déductible (achat) 5,5% TTC ACH-5.5-TTC @@ -573,28 +372,6 @@ purchase - - - TVA déd./immobilisation (achat) 19,6% - IMMO-19.6 - - percent - - - - - - - - - - - - - - purchase - - TVA déd./immobilisation (achat) 8,5% @@ -639,51 +416,7 @@ purchase - - - TVA déd./immobilisation (achat) 7,0% - IMMO-7.0 - - percent - - - - - - - - - - - - - - purchase - - - - TVA déd./immobilisation (achat) 5,0% - IMMO-5.0 - - percent - - - - - - - - - - - - - - purchase - - - TVA déd./immobilisation (achat) 5,5% IMMO-5.5 @@ -751,28 +484,6 @@ purchase - - - TVA due s/ acq. intracommunautaire (achat) 19,6% - ACH_UE_due-19.6 - - percent - - - - - - - - - - - - - - purchase - - TVA due s/ acq. intracommunautaire (achat) 8,5% @@ -817,51 +528,7 @@ purchase - - - TVA due s/ acq. intracommunautaire (achat) 7,0% - ACH_UE_due-7.0 - - percent - - - - - - - - - - - - - - purchase - - - - TVA due s/ acq. intracommunautaire (achat) 5,0% - ACH_UE_due-5.0 - - percent - - - - - - - - - - - - - - purchase - - - TVA due s/ acq. intracommunautaire (achat) 5,5% ACH_UE_due-5.5 @@ -925,24 +592,6 @@ purchase - - - TVA déd. s/ acq. intracommunautaire (achat) 19,6% - ACH_UE_ded.-19.6 - - percent - - - - - - - - - - purchase - - TVA déd. s/ acq. intracommunautaire (achat) 8,5% @@ -979,43 +628,7 @@ purchase - - - TVA déd. s/ acq. intracommunautaire (achat) 7,0% - ACH_UE_ded.-7.0 - - percent - - - - - - - - - - purchase - - - - TVA déd. s/ acq. intracommunautaire (achat) 5,0% - ACH_UE_ded.-5.0 - - percent - - - - - - - - - - purchase - - - TVA déd. s/ acq. intracommunautaire (achat) 5,5% ACH_UE_ded.-5.5 From 6abf20906e7b5d3e319a227301fbcf1a6d4492b7 Mon Sep 17 00:00:00 2001 From: jke-openerp Date: Sat, 1 Feb 2014 21:04:13 +0100 Subject: [PATCH 009/520] [Typo] Replace t-esc in t-field for amount_untaxed in checkout of website_sale. Else display monetary does not work bzr revid: jke@openerp.com-20140201200413-zww5pcobl6vmvqe1 --- addons/website_sale/views/website_sale.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website_sale/views/website_sale.xml b/addons/website_sale/views/website_sale.xml index ed2007e27c7..cc1220a57e7 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -832,7 +832,7 @@
Subtotal:
-
From c18d3504689228807d6703b920b43d6c4e6e6721 Mon Sep 17 00:00:00 2001 From: jke-openerp Date: Sat, 1 Feb 2014 21:47:41 +0100 Subject: [PATCH 010/520] [FIX] Fix bug where button with contenteditable does not work in cross browser. Fix : replace tag 'button' by 'a' with class 'a-submit' bzr revid: jke@openerp.com-20140201204741-ygdxaauq0m5axttg --- addons/website_sale/static/src/js/website_sale.js | 4 ++++ addons/website_sale/views/website_sale.xml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js index 7f6d24373f7..eedbb5a046c 100644 --- a/addons/website_sale/static/src/js/website_sale.js +++ b/addons/website_sale/static/src/js/website_sale.js @@ -60,6 +60,10 @@ $(document).ready(function () { return false; }); + $('.a-submit').on('click', function () { + $(this).closest('form').submit(); + }); + // change price when they are variants $('form.js_add_cart_json label').on('mouseup', function (ev) { ev.preventDefault(); diff --git a/addons/website_sale/views/website_sale.xml b/addons/website_sale/views/website_sale.xml index ed2007e27c7..d6c86c81e8b 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -331,7 +331,7 @@ }'/>
- + Add to Cart


From 8bbb20650464b5784fd3afa0eb219ed1802e2a4f Mon Sep 17 00:00:00 2001 From: "ddm@openerp.com" <> Date: Thu, 13 Feb 2014 16:24:49 +0100 Subject: [PATCH 011/520] [IMP] save new orders only bzr revid: ddm@openerp.com-20140213152449-tarl15agw9r3teqg --- addons/point_of_sale/point_of_sale.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index 30fcff2e5e6..dec4431a15c 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -513,13 +513,18 @@ class pos_order(osv.osv): _order = "id desc" def create_from_ui(self, cr, uid, orders, context=None): - #_logger.info("orders: %r", orders) + + # Keep only new orders + submitted_references = [o['data']['name'] for o in orders] + existing_orders = self.search_read(cr, uid, [('pos_reference', 'in', submitted_references)], context) + existing_references = set([o['pos_reference'] for o in existing_orders]) + orders_to_save = [o for o in orders if o['data']['name'] not in existing_references] + order_ids = [] - for tmp_order in orders: + for tmp_order in orders_to_save: to_invoice = tmp_order['to_invoice'] order = tmp_order['data'] - order_id = self.create(cr, uid, { 'name': order['name'], 'user_id': order['user_id'] or False, From e7f238615ca1c79700a0deacb1d1799b797a0472 Mon Sep 17 00:00:00 2001 From: "ddm@openerp.com" <> Date: Thu, 13 Feb 2014 17:38:49 +0100 Subject: [PATCH 012/520] [IMP] fetch only relevant fields when checking for dupes bzr revid: ddm@openerp.com-20140213163849-8v485vq5v4xmeocg --- addons/point_of_sale/point_of_sale.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index 9e78a29cfec..01e7a49ec8c 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -517,7 +517,7 @@ class pos_order(osv.osv): # Keep only new orders submitted_references = [o['data']['name'] for o in orders] - existing_orders = self.search_read(cr, uid, [('pos_reference', 'in', submitted_references)], context) + existing_orders = self.search_read(cr, uid, domain=[('pos_reference', 'in', submitted_references)], fields=['pos_reference'], context=context) existing_references = set([o['pos_reference'] for o in existing_orders]) orders_to_save = [o for o in orders if o['data']['name'] not in existing_references] From 3ea931c48637265183ce17b8cfb21c858f90a56d Mon Sep 17 00:00:00 2001 From: "ddm@openerp.com" <> Date: Fri, 14 Feb 2014 15:49:30 +0100 Subject: [PATCH 013/520] [IMP] save all orders at once (client) bzr revid: ddm@openerp.com-20140214144930-5zpn3miys1hwrdoa --- addons/point_of_sale/static/src/js/models.js | 97 ++++++++++---------- 1 file changed, 51 insertions(+), 46 deletions(-) diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 6b54c14113d..90aa5fece05 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -421,62 +421,67 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal // it is therefore important to only call this method from inside a mutex // this method returns a deferred indicating wether the sending was successful or not // there is a timeout parameter which is set to 2 seconds by default. - _flush_order: function(order_id, options){ - var self = this; - options = options || {}; - timeout = typeof options.timeout === 'number' ? options.timeout : 7500; - - this.set('synch',{state:'connecting', pending: this.get('synch').pending}); - - var order = this.db.get_order(order_id); - order.to_invoice = options.to_invoice || false; - - if(!order){ - // flushing a non existing order always fails - return (new $.Deferred()).reject(); - } - - // we try to send the order. shadow prevents a spinner if it takes too long. (unless we are sending an invoice, - // then we want to notify the user that we are waiting on something ) - var rpc = (new instance.web.Model('pos.order')).call('create_from_ui',[[order]],undefined,{shadow: !options.to_invoice, timeout:timeout}); - - rpc.fail(function(unused,event){ - // prevent an error popup creation by the rpc failure - // we want the failure to be silent as we send the orders in the background - event.preventDefault(); - console.error('Failed to send order:',order); - }); - - rpc.done(function(){ - self.db.remove_order(order_id); - var pending = self.db.get_orders().length; - self.set('synch',{state: pending ? 'connecting' : 'connected', pending:pending}); - }); - - return rpc; + _flush_order: function( order_id, options) { + return this._flush_all_orders([this.db.get_order(order_id)], options); }, // attempts to send all the locally stored orders. As with _flush_order, it should only be // called from within a mutex. // this method returns a deferred that always succeeds when all orders have been tried to be sent, // even if none of them could actually be sent. - _flush_all_orders: function(){ + _flush_all_orders: function () { var self = this; - var orders = this.db.get_orders(); - var tried_all = new $.Deferred(); + self.set('synch', { + state: 'connecting', + pending: self.get('synch').pending + }); + return self._save_to_server(self.db.get_orders()).done(function () { + var pending = self.db.get_orders().length; + self.set('synch', { + state: pending ? 'connecting' : 'connected', + pending: pending + }); + }); + }, - function rec_flush(index){ - if(index < orders.length){ - self._flush_order(orders[index].id).always(function(){ - rec_flush(index+1); - }) - }else{ - tried_all.resolve(); - } + // send an array of orders to the server + // available options: + // - timeout: timeout for the rpc call in ms + _save_to_server: function (orders, options) { + if (!orders || !orders.length) { + var result = $.Deferred(); + result.resolve(); + return result; } - rec_flush(0); + + options = options || {}; - return tried_all; + var self = this; + var timeout = typeof options.timeout === 'number' ? options.timeout : 7500; + + // we try to send the order. shadow prevents a spinner if it takes too long. (unless we are sending an invoice, + // then we want to notify the user that we are waiting on something ) + var posOrderModel = new instance.web.Model('pos.order'); + return posOrderModel.call('create_from_ui', + [_.map(orders, function (order) { + order.to_invoice = options.to_invoice || false; + return order; + })], + undefined, + { + shadow: !options.to_invoice, + timeout: timeout + } + ).then(function () { + _.each(orders, function (order) { + self.db.remove_order(order.id); + }); + }).fail(function (unused, event){ + // prevent an error popup creation by the rpc failure + // we want the failure to be silent as we send the orders in the background + event.preventDefault(); + console.error('Failed to send orders:', orders); + }); }, scan_product: function(parsed_code){ From e776245b8b597b57877b99aef2a15a127ee6a55e Mon Sep 17 00:00:00 2001 From: Paramjit Singh Sahota Date: Thu, 20 Feb 2014 15:37:16 +0530 Subject: [PATCH 014/520] [ADD] Added jquery.placeholder lib. because IE9 is not supporting the placeholders. bzr revid: psa@tinyerp.com-20140220100716-01mjib3w692qdgi1 --- .../jquery.placeholder/jquery.placeholder.js | 185 ++++++++++++++++++ addons/website/static/src/js/website.js | 1 + addons/website/views/website_templates.xml | 2 + 3 files changed, 188 insertions(+) create mode 100644 addons/website/static/lib/jquery.placeholder/jquery.placeholder.js diff --git a/addons/website/static/lib/jquery.placeholder/jquery.placeholder.js b/addons/website/static/lib/jquery.placeholder/jquery.placeholder.js new file mode 100644 index 00000000000..4e296b7ac4e --- /dev/null +++ b/addons/website/static/lib/jquery.placeholder/jquery.placeholder.js @@ -0,0 +1,185 @@ +/*! http://mths.be/placeholder v2.0.7 by @mathias */ +;(function(window, document, $) { + + // Opera Mini v7 doesn’t support placeholder although its DOM seems to indicate so + var isOperaMini = Object.prototype.toString.call(window.operamini) == '[object OperaMini]'; + var isInputSupported = 'placeholder' in document.createElement('input') && !isOperaMini; + var isTextareaSupported = 'placeholder' in document.createElement('textarea') && !isOperaMini; + var prototype = $.fn; + var valHooks = $.valHooks; + var propHooks = $.propHooks; + var hooks; + var placeholder; + + if (isInputSupported && isTextareaSupported) { + + placeholder = prototype.placeholder = function() { + return this; + }; + + placeholder.input = placeholder.textarea = true; + + } else { + + placeholder = prototype.placeholder = function() { + var $this = this; + $this + .filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]') + .not('.placeholder') + .bind({ + 'focus.placeholder': clearPlaceholder, + 'blur.placeholder': setPlaceholder + }) + .data('placeholder-enabled', true) + .trigger('blur.placeholder'); + return $this; + }; + + placeholder.input = isInputSupported; + placeholder.textarea = isTextareaSupported; + + hooks = { + 'get': function(element) { + var $element = $(element); + + var $passwordInput = $element.data('placeholder-password'); + if ($passwordInput) { + return $passwordInput[0].value; + } + + return $element.data('placeholder-enabled') && $element.hasClass('placeholder') ? '' : element.value; + }, + 'set': function(element, value) { + var $element = $(element); + + var $passwordInput = $element.data('placeholder-password'); + if ($passwordInput) { + return $passwordInput[0].value = value; + } + + if (!$element.data('placeholder-enabled')) { + return element.value = value; + } + if (value == '') { + element.value = value; + // Issue #56: Setting the placeholder causes problems if the element continues to have focus. + if (element != safeActiveElement()) { + // We can't use `triggerHandler` here because of dummy text/password inputs :( + setPlaceholder.call(element); + } + } else if ($element.hasClass('placeholder')) { + clearPlaceholder.call(element, true, value) || (element.value = value); + } else { + element.value = value; + } + // `set` can not return `undefined`; see http://jsapi.info/jquery/1.7.1/val#L2363 + return $element; + } + }; + + if (!isInputSupported) { + valHooks.input = hooks; + propHooks.value = hooks; + } + if (!isTextareaSupported) { + valHooks.textarea = hooks; + propHooks.value = hooks; + } + + $(function() { + // Look for forms + $(document).delegate('form', 'submit.placeholder', function() { + // Clear the placeholder values so they don't get submitted + var $inputs = $('.placeholder', this).each(clearPlaceholder); + setTimeout(function() { + $inputs.each(setPlaceholder); + }, 10); + }); + }); + + // Clear placeholder values upon page reload + $(window).bind('beforeunload.placeholder', function() { + $('.placeholder').each(function() { + this.value = ''; + }); + }); + + } + + function args(elem) { + // Return an object of element attributes + var newAttrs = {}; + var rinlinejQuery = /^jQuery\d+$/; + $.each(elem.attributes, function(i, attr) { + if (attr.specified && !rinlinejQuery.test(attr.name)) { + newAttrs[attr.name] = attr.value; + } + }); + return newAttrs; + } + + function clearPlaceholder(event, value) { + var input = this; + var $input = $(input); + if (input.value == $input.attr('placeholder') && $input.hasClass('placeholder')) { + if ($input.data('placeholder-password')) { + $input = $input.hide().next().show().attr('id', $input.removeAttr('id').data('placeholder-id')); + // If `clearPlaceholder` was called from `$.valHooks.input.set` + if (event === true) { + return $input[0].value = value; + } + $input.focus(); + } else { + input.value = ''; + $input.removeClass('placeholder'); + input == safeActiveElement() && input.select(); + } + } + } + + function setPlaceholder() { + var $replacement; + var input = this; + var $input = $(input); + var id = this.id; + if (input.value == '') { + if (input.type == 'password') { + if (!$input.data('placeholder-textinput')) { + try { + $replacement = $input.clone().attr({ 'type': 'text' }); + } catch(e) { + $replacement = $('').attr($.extend(args(this), { 'type': 'text' })); + } + $replacement + .removeAttr('name') + .data({ + 'placeholder-password': $input, + 'placeholder-id': id + }) + .bind('focus.placeholder', clearPlaceholder); + $input + .data({ + 'placeholder-textinput': $replacement, + 'placeholder-id': id + }) + .before($replacement); + } + $input = $input.removeAttr('id').hide().prev().attr('id', id).show(); + // Note: `$input[0] != input` now! + } + $input.addClass('placeholder'); + $input[0].value = $input.attr('placeholder'); + } else { + $input.removeClass('placeholder'); + } + } + + function safeActiveElement() { + // Avoid IE9 `document.activeElement` of death + // https://github.com/mathiasbynens/jquery-placeholder/pull/99 + try { + return document.activeElement; + } catch (err) {} + } + +}(this, document, jQuery)); \ No newline at end of file diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js index b249fb7136f..3faae6ee1ef 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -69,6 +69,7 @@ website.is_editable = website.is_editable || $('html').data('editable'); website.is_editable_button= website.is_editable_button || $('html').data('editable'); dom_ready.resolve(); + $('input, textarea').placeholder(); }); website.init_kanban = function ($kanban) { diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 5649ded4d4d..96e184a32a1 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -272,6 +272,8 @@ + + From 2e3e44255ebb4e7530c48b35df62a12c99b4323c Mon Sep 17 00:00:00 2001 From: Paramjit Singh Sahota Date: Thu, 20 Feb 2014 15:52:00 +0530 Subject: [PATCH 015/520] [FIX] Editor bar is showing border. bzr revid: psa@tinyerp.com-20140220102200-2r2urlrrkngv20at --- addons/website/static/src/css/editor.css | 1 + addons/website/static/src/css/editor.sass | 1 + 2 files changed, 2 insertions(+) diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 4fb4496e692..df2188e794d 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -26,6 +26,7 @@ -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; + -ms-filter: "alpha(opacity=50)"; } /* ---- OpenERP Style ---- {{{ */ diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 2e6513f7c1d..49b71ffefdf 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -23,6 +23,7 @@ background: transparent border: none +box-shadow(none) + -ms-filter: "alpha(opacity=50)" // }}} From 6305ac409444ca01388ce1ad66ef2af3783360bf Mon Sep 17 00:00:00 2001 From: Kersten Jeremy Date: Thu, 20 Feb 2014 16:08:17 +0100 Subject: [PATCH 016/520] [FIX] Disable traceback popup when rpc /calendar/notify fail bzr revid: jke@openerp.com-20140220150817-3rvvm8neeqviba6o --- addons/calendar/static/src/js/base_calendar.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/addons/calendar/static/src/js/base_calendar.js b/addons/calendar/static/src/js/base_calendar.js index 18fd4dfa20e..4384964575c 100644 --- a/addons/calendar/static/src/js/base_calendar.js +++ b/addons/calendar/static/src/js/base_calendar.js @@ -11,7 +11,7 @@ openerp.calendar = function(instance) { get_next_notif: function() { var self= this; this.rpc("/calendar/notify") - .then( + .done( function(result) { _.each(result, function(res) { setTimeout(function() { @@ -44,6 +44,12 @@ openerp.calendar = function(instance) { },res.timer * 1000); }); } + ) + .fail( + // To override error from framework.js in RPC function + function(error, event) { + event.preventDefault(); + } ); }, check_notifications: function() { @@ -51,14 +57,14 @@ openerp.calendar = function(instance) { self.get_next_notif(); setInterval(function(){ self.get_next_notif(); - }, 5 * 60 * 1000 ); + }, 5 * 60 * 1000 ); }, //Override the show_application of addons/web/static/src/js/chrome.js show_application: function() { this._super(); this.check_notifications(); - }, + }, }); From 8d8ede7f7d075c29f903b2714028c009b7a337ec Mon Sep 17 00:00:00 2001 From: Kersten Jeremy Date: Thu, 20 Feb 2014 16:08:17 +0100 Subject: [PATCH 017/520] [FIX] Disable traceback popup when rpc /calendar/notify fail bzr revid: jke@openerp.com-20140220150817-0yyovkr4ooqu8ejc --- addons/calendar/static/src/js/base_calendar.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/addons/calendar/static/src/js/base_calendar.js b/addons/calendar/static/src/js/base_calendar.js index 18fd4dfa20e..4384964575c 100644 --- a/addons/calendar/static/src/js/base_calendar.js +++ b/addons/calendar/static/src/js/base_calendar.js @@ -11,7 +11,7 @@ openerp.calendar = function(instance) { get_next_notif: function() { var self= this; this.rpc("/calendar/notify") - .then( + .done( function(result) { _.each(result, function(res) { setTimeout(function() { @@ -44,6 +44,12 @@ openerp.calendar = function(instance) { },res.timer * 1000); }); } + ) + .fail( + // To override error from framework.js in RPC function + function(error, event) { + event.preventDefault(); + } ); }, check_notifications: function() { @@ -51,14 +57,14 @@ openerp.calendar = function(instance) { self.get_next_notif(); setInterval(function(){ self.get_next_notif(); - }, 5 * 60 * 1000 ); + }, 5 * 60 * 1000 ); }, //Override the show_application of addons/web/static/src/js/chrome.js show_application: function() { this._super(); this.check_notifications(); - }, + }, }); From 2e49e46698c52edf29c7a42b1a67f8f2cb110e34 Mon Sep 17 00:00:00 2001 From: Paramjit Singh Sahota Date: Fri, 21 Feb 2014 11:42:36 +0530 Subject: [PATCH 018/520] [FIX] Fixed the sliding issue of carousel library in IE9. The transition was not working in any snippet bzr revid: psa@tinyerp.com-20140221061236-7jcbgae7m081pjv8 --- addons/website/static/lib/bootstrap/js/bootstrap.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/addons/website/static/lib/bootstrap/js/bootstrap.js b/addons/website/static/lib/bootstrap/js/bootstrap.js index b22be6fce1d..2d31d0eb3c3 100644 --- a/addons/website/static/lib/bootstrap/js/bootstrap.js +++ b/addons/website/static/lib/bootstrap/js/bootstrap.js @@ -418,6 +418,17 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery" setTimeout(function () { that.$element.trigger('slid') }, 0) }) .emulateTransitionEnd(600) + } else if(this.$element.hasClass('slide')) { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + $active.animate({left: (direction == 'right' ? '100%' : '-100%')}, 600, function(){ + $active.removeClass('active') + that.sliding = false + setTimeout(function() { that.$element.trigger('slid')}, 0) + }) + $next.addClass(type).css({left: (direction == 'right' ? '-100%' : '100%')}).animate({left: 0}, 600, function() { + $next.removeClass(type).addClass('active') + }) } else { this.$element.trigger(e) if (e.isDefaultPrevented()) return From 777506617af86dad56743ae014866b5bb6224c59 Mon Sep 17 00:00:00 2001 From: Paramjit Singh Sahota Date: Fri, 21 Feb 2014 12:45:12 +0530 Subject: [PATCH 019/520] [FIX] 'window.location.origin' is not found in IE browser thats why generated manually. bzr revid: psa@tinyerp.com-20140221071512-4uky4k2wiu6miysi --- addons/website/static/src/js/website.mobile.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/website/static/src/js/website.mobile.js b/addons/website/static/src/js/website.mobile.js index 4acf2d78db8..ac3b17ddad7 100644 --- a/addons/website/static/src/js/website.mobile.js +++ b/addons/website/static/src/js/website.mobile.js @@ -17,6 +17,9 @@ 'hidden.bs.modal': 'destroy' }, start: function () { + if (!window.location.origin) { + window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: ''); + } document.getElementById("mobile-viewport").src = window.location.origin + window.location.pathname + "#mobile-preview"; this.$el.modal(); }, From 467ec6a91953378904f2462631cc4d169a2773ba Mon Sep 17 00:00:00 2001 From: Pariket Trivedi Date: Fri, 21 Feb 2014 18:42:15 +0530 Subject: [PATCH 020/520] [FIX] Fixed a small problem which falls to load the template in mobile bzr revid: psa@tinyerp.com-20140221131215-nf1y2t3qbeiosujb --- addons/website/static/src/js/website.mobile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website/static/src/js/website.mobile.js b/addons/website/static/src/js/website.mobile.js index ac3b17ddad7..35b3e7c5acd 100644 --- a/addons/website/static/src/js/website.mobile.js +++ b/addons/website/static/src/js/website.mobile.js @@ -18,7 +18,7 @@ }, start: function () { if (!window.location.origin) { - window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: ''); + window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '') + '/'; } document.getElementById("mobile-viewport").src = window.location.origin + window.location.pathname + "#mobile-preview"; this.$el.modal(); From 8f1192e0b04a3868d0cbdb6525bd244615c5d706 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Mon, 24 Feb 2014 10:41:24 +0100 Subject: [PATCH 021/520] [ADD] Add meagtest bzr revid: jco@openerp.com-20140224094124-2vmukbsy006d8j4y --- addons/stock_megatest/__init__.py | 23 ++ addons/stock_megatest/__openerp__.py | 47 +++ addons/stock_megatest/test/megatest.yml | 286 ++++++++++++++++++ addons/stock_megatest/test/megatestmtobuy.yml | 203 +++++++++++++ 4 files changed, 559 insertions(+) create mode 100644 addons/stock_megatest/__init__.py create mode 100644 addons/stock_megatest/__openerp__.py create mode 100644 addons/stock_megatest/test/megatest.yml create mode 100644 addons/stock_megatest/test/megatestmtobuy.yml diff --git a/addons/stock_megatest/__init__.py b/addons/stock_megatest/__init__.py new file mode 100644 index 00000000000..1529644d61e --- /dev/null +++ b/addons/stock_megatest/__init__.py @@ -0,0 +1,23 @@ +# -*- 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 . +# +############################################################################## + + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/addons/stock_megatest/__openerp__.py b/addons/stock_megatest/__openerp__.py new file mode 100644 index 00000000000..3f167f72011 --- /dev/null +++ b/addons/stock_megatest/__openerp__.py @@ -0,0 +1,47 @@ +# -*- 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 . +# +############################################################################## + +{ + 'name': 'Dropshipping Module', + 'version': '1.0', + 'category': 'Hidden', + 'summary': 'Dropshipping', + 'description': """ +Manage sales quotations and stock locations +========================================== + +This adds the route to make dropshipping sales orders in which the product sold are directly transfered from the reseller to the customer (direct delivery) without creating any internal document for the transfer. + +""", + 'author': 'OpenERP SA', + 'website': 'http://www.openerp.com', + 'images': [], + 'depends': ['stock_dropshipping'], + 'init_xml': [], + 'data': [], + 'demo_xml': [], + 'test': [ + 'test/megatestmtobuy.yml' + ], + 'installable': True, + 'auto_install': False, +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock_megatest/test/megatest.yml b/addons/stock_megatest/test/megatest.yml new file mode 100644 index 00000000000..4aeb0ee2c43 --- /dev/null +++ b/addons/stock_megatest/test/megatest.yml @@ -0,0 +1,286 @@ +- + I first create a warehouse with pick-pack-ship and reception in 2 steps +- + !record {model: stock.warehouse, id: mwh_pps}: + name: Mega WareHouse PickPackShip + code: mwhpps + reception_steps: 'two_steps' + delivery_steps: 'pick_pack_ship' +- + Next I create a new product in this warehouse +- + !record {model: product.product, id: mprod_mto}: + name: "My Product" + type: product + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + seller_ids: + - delay: 1 + name: base.res_partner_2 + min_qty: 2.0 + qty: 10.0 +- + I will create another product in this warehouse +- + !record {model: product.product, id: mprod_mto2}: + name: "My Product 2" + type: product + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + seller_ids: + - delay: 2 + name: base.res_partner_2 + min_qty: 2.0 + qty: 10.0 +- + I will create another product in this warehouse +- + !record {model: product.product, id: mprod_mto3}: + name: "My Product 3" + type: product + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + seller_ids: + - delay: 4 + name: base.res_partner_2 + min_qty: 2.0 + qty: 10.0 +- + I will create another product in this warehouse +- + !record {model: product.product, id: mprod_mts}: + name: "My Product MTS" + type: product + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + seller_ids: + - delay: 2 + name: base.res_partner_2 + min_qty: 2.0 + qty: 10.0 +- + Set routes on product to be MTO and Buy +- + !python {model: product.product}: | + route_warehouse0_buy = self.pool.get('stock.warehouse').browse(cr, uid, ref('stock.warehouse0')).buy_pull_id.route_id.id + route_warehouse0_mto = self.pool.get('stock.warehouse').browse(cr, uid, ref('stock.warehouse0')).mto_pull_id.route_id.id + self.write(cr, uid, [ref('mprod_mto'), ref('mprod_mto2'), ref('mprod_mto3')], { 'route_ids': [(6, 0, [route_warehouse0_mto,route_warehouse0_buy])]}, context=context) +- + Create a sales order with 36 lines. +- + !record {model: sale.order, id: sale_order_product_mto2}: + partner_id: base.res_partner_3 + note: Create Sales order + warehouse_id: mwh_pps + order_line: + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 100.00 + - product_id: mprod_mto + product_uom_qty: 67.00 + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto2 + product_uom_qty: 100.00 + - product_id: mprod_mto2 + product_uom_qty: 99.00 + - product_id: mprod_mto3 + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto2 + product_uom_qty: 100.00 + - product_id: mprod_mto2 + product_uom_qty: 67.00 + - product_id: mprod_mto3 + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mts + product_uom_qty: 100.00 + - product_id: mprod_mts + product_uom_qty: 67.00 + - product_id: mprod_mts + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 100.00 + - product_id: mprod_mto + product_uom_qty: 67.00 + - product_id: mprod_mto2 + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto3 + product_uom_qty: 100.00 + - product_id: mprod_mto + product_uom_qty: 67.00 + - product_id: mprod_mts + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto2 + product_uom_qty: 100.00 + - product_id: mprod_mto + product_uom_qty: 67.00 + - product_id: mprod_mts + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto2 + product_uom_qty: 100.00 + - product_id: mprod_mto + product_uom_qty: 67.00 + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto3 + product_uom_qty: 100.00 + - product_id: mprod_mto2 + product_uom_qty: 67.00 + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto3 + product_uom_qty: 100.00 + - product_id: mprod_mto2 + product_uom_qty: 67.00 + - product_id: mprod_mto2 + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto2 + product_uom_qty: 100.00 + - product_id: mprod_mto3 + product_uom_qty: 67.00 + - product_id: mprod_mto2 + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 100.00 + - product_id: mprod_mto2 + product_uom_qty: 67.00 +- + Confirm the sale order +- + !workflow {model: sale.order, action: order_confirm, ref: sale_order_product_mto2} +- + Check sales order is confirmed +- + !python {model: sale.order}: + print self.browse(cr, uid, ref('sale_order_product_mto2')).state +- + Create a sales order with 36 lines. +- + !record {model: sale.order, id: sale_order_product_mto3}: + partner_id: base.res_partner_3 + note: Create Sales order + warehouse_id: mwh_pps + order_line: + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping + - product_id: mprod_mto + product_uom_qty: 500.00 + route_id: stock_dropshipping.route_drop_shipping +- + Confirm the sale order +- + !workflow {model: sale.order, action: order_confirm, ref: sale_order_product_mto3} +- + Check sales order is confirmed +- + !python {model: sale.order}: + print self.browse(cr, uid, ref('sale_order_product_mto3')).state +- + Confirm all purchase orders that are in draft related +- + !python {model: purchase.order}: | + po_ids = self.search(cr, uid, [('partner_id', '=', ref('base.res_partner_2')), ('state', '=', 'draft')]) + self.signal_purchase_confirm(cr, uid, po_ids) +- + Doing all incoming shipments +- + !python {model: purchase.order.line}: | + import time + beforebefore = time.time() + print time.time() + prod_list = [ref('mprod_mto'), ref('mprod_mto2'), ref('mprod_mto3'), ref('mprod_mts')] + po_line_ids = self.search(cr, uid, [('product_id', 'in', prod_list)]) + # Search all related moves + move_obj = self.pool.get("stock.move") + related_moves = move_obj.search(cr, uid, [('purchase_line_id', 'in', po_line_ids)]) + moves = move_obj.browse(cr, uid, related_moves) + related_pickings = [x.picking_id for x in moves] + pickings = list(set(related_pickings)) + op_obj = self.pool.get('stock.pack.operation') + pack_obj = self.pool.get('stock.quant.package') + pick_obj = self.pool.get('stock.picking') + # Process those pickings and put in boxes of 20 pieces + for pick in pickings: + pick_obj.do_prepare_partial(cr, uid, [pick.id]) + #for ops in pick.pack_operation_ids: + # pick + # while ops.product_qty > 100: + # op_obj.write(cr, uid, [ops.id], {'product_qty': ops.product_qty - 100}) + # pack_id = pack_obj.create(cr, uid, {}, context=context) + # op_obj.copy(cr, uid, ops.id, {'product_qty': 100, 'result_package_id': pack_id}) + # print "copy" + # ops.refresh() + print "moves", len(pick.move_lines), "pack_ops", len(pick.pack_operation_ids), time.time() + pick.refresh() + pick_obj.do_transfer(cr, uid, [pick.id]) + print "Done transfer", time.time() + print "totaltime", time.time() - beforebefore + #Search all dests of moves + move_dest_ids = [x.move_dest_id for x in moves] + move_dest_ids = list(set(move_dest_ids)) + related_dest_pickings = [x.picking_id for x in move_dest_ids if x.picking_id] + picks = list(set(related_dest_pickings)) + print "picks", picks + for pick in picks: + pick_obj.do_prepare_partial(cr, uid, [pick.id]) + pick_obj.do_transfer(cr, uid, [pick.id]) + print "after second transfer", time.time() + move_dest_ids = [x.move_dest_id.move_dest_id for x in moves if x.move_dest_id] + move_dest_ids = list(set(move_dest_ids)) + related_dest_pickings = [x.picking_id for x in move_dest_ids if x.picking_id] + picks = list(set(related_dest_pickings)) + print "picks", picks + for pick in picks: + pick_obj.do_prepare_partial(cr, uid, [pick.id]) + pick_obj.do_transfer(cr, uid, [pick.id]) + print "after third transfer", time.time() \ No newline at end of file diff --git a/addons/stock_megatest/test/megatestmtobuy.yml b/addons/stock_megatest/test/megatestmtobuy.yml new file mode 100644 index 00000000000..e5129e22c15 --- /dev/null +++ b/addons/stock_megatest/test/megatestmtobuy.yml @@ -0,0 +1,203 @@ +- + I first create a warehouse with pick-pack-ship and reception in 2 steps +- + !record {model: stock.warehouse, id: mwh_pps}: + name: Mega WareHouse Simple + code: mwhpps +- + Next I create a new product in this warehouse +- + !record {model: product.product, id: mprod_mto}: + name: "My Product" + type: product + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + seller_ids: + - delay: 1 + name: base.res_partner_2 + min_qty: 2.0 + qty: 10.0 +- + I will create another product in this warehouse +- + !record {model: product.product, id: mprod_mto2}: + name: "My Product 2" + type: product + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + seller_ids: + - delay: 2 + name: base.res_partner_2 + min_qty: 2.0 + qty: 10.0 +- + I will create another product in this warehouse +- + !record {model: product.product, id: mprod_mto3}: + name: "My Product 3" + type: product + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + seller_ids: + - delay: 4 + name: base.res_partner_2 + min_qty: 2.0 + qty: 10.0 +- + I will create another product in this warehouse +- + !record {model: product.product, id: mprod_mts}: + name: "My Product MTS" + type: product + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + seller_ids: + - delay: 2 + name: base.res_partner_2 + min_qty: 2.0 + qty: 10.0 +- + Set routes on product to be MTO and Buy +- + !python {model: product.product}: | + route_warehouse0_buy = self.pool.get('stock.warehouse').browse(cr, uid, ref('stock.warehouse0')).buy_pull_id.route_id.id + route_warehouse0_mto = self.pool.get('stock.warehouse').browse(cr, uid, ref('stock.warehouse0')).mto_pull_id.route_id.id + self.write(cr, uid, [ref('mprod_mto'), ref('mprod_mto2'), ref('mprod_mto3')], { 'route_ids': [(6, 0, [route_warehouse0_mto,route_warehouse0_buy])]}, context=context) +- + Create a sales order with 36 lines. +- + !record {model: sale.order, id: sale_order_product_mto2}: + partner_id: base.res_partner_3 + note: Create Sales order + warehouse_id: mwh_pps + order_line: + - product_id: mprod_mto + product_uom_qty: 500.00 + - product_id: mprod_mto + product_uom_qty: 100.00 + - product_id: mprod_mto + product_uom_qty: 67.00 + - product_id: mprod_mto + product_uom_qty: 500.00 + - product_id: mprod_mto2 + product_uom_qty: 100.00 + - product_id: mprod_mto2 + product_uom_qty: 99.00 + - product_id: mprod_mto3 + product_uom_qty: 500.00 + - product_id: mprod_mto2 + product_uom_qty: 100.00 + - product_id: mprod_mto2 + product_uom_qty: 67.00 + - product_id: mprod_mto3 + product_uom_qty: 500.00 + - product_id: mprod_mts + product_uom_qty: 100.00 + - product_id: mprod_mts + product_uom_qty: 67.00 + - product_id: mprod_mts + product_uom_qty: 500.00 + - product_id: mprod_mto + product_uom_qty: 100.00 + - product_id: mprod_mto + product_uom_qty: 67.00 + - product_id: mprod_mto2 + product_uom_qty: 500.00 + - product_id: mprod_mto3 + product_uom_qty: 100.00 + - product_id: mprod_mto + product_uom_qty: 67.00 + - product_id: mprod_mts + product_uom_qty: 500.00 + - product_id: mprod_mto2 + product_uom_qty: 100.00 + - product_id: mprod_mto + product_uom_qty: 67.00 + - product_id: mprod_mts + product_uom_qty: 500.00 + - product_id: mprod_mto2 + product_uom_qty: 100.00 + - product_id: mprod_mto + product_uom_qty: 67.00 + - product_id: mprod_mto + product_uom_qty: 500.00 + - product_id: mprod_mto3 + product_uom_qty: 100.00 + - product_id: mprod_mto2 + product_uom_qty: 67.00 + - product_id: mprod_mto + product_uom_qty: 500.00 + - product_id: mprod_mto3 + product_uom_qty: 100.00 + - product_id: mprod_mto2 + product_uom_qty: 67.00 + - product_id: mprod_mto2 + product_uom_qty: 500.00 + - product_id: mprod_mto2 + product_uom_qty: 100.00 + - product_id: mprod_mto3 + product_uom_qty: 67.00 + - product_id: mprod_mto2 + product_uom_qty: 500.00 + - product_id: mprod_mto + product_uom_qty: 100.00 + - product_id: mprod_mto2 + product_uom_qty: 67.00 +- + Confirm the sale order +- + !workflow {model: sale.order, action: order_confirm, ref: sale_order_product_mto2} +- + Check sales order is confirmed +- + !python {model: sale.order}: + print self.browse(cr, uid, ref('sale_order_product_mto2')).state +- + Confirm all purchase orders that are in draft related +- + !python {model: purchase.order}: | + po_ids = self.search(cr, uid, [('partner_id', '=', ref('base.res_partner_2')), ('state', '=', 'draft')]) + self.signal_purchase_confirm(cr, uid, po_ids) +- + Doing all incoming shipments +- + !python {model: purchase.order.line}: | + import time + beforebefore = time.time() + print time.time() + prod_list = [ref('mprod_mto'), ref('mprod_mto2'), ref('mprod_mto3'), ref('mprod_mts')] + po_line_ids = self.search(cr, uid, [('product_id', 'in', prod_list)]) + # Search all related moves + move_obj = self.pool.get("stock.move") + related_moves = move_obj.search(cr, uid, [('purchase_line_id', 'in', po_line_ids)]) + moves = move_obj.browse(cr, uid, related_moves) + related_pickings = [x.picking_id for x in moves] + pickings = list(set(related_pickings)) + op_obj = self.pool.get('stock.pack.operation') + pack_obj = self.pool.get('stock.quant.package') + pick_obj = self.pool.get('stock.picking') + # Process those pickings and put in boxes of 20 pieces + for pick in pickings: + #pick_obj.do_prepare_partial(cr, uid, [pick.id]) + #for ops in pick.pack_operation_ids: + # pick + # while ops.product_qty > 100: + # op_obj.write(cr, uid, [ops.id], {'product_qty': ops.product_qty - 100}) + # pack_id = pack_obj.create(cr, uid, {}, context=context) + # op_obj.copy(cr, uid, ops.id, {'product_qty': 100, 'result_package_id': pack_id}) + # print "copy" + # ops.refresh() + print "moves", len(pick.move_lines), "pack_ops", len(pick.pack_operation_ids), time.time() + pick.refresh() + pick_obj.do_transfer(cr, uid, [pick.id]) + print "Done transfer", time.time() + print "totaltime", time.time() - beforebefore + #Search all dests of moves + move_dest_ids = [x.move_dest_id for x in moves] + move_dest_ids = list(set(move_dest_ids)) + related_dest_pickings = [x.picking_id for x in move_dest_ids if x.picking_id] + picks = list(set(related_dest_pickings)) + for pick in picks: + #pick_obj.do_prepare_partial(cr, uid, [pick.id]) + pick_obj.do_transfer(cr, uid, [pick.id]) + print "after second transfer", time.time() \ No newline at end of file From 1bc89c97383b572ee3c199dbc1524ab7f27c7010 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 24 Feb 2014 10:48:29 +0100 Subject: [PATCH 022/520] [FIX] unbreak website_blog.css bzr revid: xmo@openerp.com-20140224094829-99ayhh5shzwu6n44 --- addons/website_blog/static/src/css/website_blog.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/website_blog/static/src/css/website_blog.css b/addons/website_blog/static/src/css/website_blog.css index 96045adb914..f7355ce783d 100644 --- a/addons/website_blog/static/src/css/website_blog.css +++ b/addons/website_blog/static/src/css/website_blog.css @@ -1,5 +1,4 @@ -@charset "utf-8"; -@import url(compass/css3.css); +@charset "UTF-8"; .css_website_mail .has-error { border-color: red; } From d1bbeb21a1dfb6001f68a7859801f3d961019c2a Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Mon, 24 Feb 2014 11:43:01 +0100 Subject: [PATCH 023/520] [FIX] website's translation problems on multiple view inheritance bzr revid: fme@openerp.com-20140224104301-n8dqg0lx4oijf66b --- openerp/addons/base/ir/ir_ui_view.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py index aafce29b7eb..d4655bf8b9c 100644 --- a/openerp/addons/base/ir/ir_ui_view.py +++ b/openerp/addons/base/ir/ir_ui_view.py @@ -316,14 +316,14 @@ class view(osv.osv): return node return None - def inherit_branding(self, specs_tree, view_id, source_id): + def inherit_branding(self, specs_tree, view_id, root_id): for node in specs_tree.iterchildren(tag=etree.Element): xpath = node.getroottree().getpath(node) if node.tag == 'data' or node.tag == 'xpath': - self.inherit_branding(node, view_id, source_id) + self.inherit_branding(node, view_id, root_id) else: node.set('data-oe-id', str(view_id)) - node.set('data-oe-source-id', str(source_id)) + node.set('data-oe-source-id', str(root_id)) node.set('data-oe-xpath', xpath) node.set('data-oe-model', 'ir.ui.view') node.set('data-oe-field', 'arch') @@ -397,7 +397,7 @@ class view(osv.osv): return source - def apply_view_inheritance(self, cr, uid, source, source_id, model, context=None): + def apply_view_inheritance(self, cr, uid, source, source_id, model, root_id=None, context=None): """ Apply all the (directly and indirectly) inheriting views. :param source: a parent architecture to modify (with parent modifications already applied) @@ -408,13 +408,15 @@ class view(osv.osv): :return: a modified source where all the modifying architecture are applied """ if context is None: context = {} + if root_id is None: + root_id = source_id sql_inherit = self.pool.get('ir.ui.view').get_inheriting_views_arch(cr, uid, source_id, model, context=context) for (specs, view_id) in sql_inherit: specs_tree = etree.fromstring(specs.encode('utf-8')) if context.get('inherit_branding'): - self.inherit_branding(specs_tree, view_id, source_id) + self.inherit_branding(specs_tree, view_id, root_id) source = self.apply_inheritance_specs(cr, uid, source, specs_tree, view_id, context=context) - source = self.apply_view_inheritance(cr, uid, source, view_id, model, context=context) + source = self.apply_view_inheritance(cr, uid, source, view_id, model, root_id=root_id, context=context) return source def read_combined(self, cr, uid, view_id, fields=None, context=None): From ad86d1afdc168562637dfa19e309745f8b5b0dc2 Mon Sep 17 00:00:00 2001 From: Kersten Jeremy Date: Mon, 24 Feb 2014 11:47:45 +0100 Subject: [PATCH 024/520] [FIX] Remove unused field from crm for calendar. By consequence, bug with email attendee is removed. bzr revid: jke@openerp.com-20140224104745-egf6hutwzk9gcweh --- addons/crm/calendar_event.py | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/addons/crm/calendar_event.py b/addons/crm/calendar_event.py index df50d99b72b..4f57e364282 100644 --- a/addons/crm/calendar_event.py +++ b/addons/crm/calendar_event.py @@ -48,31 +48,11 @@ class calendar_attendee(osv.osv): _inherit = 'calendar.attendee' _description = 'Calendar Attendee' - def _compute_data(self, cr, uid, ids, name, arg, context=None): - """ - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of compute data’s IDs - @param context: A standard dictionary for contextual values - """ - name = name[0] - result = super(calendar_attendee, self)._compute_data(cr, uid, ids, name, arg, context=context) - - for attdata in self.browse(cr, uid, ids, context=context): - id = attdata.id - result[id] = {} - if name == 'categ_id': - if attdata.ref and 'categ_id' in attdata.ref._columns: - result[id][name] = (attdata.ref.categ_id.id, attdata.ref.categ_id.name,) - else: - result[id][name] = False - return result + def _noop(self, cr, uid, ids, name, arg, context=None): + return dict.fromkeys(ids,False) _columns = { - 'categ_id': fields.function(_compute_data, \ - string='Event Type', type="many2one", \ - relation="crm.case.categ", multi='categ_id'), + 'categ_id': fields.function(_noop, string='Event Type', deprecated="Unused Field - TODO : Remove it in trunk", type="many2one", relation="crm.case.categ"), } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From bfaa40266d9ea0dec15daf1c383bc4ade262bd28 Mon Sep 17 00:00:00 2001 From: Kersten Jeremy Date: Mon, 24 Feb 2014 11:48:50 +0100 Subject: [PATCH 025/520] [IMP] Calendar : Check that user are not disconnected before to check next_potential_notif, that avoid to see an error in console every 5 minutes bzr revid: jke@openerp.com-20140224104850-wty7kk230s72tdcy --- addons/calendar/calendar.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py index ca4d00eb85e..96c1e2b68e9 100644 --- a/addons/calendar/calendar.py +++ b/addons/calendar/calendar.py @@ -483,8 +483,12 @@ class calendar_alarm_manager(osv.AbstractModel): def get_next_notif(self,cr,uid,context=None): ajax_check_every_seconds = 300 - partner = self.pool.get('res.users').browse(cr,uid,uid,context=context).partner_id; + partner = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id all_notif = [] + + if not partner or not partner.id: # If user is disconnected + return [] + all_events = self.get_next_potential_limit_alarm(cr,uid,ajax_check_every_seconds,partner_id=partner.id,mail=False,context=context) for event in all_events: # .values() From 4b57fc646541c253c4fd2cd200b367836c191966 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Mon, 24 Feb 2014 12:04:13 +0100 Subject: [PATCH 026/520] [IMP] Check if procurements are done before calling get shipped bzr revid: jco@openerp.com-20140224110413-o4wvbnch90yxxt2i --- addons/sale_stock/sale_stock.py | 3 +-- addons/stock/stock.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index ed89fa49a92..dbe64465047 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -67,7 +67,7 @@ class sale_order(osv.osv): def _get_orders_procurements(self, cr, uid, ids, context=None): res = set() for proc in self.pool.get('procurement.order').browse(cr, uid, ids, context=context): - if proc.sale_line_id: + if proc.state =='done' and proc.sale_line_id: res.add(proc.sale_line_id.order_id.id) return list(res) @@ -102,7 +102,6 @@ class sale_order(osv.osv): ], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="""On demand: A draft invoice can be created from the sales order when needed. \nOn delivery order: A draft invoice can be created from the delivery order when the products have been delivered. \nBefore delivery: A draft invoice is created from the sales order and must be paid before the products can be delivered."""), 'shipped': fields.function(_get_shipped, string='Delivered', type='boolean', store={ - 'stock.move': (_get_orders, ['state'], 10), 'procurement.order': (_get_orders_procurements, ['state'], 10) }), 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True), diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 12f3217f6e2..46b4776492b 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -32,7 +32,6 @@ import openerp.addons.decimal_precision as dp import logging _logger = logging.getLogger(__name__) - #---------------------------------------------------------- # Incoterms #---------------------------------------------------------- @@ -728,7 +727,7 @@ class stock_picking(osv.osv): 'note': fields.text('Notes', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}), 'move_type': fields.selection([('direct', 'Partial'), ('one', 'All at once')], 'Delivery Method', required=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="It specifies goods to be deliver partially or all at once"), 'state': fields.function(_state_get, type="selection", store={ - 'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_type', 'move_lines'], 20), + 'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_type'], 20), 'stock.move': (_get_pickings, ['state', 'picking_id'], 20), 'stock.quant': (_get_pickings_from_quant, ['reservation_id'], 20)}, selection=[ ('draft', 'Draft'), From f94f50c7e86a444508b096c8e9693c8d22a7abb4 Mon Sep 17 00:00:00 2001 From: Gery Debongnie Date: Mon, 24 Feb 2014 12:05:54 +0100 Subject: [PATCH 027/520] [FIX] fixes a crash caused by incorrect use of date/datetime fields of the form 'date:interval': the list view in some case tried to format the strings as a date, and it could not parse it. lp bug: https://launchpad.net/bugs/1279382 fixed bzr revid: ged@openerp.com-20140224110554-y007810prx7qg5yi --- addons/web/static/src/js/data.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index cc595c30688..24a4dc3dd2a 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -221,11 +221,12 @@ instance.web.QueryGroup = instance.web.Class.extend({ {__context: {group_by: []}, __domain: []}, read_group_group); + var raw_field = grouping_field && grouping_field.split(':')[0]; var aggregates = {}; _(fixed_group).each(function (value, key) { if (key.indexOf('__') === 0 - || key === grouping_field - || key === grouping_field + '_count') { + || key === raw_field + || key === raw_field + '_count') { return; } aggregates[key] = value || 0; @@ -234,7 +235,6 @@ instance.web.QueryGroup = instance.web.Class.extend({ this.model = new instance.web.Model( model, fixed_group.__context, fixed_group.__domain); - var raw_field = grouping_field && grouping_field.split(':')[0]; var group_size = fixed_group[raw_field + '_count'] || fixed_group.__count || 0; var leaf_group = fixed_group.__context.group_by.length === 0; From 18b02ee71e6a3067b701525aea006eea6ea70672 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Mon, 24 Feb 2014 12:24:37 +0100 Subject: [PATCH 028/520] [IMP] Change store = True of product_qty of move bzr revid: jco@openerp.com-20140224112437-if4nkqaj9balf580 --- addons/stock/stock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 46b4776492b..57fc427dc3e 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1368,7 +1368,7 @@ class stock_move(osv.osv): 'date_expected': fields.datetime('Expected Date', states={'done': [('readonly', True)]}, required=True, select=True, help="Scheduled date for the processing of this move"), 'product_id': fields.many2one('product.product', 'Product', required=True, select=True, domain=[('type', '<>', 'service')], states={'done': [('readonly', True)]}), # TODO: improve store to add dependency on product UoM - 'product_qty': fields.function(_quantity_normalize, type='float', store=True, string='Quantity', + 'product_qty': fields.function(_quantity_normalize, type='float', store={'stock.move': (lambda self, cr, uid, ids, ctx: ids, ['product_uom_qty', 'product_uom'], 20)}, string='Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), help='Quantity in the default UoM of the product'), 'product_uom_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), From bff00ec6df967f95b41493afb5c81aef1944e758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 24 Feb 2014 13:10:04 +0100 Subject: [PATCH 029/520] [FIX] res_partner: avoid duplicating users when duplicating partners, because I think your database wil hate you. bzr revid: tde@openerp.com-20140224121004-lgy2rsszsomy7mji --- openerp/addons/base/res/res_partner.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openerp/addons/base/res/res_partner.py b/openerp/addons/base/res/res_partner.py index e9573088680..46f0218e21f 100644 --- a/openerp/addons/base/res/res_partner.py +++ b/openerp/addons/base/res/res_partner.py @@ -350,6 +350,7 @@ class res_partner(osv.osv, format_address): def copy(self, cr, uid, id, default=None, context=None): if default is None: default = {} + default['user_ids'] = False name = self.read(cr, uid, [id], ['name'], context)[0]['name'] default.update({'name': _('%s (copy)') % name}) return super(res_partner, self).copy(cr, uid, id, default, context) From ed978bb90547aadb74efa53870ec05fb7f43b34e Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Mon, 24 Feb 2014 14:41:26 +0100 Subject: [PATCH 030/520] [IMP] Only check pickings of purchases when state goes to done and optimize query bzr revid: jco@openerp.com-20140224134126-jmk38b3gxtvbh1ru --- addons/purchase/purchase.py | 20 ++++++++++++++------ addons/purchase/stock.py | 2 +- addons/stock/stock.py | 3 ++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 0626f785241..56eb12147c7 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -31,6 +31,7 @@ import openerp.addons.decimal_precision as dp from openerp.osv.orm import browse_record, browse_null from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, DATETIME_FORMATS_MAP + class purchase_order(osv.osv): def _amount_all(self, cr, uid, ids, field_name, arg, context=None): @@ -155,12 +156,19 @@ class purchase_order(osv.osv): def _get_picking_ids(self, cr, uid, ids, field_names, args, context=None): res = {} - for purchase_id in ids: - picking_ids = set() - move_ids = self.pool.get('stock.move').search(cr, uid, [('purchase_line_id.order_id', '=', purchase_id)], context=context) - for move in self.pool.get('stock.move').browse(cr, uid, move_ids, context=context): - picking_ids.add(move.picking_id.id) - res[purchase_id] = list(picking_ids) + query = """ + SELECT picking_id, po.id FROM stock_picking p, stock_move m, purchase_order_line pol, purchase_order po + WHERE po.id in %s and po.id = pol.order_id and pol.id = m.purchase_line_id and m.picking_id = p.id + GROUP BY picking_id, po.id + + """ + cr.execute(query, (tuple(ids), )) + picks = cr.fetchall() + for pick in picks: + if not res.get(pick[1]): + res[pick[1]] = [pick[0]] + else: + res[pick[1]].append(pick[0]) return res STATE_SELECTION = [ diff --git a/addons/purchase/stock.py b/addons/purchase/stock.py index 5989584793d..dde18dca2c3 100644 --- a/addons/purchase/stock.py +++ b/addons/purchase/stock.py @@ -35,7 +35,7 @@ class stock_move(osv.osv): ids = [ids] res = super(stock_move, self).write(cr, uid, ids, vals, context=context) from openerp import workflow - if 'state' in vals: + if 'state' in vals and vals['state'] in ['done', 'cancel']: for move in self.browse(cr, uid, ids, context=context): if move.purchase_line_id and move.purchase_line_id.order_id: order_id = move.purchase_line_id.order_id.id diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 57fc427dc3e..041417797be 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1488,11 +1488,12 @@ class stock_move(osv.osv): if move.product_id.uom_id.category_id.id != move.product_uom.category_id.id: return False return True - + _constraints = [ (_check_uom, 'You try to move a product using a UoM that is not compatible with the UoM of the product moved. Please use an UoM in the same UoM category.', ['product_uom'])] + def copy_data(self, cr, uid, id, default=None, context=None): if default is None: From 60c75d38bfc948ed723cde3a00b1c3871ff89148 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Mon, 24 Feb 2014 15:27:51 +0100 Subject: [PATCH 031/520] [FIX] website placeholder; website_hr_recruitment required for ie bzr revid: chm@openerp.com-20140224142751-7bolxomy2nc717w0 --- addons/website/static/src/js/website.js | 3 +- .../controllers/main.py | 31 ++++++++++++++----- .../views/templates.xml | 15 ++++----- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js index 3be70b9b404..54ee4689814 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -69,7 +69,8 @@ website.is_editable = website.is_editable || $('html').data('editable'); website.is_editable_button= website.is_editable_button || $('html').data('editable'); dom_ready.resolve(); - $('input, textarea').placeholder(); + // fix for ie + if($.fn.placeholder) $('input, textarea').placeholder(); }); website.init_kanban = function ($kanban) { diff --git a/addons/website_hr_recruitment/controllers/main.py b/addons/website_hr_recruitment/controllers/main.py index c50bc6a0428..a3cbbe3cbc4 100644 --- a/addons/website_hr_recruitment/controllers/main.py +++ b/addons/website_hr_recruitment/controllers/main.py @@ -7,7 +7,6 @@ from openerp.tools.translate import _ from openerp.addons.web.http import request class website_hr_recruitment(http.Controller): - @http.route([ '/jobs', '/jobs/department/', @@ -61,27 +60,45 @@ class website_hr_recruitment(http.Controller): @http.route(['/jobs/apply/'], type='http', auth="public", website=True, multilang=True) def jobs_apply(self, job): - return request.website.render("website_hr_recruitment.apply", { 'job': job }) + error = {} + default = {} + if 'website_hr_recruitment_error' in request.session: + error = request.session.pop('website_hr_recruitment_error') + default = request.session.pop('website_hr_recruitment_default') + return request.website.render("website_hr_recruitment.apply", { 'job': job, 'error': error, 'default': default}) @http.route(['/jobs/thankyou'], methods=['POST'], type='http', auth="public", website=True, multilang=True) def jobs_thankyou(self, **post): cr, uid, context = request.cr, request.uid, request.context imd = request.registry['ir.model.data'] + + error = {} + for field_name in ["partner_name", "phone", "email_from"]: + if not post.get(field_name): + error[field_name] = 'missing' + if error: + request.session['website_hr_recruitment_error'] = error + ufile = post.pop('ufile') + if ufile: + error['ufile'] = 'reset' + request.session['website_hr_recruitment_default'] = post + return request.redirect('/jobs/apply/%s' % post.get("job_id")) + value = { - 'name': _('Online Form'), - 'user_id': False, 'source_id' : imd.xmlid_to_res_id(cr, SUPERUSER_ID, 'hr_recruitment.source_website_company'), } - for f in ['phone', 'email_from', 'partner_name', 'description', 'department_id', 'job_id']: + for f in ['phone', 'email_from', 'partner_name', 'description']: value[f] = post.get(f) + for f in ['department_id', 'job_id']: + value[f] = int(post.get(f) or 0) - job_id = request.registry['hr.applicant'].create(cr, SUPERUSER_ID, value, context=context) + applicant_id = request.registry['hr.applicant'].create(cr, SUPERUSER_ID, value, context=context) if post['ufile']: attachment_value = { 'name': post['ufile'].filename, 'res_name': value['partner_name'], 'res_model': 'hr.applicant', - 'res_id': job_id, + 'res_id': applicant_id, 'datas': base64.encodestring(post['ufile'].read()), 'datas_fname': post['ufile'].filename, } diff --git a/addons/website_hr_recruitment/views/templates.xml b/addons/website_hr_recruitment/views/templates.xml index 3e057d249f2..9dab6c6accc 100644 --- a/addons/website_hr_recruitment/views/templates.xml +++ b/addons/website_hr_recruitment/views/templates.xml @@ -161,34 +161,35 @@
-
+
- +
-
+
- +
-
+
- +
-
'); + else if (popupName === "pastetext") { + $popup.html('Paste your content here and click submit.

'); popupTypeClass = PROMPT_CLASS; } @@ -720,12 +734,12 @@ } // Execute the command and check for error - var success = true, description; - if (ie && command.toLowerCase() == "inserthtml") + var success = true, message; + if (ie && command.toLowerCase() === "inserthtml") getRange(editor).pasteHTML(value); else { try { success = editor.doc.execCommand(command, 0, value || null); } - catch (err) { description = err.description; success = false; } + catch (err) { message = err.message; success = false; } if (!success) { if ("cutcopypaste".indexOf(command) > -1) showMessage(editor, "For security reasons, your browser does not support the " + @@ -733,13 +747,14 @@ button); else showMessage(editor, - (description ? description : "Error executing the " + command + " command."), + (message ? message : "Error executing the " + command + " command."), button); } } - // Enable the buttons + // Enable the buttons and update the textarea refreshButtons(editor); + updateTextArea(editor, true); return success; } @@ -765,19 +780,26 @@ return editor.$frame[0].contentWindow.getSelection(); } - // Returns the hex value for the passed in string. - // hex("rgb(255, 0, 0)"); // #FF0000 - // hex("#FF0000"); // #FF0000 - // hex("#F00"); // #FF0000 + // hex - returns the hex value for the passed in color string function hex(s) { - var m = /rgba?\((\d+), (\d+), (\d+)/.exec(s), - c = s.split(""); + + // hex("rgb(255, 0, 0)") returns #FF0000 + var m = /rgba?\((\d+), (\d+), (\d+)/.exec(s); if (m) { - s = ( m[1] << 16 | m[2] << 8 | m[3] ).toString(16); + s = (m[1] << 16 | m[2] << 8 | m[3]).toString(16); while (s.length < 6) s = "0" + s; + return "#" + s; } - return "#" + (s.length == 6 ? s : c[1] + c[1] + c[2] + c[2] + c[3] + c[3]); + + // hex("#F00") returns #FF0000 + var c = s.split(""); + if (s.length === 4) + return "#" + c[1] + c[1] + c[2] + c[2] + c[3] + c[3]; + + // hex("#FF0000") returns #FF0000 + return s; + } // hidePopups - hides all popups @@ -792,9 +814,8 @@ // imagesPath - returns the path to the images folder function imagesPath() { - var cssFile = "jquery.cleditor.css", - href = $("link[href$='" + cssFile +"']").attr("href"); - return href.substr(0, href.length - cssFile.length) + "images/"; + var href = $("link[href*=cleditor]").attr("href"); + return href.replace(/^(.*\/)[^\/]+$/, '$1') + "images/"; } // imageUrl - Returns the css url string for a filemane @@ -813,7 +834,7 @@ editor.$frame.remove(); // Create a new iframe - var $frame = editor.$frame = $(' diff --git a/addons/website_customer/controllers/main.py b/addons/website_customer/controllers/main.py index 173b0871dbe..5f0825e132c 100644 --- a/addons/website_customer/controllers/main.py +++ b/addons/website_customer/controllers/main.py @@ -12,12 +12,12 @@ class WebsiteCustomer(http.Controller): _references_per_page = 20 @http.route([ - '/customers/', - '/customers/page//', + '/customers', + '/customers/page/', '/customers/country/', '/customers/country/-', - '/customers/country//page//', - '/customers/country/-/page//', + '/customers/country//page/', + '/customers/country/-/page/', ], type='http', auth="public", website=True, multilang=True) def customers(self, country_id=0, page=0, **post): cr, uid, context = request.cr, request.uid, request.context @@ -62,7 +62,7 @@ class WebsiteCustomer(http.Controller): # pager pager = request.website.pager( - url="/customers/", total=len(partner_ids), page=page, step=self._references_per_page, + url="/customers", total=len(partner_ids), page=page, step=self._references_per_page, scope=7, url_args=post ) @@ -83,7 +83,7 @@ class WebsiteCustomer(http.Controller): } return request.website.render("website_customer.index", values) - @http.route(['/customers//', '/customers/-/'], type='http', auth="public", website=True, multilang=True) + @http.route(['/customers/', '/customers/-'], type='http', auth="public", website=True, multilang=True) def customer(self, partner_id, **post): partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context) values = website_partner.get_partner_template_value(partner) diff --git a/addons/website_customer/views/website_customer.xml b/addons/website_customer/views/website_customer.xml index ad924cba55d..ba89f456e23 100644 --- a/addons/website_customer/views/website_customer.xml +++ b/addons/website_customer/views/website_customer.xml @@ -5,7 +5,7 @@ @@ -44,11 +44,11 @@ @@ -158,11 +158,11 @@

References

- +
- +
diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py index eb38e2dbe55..c410536a0b5 100644 --- a/addons/website_event/controllers/main.py +++ b/addons/website_event/controllers/main.py @@ -33,7 +33,7 @@ from openerp import tools import werkzeug.urls class website_event(http.Controller): - @http.route(['/event/', '/event/page/'], type='http', auth="public", website=True, multilang=True) + @http.route(['/event', '/event/page/'], type='http', auth="public", website=True, multilang=True) def events(self, page=1, **searches): cr, uid, context = request.cr, request.uid, request.context event_obj = request.registry['event.event'] @@ -134,7 +134,7 @@ class website_event(http.Controller): event_count = event_obj.search( request.cr, request.uid, dom_without("none"), count=True, context=request.context) - pager = request.website.pager(url="/event/", total=event_count, page=page, step=step, scope=5) + pager = request.website.pager(url="/event", total=event_count, page=page, step=step, scope=5) order = 'website_published desc, date_begin' if searches.get('date','all') == 'old': diff --git a/addons/website_event/views/website_event.xml b/addons/website_event/views/website_event.xml index e490b1a0687..1096287bb3b 100644 --- a/addons/website_event/views/website_event.xml +++ b/addons/website_event/views/website_event.xml @@ -50,7 +50,7 @@
  • - + not published

    @@ -243,7 +243,7 @@

    Event not found!

    Sorry, the requested event is not available anymore.

    -

    Return to the event list.

    +

    Return to the event list.

    diff --git a/addons/website_event_sale/controllers/main.py b/addons/website_event_sale/controllers/main.py index cc9ddc0ad56..2db874cf5b7 100644 --- a/addons/website_event_sale/controllers/main.py +++ b/addons/website_event_sale/controllers/main.py @@ -78,7 +78,7 @@ class website_event(website_event): order_obj.write(request.cr, SUPERUSER_ID, [order.id], {'order_line': [(4, order_line_id)]}, context=request.context) if not _values: - return request.redirect("/event/%s/" % event_id) + return request.redirect("/event/%s" % event_id) return request.redirect("/shop/checkout") def _add_event(self, event_name="New Event", context={}, **kwargs): diff --git a/addons/website_event_sale/static/src/js/website.tour.event_sale.js b/addons/website_event_sale/static/src/js/website.tour.event_sale.js index 3c74cc2317e..c67c412d7d2 100644 --- a/addons/website_event_sale/static/src/js/website.tour.event_sale.js +++ b/addons/website_event_sale/static/src/js/website.tour.event_sale.js @@ -44,7 +44,7 @@ { title: "Complete checkout", waitFor: '#top_menu .my_cart_quantity:contains(5)', - element: 'form[action="/shop/confirm_order/"] .btn:contains("Confirm")', + element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")', onload: function (tour) { if ($("input[name='name']").val() === "") $("input[name='name']").val("website_sale-test-shoptest"); diff --git a/addons/website_event_track/controllers/event.py b/addons/website_event_track/controllers/event.py index d4b7177814d..466e3d37a40 100644 --- a/addons/website_event_track/controllers/event.py +++ b/addons/website_event_track/controllers/event.py @@ -38,7 +38,7 @@ class website_event(http.Controller): return request.website.render("website_event_track.track_view", values) # TODO: not implemented - @http.route(['/event//agenda/'], type='http', auth="public", website=True, multilang=True) + @http.route(['/event//agenda'], type='http', auth="public", website=True, multilang=True) def event_agenda(self, event, tag=None, **post): values = { 'event': event, @@ -47,7 +47,7 @@ class website_event(http.Controller): return request.website.render("website_event_track.agenda", values) @http.route([ - '/event//track/', + '/event//track', '/event//track/tag/' ], type='http', auth="public", website=True, multilang=True) def event_tracks(self, event, tag=None, **post): @@ -74,7 +74,7 @@ class website_event(http.Controller): } return request.website.render("website_event_track.tracks", values) - @http.route(['/event//track_proposal/'], type='http', auth="public", website=True, multilang=True) + @http.route(['/event//track_proposal'], type='http', auth="public", website=True, multilang=True) def event_track_proposal(self, event, **post): values = { 'event': event } return request.website.render("website_event_track.event_track_proposal", values) diff --git a/addons/website_event_track/models/event.py b/addons/website_event_track/models/event.py index cfba63cfc6b..1c098232a2e 100644 --- a/addons/website_event_track/models/event.py +++ b/addons/website_event_track/models/event.py @@ -142,12 +142,12 @@ class event_event(osv.osv): context = context or {} result = super(event_event, self)._get_new_menu_pages(cr, uid, event, context=context) if event.show_tracks: - result.append( (_('Talks'), '/event/%s/track/' % slug(event))) - result.append( (_('Agenda'), '/event/%s/agenda/' % slug(event))) + result.append( (_('Talks'), '/event/%s/track' % slug(event))) + result.append( (_('Agenda'), '/event/%s/agenda' % slug(event))) if event.blog_id: - result.append( (_('News'), '/blogpost/'+slug(event.blog_ig))) + result.append( (_('News'), '/blogpost'+slug(event.blog_ig))) if event.show_track_proposal: - result.append( (_('Talk Proposals'), '/event/%s/track_proposal/' % slug(event))) + result.append( (_('Talk Proposals'), '/event/%s/track_proposal' % slug(event))) return result # diff --git a/addons/website_event_track/views/website_event.xml b/addons/website_event_track/views/website_event.xml index 03f6f750b6f..3eb2a4bbbde 100644 --- a/addons/website_event_track/views/website_event.xml +++ b/addons/website_event_track/views/website_event.xml @@ -174,7 +174,7 @@