From 3e5ae006902b3ee813bd1665397f2285bbebd1a9 Mon Sep 17 00:00:00 2001 From: "ajay javiya (OpenERP)" Date: Mon, 2 Dec 2013 18:04:37 +0530 Subject: [PATCH 01/64] [ADD]: Quote roller module bzr revid: aja@tinyerp.com-20131202123437-zn6u2ckxbid3jknb --- .../wizard/mail_compose_message.py | 4 +- addons/website_sale_quote/__init__.py | 2 + addons/website_sale_quote/__openerp__.py | 22 ++ .../controllers/__init__.py | 3 + addons/website_sale_quote/controllers/main.py | 57 ++++ addons/website_sale_quote/models/__init__.py | 1 + addons/website_sale_quote/models/order.py | 121 +++++++ addons/website_sale_quote/sale_quote_data.xml | 29 ++ addons/website_sale_quote/sale_quote_view.xml | 17 + .../static/description/icon.png | Bin 0 -> 7439 bytes .../static/src/css/sale_quote.css | 13 + .../static/src/js/sale_quote.js | 32 ++ .../views/website_sale_quote.xml | 322 ++++++++++++++++++ 13 files changed, 620 insertions(+), 3 deletions(-) create mode 100644 addons/website_sale_quote/__init__.py create mode 100644 addons/website_sale_quote/__openerp__.py create mode 100644 addons/website_sale_quote/controllers/__init__.py create mode 100644 addons/website_sale_quote/controllers/main.py create mode 100644 addons/website_sale_quote/models/__init__.py create mode 100644 addons/website_sale_quote/models/order.py create mode 100644 addons/website_sale_quote/sale_quote_data.xml create mode 100644 addons/website_sale_quote/sale_quote_view.xml create mode 100644 addons/website_sale_quote/static/description/icon.png create mode 100644 addons/website_sale_quote/static/src/css/sale_quote.css create mode 100644 addons/website_sale_quote/static/src/js/sale_quote.js create mode 100644 addons/website_sale_quote/views/website_sale_quote.xml diff --git a/addons/email_template/wizard/mail_compose_message.py b/addons/email_template/wizard/mail_compose_message.py index ac2f79b56f1..38d08c99b29 100644 --- a/addons/email_template/wizard/mail_compose_message.py +++ b/addons/email_template/wizard/mail_compose_message.py @@ -51,7 +51,7 @@ class mail_compose_message(osv.TransientModel): res.update( self.onchange_template_id( cr, uid, [], context['default_template_id'], res.get('composition_mode'), - res.get('model'), res.get('res_id'), context=context + res.get('model'), res.get('res_id', context.get('active_id')), context=context )['value'] ) return res @@ -114,7 +114,6 @@ class mail_compose_message(osv.TransientModel): values['attachment_ids'].append(ir_attach_obj.create(cr, uid, data_attach, context=context)) else: values = self.default_get(cr, uid, ['subject', 'body', 'email_from', 'email_to', 'email_cc', 'partner_to', 'reply_to', 'attachment_ids', 'mail_server_id'], context=context) - if values.get('body_html'): values['body'] = values.pop('body_html') return {'value': values} @@ -169,7 +168,6 @@ class mail_compose_message(osv.TransientModel): # filter template values fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to', 'attachment_ids', 'attachments', 'mail_server_id'] values = dict.fromkeys(res_ids, False) - template_values = self.pool.get('email.template').generate_email_batch(cr, uid, template_id, res_ids, context=context) for res_id in res_ids: res_id_values = dict((field, template_values[res_id][field]) for field in fields if template_values[res_id].get(field)) diff --git a/addons/website_sale_quote/__init__.py b/addons/website_sale_quote/__init__.py new file mode 100644 index 00000000000..9f86759e32b --- /dev/null +++ b/addons/website_sale_quote/__init__.py @@ -0,0 +1,2 @@ +import controllers +import models diff --git a/addons/website_sale_quote/__openerp__.py b/addons/website_sale_quote/__openerp__.py new file mode 100644 index 00000000000..1db0f379226 --- /dev/null +++ b/addons/website_sale_quote/__openerp__.py @@ -0,0 +1,22 @@ +{ + 'name': 'Quote Roller', + 'category': 'Website', + 'summary': 'Send Live Quotation', + 'version': '1.0', + 'description': """ +OpenERP Sale Quote Roller +================== + + """, + 'author': 'OpenERP SA', + 'depends': ['website_sale','website','product','portal_sale', 'mail'], + 'data': [ + 'views/website_sale_quote.xml', + 'sale_quote_view.xml', + 'sale_quote_data.xml' + ], + 'demo': [ + ], + 'qweb': ['static/src/xml/*.xml'], + 'installable': True, +} diff --git a/addons/website_sale_quote/controllers/__init__.py b/addons/website_sale_quote/controllers/__init__.py new file mode 100644 index 00000000000..e11f9ba81bb --- /dev/null +++ b/addons/website_sale_quote/controllers/__init__.py @@ -0,0 +1,3 @@ +import main + +# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/website_sale_quote/controllers/main.py b/addons/website_sale_quote/controllers/main.py new file mode 100644 index 00000000000..8f682136902 --- /dev/null +++ b/addons/website_sale_quote/controllers/main.py @@ -0,0 +1,57 @@ +import random +import uuid +import simplejson + +import werkzeug.exceptions + +from openerp import SUPERUSER_ID +from openerp.osv import osv +from openerp.addons.web import http +from openerp.addons.web.http import request +from openerp.addons.website.models import website + +class sale_quote(http.Controller): + + def get_quote(self, token): + quote_pool = request.registry.get('sale.quote') + quote_id = quote_pool.search(request.cr, SUPERUSER_ID, [('access_token', '=', token)], context=request.context) + return quote_id + + @website.route(['/quote/'], type='http', auth="public") + def view(self, token=None, **post): + values = {} + # http://hostname:8069/quote?id= + quote_pool = request.registry.get('sale.quote') + quotation = quote_pool.browse(request.cr, SUPERUSER_ID, self.get_quote(token))[0] + values.update({ + 'quotation' : quotation, + }) + return request.website.render('website_sale_quote.quotation', values) + + @website.route(['/quote//accept'], type='http', auth="public") + def accept(self, token=None , **post): + values = {} + quotation = request.registry.get('sale.quote').write(request.cr, SUPERUSER_ID, self.get_quote(token), {'state': 'accept'}) + return request.redirect("/quote/%s" % token) + + @website.route(['/quote//decline'], type='http', auth="public") + def decline(self, token=None , **post): + values = {} + quotation = request.registry.get('sale.quote').write(request.cr, SUPERUSER_ID, self.get_quote(token), {'state': 'cancel'}) + return request.redirect("/quote/%s" % token) + + @website.route(['/quote//post'], type='http', auth="public") + def post(self, token=None, **post): + values = {} + if post.get('new_message'): + request.session.body = post.get('new_message') + if 'body' in request.session and request.session.body: + request.registry.get('sale.quote').message_post(request.cr, SUPERUSER_ID, self.get_quote(token), + body=request.session.body, + type='comment', + subtype='mt_comment', + ) + request.session.body = False + return request.redirect("/quote/%s" % token) + + diff --git a/addons/website_sale_quote/models/__init__.py b/addons/website_sale_quote/models/__init__.py new file mode 100644 index 00000000000..49178edb61d --- /dev/null +++ b/addons/website_sale_quote/models/__init__.py @@ -0,0 +1 @@ +import order diff --git a/addons/website_sale_quote/models/order.py b/addons/website_sale_quote/models/order.py new file mode 100644 index 00000000000..adca999b588 --- /dev/null +++ b/addons/website_sale_quote/models/order.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# 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 openerp.osv import osv, fields +import hashlib +import time + +class sale_quote(osv.Model): + _name = "sale.quote" + _inherit = ['mail.thread', 'ir.needaction_mixin'] + _description = "Sales Quotations" + _columns = { +# 'template_id': fields.many2one('ir.ui.view', 'Template'), + 'order_id': fields.many2one('sale.order', 'Order', required=True), + 'state': fields.selection([ + ('draft', 'Draft Quotation'), + ('sent', 'Quotation Sent'), + ('accept', 'Accept'), + ('cancel', 'Cancelled'), + ('done', 'Done'), + ], 'Status'), + 'to_email': fields.char('Customers Email'), + 'access_token':fields.char('Quotation Token', size=256), + } + + def new_quotation_token(self, cr, uid, record_id): + db_uuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid') + quotation_token = hashlib.sha256('%s-%s-%s' % (time.time(), db_uuid, record_id)).hexdigest() + return self.write(cr, uid, [record_id],{'access_token': quotation_token} ) + + def create(self, cr, uid, vals, context=None): + if context is None: + context = {} + new_id = super(sale_quote, self).create(cr, uid, vals, context=context) + self.new_quotation_token(cr, uid, new_id) + return new_id + + +class sale_order(osv.osv): + _inherit = 'sale.order' + _columns = { + 'quote_url': fields.char('URL'), + } + +# def action_quotation_send(self, cr, uid, ids, context=None): +# ''' +# This function opens a window to compose an email, with the edi sale template message loaded by default +# ''' +# data_pool = self.pool.get('ir.model.data') +# sale_quote = self.pool.get('sale.quote') +# try: +# compose_form_id = data_pool.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1] +# except ValueError: +# compose_form_id = False +# model,template_id = data_pool.get_object_reference(cr, uid, 'website_sale_quote', "email_template_sale_quote") +# ctx = dict(context) +# for order in self.browse(cr, uid, ids, context): +# if not order.quote_id: +# new_id = sale_quote.create(cr, uid,{ +# 'state' : 'draft', +# 'to_email': order.partner_id.email, +# }) +# self.write(cr, uid, [order.id] ,{'quote_id': new_id}, context) +# ctx.update({ +# 'default_model': 'sale.order', +# 'default_res_id': order.id, +# 'default_use_template': bool(template_id), +# 'default_template_id': template_id, +# 'default_composition_mode': 'comment', +# 'mark_so_as_sent': True +# }) +# return { +# 'type': 'ir.actions.act_window', +# 'view_type': 'form', +# 'view_mode': 'form', +# 'res_model': 'mail.compose.message', +# 'views': [(compose_form_id, 'form')], +# 'view_id': compose_form_id, +# 'target': 'new', +# 'context': ctx, +# } + + def action_quotation_send(self, cr, uid, ids, context=None): + quote = super(sale_order, self).action_quotation_send(cr, uid,ids, context) + sale_quote = self.pool.get('sale.quote') + for order in self.browse(cr, uid, ids, context): + q_id = sale_quote.search(cr, uid, [('order_id','=', order.id)], context=context) + if not q_id: + new_id = sale_quote.create(cr, uid,{ + 'order_id' : order.id, + 'state' : 'draft', + 'to_email': order.partner_id.email, + }) + self.write(cr, uid, order.id, {'quote_url': self.get_signup_url(cr, uid, [order.id], context)}) + return quote + + def get_signup_url(self, cr, uid, ids, context=None): + url = False + quote_id = self.pool.get('sale.quote').search(cr, uid, [('order_id','=', ids[0])], context=context) + for quote in self.pool.get('sale.quote').browse(cr, uid, quote_id, context=context): + base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='http://localhost:8069', context=context) + url = "%s/quote/%s" % (base_url, quote.access_token) + return url diff --git a/addons/website_sale_quote/sale_quote_data.xml b/addons/website_sale_quote/sale_quote_data.xml new file mode 100644 index 00000000000..ff7c3cfc972 --- /dev/null +++ b/addons/website_sale_quote/sale_quote_data.xml @@ -0,0 +1,29 @@ + + + + + + Sales Quotation - Send by Email (Quote) + ${object.user_id.email or ''} + ${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' }) + ${object.partner_invoice_id.id} + + + + +

Hello ${object.partner_id.name},

+ +

Here is your ${object.state in ('draft', 'sent') and 'quotation' or 'order confirmation'} from ${object.company_id.name}:

+ + <% set signup_url = object.get_quote_url() %> +

+ You can access this document and pay online via our Customer Portal: +

+ View ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} + + ]]>
+
+
+
diff --git a/addons/website_sale_quote/sale_quote_view.xml b/addons/website_sale_quote/sale_quote_view.xml new file mode 100644 index 00000000000..b5120ace33a --- /dev/null +++ b/addons/website_sale_quote/sale_quote_view.xml @@ -0,0 +1,17 @@ + + + + + + + sale.order.form.payment + sale.order + + + + + + + + + diff --git a/addons/website_sale_quote/static/description/icon.png b/addons/website_sale_quote/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d7b9f4f25bd009970ad1f6bb2823c1b8593ad2bd GIT binary patch literal 7439 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 + + + + + + From dbc1dd4ac547e445dfd7dc5427c6525ea68131f9 Mon Sep 17 00:00:00 2001 From: "ajay javiya (OpenERP)" Date: Fri, 6 Dec 2013 16:13:39 +0530 Subject: [PATCH 02/64] [IMP]: flow of create a quote template, and design of tempalte bzr revid: aja@tinyerp.com-20131206104339-rt9q45g8p4ja3hnf --- addons/website_sale_quote/__openerp__.py | 1 + addons/website_sale_quote/controllers/main.py | 22 +- addons/website_sale_quote/models/order.py | 120 +++-- addons/website_sale_quote/sale_quote_demo.xml | 431 ++++++++++++++++++ addons/website_sale_quote/sale_quote_view.xml | 30 +- .../views/website_sale_quote.xml | 379 +++++++-------- 6 files changed, 684 insertions(+), 299 deletions(-) create mode 100644 addons/website_sale_quote/sale_quote_demo.xml diff --git a/addons/website_sale_quote/__openerp__.py b/addons/website_sale_quote/__openerp__.py index 1db0f379226..da6c2175db3 100644 --- a/addons/website_sale_quote/__openerp__.py +++ b/addons/website_sale_quote/__openerp__.py @@ -16,6 +16,7 @@ OpenERP Sale Quote Roller 'sale_quote_data.xml' ], 'demo': [ + 'sale_quote_demo.xml' ], 'qweb': ['static/src/xml/*.xml'], 'installable': True, diff --git a/addons/website_sale_quote/controllers/main.py b/addons/website_sale_quote/controllers/main.py index 8f682136902..a710d650b06 100644 --- a/addons/website_sale_quote/controllers/main.py +++ b/addons/website_sale_quote/controllers/main.py @@ -13,31 +13,31 @@ from openerp.addons.website.models import website class sale_quote(http.Controller): def get_quote(self, token): - quote_pool = request.registry.get('sale.quote') - quote_id = quote_pool.search(request.cr, SUPERUSER_ID, [('access_token', '=', token)], context=request.context) - return quote_id - + order_pool = request.registry.get('sale.order') + order_id = order_pool.search(request.cr, SUPERUSER_ID, [('access_token', '=', token)], context=request.context) + return order_id + @website.route(['/quote/'], type='http', auth="public") def view(self, token=None, **post): values = {} - # http://hostname:8069/quote?id= - quote_pool = request.registry.get('sale.quote') - quotation = quote_pool.browse(request.cr, SUPERUSER_ID, self.get_quote(token))[0] + order_pool = request.registry.get('sale.order') + quotation = order_pool.browse(request.cr, SUPERUSER_ID, self.get_quote(token))[0] + render_template = 'website_sale_quote.so_quotation' values.update({ 'quotation' : quotation, }) - return request.website.render('website_sale_quote.quotation', values) + return request.website.render(render_template, values) @website.route(['/quote//accept'], type='http', auth="public") def accept(self, token=None , **post): values = {} - quotation = request.registry.get('sale.quote').write(request.cr, SUPERUSER_ID, self.get_quote(token), {'state': 'accept'}) + quotation = request.registry.get('sale.order').write(request.cr, SUPERUSER_ID, self.get_quote(token), {'state': 'manual'}) return request.redirect("/quote/%s" % token) @website.route(['/quote//decline'], type='http', auth="public") def decline(self, token=None , **post): values = {} - quotation = request.registry.get('sale.quote').write(request.cr, SUPERUSER_ID, self.get_quote(token), {'state': 'cancel'}) + quotation = request.registry.get('sale.order').write(request.cr, SUPERUSER_ID, self.get_quote(token), {'state': 'cancel'}) return request.redirect("/quote/%s" % token) @website.route(['/quote//post'], type='http', auth="public") @@ -46,7 +46,7 @@ class sale_quote(http.Controller): if post.get('new_message'): request.session.body = post.get('new_message') if 'body' in request.session and request.session.body: - request.registry.get('sale.quote').message_post(request.cr, SUPERUSER_ID, self.get_quote(token), + request.registry.get('sale.order').message_post(request.cr, SUPERUSER_ID, self.get_quote(token), body=request.session.body, type='comment', subtype='mt_comment', diff --git a/addons/website_sale_quote/models/order.py b/addons/website_sale_quote/models/order.py index adca999b588..9e5ab9ff327 100644 --- a/addons/website_sale_quote/models/order.py +++ b/addons/website_sale_quote/models/order.py @@ -23,22 +23,29 @@ from openerp.osv import osv, fields import hashlib import time -class sale_quote(osv.Model): - _name = "sale.quote" - _inherit = ['mail.thread', 'ir.needaction_mixin'] - _description = "Sales Quotations" +class sale_order_line(osv.osv): + _inherit = "sale.order.line" + _description = "Sales Order Line" _columns = { -# 'template_id': fields.many2one('ir.ui.view', 'Template'), - 'order_id': fields.many2one('sale.order', 'Order', required=True), - 'state': fields.selection([ - ('draft', 'Draft Quotation'), - ('sent', 'Quotation Sent'), - ('accept', 'Accept'), - ('cancel', 'Cancelled'), - ('done', 'Done'), - ], 'Status'), - 'to_email': fields.char('Customers Email'), - 'access_token':fields.char('Quotation Token', size=256), + 'website_description':fields.html('Line Description'), + } + + def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,uom=False, qty_uos=0, uos=False, name='', partner_id=False,lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None): + res = super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty,uom, qty_uos, uos, name, partner_id,lang, update_tax, date_order, packaging, fiscal_position, flag, context) + if product: + desc = self.pool.get('product.product').browse(cr, uid, product, context).website_description + res.get('value').update({'website_description': desc}) + print res + return res + +class sale_order(osv.osv): + _inherit = 'sale.order' + _columns = { + 'quote_url': fields.char('URL'), + 'access_token':fields.char('Quotation Token', size=256), + 'template_id': fields.many2one('sale.order','Quote Template'), + 'website_description': fields.html('Description'), + 'is_template': fields.boolean('Is Template'), } def new_quotation_token(self, cr, uid, record_id): @@ -47,56 +54,34 @@ class sale_quote(osv.Model): return self.write(cr, uid, [record_id],{'access_token': quotation_token} ) def create(self, cr, uid, vals, context=None): - if context is None: - context = {} - new_id = super(sale_quote, self).create(cr, uid, vals, context=context) + new_id = super(sale_order, self).create(cr, uid, vals, context=context) self.new_quotation_token(cr, uid, new_id) return new_id - - -class sale_order(osv.osv): - _inherit = 'sale.order' - _columns = { - 'quote_url': fields.char('URL'), - } - -# def action_quotation_send(self, cr, uid, ids, context=None): -# ''' -# This function opens a window to compose an email, with the edi sale template message loaded by default -# ''' -# data_pool = self.pool.get('ir.model.data') -# sale_quote = self.pool.get('sale.quote') -# try: -# compose_form_id = data_pool.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1] -# except ValueError: -# compose_form_id = False -# model,template_id = data_pool.get_object_reference(cr, uid, 'website_sale_quote', "email_template_sale_quote") -# ctx = dict(context) -# for order in self.browse(cr, uid, ids, context): -# if not order.quote_id: -# new_id = sale_quote.create(cr, uid,{ -# 'state' : 'draft', -# 'to_email': order.partner_id.email, -# }) -# self.write(cr, uid, [order.id] ,{'quote_id': new_id}, context) -# ctx.update({ -# 'default_model': 'sale.order', -# 'default_res_id': order.id, -# 'default_use_template': bool(template_id), -# 'default_template_id': template_id, -# 'default_composition_mode': 'comment', -# 'mark_so_as_sent': True -# }) -# return { -# 'type': 'ir.actions.act_window', -# 'view_type': 'form', -# 'view_mode': 'form', -# 'res_model': 'mail.compose.message', -# 'views': [(compose_form_id, 'form')], -# 'view_id': compose_form_id, -# 'target': 'new', -# 'context': ctx, -# } + + def write(self, cr, uid, ids, vals, context=None): + template_id = vals.get('template_id', False) + lines = [] + line_pool = self.pool.get('sale.order.line') + if template_id: + order_template = self.browse(cr, uid, template_id, context) + for line in order_template.order_line: + line_pool.create(cr, uid,{ + 'name': line.name, + 'sequence': line.sequence, + 'order_id': ids[0], + 'price_unit': line.price_unit, + 'product_uom_qty': line.product_uom_qty, + 'discount': line.discount, + 'product_id': line.product_id.id, + 'tax_id': [(6, 0, [x.id for x in line.tax_id])], + 'website_description':line.website_description, + }, context=context) + vals.update({ + 'quote_url': self.get_signup_url(cr, uid, ids, context), + 'website_description': order_template.website_description, + }) + res = super(sale_order, self).write(cr, uid, ids, vals, context=context) + return res def action_quotation_send(self, cr, uid, ids, context=None): quote = super(sale_order, self).action_quotation_send(cr, uid,ids, context) @@ -114,8 +99,9 @@ class sale_order(osv.osv): def get_signup_url(self, cr, uid, ids, context=None): url = False - quote_id = self.pool.get('sale.quote').search(cr, uid, [('order_id','=', ids[0])], context=context) - for quote in self.pool.get('sale.quote').browse(cr, uid, quote_id, context=context): - base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='http://localhost:8069', context=context) - url = "%s/quote/%s" % (base_url, quote.access_token) + order = self.browse(cr, uid, ids, context=context)[0] + base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='http://localhost:8069', context=context) + url = "%s/quote/%s" % (base_url, order.access_token) return url + + diff --git a/addons/website_sale_quote/sale_quote_demo.xml b/addons/website_sale_quote/sale_quote_demo.xml new file mode 100644 index 00000000000..daee6db3edc --- /dev/null +++ b/addons/website_sale_quote/sale_quote_demo.xml @@ -0,0 +1,431 @@ + + + + + + + + + + + + +
+
+
+
+

Want to start selling OpenERP to your customers?

+

Join our partner network

+
+

Get all you need to grow your business and deliver quality services with the OpenERP Partner Program. It includes discounts on OpenERP Enterprise, technical and functional trainings, support services, marketing documents, access to the partner portal, rights to use the trademark and much more.

+
+
+ +
+
+
+
+
+

Why should you become a partner?

+

Enjoy building a business based on happy customers

+
+
+
+
+

A Valuable Product

+
+
+

Have strong value added services as you can rely on 3000 existing modules to deliver what the customer needs in a short period of time. Grow with your existing customer base by continuously proposing new modules.

+
+
+
+
+
+
+

A Strong Demand

+
+
+

Enjoy the traction of the fastest growing management software in the world. Benefit from the growing customer demand and the OpenERP brand.

+
+
+
+
+
+
+

High Margins

+
+
+

Grow by developing a recurring revenue stream from OpenERP Enterprise's commission system. Get high billing rates as you deliver a highly valuable software.

+
+
+
+
+
+
+
+
+
+
+

Get Big discounts on OpenERP Enterprise

+

Grow through recurring revenue streams from your customer base

+
+
+ +
+
+

+ As a partner, you get a big discount on OpenERP Enterprise. + This allows you to offer reduced prices to your customers and/or + to benefit from a commission on every subscription you sell to your customers. +

+
+
+
+
+

Benefit From A Dedicated Account Manager

+

Our role is to help you succeed

+
+
+ +
+
+

A dedicated account manager is assigned to every partner to help develop the OpenERP business. The account manager helps you get leads, close deals, gives you feedback and best practices, delivers sales training and is a direct point of contact for any request you may have.

+
+
+
+
+

Get access to our consultants and technical experts

+

Get access to experts for an extra fee

+

For an extra fee, partners can get access to OpenERP's core developers and functional experts. This can help you succeed in delivering more complex or bigger projects by getting the support of highly experienced consultants on demand.

+
+
+
+
+

Get recognition as an official certified partner

+

We give you visibility in our official partner list

+

OpenERP promotes its partners through various ways: publication on our website, official communication, publication of your success stories, etc. As soon as you become an OpenERP partner and have followed the official trainings, you will be visible on the partner directory listing.

+
+
+
+
+

Access to the Lead Generation Program

+

We give you visibility in our official partner list

+

Every year, we redirect more than 100,000 customer requests to our official partners. These are prospects that filled a form on the OpenERP website and wanted to use OpenERP. The right partner to fulfill the customer request is selected based on the customer localization (nearest partner) and the grade of the partner..

+
+
+
+
+

Benefit from the OpenERP branding

+

Get rights to use the OpenERP trademark

+

Every year, we redirect more than 100,000 customer requests to our official partners. These are prospects that filled a form on the OpenERP website and wanted to use OpenERP. The right partner to fulfill the customer request is selected based on the customer localization (nearest partner) and the grade of the partner..

+
+
+
+
+
+
+
+
+

Get Trained on OpenERP

+

Technical training, functional training and sales training

+

Customers need qualified partners. That's why being trained and certified is a requirement to become an official partner. When you start as a partner, you are free to choose from the following training sessions:

+
+
+
+
+

The Technical Training

+
+
+

Learn how to develop custom modules, including the development of new business objects, customization of screens, the design of new reports, the adaption of workflows and automated interfaceing with other programs or websites. (5 days)

+
+
+
+
+
+
+

The Functional Training

+
+
+

Learn how to configure and customize OpenERP, all from within the software itself - no coding required. The main applications are covered by this 5-day training are: Sales, Project Management, Accounting, Inventory, Manufacturing Resource Planning and Human Resources. (5 days)

+
+
+
+
+
+
+

The Sales Training

+
+
+

This online training is designed specifically for salespeople and presales employees who will be given an introduction course on OpenERP key features and how to present it's unique selling propositions to a potential customer.

+
+
+
+
+
+
+

Get access to online training materials

+

Train yourself on every new release using online videosGet rights to use the OpenERP trademark

+

Each partner gets access to our training videos which are available in the partner portal. The training material is updated for every new version of OpenERP. Special training sessions are organised to keep partners up to date for each new version of OpenERP.

+
+
+
+
+
+
+
+
+

Get access to our functional support services

+

Don't be stuck on your implementation project, we can help you.

+

As you go through your first implementation of an OpenERP system it is normal to need some support. We are here to answer your questions regarding the functionality of the software, or how to configure the solution. Each partner is entitled to a certain number of support hours thanks to his or her partner program.

+
+
+
+
+

Get technical support services

+

We help your developers with bugs or support questions

+

Each partner gets access to our training videos which are available in the partner portal. The training material is updated for every new version of OpenERP. Special training sessions are organised to keep partners up to date for each new version of OpenERP.

+
+
+ +
+
+

The OpenERP Support Team is 100% dedicated to helping our customers. You can contact the Support Team for any functional or technical questions and issues with regard to OpenERP development. They can help you develop custom modules.

+
+
+
+
+

Test all your developments automatically

+

Your dedicated test integration platform

+
+
+ +
+
+

Save time in your implementation project by having your developments tested automatically by our automated test servers. At every code commit, you get a full OpenERP instance that you can try out online. When this instance is deployed, your code is automatically put through our 2000+ automated unit tests. + Our automated testing server software is called Runbot, and you can try it out here: http://runbot.openerp.com. A dedicated runbot server is available for every partner.

+
+
+
+
+
+
+
+
+

Get The Partnership Level That Suits You

+

The OpenERP eco system is designed to ensure that OpenERP’s users will enjoy the best customer experience with our products. The community brings a wealth of new functionality. + OpenERP as a software publisher is committed to continuously improving the quality of the software. Our local partners provide integration and support services.

+
+
+
+
+
+
+

Ready Partners

+
+
+

The Ready membership level enables Partners to start and grow their OpenERP competencies. They benefit from a comprehensive set of services including training, support and access to leads which allow them to build their business at their own pace. + Ready partners also have a minimum of 1 certified OpenERP Functional Specialist on the latest OpenERP version (enforced starting Q3 2014)

+
+
+
+
+
+
+

Silver Partners

+
+
+

Partners which have qualified for the Silver Partner level are experienced in implementing OpenERP projects. Silver partners enjoy enhanced benefits as well as access to additional resources to support the growth of their business with OpenERP. + Silver partners also have a minimum of 2 certified OpenERP Functional Specialists on the latest OpenERP version (enforced starting Q3 2014)

+
+
+
+
+
+
+

Gold Partners

+
+
+

Gold partners have a strategic relationship with OpenERP. OpenERP is at the core of their strategy. They have committed significant resources to develop and deploy OpenERP solutions. They benefit from the highest visibility in the OpenERP ecosystem. + Gold partners also have a minimum of 3 certified OpenERP Functional Specialists on the latest OpenERP version (enforced starting Q3 2014)

+
+
+
+
+
+
+
+
+ + + + Gold Partner + + 1 + 1 + + 12950.00 + +
+
+
+
+

Why you'll love an iPad.

+

+ Right from the start, there’s a lot to love about + iPad. It’s simple yet powerful. Thin and light yet + full-featured. It can do just about everything and + be just about anything. +

+ And because it’s so easy to use, it’s easy to love. +

+
+
+ +
+
+
+
+
+
+
+
+ +
+
+

The full iPad experience.

+

There is less of it, but no less to it.

+

+ Everything you love about iPad — the beautiful + screen, fast and fluid performance, FaceTime and + iSight cameras, thousands of amazing apps, 10-hour + battery life* — is everything you’ll love about + iPad mini, too. And you can hold it in one hand. +

+
+
+
+
+
+
+ + + + Functional Training + + 1 + 1 + + 00.00 + +
+
+
+
+

Bose Mini Bluetooth Speaker.

+

+ The Bose® SoundLink® mini is Bose's smallest portable Bluetooth speaker. Its ultra-compact size fits in the + palm of your hand, yet gives you full, natural sound wirelessly from your iPhone, iPad, or iPod. Grab it and go + full-featured. It can do just about everything and + experience music just about anywhere. +

+ +
+
+

Characteristics

+
+
+
    +
  • Sleek, compact design
  • +
  • Efficient, high-quality audio
  • +
  • Remote control for power, volume, track seek
  • +
  • Auxiliary input for portable devices
  • +
  • Universal iPod docking station fits most iPod/iPhone models
  • +
  • Charges iPod/iPhone
  • +
  • Volume control on main system
  • +
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+

Plays where you play

+

+ The SoundLink® Mini speaker is small and light enough + to tuck into your bag. It weighs in at just 1.5 pounds. + Its low profile lets you place it almost anywhere and + provides a low center of gravity that makes it nearly + impossible to tip over. +

+

+ The rechargeable lithium-ion battery delivers up to seven hours of playtime. + And at home, you can listen even longer—the charging cradle lets + you listen while it charges. +

+
+
+ +
+
+

Bluetooth connectivity

+

+ The speaker has a range of about 30 feet, so you can enjoy + the sound you want without wires. It pairs easily with your + smartphone, iPad® or other Bluetooth device. + And it remembers the most recent six devices you've used, + so reconnecting is even simpler. +

+
+
+
+
+
+
+
+
+ +
+
+

More features.

+

+ Charging cradle recharges the battery and serves as a convenient + home base for your speaker, and it lets you play while it charges. +

+

+ Wall charger can be plugged into the cradle or directly into the speaker +

+

+ Auxiliary port lets you connect other audio sources, like an MP3 player +

+

+ USB port allows for software update to ensure ongoing Bluetooth device compatibility +

+

+ Soft covers are available separately in blue, green or orange. Pick a color to match your style. +

+
+
+
+
+
+
+ + + + + + + + manual + + +
+
diff --git a/addons/website_sale_quote/sale_quote_view.xml b/addons/website_sale_quote/sale_quote_view.xml index b5120ace33a..e3c62e94d63 100644 --- a/addons/website_sale_quote/sale_quote_view.xml +++ b/addons/website_sale_quote/sale_quote_view.xml @@ -1,17 +1,27 @@ - - - sale.order.form.payment - sale.order - - - - - - + sale.order.form.payment + sale.order + + + +
+ +
+
+ + + + + + + + + + +
diff --git a/addons/website_sale_quote/views/website_sale_quote.xml b/addons/website_sale_quote/views/website_sale_quote.xml index 2280a91ae00..f5d2eb7b6ec 100644 --- a/addons/website_sale_quote/views/website_sale_quote.xml +++ b/addons/website_sale_quote/views/website_sale_quote.xml @@ -91,232 +91,189 @@ - + From 7bb0380af4056ce9e5165e571a960c792f639968 Mon Sep 17 00:00:00 2001 From: "ajay javiya (OpenERP)" Date: Tue, 10 Dec 2013 18:53:16 +0530 Subject: [PATCH 06/64] [ADD]: product name in recommended product bzr revid: aja@tinyerp.com-20131210132316-8pgbtqgvs50gjmwz --- addons/website_sale_quote/views/website_sale_quote.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/website_sale_quote/views/website_sale_quote.xml b/addons/website_sale_quote/views/website_sale_quote.xml index b217bb4a548..5df3618e4a3 100644 --- a/addons/website_sale_quote/views/website_sale_quote.xml +++ b/addons/website_sale_quote/views/website_sale_quote.xml @@ -255,6 +255,7 @@
+ From 8957c6198774c7ecde2f4961b094dbd784b3e7f0 Mon Sep 17 00:00:00 2001 From: "ajay javiya (OpenERP)" Date: Wed, 11 Dec 2013 11:38:17 +0530 Subject: [PATCH 07/64] [IMP]: hide buttons in sale order if so is template bzr revid: aja@tinyerp.com-20131211060817-0vln868rycjn8ux2 --- addons/website_sale_quote/sale_quote_demo.xml | 4 ++-- addons/website_sale_quote/sale_quote_view.xml | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/addons/website_sale_quote/sale_quote_demo.xml b/addons/website_sale_quote/sale_quote_demo.xml index 1095779cac8..da4c62511bc 100644 --- a/addons/website_sale_quote/sale_quote_demo.xml +++ b/addons/website_sale_quote/sale_quote_demo.xml @@ -291,7 +291,7 @@ - Gold Partner + Fuctional Training 1 1 @@ -336,7 +336,7 @@ - Functional Training + Technical Training 1 1 diff --git a/addons/website_sale_quote/sale_quote_view.xml b/addons/website_sale_quote/sale_quote_view.xml index 11ab98d2339..4d522f45fec 100644 --- a/addons/website_sale_quote/sale_quote_view.xml +++ b/addons/website_sale_quote/sale_quote_view.xml @@ -6,6 +6,18 @@ sale.order + + + From eda0c7f3e0591a5d5bfd342299b7fb9acfe87275 Mon Sep 17 00:00:00 2001 From: "ajay javiya (OpenERP)" Date: Fri, 13 Dec 2013 10:39:23 +0530 Subject: [PATCH 10/64] [IMP] : User will post on quotation without login, user is identify from the link that he get in his mail bzr revid: aja@tinyerp.com-20131213050923-nwktcsnu3e62y3kg --- addons/website_sale_quote/controllers/main.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/addons/website_sale_quote/controllers/main.py b/addons/website_sale_quote/controllers/main.py index bea645a28d9..ca77fd5bdc5 100644 --- a/addons/website_sale_quote/controllers/main.py +++ b/addons/website_sale_quote/controllers/main.py @@ -22,6 +22,12 @@ class sale_quote(http.Controller): access_token = order_pool.browse(request.cr, SUPERUSER_ID, order_id, context=request.context).access_token return access_token or order_id + def _get_partner_user(self, order_id): + order_pool = request.registry.get('sale.order') + user_pool = request.registry.get('res.users') + partner = order_pool.browse(request.cr, SUPERUSER_ID, order_id, context=request.context).partner_id.id + return user_pool.search(request.cr, SUPERUSER_ID, [('partner_id', '=', partner)])[0] + @website.route(['/quote/','/quote/'], type='http', auth="public") def view(self, token=None, order_id=None, **post): values = {} @@ -54,7 +60,7 @@ class sale_quote(http.Controller): if post.get('new_message'): request.session.body = post.get('new_message') if 'body' in request.session and request.session.body: - request.registry.get('sale.order').message_post(request.cr, request.uid, order_id, + request.registry.get('sale.order').message_post(request.cr, self._get_partner_user(order_id), order_id, body=request.session.body, type='email', subtype='mt_comment', From 68443085e1f060bbe93896ba524e75a5084a455a Mon Sep 17 00:00:00 2001 From: "ajay javiya (OpenERP)" Date: Mon, 16 Dec 2013 10:54:51 +0530 Subject: [PATCH 11/64] [ADD]: object sale.quote.template and improve code bzr revid: aja@tinyerp.com-20131216052451-rqz5tdqzqj4cv5ar --- addons/website_sale_quote/__openerp__.py | 2 +- addons/website_sale_quote/controllers/main.py | 58 +++++---- addons/website_sale_quote/models/order.py | 117 ++++++++++++------ addons/website_sale_quote/sale_quote_demo.xml | 59 ++++----- addons/website_sale_quote/sale_quote_view.xml | 94 +++++++++++--- .../static/src/css/sale_quote.css | 23 ++-- .../static/src/js/sale_quote.js | 4 +- .../views/website_sale_quote.xml | 47 ++++--- 8 files changed, 254 insertions(+), 150 deletions(-) diff --git a/addons/website_sale_quote/__openerp__.py b/addons/website_sale_quote/__openerp__.py index da6c2175db3..dd54fa609fa 100644 --- a/addons/website_sale_quote/__openerp__.py +++ b/addons/website_sale_quote/__openerp__.py @@ -9,7 +9,7 @@ OpenERP Sale Quote Roller """, 'author': 'OpenERP SA', - 'depends': ['website_sale','website','product','portal_sale', 'mail'], + 'depends': ['website_sale','portal_sale', 'mail'], 'data': [ 'views/website_sale_quote.xml', 'sale_quote_view.xml', diff --git a/addons/website_sale_quote/controllers/main.py b/addons/website_sale_quote/controllers/main.py index ca77fd5bdc5..fa90883858a 100644 --- a/addons/website_sale_quote/controllers/main.py +++ b/addons/website_sale_quote/controllers/main.py @@ -1,18 +1,33 @@ -import random -import uuid -import simplejson - -import werkzeug.exceptions +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# 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 openerp import SUPERUSER_ID -from openerp.osv import osv from openerp.addons.web import http from openerp.addons.web.http import request from openerp.addons.website.models import website + class sale_quote(http.Controller): - def get_quote(self, token): + def _get_quote(self, token): order_pool = request.registry.get('sale.order') order_id = order_pool.search(request.cr, SUPERUSER_ID, [('access_token', '=', token)], context=request.context) return order_id @@ -28,35 +43,30 @@ class sale_quote(http.Controller): partner = order_pool.browse(request.cr, SUPERUSER_ID, order_id, context=request.context).partner_id.id return user_pool.search(request.cr, SUPERUSER_ID, [('partner_id', '=', partner)])[0] - @website.route(['/quote/','/quote/'], type='http', auth="public") + @website.route(['/quote/', '/quote/'], type='http', auth="public") def view(self, token=None, order_id=None, **post): values = {} order_pool = request.registry.get('sale.order') if token: - order_id = self.get_quote(token)[0] + order_id = self._get_quote(token)[0] quotation = order_pool.browse(request.cr, SUPERUSER_ID, order_id) - render_template = 'website_sale_quote.so_quotation' values.update({ - 'quotation' : quotation, - 'total_mail' : len(order_pool.search(request.cr, request.uid,[('id','=',order_id),('message_ids.type', '=', 'email')], context=request.context)), + 'quotation': quotation, }) - return request.website.render(render_template, values) + return request.website.render('website_sale_quote.so_quotation', values) @website.route(['/quote//accept'], type='http', auth="public") def accept(self, order_id=None, **post): - values = {} - quotation = request.registry.get('sale.order').write(request.cr, SUPERUSER_ID, [order_id], {'state': 'manual'}) + request.registry.get('sale.order').write(request.cr, SUPERUSER_ID, [order_id], {'state': 'manual'}) return request.redirect("/quote/%s" % self._get_token(order_id)) @website.route(['/quote//decline'], type='http', auth="public") def decline(self, order_id=None, **post): - values = {} - quotation = request.registry.get('sale.order').write(request.cr, SUPERUSER_ID, [order_id], {'state': 'cancel'}) + request.registry.get('sale.order').write(request.cr, SUPERUSER_ID, [order_id], {'state': 'cancel'}) return request.redirect("/quote/%s" % self._get_token(order_id)) @website.route(['/quote//post'], type='http', auth="public") def post(self, order_id=None, **post): - values = {} if post.get('new_message'): request.session.body = post.get('new_message') if 'body' in request.session and request.session.body: @@ -71,16 +81,14 @@ class sale_quote(http.Controller): @website.route(['/quote/update_line'], type='json', auth="public") def update(self, line_id=None, remove=False, unlink=False, order_id=None, **post): if unlink: - request.registry.get('sale.order.line').unlink(request.cr, SUPERUSER_ID,[int(line_id)], context=request.context) - return - else: - val = self._update_order_line(line_id=int(line_id), number=(remove and -1 or 1)) + return request.registry.get('sale.order.line').unlink(request.cr, SUPERUSER_ID, [int(line_id)], context=request.context) + val = self._update_order_line(line_id=int(line_id), number=(remove and -1 or 1)) order = request.registry.get('sale.order').browse(request.cr, SUPERUSER_ID, order_id) - return [val , order.amount_total] + return [val, order.amount_total] - def _update_order_line(self,line_id, number): + def _update_order_line(self, line_id, number): order_line_obj = request.registry.get('sale.order.line') order_line_val = order_line_obj.read(request.cr, SUPERUSER_ID, [int(line_id)], [], context=request.context)[0] quantity = order_line_val['product_uom_qty'] + number - order_line_obj.write(request.cr, SUPERUSER_ID, [int(line_id)], {'product_uom_qty':(quantity)}, context=request.context) + order_line_obj.write(request.cr, SUPERUSER_ID, [int(line_id)], {'product_uom_qty': (quantity)}, context=request.context) return quantity diff --git a/addons/website_sale_quote/models/order.py b/addons/website_sale_quote/models/order.py index c3a8d81c7fe..5334499079e 100644 --- a/addons/website_sale_quote/models/order.py +++ b/addons/website_sale_quote/models/order.py @@ -23,87 +23,130 @@ from openerp.osv import osv, fields import hashlib import time + +class sale_quote_template(osv.osv): + _name = "sale.quote.template" + _description = "Sale Quotation Template" + _columns = { + 'name': fields.char('Quotation Template', size=256, required=True), + 'website_description': fields.html('Description'), + 'quote_line': fields.one2many('sale.quote.line', 'quote_id', 'Quote Template Lines'), + 'note': fields.text('Terms and conditions'), + } + + +class sale_quote_line(osv.osv): + _name = "sale.quote.line" + _description = "Quotation Template Lines" + _columns = { + 'quote_id': fields.many2one('sale.quote.template', 'Quotation Template Reference', required=True, ondelete='cascade', select=True), + 'name': fields.text('Description', required=True), + 'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True), + 'website_description': fields.html('Line Description'), + 'price_unit': fields.float('Unit Price', required=True), + 'product_uom_qty': fields.float('Quantity', required=True), + } + + _defaults = { + 'product_uom_qty': 1, + } + + def on_change_product_id(self, cr, uid, ids, product, context=None): + vals = {} + product_obj = self.pool.get('product.product') + product_obj = product_obj.browse(cr, uid, product, context=context) + vals.update({ + 'price_unit': product_obj.list_price, + 'website_description': product_obj.website_description, + 'name': product_obj.name, + }) + return {'value': vals} + + class sale_order_line(osv.osv): _inherit = "sale.order.line" _description = "Sales Order Line" _columns = { - 'website_description':fields.html('Line Description'), + 'website_description': fields.html('Line Description'), } - def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,uom=False, qty_uos=0, uos=False, name='', partner_id=False,lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None): - res = super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty,uom, qty_uos, uos, name, partner_id,lang, update_tax, date_order, packaging, fiscal_position, flag, context) + def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None): + res = super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty, uom, qty_uos, uos, name, partner_id, lang, update_tax, date_order, packaging, fiscal_position, flag, context) if product: desc = self.pool.get('product.product').browse(cr, uid, product, context).website_description res.get('value').update({'website_description': desc}) return res + class sale_order(osv.osv): _inherit = 'sale.order' _columns = { 'quote_url': fields.char('URL', readonly=True), - 'access_token':fields.char('Quotation Token', size=256), - 'template_id': fields.many2one('sale.order','Quote Template'), + 'access_token': fields.char('Quotation Token', size=256), + 'template_id': fields.many2one('sale.quote.template', 'Quote Template'), 'website_description': fields.html('Description'), - 'is_template': fields.boolean('Is Template'), } - def new_quotation_token(self, cr, uid, ids,context=None): + def _get_token(self, cr, uid, ids, context=None): + """ + Generate token for sale order on action_quotation_send , send it to customer. + """ db_uuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid') - quotation_token = hashlib.sha256('%s-%s-%s' % (time.time(), db_uuid, ids[0])).hexdigest() - return self.write(cr, uid, ids,{'access_token': quotation_token,'quote_url': self._get_signup_url(cr, uid, False,quotation_token, context)} ) + return hashlib.sha256('%s-%s-%s' % (time.time(), db_uuid, ids[0])).hexdigest() def create(self, cr, uid, vals, context=None): - template_id = vals.get('template_id', False) new_id = super(sale_order, self).create(cr, uid, vals, context=context) - self.create_portal_user(cr, uid, new_id, context=context) - self.write(cr, uid, [new_id],{'quote_url': self._get_signup_url(cr, uid, new_id, False, context)} ) + self.write(cr, uid, [new_id], {'quote_url': self._get_signup_url(cr, uid, new_id, False, context)}) return new_id def action_quotation_send(self, cr, uid, ids, context=None): + token = self._get_token(cr, uid, ids, context) + self._create_portal_user(cr, uid, ids, context=context) + self.write(cr, uid, ids, {'access_token': token, 'quote_url': self._get_signup_url(cr, uid, False, token, context)}) res = super(sale_order, self).action_quotation_send(cr, uid, ids, context=context) - self.new_quotation_token(cr, uid, ids,context) return res - def create_portal_user(self, cr, uid, order_id, context=None): + def _create_portal_user(self, cr, uid, ids, context=None): + """ + create portal user of customer in quotation , when action_quotation_send perform. + """ + user = [] portal_ids = self.pool.get('res.groups').search(cr, uid, [('is_portal', '=', True)]) user_wizard_pool = self.pool.get('portal.wizard.user') - order = self.browse(cr, uid, order_id, context=context) - wizard_id = self.pool.get('portal.wizard').create(cr, uid,{'portal_id': portal_ids and portal_ids[0] or False}) - user_id = user_wizard_pool.create(cr, uid,{ - 'wizard_id':wizard_id, - 'partner_id':order.partner_id.id, - 'email':order.partner_id.email, - 'in_portal':True} ) - return user_wizard_pool.action_apply(cr, uid, [user_id], context=context) + for order in self.browse(cr, uid, ids, context=context): + wizard_id = self.pool.get('portal.wizard').create(cr, uid, {'portal_id': portal_ids and portal_ids[0] or False}) + user.append(user_wizard_pool.create(cr, uid, { + 'wizard_id': wizard_id, + 'partner_id': order.partner_id.id, + 'email': order.partner_id.email, + 'in_portal': True})) + return user_wizard_pool.action_apply(cr, uid, user, context=context) def _get_signup_url(self, cr, uid, order_id=False, token=False, context=None): base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='http://localhost:8069', context=context) - url = "%s/quote/%s" % (base_url ,token and token or order_id) + url = "%s/quote/%s" % (base_url, token and token or order_id) return url - def _get_sale_order_line(self, cr, uid,template_id, context=None): - line_pool = self.pool.get('sale.order.line') + def _get_sale_order_line(self, cr, uid, template_id, context=None): + """create order line from selected quote template line.""" lines = [] - order_template = self.browse(cr, uid, template_id, context) - for line in order_template.order_line: - lines.append((0,0,{ + quote_template = self.pool.get('sale.quote.template').browse(cr, uid, template_id, context) + for line in quote_template.quote_line: + lines.append((0, 0, { 'name': line.name, - 'sequence': line.sequence, 'price_unit': line.price_unit, 'product_uom_qty': line.product_uom_qty, - 'discount': line.discount, 'product_id': line.product_id.id, - 'tax_id': [(6, 0, [x.id for x in line.tax_id])], - 'website_description':line.website_description, - 'state':'draft', + 'website_description': line.website_description, + 'state': 'draft', })) - return {'order_line':lines,'website_description': order_template.website_description} + return {'order_line': lines, 'website_description': quote_template.website_description, 'note': quote_template.note} - def onchange_template_id(self, cr, uid,ids, template_id, context=None): + def onchange_template_id(self, cr, uid, ids, template_id, context=None): data = self._get_sale_order_line(cr, uid, template_id, context) - return {'value':data} + return {'value': data} - def recommended_products(self, cr, uid, ids,context=None): + def recommended_products(self, cr, uid, ids, context=None): order_line = self.browse(cr, uid, ids[0], context=context).order_line product_pool = self.pool.get('product.product') product_ids = [] diff --git a/addons/website_sale_quote/sale_quote_demo.xml b/addons/website_sale_quote/sale_quote_demo.xml index 85acb0dd55d..a53cbf3023c 100644 --- a/addons/website_sale_quote/sale_quote_demo.xml +++ b/addons/website_sale_quote/sale_quote_demo.xml @@ -12,6 +12,7 @@ Learn directly from our team and network of OpenERP experts. Choose from the available training sessions for a better functional understanding of OpenERP + QF11 @@ -28,6 +29,7 @@ Learn directly from our team and network of OpenERP experts. Choose from the available training sessions for a better technical understanding of OpenERP + QF12 @@ -44,6 +46,7 @@ Learn directly from our team and network of OpenERP experts. Choose from the available training sessions for a better functional understanding of OpenERP + QF13 @@ -60,17 +63,15 @@ Learn directly from our team and network of OpenERP experts. Choose from the available training sessions for a better functional understanding of OpenERP + QF14 - - - - - - - + + + Partnership Contract with Training + The 5-day training is modular, which means that you can choose to participate in the full training or to just 1 or 2 modules. Nevertheless, the first day of the training is compulsory for everyone. The Monday is compulsory because the introduction of OpenERP is important before going through the other modules.
@@ -106,7 +107,7 @@
- +
@@ -145,13 +146,13 @@

- As a partner, you get a big discount on OpenERP Enterprise. - This allows you to offer reduced prices to your customers and/or + As a partner, you get a big discount on OpenERP Enterprise. + This allows you to offer reduced prices to your customers and/or to benefit from a commission on every subscription you sell to your customers.

-
+

Benefit From A Dedicated Account Manager

Our role is to help you succeed

@@ -163,28 +164,28 @@

A dedicated account manager is assigned to every partner to help develop the OpenERP business. The account manager helps you get leads, close deals, gives you feedback and best practices, delivers sales training and is a direct point of contact for any request you may have.

-
+

Get access to our consultants and technical experts

Get access to experts for an extra fee

For an extra fee, partners can get access to OpenERP's core developers and functional experts. This can help you succeed in delivering more complex or bigger projects by getting the support of highly experienced consultants on demand.

-
+

Get recognition as an official certified partner

We give you visibility in our official partner list

OpenERP promotes its partners through various ways: publication on our website, official communication, publication of your success stories, etc. As soon as you become an OpenERP partner and have followed the official trainings, you will be visible on the partner directory listing.

-
+

Access to the Lead Generation Program

We give you visibility in our official partner list

Every year, we redirect more than 100,000 customer requests to our official partners. These are prospects that filled a form on the OpenERP website and wanted to use OpenERP. The right partner to fulfill the customer request is selected based on the customer localization (nearest partner) and the grade of the partner..

-
+

Benefit from the OpenERP branding

Get rights to use the OpenERP trademark

@@ -230,7 +231,7 @@
-
+

Get access to online training materials

Train yourself on every new release using online videosGet rights to use the OpenERP trademark

@@ -246,7 +247,7 @@

As you go through your first implementation of an OpenERP system it is normal to need some support. We are here to answer your questions regarding the functionality of the software, or how to configure the solution. Each partner is entitled to a certain number of support hours thanks to his or her partner program.

-
+

Get technical support services

We help your developers with bugs or support questions

@@ -259,7 +260,7 @@

The OpenERP Support Team is 100% dedicated to helping our customers. You can contact the Support Team for any functional or technical questions and issues with regard to OpenERP development. They can help you develop custom modules.

-
+

Test all your developments automatically

Your dedicated test integration platform

@@ -321,13 +322,11 @@ - - + + Fuctional Training 1 - 1 - 12950.00
@@ -366,13 +365,11 @@ - - + + Technical Training 1 - 1 - 00.00
@@ -406,15 +403,5 @@
- - - - - - - - manual - - diff --git a/addons/website_sale_quote/sale_quote_view.xml b/addons/website_sale_quote/sale_quote_view.xml index 4d522f45fec..c10701ead7e 100644 --- a/addons/website_sale_quote/sale_quote_view.xml +++ b/addons/website_sale_quote/sale_quote_view.xml @@ -6,33 +6,89 @@ sale.order - -
+ From 1e17cd0fc86157fe43493b0f163ec38424df376b Mon Sep 17 00:00:00 2001 From: "ajay javiya (OpenERP)" Date: Mon, 16 Dec 2013 14:19:35 +0530 Subject: [PATCH 12/64] [IMP]: code bzr revid: aja@tinyerp.com-20131216084935-frtb88w60l189id1 --- addons/website_sale_quote/controllers/main.py | 21 +++++++++---------- addons/website_sale_quote/models/order.py | 20 +++++++++--------- .../views/website_sale_quote.xml | 2 +- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/addons/website_sale_quote/controllers/main.py b/addons/website_sale_quote/controllers/main.py index fa90883858a..0d6ebb2217f 100644 --- a/addons/website_sale_quote/controllers/main.py +++ b/addons/website_sale_quote/controllers/main.py @@ -41,29 +41,28 @@ class sale_quote(http.Controller): order_pool = request.registry.get('sale.order') user_pool = request.registry.get('res.users') partner = order_pool.browse(request.cr, SUPERUSER_ID, order_id, context=request.context).partner_id.id - return user_pool.search(request.cr, SUPERUSER_ID, [('partner_id', '=', partner)])[0] + if partner: + user = user_pool.search(request.cr, SUPERUSER_ID, [('partner_id', '=', partner)])[0] + return user - @website.route(['/quote/', '/quote/'], type='http', auth="public") - def view(self, token=None, order_id=None, **post): + @website.route(["/quote//"], type='http', auth="public") + def view(self, order=None, token=None,**post): + assert token == order.access_token, 'No token found' values = {} - order_pool = request.registry.get('sale.order') - if token: - order_id = self._get_quote(token)[0] - quotation = order_pool.browse(request.cr, SUPERUSER_ID, order_id) values.update({ - 'quotation': quotation, + 'quotation': order, }) return request.website.render('website_sale_quote.so_quotation', values) @website.route(['/quote//accept'], type='http', auth="public") def accept(self, order_id=None, **post): request.registry.get('sale.order').write(request.cr, SUPERUSER_ID, [order_id], {'state': 'manual'}) - return request.redirect("/quote/%s" % self._get_token(order_id)) + return request.redirect("/quote/%s/%s" % (order_id, self._get_token(order_id))) @website.route(['/quote//decline'], type='http', auth="public") def decline(self, order_id=None, **post): request.registry.get('sale.order').write(request.cr, SUPERUSER_ID, [order_id], {'state': 'cancel'}) - return request.redirect("/quote/%s" % self._get_token(order_id)) + return request.redirect("/quote/%s/%s" % (order_id, self._get_token(order_id))) @website.route(['/quote//post'], type='http', auth="public") def post(self, order_id=None, **post): @@ -76,7 +75,7 @@ class sale_quote(http.Controller): subtype='mt_comment', ) request.session.body = False - return request.redirect("/quote/%s" % self._get_token(order_id)) + return request.redirect("/quote/%s/%s" % (order_id, self._get_token(order_id))) @website.route(['/quote/update_line'], type='json', auth="public") def update(self, line_id=None, remove=False, unlink=False, order_id=None, **post): diff --git a/addons/website_sale_quote/models/order.py b/addons/website_sale_quote/models/order.py index 5334499079e..7afa246776e 100644 --- a/addons/website_sale_quote/models/order.py +++ b/addons/website_sale_quote/models/order.py @@ -87,22 +87,27 @@ class sale_order(osv.osv): 'website_description': fields.html('Description'), } - def _get_token(self, cr, uid, ids, context=None): + def _get_token(self, cr, uid, oder_id, context=None): """ Generate token for sale order on action_quotation_send , send it to customer. """ db_uuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid') - return hashlib.sha256('%s-%s-%s' % (time.time(), db_uuid, ids[0])).hexdigest() + return hashlib.sha256('%s-%s-%s' % (time.time(), db_uuid, oder_id)).hexdigest() + + def _get_signup_url(self, cr, uid, order_id=False, token=False, context=None): + base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='http://localhost:8069', context=context) + url = "%s/quote/%s/%s" % (base_url, order_id, token) + return url def create(self, cr, uid, vals, context=None): new_id = super(sale_order, self).create(cr, uid, vals, context=context) - self.write(cr, uid, [new_id], {'quote_url': self._get_signup_url(cr, uid, new_id, False, context)}) + token = self._get_token(cr, uid, new_id, context) + url = self._get_signup_url(cr, uid, new_id, token, context) + self.write(cr, uid, [new_id], {'access_token': token,'quote_url': url}) return new_id def action_quotation_send(self, cr, uid, ids, context=None): - token = self._get_token(cr, uid, ids, context) self._create_portal_user(cr, uid, ids, context=context) - self.write(cr, uid, ids, {'access_token': token, 'quote_url': self._get_signup_url(cr, uid, False, token, context)}) res = super(sale_order, self).action_quotation_send(cr, uid, ids, context=context) return res @@ -122,11 +127,6 @@ class sale_order(osv.osv): 'in_portal': True})) return user_wizard_pool.action_apply(cr, uid, user, context=context) - def _get_signup_url(self, cr, uid, order_id=False, token=False, context=None): - base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='http://localhost:8069', context=context) - url = "%s/quote/%s" % (base_url, token and token or order_id) - return url - def _get_sale_order_line(self, cr, uid, template_id, context=None): """create order line from selected quote template line.""" lines = [] diff --git a/addons/website_sale_quote/views/website_sale_quote.xml b/addons/website_sale_quote/views/website_sale_quote.xml index cd5fd34a2fc..d72c1e1395d 100644 --- a/addons/website_sale_quote/views/website_sale_quote.xml +++ b/addons/website_sale_quote/views/website_sale_quote.xml @@ -165,7 +165,7 @@
  • - + From cacba108933627b178d47a74800650458b9ec493 Mon Sep 17 00:00:00 2001 From: "ajay javiya (OpenERP)" Date: Mon, 16 Dec 2013 18:53:46 +0530 Subject: [PATCH 13/64] [IMP][ADD]: template for sale.quote.template , to allow create a new template using cms bzr revid: aja@tinyerp.com-20131216132346-4lw4t6whsqplfgwq --- addons/website_sale_quote/__openerp__.py | 4 ++- addons/website_sale_quote/controllers/main.py | 8 +++++ addons/website_sale_quote/models/order.py | 30 ++++++++++------ addons/website_sale_quote/sale_quote_view.xml | 3 ++ .../views/website_sale_quote.xml | 35 ++++++++++++++++++- 5 files changed, 68 insertions(+), 12 deletions(-) diff --git a/addons/website_sale_quote/__openerp__.py b/addons/website_sale_quote/__openerp__.py index dd54fa609fa..788ab54140b 100644 --- a/addons/website_sale_quote/__openerp__.py +++ b/addons/website_sale_quote/__openerp__.py @@ -13,7 +13,9 @@ OpenERP Sale Quote Roller 'data': [ 'views/website_sale_quote.xml', 'sale_quote_view.xml', - 'sale_quote_data.xml' + 'sale_quote_data.xml', + 'security/ir.model.access.csv', + 'security/website_sale_quote.xml', ], 'demo': [ 'sale_quote_demo.xml' diff --git a/addons/website_sale_quote/controllers/main.py b/addons/website_sale_quote/controllers/main.py index 0d6ebb2217f..1a8e7c234a8 100644 --- a/addons/website_sale_quote/controllers/main.py +++ b/addons/website_sale_quote/controllers/main.py @@ -91,3 +91,11 @@ class sale_quote(http.Controller): quantity = order_line_val['product_uom_qty'] + number order_line_obj.write(request.cr, SUPERUSER_ID, [int(line_id)], {'product_uom_qty': (quantity)}, context=request.context) return quantity + + @website.route(["/template/"], type='http', auth="public") + def template_view(self, quote=None, **post): + values = {} + values.update({ + 'template': quote, + }) + return request.website.render('website_sale_quote.so_template', values) diff --git a/addons/website_sale_quote/models/order.py b/addons/website_sale_quote/models/order.py index 7afa246776e..923c78ab92d 100644 --- a/addons/website_sale_quote/models/order.py +++ b/addons/website_sale_quote/models/order.py @@ -32,8 +32,18 @@ class sale_quote_template(osv.osv): 'website_description': fields.html('Description'), 'quote_line': fields.one2many('sale.quote.line', 'quote_id', 'Quote Template Lines'), 'note': fields.text('Terms and conditions'), + 'template_url': fields.char('URL', readonly=True), } + def _get_signup_url(self, cr, uid, quote_id, context=None): + base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='http://localhost:8069', context=context) + url = "%s/template/%s" % (base_url, quote_id) + return url + + def create(self, cr, uid, ids, context=None): + new_id = super(sale_quote_template, self).create(cr, uid, ids, context) + self.write(cr, uid, new_id, {'template_url':self._get_signup_url(cr, uid, new_id, context=context)}) + return new_id class sale_quote_line(osv.osv): _name = "sale.quote.line" @@ -87,27 +97,27 @@ class sale_order(osv.osv): 'website_description': fields.html('Description'), } - def _get_token(self, cr, uid, oder_id, context=None): + def _get_token(self, cr, uid, ids, context=None): """ Generate token for sale order on action_quotation_send , send it to customer. """ db_uuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid') - return hashlib.sha256('%s-%s-%s' % (time.time(), db_uuid, oder_id)).hexdigest() + for id in ids: + token = hashlib.sha256('%s-%s-%s' % (time.time(), db_uuid, id)).hexdigest() + return token - def _get_signup_url(self, cr, uid, order_id=False, token=False, context=None): + def _get_signup_url(self, cr, uid, ids, token=False, context=None): base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='http://localhost:8069', context=context) - url = "%s/quote/%s/%s" % (base_url, order_id, token) + for id in ids: + url = "%s/quote/%s/%s" % (base_url, id, token) return url - def create(self, cr, uid, vals, context=None): - new_id = super(sale_order, self).create(cr, uid, vals, context=context) - token = self._get_token(cr, uid, new_id, context) - url = self._get_signup_url(cr, uid, new_id, token, context) - self.write(cr, uid, [new_id], {'access_token': token,'quote_url': url}) - return new_id def action_quotation_send(self, cr, uid, ids, context=None): self._create_portal_user(cr, uid, ids, context=context) + token = self._get_token(cr, uid, ids, context) + url = self._get_signup_url(cr, uid, ids, token, context) + self.write(cr, uid, ids, {'access_token': token,'quote_url': url}) res = super(sale_order, self).action_quotation_send(cr, uid, ids, context=context) return res diff --git a/addons/website_sale_quote/sale_quote_view.xml b/addons/website_sale_quote/sale_quote_view.xml index c10701ead7e..489325d6110 100644 --- a/addons/website_sale_quote/sale_quote_view.xml +++ b/addons/website_sale_quote/sale_quote_view.xml @@ -23,6 +23,9 @@ form
    +
    + +

  • From 5567caa27d3dcb6e9024dee23d4d7a5052b8444d Mon Sep 17 00:00:00 2001 From: "ajay javiya (OpenERP)" Date: Wed, 18 Dec 2013 11:26:45 +0530 Subject: [PATCH 16/64] [IMP]: overwrite record rule to allow public user to read sale.order data bzr revid: aja@tinyerp.com-20131218055645-fb9izyc3029jtz0k --- .../website_sale/security/ir.model.access.csv | 2 +- addons/website_sale_quote/__openerp__.py | 1 + addons/website_sale_quote/sale_quote_demo.xml | 4 +++ .../security/ir.model.access.csv | 1 - .../security/website_sale_quote.xml | 25 +++++++++++++++++++ 5 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 addons/website_sale_quote/security/website_sale_quote.xml diff --git a/addons/website_sale/security/ir.model.access.csv b/addons/website_sale/security/ir.model.access.csv index 4723d66b5c5..c9e88c29da9 100644 --- a/addons/website_sale/security/ir.model.access.csv +++ b/addons/website_sale/security/ir.model.access.csv @@ -12,4 +12,4 @@ access_sale_order_line_public,sale.order.line.public,model_sale_order_line,base. access_product_attribute,product.attribute.public,website_sale.model_product_attribute,,1,0,0,0 access_product_attribute_value,product.attribute.value.public,website_sale.model_product_attribute_value,,1,0,0,0 access_product_attribute_product,product.attribute.product.public,website_sale.model_product_attribute_product,,1,0,0,0 -access_website_product_style,website.product.style.public,website_sale.model_website_product_style,,1,0,0,0 \ No newline at end of file +access_website_product_style,website.product.style.public,website_sale.model_website_product_style,,1,0,0,0 diff --git a/addons/website_sale_quote/__openerp__.py b/addons/website_sale_quote/__openerp__.py index b35a305fd48..788ab54140b 100644 --- a/addons/website_sale_quote/__openerp__.py +++ b/addons/website_sale_quote/__openerp__.py @@ -15,6 +15,7 @@ OpenERP Sale Quote Roller 'sale_quote_view.xml', 'sale_quote_data.xml', 'security/ir.model.access.csv', + 'security/website_sale_quote.xml', ], 'demo': [ 'sale_quote_demo.xml' diff --git a/addons/website_sale_quote/sale_quote_demo.xml b/addons/website_sale_quote/sale_quote_demo.xml index 8e5e15f3cd0..6fd63f96685 100644 --- a/addons/website_sale_quote/sale_quote_demo.xml +++ b/addons/website_sale_quote/sale_quote_demo.xml @@ -14,6 +14,7 @@ + QF11 @@ -31,6 +32,7 @@ + QF12 @@ -48,6 +50,7 @@ + QF13 @@ -65,6 +68,7 @@ + QF14 diff --git a/addons/website_sale_quote/security/ir.model.access.csv b/addons/website_sale_quote/security/ir.model.access.csv index dfe466ca18f..97dd8b917b8 100644 --- a/addons/website_sale_quote/security/ir.model.access.csv +++ b/addons/website_sale_quote/security/ir.model.access.csv @@ -1,2 +1 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_sale_order_quote,sale.order.quote,sale.model_sale_order,base.group_sale_quote_user,1,1,1,0 diff --git a/addons/website_sale_quote/security/website_sale_quote.xml b/addons/website_sale_quote/security/website_sale_quote.xml new file mode 100644 index 00000000000..a49bccc2b03 --- /dev/null +++ b/addons/website_sale_quote/security/website_sale_quote.xml @@ -0,0 +1,25 @@ + + + + + + Public Personal Orders + + [] + + + + + + + + res_partner: portal/public: read access on my commercial partner + + [] + + + + + + + From 1b7a51b9b9682c7cfcb43dc2fc0f82e3281d2d75 Mon Sep 17 00:00:00 2001 From: "ajay javiya (OpenERP)" Date: Wed, 18 Dec 2013 12:52:35 +0530 Subject: [PATCH 17/64] [IMP]: code and security bzr revid: aja@tinyerp.com-20131218072235-fglrt10ln4ela6t3 --- addons/website_sale_quote/models/order.py | 12 +++++------- .../security/website_sale_quote.xml | 10 ++++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/addons/website_sale_quote/models/order.py b/addons/website_sale_quote/models/order.py index 923c78ab92d..cdbe6104e2f 100644 --- a/addons/website_sale_quote/models/order.py +++ b/addons/website_sale_quote/models/order.py @@ -40,9 +40,9 @@ class sale_quote_template(osv.osv): url = "%s/template/%s" % (base_url, quote_id) return url - def create(self, cr, uid, ids, context=None): - new_id = super(sale_quote_template, self).create(cr, uid, ids, context) - self.write(cr, uid, new_id, {'template_url':self._get_signup_url(cr, uid, new_id, context=context)}) + def create(self, cr, uid, vals, context=None): + new_id = super(sale_quote_template, self).create(cr, uid, vals, context) + self.write(cr, uid, new_id, {'template_url': self._get_signup_url(cr, uid, new_id, context=context)}) return new_id class sale_quote_line(osv.osv): @@ -63,8 +63,7 @@ class sale_quote_line(osv.osv): def on_change_product_id(self, cr, uid, ids, product, context=None): vals = {} - product_obj = self.pool.get('product.product') - product_obj = product_obj.browse(cr, uid, product, context=context) + product_obj = self.pool.get('product.product').browse(cr, uid, product, context=context) vals.update({ 'price_unit': product_obj.list_price, 'website_description': product_obj.website_description, @@ -112,12 +111,11 @@ class sale_order(osv.osv): url = "%s/quote/%s/%s" % (base_url, id, token) return url - def action_quotation_send(self, cr, uid, ids, context=None): self._create_portal_user(cr, uid, ids, context=context) token = self._get_token(cr, uid, ids, context) url = self._get_signup_url(cr, uid, ids, token, context) - self.write(cr, uid, ids, {'access_token': token,'quote_url': url}) + self.write(cr, uid, ids, {'access_token': token, 'quote_url': url}) res = super(sale_order, self).action_quotation_send(cr, uid, ids, context=context) return res diff --git a/addons/website_sale_quote/security/website_sale_quote.xml b/addons/website_sale_quote/security/website_sale_quote.xml index a49bccc2b03..5635117d47c 100644 --- a/addons/website_sale_quote/security/website_sale_quote.xml +++ b/addons/website_sale_quote/security/website_sale_quote.xml @@ -12,6 +12,16 @@ + + Public Personal Order lines + + [] + + + + + + res_partner: portal/public: read access on my commercial partner From 0ed13a4a00513296c9b46b9e4b0cd42c5f4319db Mon Sep 17 00:00:00 2001 From: "ajay javiya (OpenERP)" Date: Wed, 18 Dec 2013 18:25:59 +0530 Subject: [PATCH 18/64] [ADD] : modal(wizard) on decline button , [IMP]: typos and code bzr revid: aja@tinyerp.com-20131218125559-km8k9ug0izotlynh --- addons/website_sale_quote/controllers/main.py | 21 ++++-- addons/website_sale_quote/sale_quote_view.xml | 71 ++++++++----------- .../static/src/js/sale_quote.js | 1 - .../views/website_sale_quote.xml | 27 ++++++- 4 files changed, 70 insertions(+), 50 deletions(-) diff --git a/addons/website_sale_quote/controllers/main.py b/addons/website_sale_quote/controllers/main.py index 1a8e7c234a8..e66c573b93d 100644 --- a/addons/website_sale_quote/controllers/main.py +++ b/addons/website_sale_quote/controllers/main.py @@ -45,12 +45,20 @@ class sale_quote(http.Controller): user = user_pool.search(request.cr, SUPERUSER_ID, [('partner_id', '=', partner)])[0] return user + def get_message(self, order): + total = 0 + for msg in order.message_ids: + if msg.subtype_id.name in ['Sales Order Confirmed', 'Discussions']: + total += 1 + return total + @website.route(["/quote//"], type='http', auth="public") def view(self, order=None, token=None,**post): assert token == order.access_token, 'No token found' values = {} values.update({ 'quotation': order, + 'message': self.get_message(order) }) return request.website.render('website_sale_quote.so_quotation', values) @@ -59,19 +67,20 @@ class sale_quote(http.Controller): request.registry.get('sale.order').write(request.cr, SUPERUSER_ID, [order_id], {'state': 'manual'}) return request.redirect("/quote/%s/%s" % (order_id, self._get_token(order_id))) - @website.route(['/quote//decline'], type='http', auth="public") - def decline(self, order_id=None, **post): - request.registry.get('sale.order').write(request.cr, SUPERUSER_ID, [order_id], {'state': 'cancel'}) - return request.redirect("/quote/%s/%s" % (order_id, self._get_token(order_id))) + def decline(self, order_id): + return request.registry.get('sale.order').write(request.cr, SUPERUSER_ID, [order_id], {'state': 'cancel'}) @website.route(['/quote//post'], type='http', auth="public") def post(self, order_id=None, **post): if post.get('new_message'): request.session.body = post.get('new_message') + if post.get('decline_message'): + self.decline(order_id) + request.session.body = post.get('decline_message') if 'body' in request.session and request.session.body: request.registry.get('sale.order').message_post(request.cr, self._get_partner_user(order_id), order_id, body=request.session.body, - type='email', + type='comment', subtype='mt_comment', ) request.session.body = False @@ -83,7 +92,7 @@ class sale_quote(http.Controller): return request.registry.get('sale.order.line').unlink(request.cr, SUPERUSER_ID, [int(line_id)], context=request.context) val = self._update_order_line(line_id=int(line_id), number=(remove and -1 or 1)) order = request.registry.get('sale.order').browse(request.cr, SUPERUSER_ID, order_id) - return [val, order.amount_total] + return [str(val), str(order.amount_total)] def _update_order_line(self, line_id, number): order_line_obj = request.registry.get('sale.order.line') diff --git a/addons/website_sale_quote/sale_quote_view.xml b/addons/website_sale_quote/sale_quote_view.xml index 8a8ed6af33c..79b87c92818 100644 --- a/addons/website_sale_quote/sale_quote_view.xml +++ b/addons/website_sale_quote/sale_quote_view.xml @@ -29,49 +29,40 @@

    -

    - - - -
    - - - - - - - - - - - - - -
    - + +
    + + - - + + + + + + + + + + +
    + + + + + + + +
    + +
    @@ -87,7 +78,7 @@ - Quote Template + Sales Template ir.actions.act_window sale.quote.template form diff --git a/addons/website_sale_quote/static/src/js/sale_quote.js b/addons/website_sale_quote/static/src/js/sale_quote.js index c687ea7a8a2..5e187816143 100644 --- a/addons/website_sale_quote/static/src/js/sale_quote.js +++ b/addons/website_sale_quote/static/src/js/sale_quote.js @@ -5,7 +5,6 @@ $(document).ready(function () { var href = $link.attr("href"); var order_id = $link.attr("href").match(/order_id=([0-9]+)/); var line_id = href.match(/update_line\/([0-9]+)/); - console.log(line_id); openerp.jsonRpc("/quote/update_line/", 'call', { 'line_id': line_id[1], 'order_id': parseInt(order_id[1]), diff --git a/addons/website_sale_quote/views/website_sale_quote.xml b/addons/website_sale_quote/views/website_sale_quote.xml index e9a09063b00..a7514020907 100644 --- a/addons/website_sale_quote/views/website_sale_quote.xml +++ b/addons/website_sale_quote/views/website_sale_quote.xml @@ -165,7 +165,7 @@
  • - + @@ -179,7 +179,7 @@ Discuss - +
  • @@ -195,6 +195,7 @@
      +
    • @@ -210,6 +211,7 @@
    • +
    @@ -223,6 +225,25 @@
    +
    @@ -241,7 +262,7 @@
  • -
  • Quotation
  • +
  • Pricing
  • Terms & Conditions
  • From 7ec71f382b5b9a78cbc45647c79de4fd8d9c5ed0 Mon Sep 17 00:00:00 2001 From: "ajay javiya (OpenERP)" Date: Thu, 19 Dec 2013 11:06:48 +0530 Subject: [PATCH 19/64] [REM]: static layout bzr revid: aja@tinyerp.com-20131219053648-kfk8yl03wa7kt2mb --- .../views/website_sale_quote.xml | 92 +------------------ 1 file changed, 1 insertion(+), 91 deletions(-) diff --git a/addons/website_sale_quote/views/website_sale_quote.xml b/addons/website_sale_quote/views/website_sale_quote.xml index a7514020907..bec6961b1b9 100644 --- a/addons/website_sale_quote/views/website_sale_quote.xml +++ b/addons/website_sale_quote/views/website_sale_quote.xml @@ -1,96 +1,6 @@ -