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 00000000000..d7b9f4f25bd Binary files /dev/null and b/addons/website_sale_quote/static/description/icon.png differ diff --git a/addons/website_sale_quote/static/src/css/sale_quote.css b/addons/website_sale_quote/static/src/css/sale_quote.css new file mode 100644 index 00000000000..9a222cddb63 --- /dev/null +++ b/addons/website_sale_quote/static/src/css/sale_quote.css @@ -0,0 +1,13 @@ +body { padding-top:30px; +} +.widget .panel-body { padding:0px; } +.widget .list-group { margin-bottom: 0; } +.widget .panel-title { display:inline } +.widget .label-info { float: right; } +.widget li.list-group-item {border-radius: 0;border: 0;border-top: 1px solid #ddd;} +.widget li.list-group-item:hover { background-color: rgba(86,61,124,.1); } +.widget .mic-info { color: #666666;font-size: 11px; } +.widget .action { margin-top:5px; } +.widget .comment-text { font-size: 12px; } +.widget .btn-block { border-top-left-radius:0px;border-top-right-radius:0px; } +.oe_section { padding-top:40px; } \ No newline at end of file diff --git a/addons/website_sale_quote/static/src/js/sale_quote.js b/addons/website_sale_quote/static/src/js/sale_quote.js new file mode 100644 index 00000000000..74cb49fbb3f --- /dev/null +++ b/addons/website_sale_quote/static/src/js/sale_quote.js @@ -0,0 +1,32 @@ +openerp.base_calendar = function(instance) { +var _t = instance.web._t; +var QWeb = instance.web.qweb; +instance.base_calendar = {} + + instance.sale_quote.quotation = instance.web.Widget.extend({ + + init: function(parent, db, action, id, view, quotation) { + this._super(); + this.db = db; + this.action = action; + this.id = id; + this.view = view; + this.quotation = quotation; + }, + start: function() { + var self = this; + self.open_invitation_form(self.quotation); + }, + open_quotation : function(quotation){ + alert('aaa'); + this.$el.html(QWeb.render('quotation_view', {'quotation': JSON.parse(quotation)})); + }, + }); + + instance.sale_quote.view = function (db, action, id, view, quotation) { + instance.session.session_bind(instance.session.origin).done(function () { + new instance.sale_quote.quotation(null,db,action,id,view,quotation).appendTo($("body").addClass('openerp')); + }); + } +}; +//vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/website_sale_quote/views/website_sale_quote.xml b/addons/website_sale_quote/views/website_sale_quote.xml new file mode 100644 index 00000000000..2280a91ae00 --- /dev/null +++ b/addons/website_sale_quote/views/website_sale_quote.xml @@ -0,0 +1,322 @@ + + + + + + +