From 1f8c8d6dd52ebb8baa3da98ad7165068accc2ec8 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Fri, 27 Feb 2015 18:00:39 +0100 Subject: [PATCH 01/27] [IMP] doc: install: advised wkhtmltopdf version Also redirect to our builds of wkhtmltopdf 0.12.1 for debian jessie, as the wkhtmltopdf team does not provide one. --- doc/setup/install.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/setup/install.rst b/doc/setup/install.rst index 4221eb15225..701dec1a137 100644 --- a/doc/setup/install.rst +++ b/doc/setup/install.rst @@ -124,8 +124,10 @@ daemon and automatically start it. .. danger:: to print PDF reports, you must install wkhtmltopdf_ yourself: the version of wkhtmltopdf_ available in debian repositories does not support headers and footers so it can not be installed - automatically. Use the version available on - `the wkhtmltopdf download page`_. + automatically. The recommended version is 0.12.1 and is available on + `the wkhtmltopdf download page`_, in the archive section. As there + is no official release for Debian Jessie, you can find ours on + http://nightly.odoo.com/extra/. Configuration ''''''''''''' From e5a94f74aa05bbad04bf930d9c3740f318ae7cec Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sat, 28 Feb 2015 17:58:22 +0100 Subject: [PATCH 02/27] [CLA] mailmap update --- .mailmap | 173 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 90 insertions(+), 83 deletions(-) diff --git a/.mailmap b/.mailmap index de72af905da..9e806a9ffa2 100644 --- a/.mailmap +++ b/.mailmap @@ -4,6 +4,8 @@ Ajay Javiya Ajay Javiya Akash Balar Akash Balar +Akta Dakwala +Akta Dakwala EDA(Axelor) <> Albert Cervera i Areny Albert Cervera i Areny Albert Cervera i Areny - http://www.NaN-tic.com <> Albert Cervera i Areny Albert Cervera i Areny <> @@ -20,6 +22,7 @@ Alexis de Lattre Alexis de Lattre Alexis de Lattre Alexis de Lattre <> Alexis de Lattre alexis.delattre@akretion.com <> +Aliane Abdelouahab Aline Preillon Aline Preillon Amit Bhavsar @@ -103,6 +106,9 @@ Atul Patel ATP(OpenERP) <> Atul Patel ATUL (OpenERP) <> Atul Patel atp (OpenERP) <> Augustin Cisterne-Kaas Augustin Cisterne-Kaas +Avdhesh Patel +Avdhesh Patel AVP(Axelor) <> +Avdhesh Patel avp Baskhuu Jacara Jacara <> Baskhuu Jacara jacara <> Benoit Guillot @@ -113,6 +119,7 @@ Bharat Devnani Bharat Devnani Bharat Devnani (Open ERP) <> Bharat Devnani Bharat Devnani(OpenERP) <> Bharat Devnani bde@tinyerp.com <> +Bhatt Chintal bch <> Bhavik Bagdiya Bhavik Bagdiya Bhumi Patel @@ -217,6 +224,8 @@ Dhara Shah Dhara (OpenERP) <> Dhara Shah Dhara Dhara Shah dsh (Open ERP) <> Dhara Shah dsh(Open ERP) <> +Dharmesh Rathod +Dharmesh Rathod DRA Axelor <> Dharmraj Jhala Dharmraj Jhala Dharmraj Jhala @@ -320,6 +329,7 @@ Guewen Baconnier Guewen Baconnier <> Guillem Barba Guillem Barba <> Guillem Barba guillem <> Guillermo Bisheimer +Gulamfarid Ghanchi Géry Debongnie Géry Debongnie Géry Debongnie @@ -330,6 +340,7 @@ Hardik Ansodariya Hardik Ansodariya (OpenERP) <> Hardik Ansodariya Hardik Ansodariya <> Hardik R. Shamnani Hardik R. Shamnani hsa-tinyerp <> +Hardik Sanchawat Hardik Sanchawat (OpenERP) <> Harshad Modi Harshad Modi HMO (Open ERP) <> Harshad Modi HMO(OpenERP) <> @@ -345,6 +356,7 @@ Herbert Petersen Conexus.at <> Hiral Patel Hitesh Trivedi Hugo Santos hugo <> +Hugo Santos hugosdsantos Humberto Arocha hbto(humbertoarocha) <> Husen Daudi Husen Daudi @@ -352,6 +364,7 @@ Husen Daudi Husen Daudi Husen Daudi Husen Daudi JVO,HDA <> +Husen Daudi ashish Husen Daudi hda(Open ERP) <> Husen Daudi hda@tinyerp.com <> Husen Daudi (Serpent CS) @@ -412,6 +425,10 @@ Jean-Baptiste Aubort Jean-Christophe Vassort Jean-Christophe Vassort Jean-Sebastien Suzanne Jean-Sebastien SUZANNE <> +Jenil Malavia +Jenil Malavia JMA(OpenEPR) <> +Jenil Malavia JMA(OpenERP) <> +Jenil Malavia Jenil(OpenERP) <> Jeremy Kersten Jeremy Kersten Jeremy Kersten jke-openerp <> @@ -422,6 +439,7 @@ Jigar Amin Jigar Amin Jigar Amin JAM (OpenERP) <> Jigar Amin JAM(OpenERP) <> +Jigar Amin amami <> Jigar Amin jam <> Jignesh Rathod Jignesh Rathod @@ -461,6 +479,9 @@ Kuldeep Joshi Kuldeep Joshi Kuldeep Joshi kjo <> Kunal Chavda +Kunal Mathur +Kunal Mathur +Kunal Mathur KUM(OpenERP) <> Laetitia Gangloff Launchpad Launchpad Launchpad Translations on behalf of openerp <> @@ -497,6 +518,8 @@ Mantavya Gajjar Mantavya Gajjar MGA(OpenERP) <> Mantavya Gajjar Matvaya Gajjar(Openerp) <> Mantavya Gajjar mga@tinyerp.com <> +Manuel Abadia Manu <> +Manuel Abadia Manu, Jigar Amin - OpenERP <> Marcelo Hamra Marcelo Hamra <> Marcelo Hamra marcelo Mario Arias Badila @@ -579,6 +602,7 @@ Nicolas Bessi Nicolas Bessi Nicolas <> Nicolas Bessi Nicolas Bessi(C2C) <> Nicolas Bessi nicolas.bessi@camptocamp.com <> +Nicolas De Smet Zyph0s <> Nicolas Seinlet Nicolas Vanhoren Nicolas Vanhoren @@ -592,6 +616,9 @@ Niels Huylebroeck Niels Huylebroeck <> Niels Huylebroeck Niels(Red15) <> Niels Huylebroeck Niels(Rrd15) <> Niels Huylebroeck Red15 <> +Nikunj Jani +Nikunj Jani +Nikunj Jani Reporting Team(JNI OpenERP) <> Nimesh Contractor Oliver Laurent Olivier Dony @@ -599,24 +626,33 @@ Olivier Dony Olivier Dony Olivier Dony Odowan Kenobi <> Olivier Dony Olivier Dony <> -Olivier Dony Olvier Dony <> +Olivier Dony Olivier Laurent, Olivier Dony <> Olivier Dony OpenERP Framework R&D Team <> Olivier Dony odo@tinyerp.com <> Olivier Dossmann Olivier DOSSMANN Olivier Laurent Olivier Laurent Olivier Laurent -Olivier Laurent Olivier Laurent, Olivier Dony <> Olivier Laurent olt at tinyerp <> Olivier Laurent olt@tinyerp.com <> +Olivier Macchioni Omar Castiñeira Saavedra Omar Castiñeira Saavedra Omar (Pexego) <> Omar Castiñeira Saavedra Omar Omar Castiñeira Saavedra Omar(Pexego),Anup(OpenERP) <> Omar Castiñeira Saavedra omar7r <> +Ostap Yakovenko Pankaj Joshi Pankaj Joshi Pankaj Joshi Pankita Shah +Pankti Parikh +Pankti Parikh +Pankti Parikh PAP (Openerp) <> +Pankti Parikh PAP(OpenERP) <> +Pankti Parikh PAP(Openerp) <> +Pankti Parikh pap (OpenERP) <> +Pankti Parikh pap(openerp) <> +Pankti Parikh pap@tinyerp.com <> Panos Christeas Panos Christeas Panos Christeas @@ -640,8 +676,10 @@ Pariket Trivedi Parth Gajjar Parthiv Patel Parthiv Patel +Patrick Monnerat Patrik <> Patrick Thieuleux Patrick Thieuleux <> Paul J Stevens Paul J Stevens <> +Paul Rad-Dupuy vivisci <> Paulius Sladkevičius Paulius Sladkevičius Paulius Sladkevičius Paulius Sladkevičius Paulius Sladkevičius @@ -654,6 +692,7 @@ Peter Langenberg Peter Langenberg <> Philu philubert@gmail.com <> Phong Nguyen Phong Nguyen Phong Nguyen-Thanh <> +Phuong Lu phu <> Pinakin Nayi Pinakin Nayi Pinakin Nayi Pinakin Nai(OpenERP) <> @@ -732,6 +771,10 @@ Ravi Gohil Ravi Gohil Ravi Gohil Ravi Gohil (OpenERP) <> Ravi Gohil rgo <> +Ravindra Mekhiya +Ravindra Mekhiya +Ravindra Mekhiya RME(OpenERP) <> +Ravindra Mekhiya Ravindra Mekhiya (OpenERP) <> Ravish Murari Ravish Murari Ravish Murari Ravish(OpenERP) <> @@ -767,6 +810,11 @@ Rikenkumar Bhorania RCH(OpenERP). <> Rikenkumar Bhorania rch (Open ERP) <> Rikenkumar Bhorania rch (OpenERP) <> Rikenkumar Bhorania rch(OpenERP) <> +Rima Vora +Rima Vora +Rima Vora RVO <> +Rima Vora RVO(OpenERP) <> +Rima Vora Rvo(Open ERP) <> Robert Sevcik Robajz <> Roberto López López Rohan Nayani @@ -785,8 +833,23 @@ Rucha Patel Rucha Patel RPA(OpenERP) <> Rucha Patel rpa(Open ERP) <> Rucha Patel rpa-openerp <> +Sabrina Romero +Saiyed Azadahmed +Saiyed Azadahmed +Saiyed Azadahmed SAZ (Open ERP) <> +Saiyed Azadahmed SAZ (OpenERP) <> +Saiyed Azadahmed SAZ (Opn ERP) <> +Saiyed Azadahmed SAZ (OprnERP) <> +Saiyed Azadahmed SAZ(OpenERP) <> +Saiyed Azadahmed SAz <> +Saiyed Azadahmed saz <> +Saiyed Azadahmed saz(OpenERP) <> +Saiyed Azadahmed saz(openerp) <> Salvatore Trimarchi Salvi Angjeli Salvi Angjeli <> +Sananaz Mansuri +Sananaz Mansuri SMA(OpenERP) <> +Sananaz Mansuri sma@tinyerp.com <> Sandy Carter Sandy Carter Sanjay Gohel @@ -818,6 +881,7 @@ Sheth Sunny Sheth Sunny Sheth Sunny Sheth Sunny sus +Shyamli Singh Simon Lejeune Simon Lejeune Simone Orsi @@ -897,6 +961,11 @@ Ujjvala Collins uco(OpenERP) <> Vadim Chobanu Vadim - SmartMode LTD <> Vaibhav Darji Vaibhav Darji +Vaishali Thakkar +Vaishali Thakkar VTH(OpenERP) <> +Valencia Rodrigues +Valencia Rodrigues VRO(OpenERP) <> +Vee vitridex <> Vianney da Costa Vianney da Costa Vianney - www.akretion.com.br <> Vianney da Costa Vianney,Anup(OpenERP) <> @@ -941,6 +1010,9 @@ Xavier Morel Xavier Morel Xavier Morel <> Xavier Morel Xavier(OpenERP) <> Xavier Morel xmo <> +Yajushi Yagnik +Yajushi Yagnik YYA(Axelor) <> +Yajushi Yagnik YYA(OpenERP) <> Yann Papouin Yann Papouin Yann Papouin <> Yann Papouin Yann papouin <> @@ -950,6 +1022,7 @@ Yannick Vaucher Yannick Vaucher <> Yannick Vaucher Yannick Vaucher Yannick Vaucher Yannick Vaucher@Camptocamp <> Yogesh Parekh +Yogesh Parekh Yogesh Parekh Yogesh Sakhreliya Yogesh Sakhreliya @@ -968,63 +1041,12 @@ Yury Tello Yury Tello _FIXME ajm _FIXME ajm AJM (OpenERP) <> -_FIXME bch bch <> -_FIXME ggh -_FIXME jma -_FIXME jma JMA(OpenEPR) <> -_FIXME jma JMA(OpenERP) <> -_FIXME jni -_FIXME jni -_FIXME jni Reporting Team(JNI OpenERP) <> -_FIXME kum -_FIXME kum -_FIXME kum KUM(OpenERP) <> -_FIXME pap -_FIXME pap -_FIXME pap PAP (Openerp) <> -_FIXME pap PAP(OpenERP) <> -_FIXME pap PAP(Openerp) <> -_FIXME pap pap (OpenERP) <> -_FIXME pap pap(openerp) <> -_FIXME pap pap@tinyerp.com <> _FIXME rde rde <> -_FIXME rme -_FIXME rme -_FIXME rme RME(OpenERP) <> -_FIXME rvo -_FIXME rvo -_FIXME rvo RVO <> -_FIXME rvo RVO(OpenERP) <> -_FIXME rvo Rvo(Open ERP) <> -_FIXME saz -_FIXME saz -_FIXME saz SAZ (Open ERP) <> -_FIXME saz SAZ (OpenERP) <> -_FIXME saz SAZ (Opn ERP) <> -_FIXME saz SAZ (OprnERP) <> -_FIXME saz SAZ(OpenERP) <> -_FIXME saz SAz <> -_FIXME saz saz <> -_FIXME saz saz(OpenERP) <> -_FIXME saz saz(openerp) <> -_FIXME sma -_FIXME sma SMA(OpenERP) <> -_FIXME sma sma@tinyerp.com <> _FIXME sme _FIXME sme SME(OpenERP) <> _FIXME sme SME(Tiny/Axelor) <> _FIXME sme SME(tiny/Axelor) <> _FIXME sme sme <> -_FIXME ssi -_FIXME vro -_FIXME vro VRO(OpenERP) <> -_FIXME vth -_FIXME vth VTH(OpenERP) <> -_FIXME yya -_FIXME yya EDA(Axelor) <> -_FIXME yya -_FIXME yya YYA(Axelor) <> -_FIXME yya YYA(OpenERP) <> __FIXME bhumi __FIXME tiny __FIXME tiny @@ -1032,25 +1054,22 @@ __FIXME tiny tiny <> # TODO INTERNAL -# 2 AVP(Axelor) <> -# 1 DRA(Axelor) <> -# 1 avp # 2 Priyanka Jani(OpenERP Trainee) <> -# 2 Hardik Sanchawat (OpenERP) <> -# 2 Jenil(OpenERP) <> # 1 Dhruvit Darji(OpenERP Trainee) <> -# 1 Ravindra Mekhiya (OpenERP) <> -# 1 Yogesh Parekh (OpenERP Trainee) # 1 Nikita Parmar (OpenERP Trainee) # 1 Sejal Bhut (OpenERP Trainee) -# 1 Amit (Open ERP) <> -# 1 phu <> # 1 vba <> + +# TODO INTERNAL Conflict + +# 1 Amit (Open ERP) <> # 1 Ajay Patel # 1 Yogesh(Open ERP) <> # 1 Yogesh(OpenERP) <> # 1 Chirag +# TODO INTERNAL Couples + # 1 ACH,Jay <> # 2 JMA,VRA <> # 2 Vir,Amp <> @@ -1088,6 +1107,8 @@ __FIXME tiny tiny <> # 1 Olivier Dony, Jay Vora <> # 1 Christophe Simonis, Olivier Dony <> +# TODO INTERNAL Team Leaders + # 2 Framework Team <> # 2 OpenERP R&D Framework Team <> # 2 Quality Team <> @@ -1095,6 +1116,8 @@ __FIXME tiny tiny <> # 1 Mod2 Team(OpenERP) <> # 1 OnDemand Open Object User +# TODO INTERNAL Leading Space + # 1 Amit Dodiya (OpenERP) <> # 1 Amit Dodiya(OpenERP) <> # 1 Jean-Christophe VASSORT <> @@ -1108,7 +1131,6 @@ __FIXME tiny tiny <> # 2 jkei # 2 richierichrawr # 2 watermelonjuice -# 1 vrenaville # 2 youring # 2 Дмитро Катюха # 1 jbeficent @@ -1117,9 +1139,7 @@ __FIXME tiny tiny <> # 1 jmustonen # 1 celm1990 # 1 cysnake4713 -# 1 dharmesh rathod # 1 driou -# 1 dsabrinarg # 1 eneldoserrata # 1 fdesmottes # 1 g.shipilov @@ -1127,38 +1147,25 @@ __FIXME tiny tiny <> # 1 senthilnathang # 1 the-digital-engineer # 1 yingliu4203 -# 1 hugosdsantos # 1 krnkris # 1 buke -# 1 OstapYakovenko # 1 DEEP SILVER ACCESSORY # 1 neps1192 -# 1 ALIANE Abdelouahab # 1 Invitu # 1 Jorge # 1 Maria G # 1 Mack -# TODO MISSING INFO - -# 1 Olivier Macchioni -# Email not found +# TODO MISSING INFO EMAIL # 2 Denis Seleznyov <> -# Email not found # 1 openerp user # Name: Milan Tribuson -# email not found -# 1 Manu, Jigar Amin - OpenERP <> -# 1 Ferchu <> -# 1 Patrik <> # 1 R.Messier <> -# 1 Manu <> -# 1 Zyph0s <> -# 1 amami <> -# 1 vitridex <> -# 1 vivisci <> -# 1 ashish +# Name: Raphael Messier + +# 1 Ferchu <> +# Launchpad: https://launchpad.net/~fgb From ccf021938d14435c51a835254e980496d9c48440 Mon Sep 17 00:00:00 2001 From: Daniel Reis Date: Sun, 1 Mar 2015 02:01:55 +0100 Subject: [PATCH 03/27] [FIX] auth_ldap login when password is unicode python ldap library api is str based, assume utf-8 encoding for non-ascii chars. Closes #5376 lp:1079476 --- addons/auth_ldap/users_ldap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/auth_ldap/users_ldap.py b/addons/auth_ldap/users_ldap.py index b2241471c25..5f4f47b34a2 100644 --- a/addons/auth_ldap/users_ldap.py +++ b/addons/auth_ldap/users_ldap.py @@ -105,7 +105,7 @@ class CompanyLDAP(osv.osv): if results and len(results) == 1: dn = results[0][0] conn = self.connect(conf) - conn.simple_bind_s(dn, password) + conn.simple_bind_s(dn, password.encode('utf-8')) conn.unbind() entry = results[0] except ldap.INVALID_CREDENTIALS: @@ -141,7 +141,7 @@ class CompanyLDAP(osv.osv): try: conn = self.connect(conf) conn.simple_bind_s(conf['ldap_binddn'] or '', - conf['ldap_password'] or '') + conf['ldap_password'].encode('utf-8') or '') results = conn.search_st(conf['ldap_base'], ldap.SCOPE_SUBTREE, filter, retrieve_attributes, timeout=60) conn.unbind() From 04de29288559cdb4fe841e80b1f8bf41fb76dbfc Mon Sep 17 00:00:00 2001 From: Ravi Gohil Date: Sun, 1 Mar 2015 02:17:04 +0100 Subject: [PATCH 04/27] [FIX] http: support old version of psutil Add support for older versions of psutil (<1.2.0). Only used when debug_rpc is enabled. Closes #5108, #5163 --- openerp/http.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openerp/http.py b/openerp/http.py index 64023602d9d..d56e2d84649 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -559,7 +559,7 @@ class JsonRequest(WebRequest): start_time = time.time() _, start_vms = 0, 0 if psutil: - _, start_vms = psutil.Process().get_memory_info() + _, start_vms = psutil.Process(os.getpid()).get_memory_info() if rpc_request and rpc_response_flag: rpc_request.debug('%s: %s %s, %s', endpoint, model, method, pprint.pformat(args)) @@ -570,7 +570,7 @@ class JsonRequest(WebRequest): end_time = time.time() _, end_vms = 0, 0 if psutil: - _, end_vms = psutil.Process().get_memory_info() + _, end_vms = psutil.Process(os.getpid()).get_memory_info() logline = '%s: %s %s: time:%.3fs mem: %sk -> %sk (diff: %sk)' % ( endpoint, model, method, end_time - start_time, start_vms / 1024, end_vms / 1024, (end_vms - start_vms)/1024) if rpc_response_flag: From cf2b1143dc1ef10e8374f6b64181f6f9908f1b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Pigeon?= Date: Thu, 12 Feb 2015 10:29:20 +0100 Subject: [PATCH 05/27] [FIX] hr_timesheet_invoice avoid empty returns in invoice line description Closes #5242 --- addons/hr_timesheet_invoice/hr_timesheet_invoice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index ec8c89dbe64..6e6393bced2 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -278,7 +278,8 @@ class account_analytic_line(osv.osv): details.append("%s" % (line['unit_amount'], )) if data.get('name', False): details.append(line['name']) - note.append(u' - '.join(map(lambda x: unicode(x) or '',details))) + if details: + note.append(u' - '.join(map(lambda x: unicode(x) or '',details))) if note: curr_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '',note))) invoice_line_obj.create(cr, uid, curr_line, context=context) From ddb900c7c5db3b4eb38319024b50f37e70ebd568 Mon Sep 17 00:00:00 2001 From: Leonardo Pistone Date: Fri, 16 Jan 2015 11:47:51 +0100 Subject: [PATCH 06/27] [FIX] product: demo data irrelevant product description Closes #4735 --- addons/product/product_demo.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/product/product_demo.xml b/addons/product/product_demo.xml index f5b06a37375..4d520b19358 100644 --- a/addons/product/product_demo.xml +++ b/addons/product/product_demo.xml @@ -233,7 +233,6 @@ FaceTime HD Camera, 1.2 MP Photos consu - Custom computer assembled on order based on customer's requirement. Bose's smallest portable Bluetooth speaker B3423 From 631d2f71461eda852b41a429c782772c5d97f2fe Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Fri, 27 Feb 2015 11:28:18 +0100 Subject: [PATCH 07/27] [FIX] purchase: pricelist and related currency mismatch on purchase order (Bid received). pricelist must be readonly when currency is readonly because there 's an onchange on currency which depends on the pricelist. opw:627009 Fixed #4598 --- addons/purchase/purchase.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 363fdc620bd..a3ae7197f12 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -194,6 +194,13 @@ class purchase_order(osv.osv): ('done', 'Done'), ('cancel', 'Cancelled') ] + + READONLY_STATES = { + 'confirmed': [('readonly', True)], + 'approved': [('readonly', True)], + 'done': [('readonly', True)] + } + _track = { 'state': { 'purchase.mt_rfq_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirmed', @@ -222,16 +229,16 @@ class purchase_order(osv.osv): copy=False), 'date_approve':fields.date('Date Approved', readonly=1, select=True, copy=False, help="Date on which purchase order has been approved"), - 'partner_id':fields.many2one('res.partner', 'Supplier', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, + 'partner_id':fields.many2one('res.partner', 'Supplier', required=True, states=READONLY_STATES, change_default=True, track_visibility='always'), 'dest_address_id':fields.many2one('res.partner', 'Customer Address (Direct Delivery)', - states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, + states=READONLY_STATES, help="Put an address if you want to deliver directly from the supplier to the customer. " \ "Otherwise, keep empty to deliver to your own company." ), - 'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')], states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]} ), - 'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, help="The pricelist sets the currency used for this purchase order. It also computes the supplier price for the selected products/quantities."), - 'currency_id': fields.many2one('res.currency','Currency', readonly=True, required=True,states={'draft': [('readonly', False)],'sent': [('readonly', False)]}), + 'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')], states=READONLY_STATES), + 'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states=READONLY_STATES, help="The pricelist sets the currency used for this purchase order. It also computes the supplier price for the selected products/quantities."), + 'currency_id': fields.many2one('res.currency','Currency', required=True, states=READONLY_STATES), 'state': fields.selection(STATE_SELECTION, 'Status', readonly=True, help="The status of the purchase order or the quotation request. " "A request for quotation is a purchase order in a 'Draft' status. " From 85ede15793dd2912ad49ad0035bfadcc022125d2 Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Fri, 27 Feb 2015 12:44:08 +0100 Subject: [PATCH 08/27] [FIX] purchase: get taxes when create rfq from bid When creating a request for quotation from a call for bids, the suppliers taxes where not present in the quotation. Since a product `taxes_id` field is a many2many, the unused triplets syntax caused the issue. fixes #5307 --- addons/purchase_requisition/purchase_requisition.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py index 1a51dbfe30f..79c14e4037a 100644 --- a/addons/purchase_requisition/purchase_requisition.py +++ b/addons/purchase_requisition/purchase_requisition.py @@ -170,6 +170,7 @@ class purchase_requisition(osv.osv): 'order_id': purchase_id, 'product_id': product.id, 'account_analytic_id': requisition_line.account_analytic_id.id, + 'taxes_id': [(6, 0, vals.get('taxes_ids', []))], }) return vals From 50fc06e2751b9d0c213d10b5e301c93fe6eefa2d Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 27 Feb 2015 18:43:14 +0100 Subject: [PATCH 09/27] [IMP] Fasten up procurement JIT in v8 already --- addons/procurement_jit/procurement_jit.py | 6 ++++-- addons/sale/sale.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/addons/procurement_jit/procurement_jit.py b/addons/procurement_jit/procurement_jit.py index 4ad5cca5f20..0a9181ec42d 100644 --- a/addons/procurement_jit/procurement_jit.py +++ b/addons/procurement_jit/procurement_jit.py @@ -26,9 +26,11 @@ class procurement_order(osv.osv): _inherit = "procurement.order" def create(self, cr, uid, vals, context=None): + context = context or {} procurement_id = super(procurement_order, self).create(cr, uid, vals, context=context) - self.run(cr, uid, [procurement_id], context=context) - self.check(cr, uid, [procurement_id], context=context) + if not context.get('procurement_autorun_defer'): + self.run(cr, uid, [procurement_id], context=context) + self.check(cr, uid, [procurement_id], context=context) return procurement_id # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/sale.py b/addons/sale/sale.py index b772bb7511f..c64a55920f5 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -695,7 +695,7 @@ class sale_order(osv.osv): :return: True """ - context = dict(context) + context = context or {} context['lang'] = self.pool['res.users'].browse(cr, uid, uid).lang procurement_obj = self.pool.get('procurement.order') sale_line_obj = self.pool.get('sale.order.line') @@ -719,7 +719,9 @@ class sale_order(osv.osv): if (line.state == 'done') or not line.product_id: continue vals = self._prepare_order_line_procurement(cr, uid, order, line, group_id=order.procurement_group_id.id, context=context) - proc_id = procurement_obj.create(cr, uid, vals, context=context) + ctx = context.copy() + ctx['procurement_autorun_defer'] = True + proc_id = procurement_obj.create(cr, uid, vals, context=ctx) proc_ids.append(proc_id) #Confirm procurement order such that rules will be applied on it #note that the workflow normally ensure proc_ids isn't an empty list From 9339f1bfb1d6c2c680448d9ffab6c4f187606529 Mon Sep 17 00:00:00 2001 From: Julien Legros Date: Mon, 2 Mar 2015 11:06:32 +0100 Subject: [PATCH 10/27] [IMP] sale: quotation confirmed mail subtype defaults to False This reverts commit 0130ce4e84fdd038aad3b3fc74480bad04122f80. --- addons/sale/sale_data.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale/sale_data.xml b/addons/sale/sale_data.xml index e51be69e2f1..4c5347cf1d0 100644 --- a/addons/sale/sale_data.xml +++ b/addons/sale/sale_data.xml @@ -40,7 +40,7 @@ Sales Order Confirmed sale.order - + Quotation confirmed From 2bc75369dcfafc3ae3f4d8eed11945b6de944184 Mon Sep 17 00:00:00 2001 From: Andrius Preimantas Date: Thu, 26 Feb 2015 18:11:49 +0200 Subject: [PATCH 11/27] Versada signing CLA --- doc/cla/corporate/versada.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 doc/cla/corporate/versada.md diff --git a/doc/cla/corporate/versada.md b/doc/cla/corporate/versada.md new file mode 100644 index 00000000000..24610ad24fc --- /dev/null +++ b/doc/cla/corporate/versada.md @@ -0,0 +1,14 @@ +Lithuania, 2015-02-26 + +Versada agrees to the terms of the Odoo Corporate Contributor License Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this declaration. + +Signed, + +Andrius Preimantas andrius@versada.lt https://github.com/andrius-preimantas + +List of contributors: + +Andrius Preimantas andrius@versada.lt https://github.com/andrius-preimantas + From 19ce5932e270047fab366a838a61dc9d7f6d3eb5 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Mon, 2 Mar 2015 12:08:43 +0100 Subject: [PATCH 12/27] [FIX] warning: stock picking warnings Warnings on pickings no longer worked, while it was possible to set them in the partner form. The problem comes from the fact onchange_partner_in no longer exists on the picking form, by default. It was therefore never called, on the warning never displayed. opw-628956 --- addons/warning/warning.py | 5 +---- addons/warning/warning_view.xml | 11 +++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/addons/warning/warning.py b/addons/warning/warning.py index 8387f49c585..a8b55404e7e 100644 --- a/addons/warning/warning.py +++ b/addons/warning/warning.py @@ -172,10 +172,7 @@ class stock_picking(osv.osv): if partner.picking_warn == 'block': return {'value': {'partner_id': False}, 'warning': warning} - result = super(stock_picking_in, self).onchange_partner_in(cr, uid, ids, partner_id, context) - if result.get('warning',False): - warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title'] - warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message'] + result = {'value': {}} if warning: result['warning'] = warning diff --git a/addons/warning/warning_view.xml b/addons/warning/warning_view.xml index fdb199d0f4c..1415fc84ab1 100644 --- a/addons/warning/warning_view.xml +++ b/addons/warning/warning_view.xml @@ -60,5 +60,16 @@ + + stock.picking.form.warning + stock.picking + + + + onchange_partner_in(partner_id) + + + + From d4152c2a40877fd62815d0a55bce5e64b053db72 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 27 Feb 2015 09:56:06 +0100 Subject: [PATCH 13/27] [IMP] Optimize child_of with parent_left and parent_right [FIX] Make sure it does not give errors on runbot by providing an alternative when parent_left would be zero --- addons/stock/product.py | 35 +++++++++++++++++++++++++++++------ addons/stock/stock.py | 8 +++++--- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/addons/stock/product.py b/addons/stock/product.py index 56770784f9e..1c6769213a0 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -93,11 +93,29 @@ class product_product(osv.osv): operator = context.get('compute_child', True) and 'child_of' or 'in' domain = context.get('force_company', False) and ['&', ('company_id', '=', context['force_company'])] or [] - return ( - domain + [('location_id', operator, location_ids)], - domain + ['&', ('location_dest_id', operator, location_ids), '!', ('location_id', operator, location_ids)], - domain + ['&', ('location_id', operator, location_ids), '!', ('location_dest_id', operator, location_ids)] - ) + locations = location_obj.browse(cr, uid, location_ids, context=context) + if operator == "child_of" and locations and locations[0].parent_left != 0: + loc_domain = [] + dest_loc_domain = [] + for loc in locations: + if loc_domain: + loc_domain = ['|'] + loc_domain + ['&', ('location_id.parent_left', '>=', loc.parent_left), ('location_id.parent_left', '<', loc.parent_right)] + dest_loc_domain = ['|'] + dest_loc_domain + ['&', ('location_dest_id.parent_left', '>=', loc.parent_left), ('location_dest_id.parent_left', '<', loc.parent_right)] + else: + loc_domain += ['&', ('location_id.parent_left', '>=', loc.parent_left), ('location_id.parent_left', '<', loc.parent_right)] + dest_loc_domain += ['&', ('location_dest_id.parent_left', '>=', loc.parent_left), ('location_dest_id.parent_left', '<', loc.parent_right)] + + return ( + domain + loc_domain, + domain + ['&'] + dest_loc_domain + ['!'] + loc_domain, + domain + ['&'] + loc_domain + ['!'] + dest_loc_domain + ) + else: + return ( + domain + [('location_id', operator, location_ids)], + domain + ['&', ('location_dest_id', operator, location_ids), '!', ('location_id', operator, location_ids)], + domain + ['&', ('location_id', operator, location_ids), '!', ('location_dest_id', operator, location_ids)] + ) def _get_domain_dates(self, cr, uid, ids, context): from_date = context.get('from_date', False) @@ -114,7 +132,8 @@ class product_product(osv.osv): field_names = field_names or [] domain_products = [('product_id', 'in', ids)] - domain_quant, domain_move_in, domain_move_out = self._get_domain_locations(cr, uid, ids, context=context) + domain_quant, domain_move_in, domain_move_out = [], [], [] + domain_quant_loc, domain_move_in_loc, domain_move_out_loc = self._get_domain_locations(cr, uid, ids, context=context) domain_move_in += self._get_domain_dates(cr, uid, ids, context=context) + [('state', 'not in', ('done', 'cancel', 'draft'))] + domain_products domain_move_out += self._get_domain_dates(cr, uid, ids, context=context) + [('state', 'not in', ('done', 'cancel', 'draft'))] + domain_products domain_quant += domain_products @@ -128,9 +147,13 @@ class product_product(osv.osv): moves_in = [] moves_out = [] else: + domain_move_in += domain_move_in_loc + domain_move_out += domain_move_out_loc moves_in = self.pool.get('stock.move').read_group(cr, uid, domain_move_in, ['product_id', 'product_qty'], ['product_id'], context=context) moves_out = self.pool.get('stock.move').read_group(cr, uid, domain_move_out, ['product_id', 'product_qty'], ['product_id'], context=context) + domain_quant += domain_quant_loc + print domain_quant quants = self.pool.get('stock.quant').read_group(cr, uid, domain_quant, ['product_id', 'qty'], ['product_id'], context=context) quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants)) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 7a694f7cc32..df401cf2a06 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -293,7 +293,7 @@ class stock_quant(osv.osv): _columns = { 'name': fields.function(_get_quant_name, type='char', string='Identifier'), 'product_id': fields.many2one('product.product', 'Product', required=True, ondelete="restrict", readonly=True, select=True), - 'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="restrict", readonly=True, select=True), + 'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="restrict", readonly=True, select=True, auto_join=True), 'qty': fields.float('Quantity', required=True, help="Quantity of products in this quant, in the default unit of measure of the product", readonly=True, select=True), 'package_id': fields.many2one('stock.quant.package', string='Package', help="The package containing this quant", readonly=True, select=True), 'packaging_type_id': fields.related('package_id', 'packaging_id', type='many2one', relation='product.packaging', string='Type of packaging', readonly=True, store=True), @@ -1742,8 +1742,10 @@ class stock_move(osv.osv): 'product_packaging': fields.many2one('product.packaging', 'Prefered Packaging', help="It specifies attributes of packaging like type, quantity of packaging,etc."), - 'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True, states={'done': [('readonly', True)]}, help="Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations."), - 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True, states={'done': [('readonly', True)]}, select=True, help="Location where the system will stock the finished products."), + 'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True, auto_join=True, + states={'done': [('readonly', True)]}, help="Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations."), + 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True, states={'done': [('readonly', True)]}, select=True, + auto_join=True, help="Location where the system will stock the finished products."), 'partner_id': fields.many2one('res.partner', 'Destination Address ', states={'done': [('readonly', True)]}, help="Optional address where goods are to be delivered, specifically used for allotment"), From acbef5552dc05b70e5097b9a9176c87d68e07cab Mon Sep 17 00:00:00 2001 From: Denis Michiels Date: Thu, 26 Feb 2015 15:00:16 +0100 Subject: [PATCH 14/27] [FIX] stock: improvement search on product - The search on qty_available is faster by looking only to the quants. - Less SQL queries --- addons/stock/product.py | 88 +++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 39 deletions(-) diff --git a/addons/stock/product.py b/addons/stock/product.py index 1c6769213a0..89029f7ffae 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -185,17 +185,33 @@ class product_product(osv.osv): if operator == '=': operator = '==' - product_ids = self.search(cr, uid, [], context=context) ids = [] - if product_ids: - #TODO: use a query instead of this browse record which is probably making the too much requests, but don't forget - #the context that can be set with a location, an owner... - for element in self.browse(cr, uid, product_ids, context=context): - if eval(str(element[field]) + operator + str(value)): - ids.append(element.id) - res.append(('id', 'in', ids)) + if name == 'qty_available': + res.append(('id', 'in', self._search_qty_available(cr, uid, operator, value, context))) + else: + product_ids = self.search(cr, uid, [], context=context) + if product_ids: + #TODO: use a query instead of this browse record which is probably making the too much requests, but don't forget + #the context that can be set with a location, an owner... + for element in self.browse(cr, uid, product_ids, context=context): + if eval(str(element[field]) + operator + str(value)): + ids.append(element.id) + res.append(('id', 'in', ids)) return res + def _search_qty_available(self, cr, uid, operator, value, context): + domain_quant = self._get_domain_locations(cr, uid, [], context=context)[0] + if context.get('lot_id'): + domain_quant.append(('lot_id', '=', context['lot_id'])) + if context.get('owner_id'): + domain_quant.append(('owner_id', '=', context['owner_id'])) + if context.get('package_id'): + domain_quant.append(('package_id', '=', context['package_id'])) + quants = self.pool.get('stock.quant').read_group(cr, uid, domain_quant, ['product_id', 'qty'], ['product_id'], context=context) + quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants)) + quants = dict((k, v) for k, v in quants.iteritems() if eval(str(v) + operator + str(value))) + return(list(quants)) + def _product_available_text(self, cr, uid, ids, field_names=None, arg=False, context=None): res = {} for product in self.browse(cr, uid, ids, context=context): @@ -316,41 +332,35 @@ class product_template(osv.osv): _inherit = 'product.template' def _product_available(self, cr, uid, ids, name, arg, context=None): - res = dict.fromkeys(ids, 0) - for product in self.browse(cr, uid, ids, context=context): - res[product.id] = { - # "reception_count": sum([p.reception_count for p in product.product_variant_ids]), - # "delivery_count": sum([p.delivery_count for p in product.product_variant_ids]), - "qty_available": sum([p.qty_available for p in product.product_variant_ids]), - "virtual_available": sum([p.virtual_available for p in product.product_variant_ids]), - "incoming_qty": sum([p.incoming_qty for p in product.product_variant_ids]), - "outgoing_qty": sum([p.outgoing_qty for p in product.product_variant_ids]), + prod_available = dict.fromkeys(ids, 0) + product_ids = self.browse(cr, uid, ids, context=context) + var_ids = [] + for product in product_ids: + var_ids += [p.id for p in product.product_variant_ids] + variant_available= self.pool['product.product']._product_available(cr, uid, var_ids, context=context) + + for product in product_ids: + qty_available = 0 + virtual_available = 0 + incoming_qty = 0 + outgoing_qty = 0 + for p in product.product_variant_ids: + qty_available += variant_available[p.id]["qty_available"] + virtual_available += variant_available[p.id]["virtual_available"] + incoming_qty += variant_available[p.id]["incoming_qty"] + outgoing_qty += variant_available[p.id]["outgoing_qty"] + prod_available[product.id] = { + "qty_available":qty_available, + "virtual_available":virtual_available, + "incoming_qty":incoming_qty, + "outgoing_qty":outgoing_qty, } - return res + return prod_available def _search_product_quantity(self, cr, uid, obj, name, domain, context): prod = self.pool.get("product.product") - res = [] - for field, operator, value in domain: - #to prevent sql injections - assert field in ('qty_available', 'virtual_available', 'incoming_qty', 'outgoing_qty'), 'Invalid domain left operand' - assert operator in ('<', '>', '=', '!=', '<=', '>='), 'Invalid domain operator' - assert isinstance(value, (float, int)), 'Invalid domain right operand' - - if operator == '=': - operator = '==' - - product_ids = prod.search(cr, uid, [], context=context) - ids = [] - if product_ids: - #TODO: use a query instead of this browse record which is probably making the too much requests, but don't forget - #the context that can be set with a location, an owner... - for element in prod.browse(cr, uid, product_ids, context=context): - if eval(str(element[field]) + operator + str(value)): - ids.append(element.id) - res.append(('product_variant_ids', 'in', ids)) - return res - + product_variant_ids = prod.search(cr, uid, domain, context=context) + return [('product_variant_ids', 'in', product_variant_ids)] def _product_available_text(self, cr, uid, ids, field_names=None, arg=False, context=None): res = {} From edaec2183595c1d50ad148978359d555a1919995 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Mon, 2 Mar 2015 12:17:15 +0100 Subject: [PATCH 15/27] [IMP] Change order of search terms when creating domain for search qty, clean --- addons/stock/product.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/addons/stock/product.py b/addons/stock/product.py index 89029f7ffae..4627f5fabbe 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -153,7 +153,6 @@ class product_product(osv.osv): moves_out = self.pool.get('stock.move').read_group(cr, uid, domain_move_out, ['product_id', 'product_qty'], ['product_id'], context=context) domain_quant += domain_quant_loc - print domain_quant quants = self.pool.get('stock.quant').read_group(cr, uid, domain_quant, ['product_id', 'qty'], ['product_id'], context=context) quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants)) @@ -191,8 +190,7 @@ class product_product(osv.osv): else: product_ids = self.search(cr, uid, [], context=context) if product_ids: - #TODO: use a query instead of this browse record which is probably making the too much requests, but don't forget - #the context that can be set with a location, an owner... + #TODO: Still optimization possible when searching virtual quantities for element in self.browse(cr, uid, product_ids, context=context): if eval(str(element[field]) + operator + str(value)): ids.append(element.id) @@ -200,13 +198,14 @@ class product_product(osv.osv): return res def _search_qty_available(self, cr, uid, operator, value, context): - domain_quant = self._get_domain_locations(cr, uid, [], context=context)[0] + domain_quant = [] if context.get('lot_id'): domain_quant.append(('lot_id', '=', context['lot_id'])) if context.get('owner_id'): domain_quant.append(('owner_id', '=', context['owner_id'])) if context.get('package_id'): domain_quant.append(('package_id', '=', context['package_id'])) + domain_quant += self._get_domain_locations(cr, uid, [], context=context)[0] quants = self.pool.get('stock.quant').read_group(cr, uid, domain_quant, ['product_id', 'qty'], ['product_id'], context=context) quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants)) quants = dict((k, v) for k, v in quants.iteritems() if eval(str(v) + operator + str(value))) @@ -332,7 +331,7 @@ class product_template(osv.osv): _inherit = 'product.template' def _product_available(self, cr, uid, ids, name, arg, context=None): - prod_available = dict.fromkeys(ids, 0) + prod_available = {} product_ids = self.browse(cr, uid, ids, context=context) var_ids = [] for product in product_ids: @@ -350,10 +349,10 @@ class product_template(osv.osv): incoming_qty += variant_available[p.id]["incoming_qty"] outgoing_qty += variant_available[p.id]["outgoing_qty"] prod_available[product.id] = { - "qty_available":qty_available, - "virtual_available":virtual_available, - "incoming_qty":incoming_qty, - "outgoing_qty":outgoing_qty, + "qty_available": qty_available, + "virtual_available": virtual_available, + "incoming_qty": incoming_qty, + "outgoing_qty": outgoing_qty, } return prod_available From 84b47f62eb5fe4123240402b049e2affaeae6bf1 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 20 Feb 2015 14:26:47 +0100 Subject: [PATCH 16/27] [IMP] Add already the possibility to fill in the products you want in an inventory In order to do that, we change the theoretical quantity into a functional stored field. Therefore the on_change changes, but the old still work. The UoM of the inventory line is also taken into account [IMP] Manual selection, no theoretical qty compute on import, comments --- addons/stock/stock.py | 93 ++++++++++++++++++++++--------------- addons/stock/stock_view.xml | 22 ++++----- 2 files changed, 66 insertions(+), 49 deletions(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index df401cf2a06..9816ebb20f4 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2573,7 +2573,7 @@ class stock_inventory(osv.osv): :rtype: list of tuple """ #default available choices - res_filter = [('none', _('All products')), ('product', _('One product only'))] + res_filter = [('none', _('All products')), ('partial', _('Manual Selection of Products')), ('product', _('One product only'))] settings_obj = self.pool.get('stock.config.settings') config_ids = settings_obj.search(cr, uid, [], limit=1, order='id DESC', context=context) #If we don't have updated config until now, all fields are by default false and so should be not dipslayed @@ -2616,7 +2616,10 @@ class stock_inventory(osv.osv): 'partner_id': fields.many2one('res.partner', 'Inventoried Owner', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Owner to focus your inventory on a particular Owner."), 'lot_id': fields.many2one('stock.production.lot', 'Inventoried Lot/Serial Number', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Lot/Serial Number to focus your inventory on a particular Lot/Serial Number.", copy=False), 'move_ids_exist': fields.function(_get_move_ids_exist, type='boolean', string=' Stock Move Exists?', help='technical field for attrs in view'), - 'filter': fields.selection(_get_available_filters, 'Selection Filter', required=True), + 'filter': fields.selection(_get_available_filters, 'Inventory of', required=True, + help="If you do an entire inventory, you can choose 'All Products' and it will prefill the inventory with the current stock. If you only do some products "\ + "(e.g. Cycle Counting) you can choose 'Manual Selection of Products' and the system won't propose anything. You can also let the "\ + "system propose for a single product / lot /... "), 'total_qty': fields.function(_get_total_qty, type="float"), } @@ -2728,25 +2731,11 @@ class stock_inventory(osv.osv): for inventory in self.browse(cr, uid, ids, context=context): # If there are inventory lines already (e.g. from import), respect those and set their theoretical qty line_ids = [line.id for line in inventory.line_ids] - if not line_ids: + if not line_ids and inventory.filter != 'partial': #compute the inventory lines and create them vals = self._get_inventory_lines(cr, uid, inventory, context=context) for product_line in vals: inventory_line_obj.create(cr, uid, product_line, context=context) - else: - # On import calculate theoretical quantity - quant_obj = self.pool.get("stock.quant") - for line in inventory.line_ids: - dom = [('company_id', '=', line.company_id.id), ('location_id', 'child_of', line.location_id.id), ('lot_id', '=', line.prod_lot_id.id), - ('product_id','=', line.product_id.id), ('owner_id', '=', line.partner_id.id)] - if line.package_id: - dom += [('package_id', '=', line.package_id.id)] - quants = quant_obj.search(cr, uid, dom, context=context) - tot_qty = 0 - for quant in quant_obj.browse(cr, uid, quants, context=context): - tot_qty += quant.qty - inventory_line_obj.write(cr, uid, [line.id], {'theoretical_qty': tot_qty}, context=context) - return self.write(cr, uid, ids, {'state': 'confirm', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}) def _get_inventory_lines(self, cr, uid, inventory, context=None): @@ -2802,6 +2791,19 @@ class stock_inventory_line(osv.osv): def _get_prodlot_change(self, cr, uid, ids, context=None): return self.pool.get('stock.inventory.line').search(cr, uid, [('prod_lot_id', 'in', ids)], context=context) + def _get_theoretical_qty(self, cr, uid, ids, name, args, context=None): + res = {} + quant_obj = self.pool["stock.quant"] + uom_obj = self.pool["product.uom"] + for line in self.browse(cr, uid, ids, context=context): + quant_ids = self._get_quants(cr, uid, line, context=context) + quants = quant_obj.browse(cr, uid, quant_ids, context=context) + tot_qty = sum([x.qty for x in quants]) + if line.product_uom_id and line.product_id.uom_id.id != line.product_uom_id.id: + tot_qty = uom_obj._compute_qty_obj(cr, uid, line.product_id.uom_id, tot_qty, line.product_uom_id, context=context) + res[line.id] = tot_qty + return res + _columns = { 'inventory_id': fields.many2one('stock.inventory', 'Inventory', ondelete='cascade', select=True), 'location_id': fields.many2one('stock.location', 'Location', required=True, select=True), @@ -2812,7 +2814,8 @@ class stock_inventory_line(osv.osv): 'company_id': fields.related('inventory_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, select=True, readonly=True), 'prod_lot_id': fields.many2one('stock.production.lot', 'Serial Number', domain="[('product_id','=',product_id)]"), 'state': fields.related('inventory_id', 'state', type='char', string='Status', readonly=True), - 'theoretical_qty': fields.float('Theoretical Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), readonly=True), + 'theoretical_qty': fields.function(_get_theoretical_qty, type='float', digits_compute=dp.get_precision('Product Unit of Measure'), + store=True, readonly=True, string="Theoretical Quantity"), 'partner_id': fields.many2one('res.partner', 'Owner'), 'product_name': fields.related('product_id', 'name', type='char', string='Product Name', store={ 'product.product': (_get_product_name_change, ['name', 'default_code'], 20), @@ -2829,9 +2832,40 @@ class stock_inventory_line(osv.osv): } _defaults = { - 'product_qty': 1, + 'product_qty': 0, } + def _get_quants(self, cr, uid, line, context=None): + quant_obj = self.pool["stock.quant"] + dom = [('company_id', '=', line.company_id.id), ('location_id', 'child_of', line.location_id.id), ('lot_id', '=', line.prod_lot_id.id), + ('product_id','=', line.product_id.id), ('owner_id', '=', line.partner_id.id), ('package_id', '=', line.package_id.id)] + quants = quant_obj.search(cr, uid, dom, context=context) + return quants + + def onchange_createline(self, cr, uid, ids, location_id=False, product_id=False, uom_id=False, package_id=False, prod_lot_id=False, partner_id=False, company_id=False, context=None): + quant_obj = self.pool["stock.quant"] + uom_obj = self.pool["product.uom"] + res = {'value': {}} + # If no UoM already put the default UoM of the product + if product_id and not uom_id: + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + res['value']['product_uom_id'] = product.uom_id.id + res['domain'] = {'product_uom_id': [('category_id','=',product.uom_id.category_id.id)]} + uom_id = product.uom_id.id + # Calculate theoretical quantity by searching the quants as in quants_get + if product_id and location_id: + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + if not company_id: + company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id + dom = [('company_id', '=', company_id), ('location_id', 'child_of', location_id), ('lot_id', '=', prod_lot_id), + ('product_id','=', product_id), ('owner_id', '=', partner_id), ('package_id', '=', package_id)] + quants = quant_obj.search(cr, uid, dom, context=context) + th_qty = sum([x.qty for x in quant_obj.browse(cr, uid, quants, context=context)]) + if product_id and uom_id and product.uom_id.id != uom_id: + th_qty = uom_obj._compute_qty(cr, uid, product.uom_id.id, th_qty, uom_id) + res['value']['theoretical_qty'] = th_qty + return res + def _resolve_inventory_line(self, cr, uid, inventory_line, context=None): stock_move_obj = self.pool.get('stock.move') diff = inventory_line.theoretical_qty - inventory_line.product_qty @@ -2862,26 +2896,11 @@ class stock_inventory_line(osv.osv): vals['product_uom_qty'] = diff return stock_move_obj.create(cr, uid, vals, context=context) + # Should be left out in next version def restrict_change(self, cr, uid, ids, theoretical_qty, context=None): - if ids and theoretical_qty: - #if the user try to modify a line prepared by openerp, reject the change and display an error message explaining how he should do - old_value = self.browse(cr, uid, ids[0], context=context) - return { - 'value': { - 'product_id': old_value.product_id.id, - 'product_uom_id': old_value.product_uom_id.id, - 'location_id': old_value.location_id.id, - 'prod_lot_id': old_value.prod_lot_id.id, - 'package_id': old_value.package_id.id, - 'partner_id': old_value.partner_id.id, - }, - 'warning': { - 'title': _('Error'), - 'message': _('You can only change the checked quantity of an existing inventory line. If you want modify a data, please set the checked quantity to 0 and create a new inventory line.') - } - } return {} + # Should be left out in next version def on_change_product_id(self, cr, uid, ids, product, uom, theoretical_qty, context=None): """ Changes UoM @param location_id: Location id @@ -2889,8 +2908,6 @@ class stock_inventory_line(osv.osv): @param uom: UoM product @return: Dictionary of changed values """ - if ids and theoretical_qty: - return self.restrict_change(cr, uid, ids, theoretical_qty, context=context) if not product: return {'value': {'product_uom_id': False}} obj_product = self.pool.get('product.product').browse(cr, uid, product, context=context) diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index 41870b3d829..ee751063931 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -53,7 +53,7 @@ - + stock.inventory.filter stock.inventory @@ -107,10 +107,10 @@ - - - - + + + + @@ -118,12 +118,12 @@