From 883a056a8e9e406b5b79814219e76319838e87fd Mon Sep 17 00:00:00 2001
From: "pankita shah (Open ERP)"
Date: Mon, 26 Nov 2012 11:57:17 +0530
Subject: [PATCH 06/87] [IMP] help of unit of meausure
bzr revid: shp@tinyerp.com-20121126062717-197cypp9h9x1ec23
---
addons/delivery/stock.py | 2 +-
addons/product/product.py | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py
index a84a22654b2..efc793af33c 100644
--- a/addons/delivery/stock.py
+++ b/addons/delivery/stock.py
@@ -66,7 +66,7 @@ class stock_picking(osv.osv):
}),
'carrier_tracking_ref': fields.char('Carrier Tracking Ref', size=32),
'number_of_packages': fields.integer('Number of Packages'),
- 'product_uom_weight': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Duration",),
+ 'product_uom_weight': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",),
}
def _prepare_shipping_invoice_line(self, cr, uid, picking, invoice, context=None):
diff --git a/addons/product/product.py b/addons/product/product.py
index 6bee0567480..e73d0d57241 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -784,9 +784,9 @@ class product_packaging(osv.osv):
'height': fields.float('Height', help='The height of the package'),
'width': fields.float('Width', help='The width of the package'),
'length': fields.float('Length', help='The length of the package'),
- 'product_uom_width': fields.many2one('product.uom', 'Unit of Measure',readonly="1", required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for Duration",),
- 'product_uom_length': fields.many2one('product.uom', 'Unit of Measure', readonly="1",required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for Duration",),
- 'product_uom_height': fields.many2one('product.uom', 'Unit of Measure',readonly="1", required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for Duration",),
+ 'product_uom_width': fields.many2one('product.uom', 'Unit of Measure',readonly="1", required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for width",),
+ 'product_uom_length': fields.many2one('product.uom', 'Unit of Measure', readonly="1",required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for Length",),
+ 'product_uom_height': fields.many2one('product.uom', 'Unit of Measure',readonly="1", required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for Height",),
}
From 016aa2cc078e9424ef9aa2c6b733b7ce552e6e2d Mon Sep 17 00:00:00 2001
From: "pankita shah (Open ERP)"
Date: Tue, 4 Dec 2012 16:28:29 +0530
Subject: [PATCH 07/87] [IMP] field name of
height_uom_id,weight_uom_id,height_uom_id,product_uom_weight1,product_uom_weight2
and add kg in weight,net weight
bzr revid: shp@tinyerp.com-20121204105829-g3z94mu5cr8fqr3v
---
addons/delivery/delivery_view.xml | 16 ++++++++++++----
addons/delivery/stock.py | 11 ++++++++---
addons/product/product.py | 12 ++++++------
addons/product/product_view.xml | 6 +++---
4 files changed, 29 insertions(+), 16 deletions(-)
diff --git a/addons/delivery/delivery_view.xml b/addons/delivery/delivery_view.xml
index 65e94784c0d..c79a8be06fe 100644
--- a/addons/delivery/delivery_view.xml
+++ b/addons/delivery/delivery_view.xml
@@ -243,10 +243,10 @@
-
+
-
+
@@ -283,8 +283,16 @@
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py
index efc793af33c..ae42a440f83 100644
--- a/addons/delivery/stock.py
+++ b/addons/delivery/stock.py
@@ -66,7 +66,7 @@ class stock_picking(osv.osv):
}),
'carrier_tracking_ref': fields.char('Carrier Tracking Ref', size=32),
'number_of_packages': fields.integer('Number of Packages'),
- 'product_uom_weight': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",),
+ 'weight_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",),
}
def _prepare_shipping_invoice_line(self, cr, uid, picking, invoice, context=None):
@@ -135,7 +135,7 @@ class stock_picking(osv.osv):
return result
_defaults = {
- 'product_uom_weight': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('kg'))], context=c)[0],
+ 'weight_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('kg'))], context=c)[0],
}
stock_picking()
@@ -174,8 +174,13 @@ class stock_move(osv.osv):
store={
'stock.move': (lambda self, cr, uid, ids, c=None: ids, ['product_id', 'product_qty', 'product_uom'], 20),
}),
+ 'weight_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",),
+ 'weight_net_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",),
}
-
+ _defaults = {
+ 'weight_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('kg'))], context=c)[0],
+ 'weight_net_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('kg'))], context=c)[0],
+ }
stock_move()
# Redefinition of the new fields in order to update the model stock.picking.out in the orm
diff --git a/addons/product/product.py b/addons/product/product.py
index 9085e518688..c85e81ced4c 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -784,9 +784,9 @@ class product_packaging(osv.osv):
'height': fields.float('Height', help='The height of the package'),
'width': fields.float('Width', help='The width of the package'),
'length': fields.float('Length', help='The length of the package'),
- 'product_uom_width': fields.many2one('product.uom', 'Unit of Measure',readonly="1", required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for width",),
- 'product_uom_length': fields.many2one('product.uom', 'Unit of Measure', readonly="1",required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for Length",),
- 'product_uom_height': fields.many2one('product.uom', 'Unit of Measure',readonly="1", required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for Height",),
+ 'width_uom_id': fields.many2one('product.uom', 'Unit of Measure',readonly="1", required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for width",),
+ 'length_uom_id': fields.many2one('product.uom', 'Unit of Measure', readonly="1",required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for Length",),
+ 'height_uom_id': fields.many2one('product.uom', 'Unit of Measure',readonly="1", required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for Height",),
}
@@ -816,9 +816,9 @@ class product_packaging(osv.osv):
'rows' : lambda *a : 3,
'sequence' : lambda *a : 1,
'ul' : _get_1st_ul,
- 'product_uom_width': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('cm'))], context=c)[0],
- 'product_uom_length': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('cm'))], context=c)[0],
- 'product_uom_height': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('cm'))], context=c)[0]
+ 'width_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('cm'))], context=c)[0],
+ 'length_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('cm'))], context=c)[0],
+ 'height_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('cm'))], context=c)[0]
}
diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml
index 54e3249cd9f..3822cf12eab 100644
--- a/addons/product/product_view.xml
+++ b/addons/product/product_view.xml
@@ -566,18 +566,18 @@
-
+
-
+
-
+
From b90834e0e7ad5787f2370cfdfc5d8176b1598432 Mon Sep 17 00:00:00 2001
From: "pankita shah (Open ERP)"
Date: Wed, 5 Dec 2012 15:25:33 +0530
Subject: [PATCH 08/87] [IMP] add kg in to net weight improve pickaging field
bzr revid: shp@tinyerp.com-20121205095533-8a41to3zm6p8830q
---
addons/delivery/delivery_view.xml | 7 +++-
addons/delivery/stock.py | 2 ++
addons/product/product_view.xml | 56 +++++++++++--------------------
3 files changed, 28 insertions(+), 37 deletions(-)
diff --git a/addons/delivery/delivery_view.xml b/addons/delivery/delivery_view.xml
index c79a8be06fe..f52b6a18d6f 100644
--- a/addons/delivery/delivery_view.xml
+++ b/addons/delivery/delivery_view.xml
@@ -248,7 +248,12 @@
-
+
+
+
+
+
+
diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py
index ae42a440f83..0116ea0d9c9 100644
--- a/addons/delivery/stock.py
+++ b/addons/delivery/stock.py
@@ -67,6 +67,7 @@ class stock_picking(osv.osv):
'carrier_tracking_ref': fields.char('Carrier Tracking Ref', size=32),
'number_of_packages': fields.integer('Number of Packages'),
'weight_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",),
+ 'weight_net_uom_id':fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",),
}
def _prepare_shipping_invoice_line(self, cr, uid, picking, invoice, context=None):
@@ -136,6 +137,7 @@ class stock_picking(osv.osv):
_defaults = {
'weight_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('kg'))], context=c)[0],
+ 'weight_net_uom_id':lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('kg'))], context=c)[0],
}
stock_picking()
diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml
index 3822cf12eab..f1554f08346 100644
--- a/addons/product/product_view.xml
+++ b/addons/product/product_view.xml
@@ -547,42 +547,26 @@
product.packaging
From c4f8c582535dad9064f9741ee6e158a5db92e675 Mon Sep 17 00:00:00 2001
From: "Nimesh (Open ERP)"
Date: Wed, 5 Dec 2012 16:36:25 +0530
Subject: [PATCH 09/87] [REMOVE] remove unused fields.
bzr revid: nco@tinyerp.com-20121205110625-uo19pd0l82f9s6ej
---
addons/product/product.py | 7 -------
1 file changed, 7 deletions(-)
diff --git a/addons/product/product.py b/addons/product/product.py
index c85e81ced4c..fb28f85b10f 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -784,9 +784,6 @@ class product_packaging(osv.osv):
'height': fields.float('Height', help='The height of the package'),
'width': fields.float('Width', help='The width of the package'),
'length': fields.float('Length', help='The length of the package'),
- 'width_uom_id': fields.many2one('product.uom', 'Unit of Measure',readonly="1", required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for width",),
- 'length_uom_id': fields.many2one('product.uom', 'Unit of Measure', readonly="1",required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for Length",),
- 'height_uom_id': fields.many2one('product.uom', 'Unit of Measure',readonly="1", required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for Height",),
}
@@ -816,10 +813,6 @@ class product_packaging(osv.osv):
'rows' : lambda *a : 3,
'sequence' : lambda *a : 1,
'ul' : _get_1st_ul,
- 'width_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('cm'))], context=c)[0],
- 'length_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('cm'))], context=c)[0],
- 'height_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('cm'))], context=c)[0]
-
}
def checksum(ean):
From 621dd7ba64f155c40de009f65d935accc04b35ae Mon Sep 17 00:00:00 2001
From: "Nimesh (Open ERP)"
Date: Thu, 6 Dec 2012 15:44:45 +0530
Subject: [PATCH 10/87] [REMOVE] remove weight net uom and improve order
sequence for stock.move.
bzr revid: nco@tinyerp.com-20121206101445-zntwb2tk3kl40rs4
---
addons/delivery/delivery_view.xml | 15 +++------------
addons/delivery/stock.py | 4 ----
addons/stock/stock.py | 2 +-
3 files changed, 4 insertions(+), 17 deletions(-)
diff --git a/addons/delivery/delivery_view.xml b/addons/delivery/delivery_view.xml
index f52b6a18d6f..ff19659a830 100644
--- a/addons/delivery/delivery_view.xml
+++ b/addons/delivery/delivery_view.xml
@@ -243,17 +243,12 @@
-
+
-
-
-
-
-
-
+
@@ -293,11 +288,7 @@
-
-
-
-
-
+
diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py
index 0116ea0d9c9..ffd4398e0dd 100644
--- a/addons/delivery/stock.py
+++ b/addons/delivery/stock.py
@@ -67,7 +67,6 @@ class stock_picking(osv.osv):
'carrier_tracking_ref': fields.char('Carrier Tracking Ref', size=32),
'number_of_packages': fields.integer('Number of Packages'),
'weight_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",),
- 'weight_net_uom_id':fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",),
}
def _prepare_shipping_invoice_line(self, cr, uid, picking, invoice, context=None):
@@ -137,7 +136,6 @@ class stock_picking(osv.osv):
_defaults = {
'weight_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('kg'))], context=c)[0],
- 'weight_net_uom_id':lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('kg'))], context=c)[0],
}
stock_picking()
@@ -177,11 +175,9 @@ class stock_move(osv.osv):
'stock.move': (lambda self, cr, uid, ids, c=None: ids, ['product_id', 'product_qty', 'product_uom'], 20),
}),
'weight_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",),
- 'weight_net_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",),
}
_defaults = {
'weight_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('kg'))], context=c)[0],
- 'weight_net_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('kg'))], context=c)[0],
}
stock_move()
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index 684c984dfa2..87a024d533a 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -1606,7 +1606,7 @@ class stock_move(osv.osv):
_name = "stock.move"
_description = "Stock Move"
- _order = 'id desc, picking_id'
+ _order = 'date_expected desc, id'
_log_create = False
def action_partial_move(self, cr, uid, ids, context=None):
From 3084aa07c6a2b56abf140fc5cf10d22bc4a163bf Mon Sep 17 00:00:00 2001
From: "Nimesh (Open ERP)"
Date: Thu, 6 Dec 2012 19:07:23 +0530
Subject: [PATCH 11/87] [IMP] improve default
bzr revid: nco@tinyerp.com-20121206133723-tz1kd5jwh2thc83k
---
addons/delivery/stock.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py
index ffd4398e0dd..4b3fa9096f8 100644
--- a/addons/delivery/stock.py
+++ b/addons/delivery/stock.py
@@ -135,7 +135,7 @@ class stock_picking(osv.osv):
return result
_defaults = {
- 'weight_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('kg'))], context=c)[0],
+ 'weight_uom_id': lambda self,cr,uid,c: self.pool.get('ir.model.data').get_object_reference(cr, uid, 'product', 'product_uom_kgm')[1]
}
stock_picking()
@@ -177,7 +177,7 @@ class stock_move(osv.osv):
'weight_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",),
}
_defaults = {
- 'weight_uom_id': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('kg'))], context=c)[0],
+ 'weight_uom_id': lambda self,cr,uid,c: self.pool.get('ir.model.data').get_object_reference(cr, uid, 'product', 'product_uom_kgm')[1]
}
stock_move()
From 055f46f29029f054a792b19619081221f41085b2 Mon Sep 17 00:00:00 2001
From: Vo Minh Thu
Date: Tue, 11 Dec 2012 13:28:44 +0100
Subject: [PATCH 12/87] [IMP] tests: add test_acl to the openerp.tests suite.
bzr revid: vmt@openerp.com-20121211122844-6y060gex2lbq76hp
---
openerp/tests/__init__.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/openerp/tests/__init__.py b/openerp/tests/__init__.py
index 92a40ab306c..2606f421669 100644
--- a/openerp/tests/__init__.py
+++ b/openerp/tests/__init__.py
@@ -8,6 +8,7 @@ Tests can be explicitely added to the `fast_suite` or `checks` lists or not.
See the :ref:`test-framework` section in the :ref:`features` list.
"""
+from . import test_acl
from . import test_expression, test_mail, test_ir_sequence, test_orm, \
test_fields, test_basecase, \
test_view_validation, test_uninstall, test_misc, test_db_cursor, \
@@ -20,6 +21,7 @@ fast_suite = [
]
checks = [
+ test_acl,
test_expression,
test_mail,
test_db_cursor,
From 559e2ff23c7cfc46fdb5d3afbb11d43d61f77e59 Mon Sep 17 00:00:00 2001
From: Vo Minh Thu
Date: Tue, 11 Dec 2012 15:49:03 +0100
Subject: [PATCH 13/87] [FIX] demo: add the group_partner_manager group to the
demo user (needed to pass the test_acl sanity check).
bzr revid: vmt@openerp.com-20121211144903-737sc04h41hmzaro
---
openerp/addons/base/base_demo.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/openerp/addons/base/base_demo.xml b/openerp/addons/base/base_demo.xml
index 69cc4994f82..519bcf10eea 100644
--- a/openerp/addons/base/base_demo.xml
+++ b/openerp/addons/base/base_demo.xml
@@ -14,7 +14,7 @@
--
Mr Demo
-
+ /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEP
ERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4e
Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCACEAIQDASIA
From 9d2afcae3fa570e1ac97697cdf2f75872a81fbb2 Mon Sep 17 00:00:00 2001
From: Vo Minh Thu
Date: Wed, 12 Dec 2012 12:36:47 +0100
Subject: [PATCH 14/87] [IMP] orm: check groups-based access rights on model
fields in read() and write().
The commented-out tests present in test_acl.py now pass.
Other tests now fail :-(.
bzr revid: vmt@openerp.com-20121212113647-11y3buulifg6tyhj
---
openerp/osv/orm.py | 27 +++++++++++++++++++++++++--
openerp/tests/test_acl.py | 16 +++++++++-------
2 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py
index 060d6da0277..1c0afa7131a 100644
--- a/openerp/osv/orm.py
+++ b/openerp/osv/orm.py
@@ -3536,6 +3536,29 @@ class BaseModel(object):
return res
+ def check_field_access_rights(self, cr, user, operation, fields, context=None):
+ """
+ Check the user access rights on the given fields. This raises Access
+ Denied if the user does not have the rights. Otherwise it returns the
+ fields (as is if the fields is not falsy, or the readable/writable
+ fields if fields is falsy).
+ """
+ def p(field_name):
+ """Predicate to test if the user has access to the given field name."""
+ field = self._all_columns[field_name].column
+ if field.groups:
+ return self.user_has_groups(cr, user, groups=field.groups, context=context)
+ else:
+ return True
+ if not fields:
+ fields = filter(p, self._all_columns.keys())
+ else:
+ filtered_fields = filter(lambda a: not p(a), fields)
+ if filtered_fields:
+ _logger.warning('Access Denied by ACLs for operation: %s, uid: %s, model: %s, fields: %s', operation, user, self._name, ', '.join(filtered_fields))
+ raise except_orm(_('Access Denied'), 'TODO')
+ return fields
+
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
""" Read records with given ids with the given fields
@@ -3561,8 +3584,7 @@ class BaseModel(object):
if not context:
context = {}
self.check_access_rights(cr, user, 'read')
- if not fields:
- fields = list(set(self._columns.keys() + self._inherit_fields.keys()))
+ fields = self.check_field_access_rights(cr, user, 'read', fields)
if isinstance(ids, (int, long)):
select = [ids]
else:
@@ -4018,6 +4040,7 @@ class BaseModel(object):
"""
readonly = None
+ self.check_field_access_rights(cr, user, 'write', vals.keys())
for field in vals.copy():
fobj = None
if field in self._columns:
diff --git a/openerp/tests/test_acl.py b/openerp/tests/test_acl.py
index a10a705092c..1d8d6bfb22a 100644
--- a/openerp/tests/test_acl.py
+++ b/openerp/tests/test_acl.py
@@ -1,6 +1,9 @@
import unittest2
from lxml import etree
+import openerp
+from openerp.tools.misc import mute_logger
+
import common
# test group that demo user should not have
@@ -55,6 +58,7 @@ class TestACL(common.TransactionCase):
self.tech_group.write({'users': [(3, self.demo_uid)]})
self.res_currency._columns['rate'].groups = False
+ @mute_logger('openerp.osv.orm')
def test_field_crud_restriction(self):
"Read/Write RPC access to restricted field should be forbidden"
# Verify the test environment first
@@ -65,12 +69,10 @@ class TestACL(common.TransactionCase):
# Now restrict access to the field and check it's forbidden
self.res_partner._columns['bank_ids'].groups = GROUP_TECHNICAL_FEATURES
- # FIXME TODO: enable next tests when access rights checks per field are implemented
- # from openerp.osv.orm import except_orm
- # with self.assertRaises(except_orm):
- # self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids'])
- # with self.assertRaises(except_orm):
- # self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []})
+ with self.assertRaises(openerp.osv.orm.except_orm):
+ self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids'])
+ with self.assertRaises(openerp.osv.orm.except_orm):
+ self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []})
# Add the restricted group, and check that it works again
self.tech_group.write({'users': [(4, self.demo_uid)]})
@@ -86,4 +88,4 @@ class TestACL(common.TransactionCase):
if __name__ == '__main__':
unittest2.main()
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
From 1a4fd7188093d49ff688f153be0d589cf4e5d47d Mon Sep 17 00:00:00 2001
From: Vo Minh Thu
Date: Wed, 12 Dec 2012 14:47:55 +0100
Subject: [PATCH 15/87] [FIX] test_fields: _all_columns must be modified
similarly to _columns.
(Because of the newly added method check_field_access_rights().)
bzr revid: vmt@openerp.com-20121212134755-ykfdykqepwzti5uf
---
openerp/osv/fields.py | 2 +-
openerp/tests/test_fields.py | 15 +++++++++++++++
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py
index 3d2c83b2621..a963b25b9e6 100644
--- a/openerp/osv/fields.py
+++ b/openerp/osv/fields.py
@@ -1560,7 +1560,7 @@ class column_info(object):
def __str__(self):
return '%s(%s, %s, %s, %s, %s)' % (
- self.__name__, self.name, self.column,
+ self.__class__.__name__, self.name, self.column,
self.parent_model, self.parent_column, self.original_parent)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/test_fields.py b/openerp/tests/test_fields.py
index ae401e77032..6f463a1ac7a 100644
--- a/openerp/tests/test_fields.py
+++ b/openerp/tests/test_fields.py
@@ -53,31 +53,46 @@ class TestRelatedField(common.TransactionCase):
def test_1_single_related(self):
""" test a related field with a single indirection like fields.related('foo') """
# add a related field test_related_company_id on res.partner
+ # and simulate a _inherits_reload() to populate _all_columns.
old_columns = self.partner._columns
+ old_all_columns = self.partner._all_columns
self.partner._columns = dict(old_columns)
+ self.partner._all_columns = dict(old_all_columns)
self.partner._columns.update({
'single_related_company_id': fields.related('company_id', type='many2one', obj='res.company'),
})
+ self.partner._all_columns.update({
+ 'single_related_company_id': fields.column_info('single_related_company_id', self.partner._columns['single_related_company_id'], None, None, None)
+ })
self.do_test_company_field('single_related_company_id')
# restore res.partner fields
self.partner._columns = old_columns
+ self.partner._all_columns = old_all_columns
def test_2_related_related(self):
""" test a related field referring to a related field """
# add a related field on a related field on res.partner
+ # and simulate a _inherits_reload() to populate _all_columns.
old_columns = self.partner._columns
+ old_all_columns = self.partner._all_columns
self.partner._columns = dict(old_columns)
+ self.partner._all_columns = dict(old_all_columns)
self.partner._columns.update({
'single_related_company_id': fields.related('company_id', type='many2one', obj='res.company'),
'related_related_company_id': fields.related('single_related_company_id', type='many2one', obj='res.company'),
})
+ self.partner._all_columns.update({
+ 'single_related_company_id': fields.column_info('single_related_company_id', self.partner._columns['single_related_company_id'], None, None, None)
+ 'related_related_company_id': fields.column_info('related_related_company_id', self.partner._columns['related_related_company_id'], None, None, None)
+ })
self.do_test_company_field('related_related_company_id')
# restore res.partner fields
self.partner._columns = old_columns
+ self.partner._all_columns = old_all_columns
def test_3_read_write(self):
""" write on a related field """
From eb0fcc35d2233b15d095b64c53587ef0d0f81b89 Mon Sep 17 00:00:00 2001
From: Vo Minh Thu
Date: Wed, 12 Dec 2012 16:15:09 +0100
Subject: [PATCH 16/87] [FIX] check_field_access_rights: ignore nonexisting
fields
It seems name_alias is requested but does not exist.
bzr revid: vmt@openerp.com-20121212151509-22494edob4e3fqaf
---
openerp/osv/orm.py | 4 ++++
openerp/tests/test_fields.py | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py
index 1c0afa7131a..ce2f8fc671a 100644
--- a/openerp/osv/orm.py
+++ b/openerp/osv/orm.py
@@ -3545,6 +3545,10 @@ class BaseModel(object):
"""
def p(field_name):
"""Predicate to test if the user has access to the given field name."""
+ # Ignore requested field if it doesn't exist. This is ugly but
+ # it seems to happen at least with 'name_alias' on res.partner.
+ if field_name not in self._all_columns:
+ return True
field = self._all_columns[field_name].column
if field.groups:
return self.user_has_groups(cr, user, groups=field.groups, context=context)
diff --git a/openerp/tests/test_fields.py b/openerp/tests/test_fields.py
index 6f463a1ac7a..efa7c67126d 100644
--- a/openerp/tests/test_fields.py
+++ b/openerp/tests/test_fields.py
@@ -84,7 +84,7 @@ class TestRelatedField(common.TransactionCase):
'related_related_company_id': fields.related('single_related_company_id', type='many2one', obj='res.company'),
})
self.partner._all_columns.update({
- 'single_related_company_id': fields.column_info('single_related_company_id', self.partner._columns['single_related_company_id'], None, None, None)
+ 'single_related_company_id': fields.column_info('single_related_company_id', self.partner._columns['single_related_company_id'], None, None, None),
'related_related_company_id': fields.column_info('related_related_company_id', self.partner._columns['related_related_company_id'], None, None, None)
})
From 0d168ae647fb2a1364af26ae468bf82e35dfa7de Mon Sep 17 00:00:00 2001
From: "dle@openerp.com" <>
Date: Wed, 12 Dec 2012 18:42:32 +0100
Subject: [PATCH 17/87] [FIX] Wrong pywebdav lib import
bzr revid: dle@openerp.com-20121212174232-5pnme7whe9yskrj7
---
addons/document_webdav/__init__.py | 10 +++-------
addons/document_webdav/dav_fs.py | 14 ++++++++++----
addons/document_webdav/webdav.py | 15 +++++++++++----
addons/document_webdav/webdav_server.py | 14 ++++++++++----
4 files changed, 34 insertions(+), 19 deletions(-)
diff --git a/addons/document_webdav/__init__.py b/addons/document_webdav/__init__.py
index 11bbd35f7c3..3dc4c104c80 100644
--- a/addons/document_webdav/__init__.py
+++ b/addons/document_webdav/__init__.py
@@ -20,13 +20,9 @@
##############################################################################
import logging
-_logger = logging.getLogger(__name__)
+import webdav
+import webdav_server
+import document_webdav
-try:
- import webdav
- import webdav_server
- import document_webdav
-except ImportError:
- _logger.info('document_webdav disabled please install PyWebDAV from http://code.google.com/p/pywebdav/downloads/detail?name=PyWebDAV-0.9.4.tar.gz&can=2&q=/')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/document_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py
index 1b826434f74..0a9df3d50d4 100644
--- a/addons/document_webdav/dav_fs.py
+++ b/addons/document_webdav/dav_fs.py
@@ -27,13 +27,19 @@ import errno
import netsvc
import urlparse
+try:
+ from pywebdav.lib.constants import COLLECTION # , OBJECT
+ from pywebdav.lib.errors import DAV_Error, DAV_Forbidden, DAV_NotFound
+ from pywebdav.lib.iface import dav_interface
+ from pywebdav.lib.davcmd import copyone, copytree, moveone, movetree, delone, deltree
+except ImportError:
+ from DAV.constants import COLLECTION #, OBJECT
+ from DAV.errors import DAV_Error, DAV_Forbidden, DAV_NotFound
+ from DAV.iface import dav_interface
+ from DAV.davcmd import copyone, copytree, moveone, movetree, delone, deltree
-from DAV.constants import COLLECTION #, OBJECT
-from DAV.errors import DAV_Error, DAV_Forbidden, DAV_NotFound
-from DAV.iface import dav_interface
import urllib
-from DAV.davcmd import copyone, copytree, moveone, movetree, delone, deltree
from cache import memoize
from tools import misc
diff --git a/addons/document_webdav/webdav.py b/addons/document_webdav/webdav.py
index bf8b843a0a0..f7780883bda 100644
--- a/addons/document_webdav/webdav.py
+++ b/addons/document_webdav/webdav.py
@@ -20,6 +20,9 @@
#
##############################################################################
+import logging
+
+_logger = logging.getLogger(__name__)
import xml.dom.minidom
domimpl = xml.dom.minidom.getDOMImplementation()
from xml.dom.minicompat import StringTypes
@@ -29,10 +32,14 @@ import urllib
from osv import osv
from tools.translate import _
-from DAV import utils
-from DAV.propfind import PROPFIND
-from DAV.report import REPORT
-
+try:
+ from pywebdav.lib import utils
+ from pywebdav.lib.propfind import PROPFIND
+ from pywebdav.lib.report import REPORT
+except ImportError:
+ from DAV import utils
+ from DAV.propfind import PROPFIND
+ from DAV.report import REPORT
import tools
diff --git a/addons/document_webdav/webdav_server.py b/addons/document_webdav/webdav_server.py
index 70ad4d2657f..1dbad3510ca 100644
--- a/addons/document_webdav/webdav_server.py
+++ b/addons/document_webdav/webdav_server.py
@@ -38,7 +38,16 @@ import logging
import netsvc
from dav_fs import openerp_dav_handler
from tools.config import config
-from DAV.WebDAVServer import DAVRequestHandler
+try:
+ from pywebdav.lib.WebDAVServer import DAVRequestHandler
+ from pywebdav.lib.utils import IfParser, TagList
+ from pywebdav.lib.errors import DAV_Error, DAV_Forbidden, DAV_NotFound
+ from pywebdav.lib.propfind import PROPFIND
+except ImportError:
+ from DAV.WebDAVServer import DAVRequestHandler
+ from DAV.utils import IfParser, TagList
+ from DAV.errors import DAV_Error, DAV_Forbidden, DAV_NotFound
+ from DAV.propfind import PROPFIND
from service import http_server
from service.websrv_lib import FixSendError, HttpOptions
from BaseHTTPServer import BaseHTTPRequestHandler
@@ -48,9 +57,6 @@ import re
import time
from string import atoi
import addons
-from DAV.utils import IfParser, TagList
-from DAV.errors import DAV_Error, DAV_Forbidden, DAV_NotFound
-from DAV.propfind import PROPFIND
# from DAV.constants import DAV_VERSION_1, DAV_VERSION_2
from xml.dom import minidom
from redirect import RedirectHTTPHandler
From d2cc57ffb6ac20be79e856a21e939eef756f9153 Mon Sep 17 00:00:00 2001
From: "dle@openerp.com" <>
Date: Wed, 12 Dec 2012 19:24:13 +0100
Subject: [PATCH 18/87] [FIX]Copy colly of handle function of basehttpserver.
Lucky guess
bzr revid: dle@openerp.com-20121212182413-kusxwx9g9qrg7jdv
---
addons/document_webdav/webdav_server.py | 42 ++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/addons/document_webdav/webdav_server.py b/addons/document_webdav/webdav_server.py
index 1dbad3510ca..887c0bc1d03 100644
--- a/addons/document_webdav/webdav_server.py
+++ b/addons/document_webdav/webdav_server.py
@@ -93,8 +93,48 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler):
def _log(self, message):
self._logger.debug(message)
+ def handle_one_request(self):
+ """Handle a single HTTP request.
+
+ You normally don't need to override this method; see the class
+ __doc__ string for information on how to handle specific HTTP
+ commands such as GET and POST.
+
+ """
+ try:
+ self.raw_requestline = self.rfile.readline(65537)
+ if len(self.raw_requestline) > 65536:
+ self.requestline = ''
+ self.request_version = ''
+ self.command = ''
+ self.send_error(414)
+ return
+ if not self.raw_requestline:
+ self.close_connection = 1
+ return
+ if not self.parse_request():
+ # An error code has been sent, just exit
+ return
+ mname = 'do_' + self.command
+ if not hasattr(self, mname):
+ self.send_error(501, "Unsupported method (%r)" % self.command)
+ return
+ method = getattr(self, mname)
+ method()
+ self.wfile.flush() #actually send the response if not already done.
+ except Exception as e:
+ #a read or a write timed out. Discard this connection
+ self.log_error("Request timed out: %r", e)
+ self.close_connection = 1
+ return
+
def handle(self):
- self._init_buffer()
+ """Handle multiple requests if necessary."""
+ self.close_connection = 1
+
+ self.handle_one_request()
+ while not self.close_connection:
+ self.handle_one_request()
def finish(self):
pass
From 42ad6511e6cab7631ed51dd23c1760b78da1fc4e Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Thu, 13 Dec 2012 10:56:43 +0100
Subject: [PATCH 19/87] [IMP] email_template: render mako templates with jinja2
sandboxed environment
bzr revid: rco@openerp.com-20121213095643-czuw8ls0he5ru1lx
---
addons/email_template/email_template.py | 44 ++++++++++++++++++-------
1 file changed, 33 insertions(+), 11 deletions(-)
diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py
index a9bee623f66..826b82186e4 100644
--- a/addons/email_template/email_template.py
+++ b/addons/email_template/email_template.py
@@ -29,12 +29,32 @@ from osv import fields
import tools
from tools.translate import _
from urllib import quote as quote
+
_logger = logging.getLogger(__name__)
try:
- from mako.template import Template as MakoTemplate
+ # We use a jinja2 sandboxed environment to render mako templates.
+ # Note that the rendering does not cover all the mako syntax, in particular
+ # arbitrary Python statements are not accepted, and not all expressions are
+ # allowed: only "public" attributes (not starting with '_') of objects may
+ # be accessed.
+ # This is done on purpose: it prevents incidental or malicious execution of
+ # Python code that may break the security of the server.
+ from jinja2.sandbox import SandboxedEnvironment
+ mako_template_env = SandboxedEnvironment(
+ block_start_string="<%",
+ block_end_string="%>",
+ variable_start_string="${",
+ variable_end_string="}",
+ comment_start_string="<%doc>",
+ comment_end_string="%doc>",
+ line_statement_prefix="%",
+ line_comment_prefix="##",
+ trim_blocks=True, # do not output newline after blocks
+ autoescape=True, # XML/HTML automatic escaping
+ )
except ImportError:
- _logger.warning("email_template: mako templates not available, templating features will not work!")
+ _logger.warning("jinja2 not available, templating features will not work!")
class email_template(osv.osv):
"Templates for sending email"
@@ -55,7 +75,8 @@ class email_template(osv.osv):
:param str model: model name of the document record this mail is related to.
:param int res_id: id of the document record this mail is related to.
"""
- if not template: return u""
+ if not template:
+ return u""
if context is None:
context = {}
try:
@@ -64,14 +85,15 @@ class email_template(osv.osv):
if res_id:
record = self.pool.get(model).browse(cr, uid, res_id, context=context)
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
- result = MakoTemplate(template).render_unicode(object=record,
- user=user,
- # context kw would clash with mako internals
- ctx=context,
- quote=quote,
- format_exceptions=True)
- if result == u'False':
- result = u''
+ variables = {
+ 'object': record,
+ 'user': user,
+ 'ctx': context, # context kw would clash with mako internals
+ 'quote': quote,
+ }
+ result = mako_template_env.from_string(template).render(variables)
+ if result == u"False":
+ result = u""
return result
except Exception:
_logger.exception("failed to render mako template value %r", template)
From 8256b41f0a440660895f3157b417ef20d3615961 Mon Sep 17 00:00:00 2001
From: "dle@openerp.com" <>
Date: Thu, 13 Dec 2012 10:56:51 +0100
Subject: [PATCH 20/87] [FIX]Try to make things work on runbot
bzr revid: dle@openerp.com-20121213095651-be2skhlkh0i4dlnz
---
addons/document_webdav/webdav_server.py | 58 +++++++++++++------------
1 file changed, 31 insertions(+), 27 deletions(-)
diff --git a/addons/document_webdav/webdav_server.py b/addons/document_webdav/webdav_server.py
index 887c0bc1d03..3d517f9e665 100644
--- a/addons/document_webdav/webdav_server.py
+++ b/addons/document_webdav/webdav_server.py
@@ -101,40 +101,44 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler):
commands such as GET and POST.
"""
- try:
- self.raw_requestline = self.rfile.readline(65537)
- if len(self.raw_requestline) > 65536:
- self.requestline = ''
- self.request_version = ''
- self.command = ''
- self.send_error(414)
- return
- if not self.raw_requestline:
- self.close_connection = 1
- return
- if not self.parse_request():
- # An error code has been sent, just exit
- return
- mname = 'do_' + self.command
- if not hasattr(self, mname):
- self.send_error(501, "Unsupported method (%r)" % self.command)
- return
- method = getattr(self, mname)
- method()
- self.wfile.flush() #actually send the response if not already done.
- except Exception as e:
- #a read or a write timed out. Discard this connection
- self.log_error("Request timed out: %r", e)
+ self.raw_requestline = self.rfile.readline(65537)
+ if len(self.raw_requestline) > 65536:
+ self.requestline = ''
+ self.request_version = ''
+ self.command = ''
+ self.send_error(414)
+ return
+ if not self.raw_requestline:
self.close_connection = 1
return
+ if not self.parse_request():
+ # An error code has been sent, just exit
+ return
+ mname = 'do_' + self.command
+ if not hasattr(self, mname):
+ self.send_error(501, "Unsupported method (%r)" % self.command)
+ return
+ method = getattr(self, mname)
+ method()
+ self.wfile.flush() #actually send the response if not already done.
+
+ return
def handle(self):
"""Handle multiple requests if necessary."""
self.close_connection = 1
-
- self.handle_one_request()
- while not self.close_connection:
+ try:
self.handle_one_request()
+ while not self.close_connection:
+ self.handle_one_request()
+ except Exception as e:
+ try:
+ self.log_error("Request timed out: %r \n Trying old version of HTTPServer", e)
+ self._init_buffer()
+ except Exception as e:
+ #a read or a write timed out. Discard this connection
+ self.log_error("Not working neither, closing connection\n %r", e)
+ self.close_connection = 1
def finish(self):
pass
From 8efb6d3ea3e42dedd89937c0a5512728228e68ba Mon Sep 17 00:00:00 2001
From: "dle@openerp.com" <>
Date: Thu, 13 Dec 2012 14:55:05 +0100
Subject: [PATCH 21/87] [ADD]filter db
bzr revid: dle@openerp.com-20121213135505-fnmlrvscc6upliiv
---
addons/document_webdav/dav_fs.py | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/addons/document_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py
index 0a9df3d50d4..5b3e4272042 100644
--- a/addons/document_webdav/dav_fs.py
+++ b/addons/document_webdav/dav_fs.py
@@ -24,7 +24,9 @@ import sql_db
import os
import time
import errno
+import re
+import openerp
import netsvc
import urlparse
try:
@@ -361,7 +363,8 @@ class openerp_dav_handler(dav_interface):
return self.parent.get_baseuri(self) + '/'.join(ajoin)
@memoize(4)
- def db_list(self):
+ def _all_db_list(self):
+ """return all databases who have module document_webdav installed"""
s = netsvc.ExportService.getService('db')
result = s.exp_list()
self.db_name_list=[]
@@ -370,7 +373,7 @@ class openerp_dav_handler(dav_interface):
try:
db = sql_db.db_connect(db_name)
cr = db.cursor()
- cr.execute("SELECT id FROM ir_module_module WHERE name = 'document' AND state='installed' ")
+ cr.execute("SELECT id FROM ir_module_module WHERE name = 'document_webdav' AND state='installed' ")
res=cr.fetchone()
if res and len(res):
self.db_name_list.append(db_name)
@@ -381,6 +384,15 @@ class openerp_dav_handler(dav_interface):
cr.close()
return self.db_name_list
+ def db_list(self, uri):
+ # import pudb;pudb.set_trace()
+ u = urlparse.urlsplit(uri)
+ h = u.hostname
+ d = h.split('.')[0]
+ r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d',d)
+ dbs = [i for i in self._all_db_list() if re.match(r, i)]
+ return dbs
+
def get_childs(self,uri, filters=None):
""" return the child objects as self.baseuris for the given URI """
self.parent.log_message('get children: %s' % uri)
@@ -388,7 +400,7 @@ class openerp_dav_handler(dav_interface):
if not dbname:
if cr: cr.close()
- res = map(lambda x: self.urijoin(x), self.db_list())
+ res = map(lambda x: self.urijoin(x), self.db_list(uri))
return res
result = []
node = self.uri2object(cr, uid, pool, uri2[:])
From 0619510955104a2ce1da973d4d56ca13da36e902 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Thu, 13 Dec 2012 15:01:05 +0100
Subject: [PATCH 22/87] [IMP] account_followup: adapt email templates
bzr revid: rco@openerp.com-20121213140105-931zv0c5hc9a5yh5
---
.../account_followup_data.xml | 81 +++++++++----------
1 file changed, 40 insertions(+), 41 deletions(-)
diff --git a/addons/account_followup/account_followup_data.xml b/addons/account_followup/account_followup_data.xml
index 62ea7bce9c6..d5b257a052f 100644
--- a/addons/account_followup/account_followup_data.xml
+++ b/addons/account_followup/account_followup_data.xml
@@ -3,19 +3,19 @@
-
-
+
+ First polite payment follow-up reminder email
- ${user.email or '' | h}
- ${user.company_id.name | h} Payment Reminder
- ${object.email | h}
- ${object.lang | h}
+ ${user.email or ''|safe}
+ ${user.company_id.name} Payment Reminder
+ ${object.email|safe}
+ ${object.lang}
-
Dear ${object.name | h},
+
Dear ${object.name},
Exception made if there was a mistake of ours, it seems that the following amount stays unpaid. Please, take
appropriate measures in order to carry out this payment in the next 8 days.
@@ -28,13 +28,13 @@ contact our accounting department.
Best Regards,
-
-
-
-
+
+ A bit urging second payment follow-up reminder email
- ${user.email or '' | h}
- ${user.company_id.name | h} Payment Reminder
- ${object.email | h}
- ${object.lang | h}
+ ${user.email or ''|safe}
+ ${user.company_id.name} Payment Reminder
+ ${object.email|safe}
+ ${object.lang}
-
Dear ${object.name | h},
+
Dear ${object.name},
- We are disappointed to see that despite sending a reminder, that your account is now seriously overdue.
+ We are disappointed to see that despite sending a reminder, that your account is now seriously overdue.
It is essential that immediate payment is made, otherwise we will have to consider placing a stop on your account
which means that we will no longer be able to supply your company with (goods/services).
Please, take appropriate measures in order to carry out this payment in the next 8 days.
@@ -71,31 +69,32 @@ Best Regards,
Despite several reminders, your account is still not settled.
Unless full payment is made in next 8 days, legal action for the recovery of the debt will be taken without
@@ -107,12 +106,12 @@ In case of any queries concerning this matter, do not hesitate to contact our ac
Best Regards,
Exception made if there was a mistake of ours, it seems that the following amount stays unpaid. Please, take
appropriate measures in order to carry out this payment in the next 8 days.
@@ -144,11 +142,11 @@ contact our accounting department.
Best Regards,
-${user.name | h}
+${user.name}
-${ctx.get('followup_table','')}
+${ctx.get('followup_table','') | safe}
@@ -174,7 +172,7 @@ Would your payment have been carried out after this mail was sent, please ignore
Best Regards,
-
+
@@ -198,8 +196,9 @@ If there is a problem with paying invoice that we are not aware of, do not hesit
Details of due payments is printed below.
Best Regards,
-
+
+
Call the customer on the phone3
@@ -221,7 +220,7 @@ I trust that this action will prove unnecessary and details of due payments is p
In case of any queries concerning this matter, do not hesitate to contact our accounting department.
Best Regards,
-
+
From 33463a6bc3cee1271fc5511b6ad1e8f5b5696ef4 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Thu, 13 Dec 2012 15:01:35 +0100
Subject: [PATCH 23/87] [IMP] account: adapt edi email template
bzr revid: rco@openerp.com-20121213140135-zw9zhmx85ryucn81
---
addons/account/edi/invoice_action_data.xml | 38 +++++++++++++---------
addons/email_template/email_template.py | 5 ++-
2 files changed, 27 insertions(+), 16 deletions(-)
diff --git a/addons/account/edi/invoice_action_data.xml b/addons/account/edi/invoice_action_data.xml
index c4b0c8103f1..bdb08d66cfe 100644
--- a/addons/account/edi/invoice_action_data.xml
+++ b/addons/account/edi/invoice_action_data.xml
@@ -22,8 +22,8 @@
Invoice - Send by Email
- ${object.user_id.email or object.company_id.email or 'noreply@localhost'}
- ${object.company_id.name} Invoice (Ref ${object.number or 'n/a' })
+ ${object.user_id.email or object.company_id.email or 'noreply@localhost'|safe}
+ ${object.company_id.name} Invoice (Ref ${object.number or 'n/a'})${object.partner_id.id}
@@ -33,7 +33,7 @@
-
Hello${object.partner_id.name and ' ' or ''}${object.partner_id.name or ''},
%endif
diff --git a/addons/sale/edi/sale_order_action_data.xml b/addons/sale/edi/sale_order_action_data.xml
index ad96ae39109..2a2061f1e6b 100644
--- a/addons/sale/edi/sale_order_action_data.xml
+++ b/addons/sale/edi/sale_order_action_data.xml
@@ -21,7 +21,7 @@
Sale Order - Send by Email
- ${object.user_id.email or ''}
+ ${object.user_id.email or ''|safe}${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })${object.partner_invoice_id.id}
@@ -32,7 +32,7 @@
-
Hello${object.partner_id.name and ' ' or ''}${object.partner_id.name or ''},
+
Hello ${object.partner_id.name or ''},
Here is your ${object.state in ('draft', 'sent') and 'quotation' or 'order confirmation'} from ${object.company_id.name}:
%endif
From c90e29626456c331cda84996a17d464911dff2a2 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Thu, 13 Dec 2012 15:38:53 +0100
Subject: [PATCH 26/87] [IMP] portal_sale: move code around, to extend models
in one place
bzr revid: rco@openerp.com-20121213143853-uk5ohmyc4tpqc5qu
---
addons/portal_sale/__init__.py | 2 -
addons/portal_sale/account_invoice.py | 43 ------------------
addons/portal_sale/portal_sale.py | 54 +++++++++++++++++++++++
addons/portal_sale/sale.py | 63 ---------------------------
4 files changed, 54 insertions(+), 108 deletions(-)
delete mode 100644 addons/portal_sale/account_invoice.py
delete mode 100644 addons/portal_sale/sale.py
diff --git a/addons/portal_sale/__init__.py b/addons/portal_sale/__init__.py
index 296de46594a..ba847ba7169 100644
--- a/addons/portal_sale/__init__.py
+++ b/addons/portal_sale/__init__.py
@@ -19,7 +19,5 @@
#
##############################################################################
-import account_invoice
-import sale
import portal_sale
import res_config
diff --git a/addons/portal_sale/account_invoice.py b/addons/portal_sale/account_invoice.py
deleted file mode 100644
index fa81b2049e0..00000000000
--- a/addons/portal_sale/account_invoice.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2011 OpenERP S.A ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-
-from osv import fields,osv
-
-class account_invoice(osv.osv):
- _inherit = 'account.invoice'
-
- def invoice_validate(self, cr, uid, ids, context=None):
- # fetch the partner's id and subscribe the partner to the sale order
- partner = self.browse(cr, uid, ids[0], context=context)['partner_id']
- if partner.id not in self.browse(cr, uid, ids[0], context=context)['message_follower_ids']:
- self.message_subscribe(cr, uid, ids, [partner.id], context=context)
- document = self.browse(cr, uid, ids[0], context=context)
- mail_values = {
- 'email_from': self.pool.get('res.users').browse(cr, uid, uid, context=context)['partner_id']['email'],
- 'email_to': partner.email,
- 'subject': 'Invitation to follow %s' % document.name_get()[0][1],
- 'body_html': 'You have been invited to follow %s' % document.name_get()[0][1],
- 'auto_delete': True,
- }
- mail_obj = self.pool.get('mail.mail')
- mail_id = mail_obj.create(cr, uid, mail_values, context=context)
- mail_obj.send(cr, uid, [mail_id], recipient_ids=[partner.id], context=context)
- return super(account_invoice, self).invoice_validate(cr, uid, ids, context=context)
\ No newline at end of file
diff --git a/addons/portal_sale/portal_sale.py b/addons/portal_sale/portal_sale.py
index 7cda77ed9ea..ba3addfb83a 100644
--- a/addons/portal_sale/portal_sale.py
+++ b/addons/portal_sale/portal_sale.py
@@ -53,6 +53,25 @@ class sale_order(osv.Model):
pass
return action_dict
+ def action_button_confirm(self, cr, uid, ids, context=None):
+ # fetch the partner's id and subscribe the partner to the sale order
+ partner = self.browse(cr, uid, ids[0], context=context)['partner_id']
+ if partner.id not in self.browse(cr, uid, ids[0], context=context)['message_follower_ids']:
+ self.message_subscribe(cr, uid, ids, [partner.id], context=context)
+ document = self.browse(cr, uid, ids[0], context=context)
+ mail_values = {
+ 'email_from': self.pool.get('res.users').browse(cr, uid, uid, context=context)['partner_id']['email'],
+ 'email_to': partner.email,
+ 'subject': 'Invitation to follow %s' % document.name_get()[0][1],
+ 'body_html': 'You have been invited to follow %s' % document.name_get()[0][1],
+ 'auto_delete': True,
+ }
+ mail_obj = self.pool.get('mail.mail')
+ mail_id = mail_obj.create(cr, uid, mail_values, context=context)
+ mail_obj.send(cr, uid, [mail_id], recipient_ids=[partner.id], context=context)
+ return super(sale_order, self).action_button_confirm(cr, uid, ids, context=context)
+
+
class account_invoice(osv.Model):
_inherit = 'account.invoice'
@@ -84,3 +103,38 @@ class account_invoice(osv.Model):
except Exception:
pass
return action_dict
+
+ def invoice_validate(self, cr, uid, ids, context=None):
+ # fetch the partner's id and subscribe the partner to the sale order
+ partner = self.browse(cr, uid, ids[0], context=context)['partner_id']
+ if partner.id not in self.browse(cr, uid, ids[0], context=context)['message_follower_ids']:
+ self.message_subscribe(cr, uid, ids, [partner.id], context=context)
+ document = self.browse(cr, uid, ids[0], context=context)
+ mail_values = {
+ 'email_from': self.pool.get('res.users').browse(cr, uid, uid, context=context)['partner_id']['email'],
+ 'email_to': partner.email,
+ 'subject': 'Invitation to follow %s' % document.name_get()[0][1],
+ 'body_html': 'You have been invited to follow %s' % document.name_get()[0][1],
+ 'auto_delete': True,
+ }
+ mail_obj = self.pool.get('mail.mail')
+ mail_id = mail_obj.create(cr, uid, mail_values, context=context)
+ mail_obj.send(cr, uid, [mail_id], recipient_ids=[partner.id], context=context)
+ return super(account_invoice, self).invoice_validate(cr, uid, ids, context=context)
+
+
+class mail_mail(osv.osv):
+ _inherit = 'mail.mail'
+
+ def _postprocess_sent_message(self, cr, uid, mail, context=None):
+ if mail.model == 'sale.order':
+ so_obj = self.pool.get('sale.order')
+ partner = so_obj.browse(cr, uid, mail.res_id, context=context)['partner_id']
+ # Add the customer in the SO as follower
+ if partner.id not in so_obj.browse(cr, uid, mail.res_id, context=context)['message_follower_ids']:
+ so_obj.message_subscribe(cr, uid, [mail.res_id], [partner.id], context=context)
+ # Add all recipients of the email as followers
+ for p in mail.partner_ids:
+ if p.id not in so_obj.browse(cr, uid, mail.res_id, context=context)['message_follower_ids']:
+ so_obj.message_subscribe(cr, uid, [mail.res_id], [p.id], context=context)
+ return super(mail_mail, self)._postprocess_sent_message(cr, uid, mail=mail, context=context)
diff --git a/addons/portal_sale/sale.py b/addons/portal_sale/sale.py
deleted file mode 100644
index 79fcbdef6fd..00000000000
--- a/addons/portal_sale/sale.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-
-from osv import fields, osv
-
-class sale_order(osv.osv):
- _inherit = 'sale.order'
-
- def action_button_confirm(self, cr, uid, ids, context=None):
- # fetch the partner's id and subscribe the partner to the sale order
- partner = self.browse(cr, uid, ids[0], context=context)['partner_id']
- if partner.id not in self.browse(cr, uid, ids[0], context=context)['message_follower_ids']:
- self.message_subscribe(cr, uid, ids, [partner.id], context=context)
- document = self.browse(cr, uid, ids[0], context=context)
- mail_values = {
- 'email_from': self.pool.get('res.users').browse(cr, uid, uid, context=context)['partner_id']['email'],
- 'email_to': partner.email,
- 'subject': 'Invitation to follow %s' % document.name_get()[0][1],
- 'body_html': 'You have been invited to follow %s' % document.name_get()[0][1],
- 'auto_delete': True,
- }
- mail_obj = self.pool.get('mail.mail')
- mail_id = mail_obj.create(cr, uid, mail_values, context=context)
- mail_obj.send(cr, uid, [mail_id], recipient_ids=[partner.id], context=context)
- return super(sale_order, self).action_button_confirm(cr, uid, ids, context=context)
-
-sale_order()
-
-class mail_mail(osv.osv):
- _inherit = 'mail.mail'
-
- def _postprocess_sent_message(self, cr, uid, mail, context=None):
- if mail.model == 'sale.order':
- so_obj = self.pool.get('sale.order')
- partner = so_obj.browse(cr, uid, mail.res_id, context=context)['partner_id']
- # Add the customer in the SO as follower
- if partner.id not in so_obj.browse(cr, uid, mail.res_id, context=context)['message_follower_ids']:
- so_obj.message_subscribe(cr, uid, [mail.res_id], [partner.id], context=context)
- # Add all recipients of the email as followers
- for p in mail.partner_ids:
- if p.id not in so_obj.browse(cr, uid, mail.res_id, context=context)['message_follower_ids']:
- so_obj.message_subscribe(cr, uid, [mail.res_id], [p.id], context=context)
- return super(mail_mail, self)._postprocess_sent_message(cr, uid, mail=mail, context=context)
-
-mail_mail()
\ No newline at end of file
From e65c73bc952d9905043de8963008b4027595162e Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Thu, 13 Dec 2012 15:50:11 +0100
Subject: [PATCH 27/87] [IMP] portal_sale: refactor and clean up code
bzr revid: rco@openerp.com-20121213145011-2wcif6d2o9dbcqts
---
addons/portal_sale/portal_sale.py | 41 +++++++++++++++++--------------
1 file changed, 23 insertions(+), 18 deletions(-)
diff --git a/addons/portal_sale/portal_sale.py b/addons/portal_sale/portal_sale.py
index ba3addfb83a..62f3f1ff9cc 100644
--- a/addons/portal_sale/portal_sale.py
+++ b/addons/portal_sale/portal_sale.py
@@ -21,11 +21,12 @@
from openerp.osv import osv, fields
+
class sale_order(osv.Model):
_inherit = 'sale.order'
# make the real method inheritable
- _payment_block_proxy = lambda self,*a,**kw: self._portal_payment_block(*a, **kw)
+ _payment_block_proxy = lambda self, *a, **kw: self._portal_payment_block(*a, **kw)
_columns = {
'portal_payment_options': fields.function(_payment_block_proxy, type="html", string="Portal Payment Options"),
@@ -35,14 +36,14 @@ class sale_order(osv.Model):
result = dict.fromkeys(ids, False)
payment_acquirer = self.pool.get('portal.payment.acquirer')
for this in self.browse(cr, uid, ids, context=context):
- if this.state not in ('draft','cancel') and not this.invoiced:
+ if this.state not in ('draft', 'cancel') and not this.invoiced:
result[this.id] = payment_acquirer.render_payment_block(cr, uid, this, this.name,
this.pricelist_id.currency_id, this.amount_total, context=context)
return result
def action_quotation_send(self, cr, uid, ids, context=None):
''' Override to use a modified template that includes a portal signup link '''
- action_dict = super(sale_order, self).action_quotation_send(cr, uid, ids, context=context)
+ action_dict = super(sale_order, self).action_quotation_send(cr, uid, ids, context=context)
try:
template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal_sale', 'email_template_edi_sale')[1]
# assume context is still a dict, as prepared by super
@@ -55,12 +56,14 @@ class sale_order(osv.Model):
def action_button_confirm(self, cr, uid, ids, context=None):
# fetch the partner's id and subscribe the partner to the sale order
- partner = self.browse(cr, uid, ids[0], context=context)['partner_id']
- if partner.id not in self.browse(cr, uid, ids[0], context=context)['message_follower_ids']:
+ assert len(ids) == 1
+ document = self.browse(cr, uid, ids[0], context=context)
+ partner = document.partner_id
+ if partner.id not in document.message_follower_ids:
self.message_subscribe(cr, uid, ids, [partner.id], context=context)
- document = self.browse(cr, uid, ids[0], context=context)
+ user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
mail_values = {
- 'email_from': self.pool.get('res.users').browse(cr, uid, uid, context=context)['partner_id']['email'],
+ 'email_from': user.partner_id.email,
'email_to': partner.email,
'subject': 'Invitation to follow %s' % document.name_get()[0][1],
'body_html': 'You have been invited to follow %s' % document.name_get()[0][1],
@@ -76,7 +79,7 @@ class account_invoice(osv.Model):
_inherit = 'account.invoice'
# make the real method inheritable
- _payment_block_proxy = lambda self,*a,**kw: self._portal_payment_block(*a, **kw)
+ _payment_block_proxy = lambda self, *a, **kw: self._portal_payment_block(*a, **kw)
_columns = {
'portal_payment_options': fields.function(_payment_block_proxy, type="html", string="Portal Payment Options"),
@@ -86,14 +89,14 @@ class account_invoice(osv.Model):
result = dict.fromkeys(ids, False)
payment_acquirer = self.pool.get('portal.payment.acquirer')
for this in self.browse(cr, uid, ids, context=context):
- if this.type == 'out_invoice' and this.state not in ('draft','done') and not this.reconciled:
+ if this.type == 'out_invoice' and this.state not in ('draft', 'done') and not this.reconciled:
result[this.id] = payment_acquirer.render_payment_block(cr, uid, this, this.number,
this.currency_id, this.residual, context=context)
return result
def action_invoice_sent(self, cr, uid, ids, context=None):
''' Override to use a modified template that includes a portal signup link '''
- action_dict = super(account_invoice, self).action_invoice_sent(cr, uid, ids, context=context)
+ action_dict = super(account_invoice, self).action_invoice_sent(cr, uid, ids, context=context)
try:
template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal_sale', 'email_template_edi_invoice')[1]
# assume context is still a dict, as prepared by super
@@ -105,13 +108,14 @@ class account_invoice(osv.Model):
return action_dict
def invoice_validate(self, cr, uid, ids, context=None):
- # fetch the partner's id and subscribe the partner to the sale order
- partner = self.browse(cr, uid, ids[0], context=context)['partner_id']
- if partner.id not in self.browse(cr, uid, ids[0], context=context)['message_follower_ids']:
+ # fetch the partner's id and subscribe the partner to the invoice
+ document = self.browse(cr, uid, ids[0], context=context)
+ partner = document.partner_id
+ if partner.id not in document.message_follower_ids:
self.message_subscribe(cr, uid, ids, [partner.id], context=context)
- document = self.browse(cr, uid, ids[0], context=context)
+ user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
mail_values = {
- 'email_from': self.pool.get('res.users').browse(cr, uid, uid, context=context)['partner_id']['email'],
+ 'email_from': user.partner_id.email,
'email_to': partner.email,
'subject': 'Invitation to follow %s' % document.name_get()[0][1],
'body_html': 'You have been invited to follow %s' % document.name_get()[0][1],
@@ -129,12 +133,13 @@ class mail_mail(osv.osv):
def _postprocess_sent_message(self, cr, uid, mail, context=None):
if mail.model == 'sale.order':
so_obj = self.pool.get('sale.order')
- partner = so_obj.browse(cr, uid, mail.res_id, context=context)['partner_id']
+ order = so_obj.browse(cr, uid, mail.res_id, context=context)
+ partner = order.partner_id
# Add the customer in the SO as follower
- if partner.id not in so_obj.browse(cr, uid, mail.res_id, context=context)['message_follower_ids']:
+ if partner.id not in order.message_follower_ids:
so_obj.message_subscribe(cr, uid, [mail.res_id], [partner.id], context=context)
# Add all recipients of the email as followers
for p in mail.partner_ids:
- if p.id not in so_obj.browse(cr, uid, mail.res_id, context=context)['message_follower_ids']:
+ if p.id not in order.message_follower_ids:
so_obj.message_subscribe(cr, uid, [mail.res_id], [p.id], context=context)
return super(mail_mail, self)._postprocess_sent_message(cr, uid, mail=mail, context=context)
From e07f8a7519330aa7ac1b1ba66293aa71d96cca62 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Thu, 13 Dec 2012 16:37:28 +0100
Subject: [PATCH 28/87] [IMP] portal_sale: adapt edi email templates
bzr revid: rco@openerp.com-20121213153728-v7yv82x09qtletix
---
addons/portal_sale/portal_sale.py | 16 +++++
addons/portal_sale/portal_sale_data.xml | 89 +++++++++++++------------
2 files changed, 63 insertions(+), 42 deletions(-)
diff --git a/addons/portal_sale/portal_sale.py b/addons/portal_sale/portal_sale.py
index 62f3f1ff9cc..3556fd5ca0e 100644
--- a/addons/portal_sale/portal_sale.py
+++ b/addons/portal_sale/portal_sale.py
@@ -74,6 +74,14 @@ class sale_order(osv.Model):
mail_obj.send(cr, uid, [mail_id], recipient_ids=[partner.id], context=context)
return super(sale_order, self).action_button_confirm(cr, uid, ids, context=context)
+ def get_signup_url(self, cr, uid, ids, context=None):
+ assert len(ids) == 1
+ document = self.browse(cr, uid, ids[0], context=context)
+ partner = document.partner_id
+ action = 'portal_sale.action_quotations_portal' if document.state in ('draft', 'sent') else 'portal_sale.action_orders_portal'
+ partner.signup_prepare()
+ return partner._get_signup_url_for_action(action=action, view_type='form', res_id=document.id)[partner.id]
+
class account_invoice(osv.Model):
_inherit = 'account.invoice'
@@ -126,6 +134,14 @@ class account_invoice(osv.Model):
mail_obj.send(cr, uid, [mail_id], recipient_ids=[partner.id], context=context)
return super(account_invoice, self).invoice_validate(cr, uid, ids, context=context)
+ def get_signup_url(self, cr, uid, ids, context=None):
+ assert len(ids) == 1
+ document = self.browse(cr, uid, ids[0], context=context)
+ partner = document.partner_id
+ action = 'portal_sale.portal_action_invoices'
+ partner.signup_prepare()
+ return partner._get_signup_url_for_action(action=action, view_type='form', res_id=document.id)[partner.id]
+
class mail_mail(osv.osv):
_inherit = 'mail.mail'
diff --git a/addons/portal_sale/portal_sale_data.xml b/addons/portal_sale/portal_sale_data.xml
index f40897f6998..bdf51fd2fe7 100644
--- a/addons/portal_sale/portal_sale_data.xml
+++ b/addons/portal_sale/portal_sale_data.xml
@@ -6,7 +6,7 @@
Sale Order - Send by Email (Portal)
- ${object.user_id.email or ''}
+ ${object.user_id.email or ''|safe}${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })${object.partner_invoice_id.id}
@@ -16,7 +16,7 @@
-
Hello${object.partner_id.name and ' ' or ''}${object.partner_id.name or ''},
+
Hello ${object.partner_id.name or ''},
Here is your ${object.state in ('draft', 'sent') and 'quotation' or 'order confirmation'} from ${object.company_id.name}:
@@ -32,38 +32,40 @@
Your reference: ${object.client_order_ref}
% endif
% if object.user_id:
- Your contact: ${object.user_id.name}
+ Your contact: ${object.user_id.name}
% endif
- <%
- action = 'portal_sale.action_quotations_portal' if object.state in ('draft', 'sent') else 'portal_sale.action_orders_portal'
- object.partner_id.signup_prepare()
- signup_url = object.partner_id._get_signup_url_for_action(action=action,view_type='form',res_id=object.id)[object.partner_id.id]
- %>
+ <% set signup_url = object.get_signup_url() %>
% if signup_url:
You can access this document and pay online via our Customer Portal: