[ADD]: object sale.quote.template and improve code

bzr revid: aja@tinyerp.com-20131216052451-rqz5tdqzqj4cv5ar
This commit is contained in:
ajay javiya (OpenERP) 2013-12-16 10:54:51 +05:30
parent 558e5fdf9b
commit 68443085e1
8 changed files with 254 additions and 150 deletions

View File

@ -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',

View File

@ -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 (<http://www.openerp.com>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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
@ -33,30 +48,25 @@ class sale_quote(http.Controller):
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)),
})
return request.website.render(render_template, values)
return request.website.render('website_sale_quote.so_quotation', values)
@website.route(['/quote/<int:order_id>/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/<int:order_id>/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/<int:order_id>/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,9 +81,7 @@ 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:
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]

View File

@ -23,6 +23,46 @@ 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"
@ -37,44 +77,50 @@ class sale_order_line(osv.osv):
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'),
'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)})
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)
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_id = user_wizard_pool.create(cr, uid,{
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_id], context=context)
'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)
@ -82,22 +128,19 @@ class sale_order(osv.osv):
return url
def _get_sale_order_line(self, cr, uid, template_id, context=None):
line_pool = self.pool.get('sale.order.line')
"""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:
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',
}))
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):
data = self._get_sale_order_line(cr, uid, template_id, context)

View File

@ -12,6 +12,7 @@
<field name="uom_po_id" ref="product.product_uom_unit"/>
<field name="description_sale">Learn directly from our team and network of OpenERP experts. Choose from the available training sessions for a better functional understanding of OpenERP</field>
</record>
<record id="product_product_quote_1" model="product.product">
<field name="product_tmpl_id" ref="product_template_quote_1"/>
<field name="default_code">QF11</field>
@ -28,6 +29,7 @@
<field name="uom_po_id" ref="product.product_uom_unit"/>
<field name="description_sale">Learn directly from our team and network of OpenERP experts. Choose from the available training sessions for a better technical understanding of OpenERP</field>
</record>
<record id="product_product_quote_2" model="product.product">
<field name="product_tmpl_id" ref="product_template_quote_2"/>
<field name="default_code">QF12</field>
@ -44,6 +46,7 @@
<field name="uom_po_id" ref="product.product_uom_unit"/>
<field name="description_sale">Learn directly from our team and network of OpenERP experts. Choose from the available training sessions for a better functional understanding of OpenERP</field>
</record>
<record id="product_product_quote_3" model="product.product">
<field name="product_tmpl_id" ref="product_template_quote_3"/>
<field name="default_code">QF13</field>
@ -60,17 +63,15 @@
<field name="uom_po_id" ref="product.product_uom_unit"/>
<field name="description_sale">Learn directly from our team and network of OpenERP experts. Choose from the available training sessions for a better functional understanding of OpenERP</field>
</record>
<record id="product_product_quote_4" model="product.product">
<field name="product_tmpl_id" ref="product_template_quote_4"/>
<field name="default_code">QF14</field>
</record>
<record id="website_sale_order_1" model="sale.order">
<field name="partner_id" ref="base.res_partner_2"/>
<field name="partner_invoice_id" ref="base.res_partner_2"/>
<field name="partner_shipping_id" ref="base.res_partner_2"/>
<field name="user_id" ref="base.user_demo"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="is_template" eval="True"/>
<record id="website_quote_template_1" model="sale.quote.template">
<field name="name">Partnership Contract with Training</field>
<field name="note">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.</field>
<field name="website_description" type="html">
<section id="whatsuit" class="tab-pane active oe_section jumbotron">
<div class="container panel panel-default tab-pane active oe_section">
@ -321,13 +322,11 @@
</field>
</record>
<record id="website_sale_order_line_1" model="sale.order.line">
<field name="order_id" ref="website_sale_order_1"/>
<record id="website_sale_order_line_1" model="sale.quote.line">
<field name="quote_id" ref="website_quote_template_1"/>
<field name="name">Fuctional Training</field>
<field name="product_id" ref="product_product_quote_1"/>
<field name="product_uom_qty">1</field>
<field name="product_uos_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="price_unit">12950.00</field>
<field name="website_description" type="html">
<section data-snippet-id="text-image" class="tab-pane oe_section jumbotron">
@ -366,13 +365,11 @@
</field>
</record>
<record id="website_sale_order_line_2" model="sale.order.line">
<field name="order_id" ref="website_sale_order_1"/>
<record id="website_sale_order_line_2" model="sale.quote.line">
<field name="quote_id" ref="website_quote_template_1"/>
<field name="name">Technical Training</field>
<field name="product_id" ref="product_product_quote_2"/>
<field name="product_uom_qty">1</field>
<field name="product_uos_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="price_unit">00.00</field>
<field name="website_description" type="html">
<section id="whatsuit" class="tab-pane active oe_section jumbotron">
@ -406,15 +403,5 @@
</section>
</field>
</record>
<record id="sale_order_2" model="sale.order">
<field name="partner_id" ref="base.res_partner_7"/>
<field name="partner_invoice_id" ref="base.res_partner_address_13"/>
<field name="partner_shipping_id" ref="base.res_partner_address_13"/>
<field name="user_id" ref="base.user_root"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="order_policy">manual</field>
</record>
</data>
</openerp>

View File

@ -6,33 +6,89 @@
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//button[@name='action_quotation_send']" position="replace">
<button name="action_quotation_send" string="Send by Email" type="object" class="oe_highlight" groups="base.group_user" attrs="{'invisible': ['|',('is_template', '=', True),('state','!=','draft')]}"/>
</xpath>
<xpath expr="//button[@string='Print'][@states='draft']" position="replace">
<button name="print_quotation" string="Print" type="object" class="oe_highlight" groups="base.group_user" attrs="{'invisible': ['|',('is_template', '=', True),('state','!=','draft')]}"/>
</xpath>
<xpath expr="//button[@string='Confirm Sale'][@states='draft']" position="replace">
<button name="action_button_confirm" string="Confirm Sale" type="object" groups="base.group_user" attrs="{'invisible': ['|',('is_template', '=', True),('state','!=','draft')]}"/>
</xpath>
<xpath expr="//button[@string='Cancel Quotation']" position="replace">
<button name="cancel" string="Cancel Quotation" groups="base.group_user" attrs="{'invisible': ['|',('is_template', '=', True),('state','not in',('draft','sent'))]}"/>
</xpath>
<xpath expr="//header" position="after">
<div class="oe_form_box_info oe_text_center" attrs="{'invisible': ['|',('quote_url', '=', False),('template_id','=',False)]}">
<field name="quote_url" widget="url"/>
</div>
</xpath>
<xpath expr="//field[@name='client_order_ref']" position="after">
<field name="is_template"/>
<field name="template_id" domain ="[('is_template','=',True)]" attrs="{'invisible': [('is_template', '=', True)]}" on_change="onchange_template_id(template_id)"/>
</xpath>
<xpath expr="//notebook" position="inside">
<page string="Introduction">
<field name="website_description"/>
</page>
<field name="template_id" on_change="onchange_template_id(template_id)"/>
</xpath>
</field>
</record>
<record model="ir.ui.view" id="view_sale_quote_template_form">
<field name="name">sale.quote.template.form</field>
<field name="model">sale.quote.template</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Sale Quotation Template" version="7.0">
<sheet>
<h1>
<label string="Quotation Template"/>
<field name="name"/>
</h1>
<notebook>
<page string="Quote Lines">
<field name="quote_line">
<form string="Sales Quote Template Lines" version="7.0">
<group>
<group>
<field name="product_id" on_change="on_change_product_id(product_id)"/>
<label for="product_uom_qty"/>
<div>
<field
name="product_uom_qty" class="oe_inline"/>
</div>
<field name="price_unit"/>
</group>
</group>
<notebook colspan="4">
<page string="Description">
<field name="name" />
</page>
<page string="Website Description">
<field name="website_description" />
</page>
</notebook>
</form>
<tree string="Sales Quote Template Lines" editable="bottom">
<field name="product_id" on_change="on_change_product_id(product_id)"/>
<field name="name"/>
<field name="product_uom_qty"/>
<field name="price_unit"/>
</tree>
</field>
</page>
<page string="Website Description">
<field name="website_description"/>
</page>
<page string="Terms &amp; Conditions">
<field name="note" placeholder="Terms and conditions..."/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="view_sale_quote_template_tree">
<field name="name">sale.quote.template.tree</field>
<field name="model">sale.quote.template</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Sale Quote Template">
<field name="name"/>
</tree>
</field>
</record>
<record id="action_sale_quotation_template" model="ir.actions.act_window">
<field name="name">Quote Template</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.quote.template</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem action="action_sale_quotation_template" id="menu_sale_quote_template" parent="base.menu_base_config" sequence="6" groups="base.group_sale_salesman,base.group_sale_manager"/>
</data>
</openerp>

View File

@ -1,13 +1,12 @@
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; }
body { padding-top:30px; }
.panel-chatter .panel-body { padding:0px; }
.panel-chatter .list-group { margin-bottom: 0; }
.panel-chatter .panel-title { display:inline }
.panel-chatter .label-info { float: right; }
.panel-chatter li.list-group-item {border-radius: 0;border: 0;border-top: 1px solid #ddd;}
.panel-chatter li.list-group-item:hover { background-color: rgba(86,61,124,.1); }
.panel-chatter .mic-info { color: #666666;font-size: 11px; }
.panel-chatter .action { margin-top:5px; }
.panel-chatter .comment-text { font-size: 12px; }
.panel-chatter .btn-block { border-top-left-radius:0px;border-top-right-radius:0px; }
.oe_section { padding-top:40px; }

View File

@ -10,7 +10,7 @@ $(document).ready(function () {
'line_id': line_id[1],
'order_id': parseInt(order_id[1]),
'remove': $link.is('[href*="remove"]'),
'unlink': $link.is('[href*="unlink"]'),
'unlink': $link.is('[href*="unlink"]')
})
.then(function (data) {
if(!data){

View File

@ -29,7 +29,6 @@
<meta name="keywords" t-att-value="main_object and 'website_meta_keywords' in main_object
and main_object.website_meta_keywords or website_meta_keywords"/>
<!-- Load stylesheets before scripts to avoid blocking -->
<link rel='stylesheet' href='/web/static/lib/fontawesome/css/font-awesome.css'/>
<t t-if="editable">
<link rel='stylesheet' href='/website/static/src/css/snippets.css'/>
@ -166,7 +165,7 @@
</a>
</li>
<li>
<a type="submit" t-if="quotation.state != 'cancel'" t-href="/quote/#{ quotation.id }/decline">
<a href="#" class="btn" t-if="quotation.state != 'cancel'" id="cancelQuotation">
<span class="fa-stack">
<i class="fa fa-circle fa-stack-2x" style="color:#d9534f;"></i>
<i class="fa fa-times fa-stack-1x fa-inverse"></i>
@ -180,7 +179,7 @@
<i class="fa fa-circle fa-stack-2x" style="color:#428bca;"></i>
<i class="fa fa-comment fa-stack-1x fa-inverse"></i>
</span>Discuss
<sup class="label label-info"><t t-esc="total_mail"/></sup>
<sup class="label label-info"><t t-esc="len(quotation.message_ids)"/></sup>
</a>
</li>
</ul>
@ -188,7 +187,7 @@
</div>
</template>
<template id="chatter">
<div id="chat" class="panel panel-default widget">
<div id="chat" class="panel panel-default panel-chatter">
<div class="panel-heading">
<span class="glyphicon glyphicon-comment"></span>
<h3 class="panel-title">Recent Comments</h3>
@ -196,7 +195,6 @@
<div class="panel-body">
<ul class="list-group">
<t t-foreach="quotation.message_ids" t-as="message">
<t t-if="message.type == 'email'">
<li class="list-group-item">
<div class="row">
<div class="col-xs-2 col-md-1">
@ -213,7 +211,6 @@
</div>
</li>
</t>
</t>
</ul>
</div>
</div>
@ -234,6 +231,7 @@
</section>
</t>
<t t-call="website_sale_quote.pricing"/>
<t t-call="website_sale_quote.terms_and_conditions"/>
<t t-call="website_sale_quote.product_recommendation"/>
<t t-call="website_sale_quote.chatter"/>
</div>
@ -244,6 +242,7 @@
<li><a t-att-href="'#%s'% line.id" data-toggle="tab"><i class="icon-chevron-right"></i><t t-raw="line.product_id.name_template"/> </a></li>
</t>
<li><a href="#quote" data-toggle="tab"><i class="icon-chevron-right"></i> Quotation</a></li>
<li><a href="#terms" data-toggle="tab"><i class="icon-chevron-right"></i> Terms &amp; Conditions</a></li>
</ul>
</div>
</t>
@ -263,5 +262,17 @@
</div>
</div>
</template>
<template id="terms_and_conditions" name="Terms &amp; Conditions">
<section id="terms" class="tab-pane oe_section">
<div class="container panel panel-default tab-pane oe_section">
<div class="row panel-body">
<div class="text-center">
<h2><strong>Terms &amp; Conditions</strong></h2>
<p class="lead" t-field="quotation.note"/>
</div>
</div>
</div>
</section>
</template>
</data>
</openerp>