[IMP] website_quote new modules + small fixes

bzr revid: fp@tinyerp.com-20140125234925-mlg0rlwg3moiueuk
This commit is contained in:
Fabien Pinckaers 2014-01-26 00:49:25 +01:00
commit 28a0355775
32 changed files with 2021 additions and 9 deletions

View File

@ -50,7 +50,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

View File

@ -93,6 +93,8 @@ Example: 10% for retailers, promotion of 5 EUR on this product, etc."""),
help='This adds the \'Margin\' on sales order.\n'
'This gives the profitability by calculating the difference between the Unit Price and Cost Price.\n'
'-This installs the module sale_margin.'),
'module_website_quote': fields.boolean("Allow online quotations and templates",
help='This adds the online quotation'),
'module_sale_journal': fields.boolean("Allow batch invoicing of delivery orders through journals",
help='Allows you to categorize your sales and deliveries (picking lists) between different journals, '
'and perform batch operations on journals.\n'

View File

@ -80,6 +80,10 @@
</group>
</group>
<div name="Sale Features" position="inside">
<div name="module_website_quote">
<field name="module_website_quote" class="oe_inline"/>
<label for="module_website_quote"/>
</div>
<div name="module_sale_margin">
<field name="module_sale_margin" class="oe_inline"/>
<label for="module_sale_margin"/>

View File

@ -36,7 +36,7 @@ class Website(openerp.addons.web.controllers.main.Home):
try:
main_menu = request.registry['ir.model.data'].get_object(request.cr, request.uid, 'website', 'main_menu')
first_menu = main_menu.child_id and main_menu.child_id[0]
if first_menu and first_menu.url != '/':
if first_menu and not ((first_menu.url == '/') or first_menu.url.startswith('/#') or first_menu.url.startswith('/?')):
return request.redirect(first_menu.url)
except:
pass

View File

@ -27,7 +27,7 @@
</record>
<record id="public_user" model="res.users">
<field name="name">public</field>
<field name="name">Anonymous</field>
<field name="login">public</field>
<field name="password">public</field>
<!-- Avoid auto-including this demo user in any default group -->
@ -84,7 +84,7 @@
<record id="menu_website" model="ir.ui.menu">
<field name="name">Website</field>
<field name="sequence" eval="11"/>
<field name="sequence" eval="400"/>
<field name="action" ref="action_website"/>
</record>
<record id="base.open_menu" model="ir.actions.todo">

View File

@ -470,7 +470,7 @@
<div data-snippet-id="pricing" data-selector-children=".oe_structure, [data-oe-type=html]">
<div class="oe_snippet_thumbnail">
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_comparison.png"/>
<span class="oe_snippet_thumbnail_title">Comparisons</span>
<span class="oe_snippet_thumbnail_title">Comparisons</span>
</div>
<section class="oe_snippet_body">
<div class="container">

View File

@ -554,7 +554,7 @@
<div class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="collapse navbar-collapse navbar-top-collapse">
<ul class="nav navbar-nav">
<ul class="nav navbar-nav pull-right">
<li><a href="/">Home</a></li>
<li><a href="javascript: window.history.back()">Back</a></li>
</ul>

View File

@ -0,0 +1,2 @@
import controllers
import models

View File

@ -0,0 +1,24 @@
{
'name': 'Online Quotation',
'category': 'Website',
'summary': 'Send Live Quotation',
'version': '1.0',
'description': """
OpenERP Sale Quote Roller
==================
""",
'author': 'OpenERP SA',
'depends': ['website','sale', 'mail'],
'data': [
'views/website_quotation.xml',
'views/website_quotation_backend.xml',
'data/website_quotation_data.xml',
'security/ir.model.access.csv',
],
'demo': [
'data/website_quotation_demo.xml'
],
'qweb': ['static/src/xml/*.xml'],
'installable': True,
}

View File

@ -0,0 +1,3 @@
import main
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,171 @@
# -*- 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.addons.web import http
from openerp.addons.web.http import request
from openerp.addons.website.models import website
import werkzeug
import datetime
import time
from openerp.tools.translate import _
class sale_quote(http.Controller):
@http.route([
"/quote/<int:order_id>",
"/quote/<int:order_id>/<token>"
], type='http', auth="public", website=True)
def view(self, order_id, token=None, message=False, **post):
# use SUPERUSER_ID allow to access/view order for public user
# only if he knows the private token
order = request.registry.get('sale.order').browse(request.cr, token and SUPERUSER_ID or request.uid, order_id)
now = time.strftime('%Y-%m-%d')
if token:
if token != order.access_token:
return request.website.render('website.404')
# Log only once a day
if request.httprequest.session.get('view_quote',False)!=now:
request.httprequest.session['view_quote'] = now
body=_('Quotation viewed by customer')
self.__message_post(body, order_id, type='comment')
days = 0
if order.validity_date:
days = (datetime.datetime.strptime(order.validity_date, '%Y-%m-%d') - datetime.datetime.now()).days + 1
values = {
'quotation': order,
'message': message and int(message) or False,
'option': bool(filter(lambda x: not x.line_id, order.options)),
'order_valid': (not order.validity_date) or (now <= order.validity_date),
'days_valid': max(days, 0)
}
return request.website.render('website_quote.so_quotation', values)
@http.route(['/quote/accept'], type='json', auth="public", website=True)
def accept(self, order_id=None, token=None, signer=None, sign=None, **post):
order_obj = request.registry.get('sale.order')
order = order_obj.browse(request.cr, SUPERUSER_ID, order_id)
if token != order.access_token:
return request.website.render('website.404')
attachments=sign and [('signature.png', sign)] or []
order_obj.signal_order_confirm(request.cr, SUPERUSER_ID, [order_id], context=request.context)
message = _('Order signed by %s') % (signer,)
self.__message_post(message, order_id, type='comment', subtype='mt_comment', attachments=attachments)
return True
@http.route(['/quote/<int:order_id>/<token>/decline'], type='http', auth="public", website=True)
def decline(self, order_id, token, **post):
order_obj = request.registry.get('sale.order')
order = order_obj.browse(request.cr, SUPERUSER_ID, order_id)
if token != order.access_token:
return request.website.render('website.404')
request.registry.get('sale.order').action_cancel(request.cr, SUPERUSER_ID, [order_id])
message = post.get('decline_message')
if message:
self.__message_post(message, order_id, type='comment', subtype='mt_comment')
return werkzeug.utils.redirect("/quote/%s/%s?message=2" % (order_id, token))
@http.route(['/quote/<int:order_id>/<token>/post'], type='http', auth="public", website=True)
def post(self, order_id, token, **post):
# use SUPERUSER_ID allow to access/view order for public user
order_obj = request.registry.get('sale.order')
order = order_obj.browse(request.cr, SUPERUSER_ID, order_id)
message = post.get('comment')
if token != order.access_token:
return request.website.render('website.404')
if message:
self.__message_post(message, order_id, type='comment', subtype='mt_comment')
return werkzeug.utils.redirect("/quote/%s/%s?message=1" % (order_id, token))
def __message_post(self, message, order_id, type='comment', subtype=False, attachments=[]):
request.session.body = message
cr, uid, context = request.cr, request.uid, request.context
user = request.registry['res.users'].browse(cr, SUPERUSER_ID, uid, context=context)
if 'body' in request.session and request.session.body:
request.registry.get('sale.order').message_post(cr, SUPERUSER_ID, order_id,
body=request.session.body,
type=type,
subtype=subtype,
author_id=user.partner_id.id,
context=context,
attachments=attachments
)
request.session.body = False
return True
@http.route(['/quote/update_line'], type='json', auth="public", website=True)
def update(self, line_id=None, remove=False, unlink=False, order_id=None, token=None, **post):
order = request.registry.get('sale.order').browse(request.cr, SUPERUSER_ID, int(order_id))
if token != order.access_token:
return request.website.render('website.404')
if order.state not in ('draft','sent'):
return False
line_id=int(line_id)
if unlink:
request.registry.get('sale.order.line').unlink(request.cr, SUPERUSER_ID, [line_id], context=request.context)
return False
number=(remove and -1 or 1)
order_line_obj = request.registry.get('sale.order.line')
order_line_val = order_line_obj.read(request.cr, SUPERUSER_ID, [line_id], [], context=request.context)[0]
quantity = order_line_val['product_uom_qty'] + number
order_line_obj.write(request.cr, SUPERUSER_ID, [line_id], {'product_uom_qty': (quantity)}, context=request.context)
return [str(quantity), str(order.amount_total)]
@http.route(["/quote/template/<model('sale.quote.template'):quote>"], type='http', auth="user", website=True)
def template_view(self, quote, **post):
values = { 'template': quote }
return request.website.render('website_quote.so_template', values)
@http.route(["/quote/add_line/<int:option_id>/<int:order_id>/<token>"], type='http', auth="public", website=True)
def add(self, option_id, order_id, token, **post):
vals = {}
order = request.registry.get('sale.order').browse(request.cr, SUPERUSER_ID, order_id)
if token != order.access_token:
return request.website.render('website.404')
option_obj = request.registry.get('sale.order.option')
option = option_obj.browse(request.cr, SUPERUSER_ID, option_id)
res = request.registry.get('sale.order.line').product_id_change(request.cr, SUPERUSER_ID, order_id,
False, option.product_id.id, option.quantity, option.uom_id.id, option.quantity, option.uom_id.id,
option.name, order.partner_id.id, False, True, time.strftime('%Y-%m-%d'),
False, order.fiscal_position.id, True, request.context)
vals = res.get('value', {})
if 'tax_id' in vals:
vals['tax_id'] = [(6, 0, vals['tax_id'])]
vals.update({
'price_unit': option.price_unit,
'website_description': option.website_description,
'name': option.name,
'order_id': order.id,
'product_id' : option.product_id.id,
'product_uos_qty': option.quantity,
'product_uos': option.uom_id.id,
'product_uom_qty': option.quantity,
'product_uom': option.uom_id.id,
'discount': option.discount,
})
line = request.registry.get('sale.order.line').create(request.cr, SUPERUSER_ID, vals, context=request.context)
option_obj.write(request.cr, SUPERUSER_ID, [option.id], {'line_id': line}, context=request.context)
return werkzeug.utils.redirect("/quote/%s/%s#pricing" % (order.id, token))

View File

@ -0,0 +1,86 @@
<openerp>
<data>
<!--
Update Email template to send right quote url
-->
<record id="sale.email_template_edi_sale" model="email.template">
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
<p>Hello ${object.partner_id.name},</p>
<p>Here is your ${object.state in ('draft', 'sent') and 'quotation' or 'order confirmation'} from ${object.company_id.name}: </p>
<p style="border-left: 1px solid #8e0000; margin-left: 30px;">
&nbsp;&nbsp;<strong>REFERENCES</strong><br />
&nbsp;&nbsp;Order number: <strong>${object.name}</strong><br />
&nbsp;&nbsp;Order total: <strong>${object.amount_total} ${object.pricelist_id.currency_id.name}</strong><br />
&nbsp;&nbsp;Order date: ${object.date_order}<br />
% if object.origin:
&nbsp;&nbsp;Order reference: ${object.origin}<br />
% endif
% if object.client_order_ref:
&nbsp;&nbsp;Your reference: ${object.client_order_ref}<br />
% endif
% if object.user_id:
&nbsp;&nbsp;Your contact: <a href="mailto:${object.user_id.email or ''}?subject=Order%20${object.name}">${object.user_id.name}</a>
% endif
</p>
<p>
You can view your quotation online:
</p>
<a style="display:block; width: 150px; height:20px; margin-left: 120px; color: #DDD; font-family: 'Lucida Grande', Helvetica, Arial, sans-serif; font-size: 13px; font-weight: bold; text-align: center; text-decoration: none !important; line-height: 1; padding: 5px 0px 0px 0px; background-color: #8E0000; border-radius: 5px 5px; background-repeat: repeat no-repeat;"
href="/quote/${object.id}/${object.access_token}">View ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'}</a>
% if object.paypal_url:
<br/>
<p>It is also possible to directly pay with Paypal:</p>
<a style="margin-left: 120px;" href="${object.paypal_url}">
<img class="oe_edi_paypal_button" src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"/>
</a>
% endif
<br/>
<p>If you have any question, do not hesitate to contact us.</p>
<p>Thank you for choosing ${object.company_id.name or 'us'}!</p>
<br/>
<br/>
<div style="width: 375px; margin: 0px; padding: 0px; background-color: #8E0000; border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; background-repeat: repeat no-repeat;">
<h3 style="margin: 0px; padding: 2px 14px; font-size: 12px; color: #DDD;">
<strong style="text-transform:uppercase;">${object.company_id.name}</strong></h3>
</div>
<div style="width: 347px; margin: 0px; padding: 5px 14px; line-height: 16px; background-color: #F2F2F2;">
<span style="color: #222; margin-bottom: 5px; display: block; ">
% if object.company_id.street:
${object.company_id.street}<br/>
% endif
% if object.company_id.street2:
${object.company_id.street2}<br/>
% endif
% if object.company_id.city or object.company_id.zip:
${object.company_id.zip} ${object.company_id.city}<br/>
% endif
% if object.company_id.country_id:
${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}<br/>
% endif
</span>
% if object.company_id.phone:
<div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">
Phone:&nbsp; ${object.company_id.phone}
</div>
% endif
% if object.company_id.website:
<div>
Web :&nbsp;<a href="${object.company_id.website}">${object.company_id.website}</a>
</div>
%endif
<p></p>
</div>
</div>
]]></field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,531 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="product_template_quote_1" model="product.template">
<field name="name">Functional Training</field>
<field name="categ_id" ref="product.product_category_5"/>
<field name="public_categ_id" ref="product.Computer_all_in_one"/>
<field name="standard_price">50000.0</field>
<field name="list_price">750000.0</field>
<field name="type">service</field>
<field name="uom_id" ref="product.product_uom_unit"/>
<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>
</record>
<record id="product_template_quote_2" model="product.template">
<field name="name">Technical Training</field>
<field name="categ_id" ref="product.product_category_5"/>
<field name="public_categ_id" ref="product.Computer_all_in_one"/>
<field name="standard_price">50000.0</field>
<field name="list_price">0.0</field>
<field name="type">service</field>
<field name="uom_id" ref="product.product_uom_unit"/>
<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>
</record>
<record id="product_template_quote_3" model="product.template">
<field name="name">Advanced CRM Functional</field>
<field name="categ_id" ref="product.product_category_5"/>
<field name="public_categ_id" ref="product.Computer_all_in_one"/>
<field name="standard_price">50000.0</field>
<field name="list_price">750000.0</field>
<field name="type">service</field>
<field name="uom_id" ref="product.product_uom_unit"/>
<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>
</record>
<record id="website_quote_template_1" model="sale.quote.template">
<field name="name">Partnership Contract</field>
<field name="number_of_days">45</field>
<field name="website_description" type="xml">
<section data-snippet-id="title">
<h1 class="page-header">Our Partnership Offer</h1>
</section>
<section data-snippet-id="text-block">
<div class="row">
<div class="col-md-12">
<p>
Our partnership offer includes all you need to
grow your business and deliver quality services
with the OpenERP Partner Program.
</p><p>
It includes <strong>discounts on OpenERP
Enterprise</strong>, technical and/or functional
<strong>trainings</strong>,
<strong>support</strong> services,
<strong>marketing documents</strong>, access to
the <strong>partner portal</strong>, rights to
<strong>use the trademark</strong>, sales support
from a <strong>dedicated account manager</strong>.
</p>
</div>
</div>
</section>
<section data-snippet-id="pricing">
<div class="row mt32">
<div class="col-md-4">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">A Valuable Product</h3>
</div>
<div class="panel-body">
<p>
Deliver <strong>strong value added services</strong> as you can
rely on a leading open source software, with
the support of the publisher.
<p></p>
<strong>Grow with your existing customer base</strong>
by continuously proposing new modules.
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">A Strong Demand</h3>
</div>
<div class="panel-body">
<p>
Enjoy the traction of the <strong>fastest growing
management software</strong> in the world.
</p><p>
Benefit from the growing customer demand
and our OpenERP brand.
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">High Margins</h3>
</div>
<div class="panel-body">
<p>
<strong>Get high billing rates</strong> as you deliver a
highly valuable software.
</p><p>
Grow by developing a <strong>recurring
revenue flow</strong> from OpenERP
Enterprise's commission system.
</p>
</div>
</div>
</div>
</div>
</section>
<section data-snippet-id="text-block">
<h2>A Dedicated Account Manager</h2>
<p>
We will assign you a dedicated account manager, an
experienced sales person, to help you develop your
OpenERP business. The account manager helps you get
leads, close deals, gives you feedback and best
practices, delivers sales training and is your direct
point of contact for any request you may have.
</p>
</section>
<section data-snippet-id="text-block">
<div class="row">
<div class="col-md-12">
<h2>Get access to our experts</h2>
</div>
<div class="col-md-7">
<p>
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.
</p>
</div>
<div class="col-md-5">
<img src="/website_quote/static/src/img/partner_icon_01.png"/>
</div>
</div>
</section>
<section data-snippet-id="text-block">
<div class="row">
<div class="col-md-12">
<h2>Official certified partner</h2>
</div>
<div class="col-md-7">
<p>
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.
</p>
</div>
<div class="col-md-5">
<img src="/website_quote/static/src/img/openerp_gold_partner.png"/>
</div>
</div>
</section>
<section data-snippet-id="text-block">
<div class="row">
<div class="col-md-12">
<h2>Access to the Lead</h2>
</div>
<div class="col-md-7">
<p>
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.
</p>
</div>
<div class="col-md-5">
<img src="/website_quote/static/src/img/partner_icon_02.png" class="img"/>
</div>
</div>
</section>
<section data-snippet-id="text-block">
<h2>Benefit from the OpenERP branding</h2>
<p>
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.
</p>
</section>
<section class="mb32" data-snippet-id="text-block">
<h2>Test developments automatically</h2>
<div class="row">
<div class="col-md-5">
<img src="/website_quote/static/src/img/partner_sc_01.png" class="img shadow"/>
</div>
<div class="col-md-7">
<p class="mt23">
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.
</p><p>
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.
</p>
</div>
</div>
</section>
</field>
</record>
<record id="website_sale_order_line_1" model="sale.quote.line">
<field name="quote_id" ref="website_quote_template_1"/>
<field name="name">Functional Training</field>
<field name="product_id" ref="product_product_quote_1"/>
<field name="product_uom_qty">1</field>
<field name="product_uom_id" ref="product.product_uom_unit"/>
<field name="price_unit">12950.00</field>
<field name="discount">10.00</field>
<field name="website_description" type="html">
<section data-snippet-id="title">
<h1>Online Training + Certification</h1>
</section>
<section data-snippet-id="text-image">
<div class="row">
<div class="col-md-12 mb32">
<p>These courses feature the same high quality course content found in our traditional classroom trainings, supplemented with modular sessions and cloud-based labs. Many of our online learning courses also include dozens of recorded webinars and live sessions by our senior instructors. At the end of the training, you can pass the OpenERP Certification exam in one of the 5000+ Pearson VUE test centers worldwide.</p>
</div>
<div class="col-md-offset-1">
<h3>Your advantages</h3>
<ul>
<li>Modular approach applied to the learning method</li>
<li>New interactive learning experience</li>
<li>Lower training budget for the same quality courses</li>
<li>Better comfort to facilitate your learning process</li>
</ul>
</div>
<div class="col-md-12">
<h2>Structure of the Training</h2>
</div>
<div class="col-md-5 col-md-offset-1 mt16 mb16">
<img class="img img-responsive" src="https://www.openerp.com/saas_master/static/site_new/img/layout/online_training.png"/>
</div>
<div class="col-md-6 mt32">
<h4><strong>There are three components to the training</strong></h4>
<ul>
<li>Videos with detailed demos</li>
<li>Hands-on exercises and their solutions</li>
<li>Live Q&amp;A sessions with a trainer</li>
</ul>
</div>
</div>
</section>
</field>
</record>
<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_uom_id" ref="product.product_uom_unit"/>
<field name="price_unit">00.00</field>
<field name="website_description" type="html">
<section data-snippet-id="title">
<h1>Technical Training</h1>
</section>
<section id="whatsuit" data-snippet-id="text-block">
<div class="container">
<div class="row">
<div class="col-md-12 text-center mb8 mt0" data-snippet-id="colmd">
<h2>Course Summary</h2>
</div>
<div class="col-md-12 mt16 mb0" data-snippet-id="colmd">
<p><span style="text-align: -webkit-center; ">This course is dedicated to developers who need to grasp knowledge of the <strong>business applications development </strong>process. This course is for new developers or for IT professionals eager to learn more about technical aspects.</span></p>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-12 text-center mt0 mb16" data-snippet-id="colmd">
<h2>What you will learn?</h2>
</div>
<div class="col-md-4" data-snippet-id="colmd">
<div class="panel panel-info">
<div class="panel-heading text-center">
<h3 style="margin: 0">Day 1</h3>
<p class="text-muted" style="margin: 0">Introduction to Javascript</p>
</div>
<ul class="list-group">
<li class="list-group-item">Hello World</li>
<li class="list-group-item">Variables &amp; Operators</li>
<li class="list-group-item">Dive into Strings</li>
<li class="list-group-item">Functions</li>
<li class="list-group-item">Loops</li>
<li class="list-group-item">Arrays</li>
</ul>
<div class="panel-footer text-center">
<p class="text-muted"><i>You will be able to develop simple dynamic compenents in HTML pages.</i></p>
</div>
</div>
</div>
<div class="col-md-4" data-snippet-id="colmd">
<div class="panel panel-info">
<div class="panel-heading text-center">
<h3 style="margin: 0">Day 2</h3>
<p class="text-muted" style="margin: 0">OpenERP Web Client</p>
</div>
<ul class="list-group">
<li class="list-group-item">Introduction to JQuery</li>
<li class="list-group-item">Advanced JQuery</li>
<li class="list-group-item">Underscore</li>
<li class="list-group-item">Introduction to QWeb</li>
<li class="list-group-item">Controlers and Views</li>
<li class="list-group-item">Bootstrap CSS</li>
<li class="list-group-item">Calling the ORM</li>
</ul>
<div class="panel-footer text-center">
<p class="text-muted"><i>You will be able to create dynamic page interacting with the ORM.</i></p>
</div>
</div>
</div>
<div class="col-md-4" data-snippet-id="colmd">
<div class="panel panel-info">
<div class="panel-heading text-center">
<h3 style="margin: 0">Day 3</h3>
<p class="text-muted" style="margin: 0">Building a Full Application</p>
</div>
<ul class="list-group">
<li class="list-group-item">Modules</li>
<li class="list-group-item">Python Objects</li>
<li class="list-group-item">Report Engine</li>
<li class="list-group-item">Workflows</li>
<li class="list-group-item">Training Center Module</li>
<li class="list-group-item">Integrated Help</li>
<li class="list-group-item">How to Debug</li>
</ul>
<div class="panel-footer text-center">
<p class="text-muted"><i>You will be able to develop a full application with backend and user interface.</i></p>
</div>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-12 text-center mb16 mt0" data-snippet-id="colmd">
<h2>Requirements</h2>
</div>
<div class="col-md-12 mb16 mt16" data-snippet-id="colmd">
<p><strong>Objectives:</strong></p>
<p><strong>Having attended this course, participants should be able to:</strong></p>
<ul><li>Understand the development concepts and architecture;</li>
<li>Install and administer your own server;</li>
<li>Develop a new module for a particular application.</li>
</ul><p></p>
<p><strong>Our prices include:</strong></p>
<ul><li>drinks and lunch;</li>
<li>training material.</li>
</ul><p></p>
<p><strong>Requirements:</strong></p>
<ul><li>Bring your own laptop.</li>
<li>Participants are expected to have some knowledge in programming. A basic knowledge of the Python programming is recommended.</li>
<li>Participants preferably have a functional knowledge of our software (see Functional Training).</li>
</ul><p></p>
<p>To get more information, visit the <a href="http://openerp.com/">OpenERP Official Website</a>.</p>
</div>
</div>
</div>
</section>
</field>
</record>
<record id="website_sale_option_line_1" model="sale.quote.option">
<field name="template_id" ref="website_quote_template_1"/>
<field name="name">Advanced CRM Functional</field>
<field name="product_id" ref="product_product_quote_3"/>
<field name="quantity">1</field>
<field name="uom_id" ref="product.product_uom_unit"/>
<field name="price_unit">9000.00</field>
<field name="discount">10</field>
<field name="website_description" type="html">
<section data-snippet-id="title">
<h1>Advanced CRM Functional</h1>
</section>
<section data-snippet-id="text-image">
<div class="container">
<div class="row">
<div class="col-md-12 text-center mb8 mt0" data-snippet-id="colmd">
<h2>Objectives</h2>
</div>
<div class="col-md-12 mt16 mb0" data-snippet-id="colmd">
<p><span style="text-align: -webkit-center; ">Upon completion of the training, the participant will be able to:</span></p>
<ul class="list-group">
<li class="list-group-item"> Install and administer OpenERP.</li>
<li class="list-group-item"> Become an OpenERP Consultant.</li>
<li class="list-group-item"> Do the GAP analysis of any Business Process.</li>
<li class="list-group-item"> Understand the functional concepts, business processes byOpenERP.</li>
<li class="list-group-item"> Operate/Work with OpenERP Smoothly on regular basis.</li>
<li class="list-group-item"> Configure OpenERP using the standard modules.</li>
<li class="list-group-item"> Change the look and feel from the front-end(GUI) rather than aneed of technical knowledge.</li>
</ul>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-12 text-center mt0 mb16" data-snippet-id="colmd">
<h2>What you will learn?</h2>
</div>
<div class="col-md-4" data-snippet-id="colmd">
<div class="panel panel-info">
<div class="panel-heading text-center">
<h3 style="margin: 0">Day 1</h3>
</div>
<ul class="list-group">
<li class="list-group-item">Introduction</li>
<li class="list-group-item">Installation and Configuration</li>
<li class="list-group-item">Database management</li>
<li class="list-group-item">Module Installation</li>
<li class="list-group-item">Company Configuration and Multi Company Management</li>
<li class="list-group-item">Multi Language Management</li>
<li class="list-group-item">Fetchmail Configuration</li>
</ul>
</div>
</div>
<div class="col-md-4" data-snippet-id="colmd">
<div class="panel panel-info">
<div class="panel-heading text-center">
<h3 style="margin: 0">Day 2</h3>
</div>
<ul class="list-group">
<li class="list-group-item">Product Category &amp; product Configuration with Order point</li>
<li class="list-group-item">Customers/Suppliers</li>
<li class="list-group-item">Pricelist and auto Segmentation</li>
<li class="list-group-item">Convert Lead to Opportunity &amp; Customer management</li>
<li class="list-group-item">Schedule Phone Calls and Meetings</li>
<li class="list-group-item">Negotiation and Quotation Revisions</li>
<li class="list-group-item">Generate Sale Order &amp; direct mail to customer</li>
</ul>
</div>
</div>
<div class="col-md-4" data-snippet-id="colmd">
<div class="panel panel-info">
<div class="panel-heading text-center">
<h3 style="margin: 0">Day 3</h3>
</div>
<ul class="list-group">
<li class="list-group-item">Warehouse Management Introduction</li>
<li class="list-group-item">Shop, Location &amp; Warehouse Configuration</li>
<li class="list-group-item">Opening Stock and Physical Inventory</li>
<li class="list-group-item">Purchase Requisition and Purchase Order Management</li>
<li class="list-group-item">Partial and Full Shipment / Delivery</li>
<li class="list-group-item">Product Expiry and Warranty</li>
<li class="list-group-item">After Sales Service (Helpdesk)</li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-12 mb16 mt16" data-snippet-id="colmd">
<h2>Requirements</h2>
<ul><li>Bring your own laptop.</li>
<li>Participants are expected to have some knowledge in programming. A basic knowledge of the Python programming is recommended.</li>
<li>Participants preferably have a functional knowledge of our software (see Functional Training).</li>
</ul><p></p>
<p>To get more information, visit the <a href="http://openerp.com/">OpenERP Official Website</a>.</p>
</div>
</div>
</div>
</section>
</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1 @@
import order

View File

@ -0,0 +1,234 @@
# -*- 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.osv import osv, fields
import uuid
import time
import datetime
import openerp.addons.decimal_precision as dp
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', translate=True),
'quote_line': fields.one2many('sale.quote.line', 'quote_id', 'Quote Template Lines'),
'note': fields.text('Terms and conditions'),
'options': fields.one2many('sale.quote.option', 'template_id', 'Optional Products Lines'),
'number_of_days': fields.integer('Quote Duration', help='Number of days for the validaty date computation of the quotation'),
}
def open_template(self, cr, uid, quote_id, context=None):
return {
'type': 'ir.actions.act_url',
'target': 'self',
'url': '/quote/template/%d' % quote_id[0]
}
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, translate=True),
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], required=True),
'website_description': fields.html('Line Description', translate=True),
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount')),
'product_uom_qty': fields.float('Quantity', required=True, digits_compute= dp.get_precision('Product UoS')),
'product_uom_id': fields.many2one('product.uom', 'Unit of Measure ', required=True),
}
_defaults = {
'product_uom_qty': 1,
'discount': 0.0,
}
def on_change_product_id(self, cr, uid, ids, product, context=None):
vals = {}
product_obj = self.pool.get('product.product').browse(cr, uid, product, context=context)
vals.update({
'price_unit': product_obj.list_price,
'product_uom_id': product_obj.uom_id.id,
'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'),
'option_line_id':fields.one2many('sale.order.option', 'line_id', 'Optional Products Lines'),
}
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'
def _get_total(self, cr, uid, ids, name, arg, context=None):
res = {}
for order in self.browse(cr, uid, ids, context=context):
total = 0.0
for line in order.order_line:
total += (line.product_uom_qty * line.price_unit)
res[order.id] = total
return res
_columns = {
'access_token': fields.char('Security Token', size=256, required=True),
'template_id': fields.many2one('sale.quote.template', 'Quote Template'),
'website_description': fields.html('Description'),
'options' : fields.one2many('sale.order.option', 'order_id', 'Optional Products Lines'),
'validity_date': fields.date('Validity Date'),
'amount_undiscounted': fields.function(_get_total, string='Amount Before Discount', type="float",
digits_compute=dp.get_precision('Account'))
}
_defaults = {
'access_token': lambda self, cr, uid, ctx={}: str(uuid.uuid4())
}
def open_quotation(self, cr, uid, quote_id, context=None):
quote = self.browse(cr, uid, quote_id[0], context=context)
return {
'type': 'ir.actions.act_url',
'target': 'self',
'url': '/quote/%s' % (quote.id)
}
def onchange_template_id(self, cr, uid, ids, template_id, partner=False, fiscal_position=False, context=None):
lines = []
quote_template = self.pool.get('sale.quote.template').browse(cr, uid, template_id, context=context)
for line in quote_template.quote_line:
res = self.pool.get('sale.order.line').product_id_change(cr, uid, False,
False, line.product_id.id, line.product_uom_qty, line.product_uom_id.id, line.product_uom_qty,
line.product_uom_id.id, line.name, partner, False, True, time.strftime('%Y-%m-%d'),
False, fiscal_position, True, context)
data = res.get('value', {})
if 'tax_id' in data:
data['tax_id'] = [(6, 0, data['tax_id'])]
data.update({
'name': line.name,
'price_unit': line.price_unit,
'discount': line.discount,
'product_uom_qty': line.product_uom_qty,
'product_id': line.product_id.id,
'product_uom': line.product_uom_id.id,
'website_description': line.website_description,
'state': 'draft',
})
lines.append((0, 0, data))
options = []
for option in quote_template.options:
options.append((0, 0, {
'product_id': option.product_id.id,
'name': option.name,
'quantity': option.quantity,
'uom_id': option.uom_id.id,
'price_unit': option.price_unit,
'discount': option.discount,
'website_description': option.website_description,
}))
date = False
if quote_template.number_of_days > 0:
date = (datetime.datetime.now() + datetime.timedelta(quote_template.number_of_days)).strftime("%Y-%m-%d")
data = {'order_line': lines, 'website_description': quote_template.website_description, 'note': quote_template.note, 'options': options, 'validity_date': date}
return {'value': data}
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')
products = []
for line in order_line:
products += line.product_id.product_tmpl_id.recommended_products(context=context)
return products
class sale_quote_option(osv.osv):
_name = "sale.quote.option"
_description = "Quote Option"
_columns = {
'template_id': fields.many2one('sale.quote.template', 'Quotation Template Reference', ondelete='cascade', select=True, required=True),
'name': fields.text('Description', required=True, translate=True),
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], required=True),
'website_description': fields.html('Option Description', translate=True),
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount')),
'uom_id': fields.many2one('product.uom', 'Unit of Measure ', required=True),
'quantity': fields.float('Quantity', required=True, digits_compute= dp.get_precision('Product UoS')),
}
_defaults = {
'quantity': 1,
}
def on_change_product_id(self, cr, uid, ids, product, context=None):
vals = {}
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.product_tmpl_id.website_description,
'name': product_obj.name,
'uom_id': product_obj.product_tmpl_id.uom_id.id,
})
return {'value': vals}
class sale_order_option(osv.osv):
_name = "sale.order.option"
_description = "Sale Options"
_columns = {
'order_id': fields.many2one('sale.order', 'Sale Order Reference', ondelete='cascade', select=True),
'line_id': fields.many2one('sale.order.line', on_delete="set null"),
'name': fields.text('Description', required=True),
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)]),
'website_description': fields.html('Line Description'),
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount')),
'uom_id': fields.many2one('product.uom', 'Unit of Measure ', required=True),
'quantity': fields.float('Quantity', required=True,
digits_compute= dp.get_precision('Product UoS')),
}
_defaults = {
'quantity': 1,
}
def on_change_product_id(self, cr, uid, ids, product, context=None):
vals = {}
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.product_tmpl_id.website_description,
'name': product_obj.name,
'uom_id': product_obj.product_tmpl_id.uom_id.id,
})
return {'value': vals}
class product_template(osv.Model):
_inherit = "product.template"
_columns = {
'website_description': fields.html('Description for the website'),
}

View File

@ -0,0 +1,8 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sale_quote_template,sale.quote.template,model_sale_quote_template,base.group_sale_salesman,1,0,0,0
access_sale_quote_template_manager,sale.quote.template,model_sale_quote_template,base.group_sale_manager,1,1,1,1
access_sale_quote_line,sale.quote.line,model_sale_quote_line,base.group_sale_salesman,1,0,0,0
access_sale_quote_line_manager,sale.quote.line,model_sale_quote_line,base.group_sale_manager,1,1,1,1
access_sale_quote_option,sale.quote.option,model_sale_quote_option,base.group_sale_salesman,1,0,0,0
access_sale_quote_option_manager,sale.quote.option,model_sale_quote_option,base.group_sale_manager,1,1,1,1
access_sale_order_option,sale.order.option,model_sale_order_option,base.group_sale_salesman,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sale_quote_template sale.quote.template model_sale_quote_template base.group_sale_salesman 1 0 0 0
3 access_sale_quote_template_manager sale.quote.template model_sale_quote_template base.group_sale_manager 1 1 1 1
4 access_sale_quote_line sale.quote.line model_sale_quote_line base.group_sale_salesman 1 0 0 0
5 access_sale_quote_line_manager sale.quote.line model_sale_quote_line base.group_sale_manager 1 1 1 1
6 access_sale_quote_option sale.quote.option model_sale_quote_option base.group_sale_salesman 1 0 0 0
7 access_sale_quote_option_manager sale.quote.option model_sale_quote_option base.group_sale_manager 1 1 1 1
8 access_sale_order_option sale.order.option model_sale_order_option base.group_sale_salesman 1 1 1 1

View File

@ -0,0 +1,63 @@
<section class="oe_container">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan">Send Quotes Online</h2>
<h3 class="oe_slogan">Create polished, professional quotes in minutes</h3>
<div class="oe_span6">
<div class="oe_demo oe_picture oe_screenshot">
<a href="https://www.openerp.com/saas_master/demo?lang=en_US&module=sale">
<img src="sale_quote.png">
</a>
<div class="oe_demo_footer oe_centeralign">Online Demo</div>
</div>
</div>
<div class="oe_span6">
<p class='oe_mt32'>
Use templates to create polished, professional quotes in minutes. Send these
quotes by email and let your customer sign online. Use cross-selling and
discounts to push boost your sales.
</p>
<div class="oe_centeralign oe_websiteonly">
<a href="http://www.openerp.com/start?app=sale" class="oe_button oe_big oe_tacky">Start your <span class="oe_emph">free</span> trial</a>
</div>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row">
<h2 class="oe_slogan">Electronic Signature</h2>
<h3 class="oe_slogan">No more fax or scans in emails</h3>
<div class="oe_span6">
<p class='oe_mt32'>
Let your customers sign proposals online. Use up-selling techniques to
propose them some options.
</p>
<p class='oe_mt32'>
Spend the extra time focusing on selling, not recording data.
</p>
</div>
<div class="oe_span6">
<div class="oe_bg_img">
<img class="oe_picture oe_screenshot" src="quote_sign.png">
</div>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row">
<h2 class="oe_slogan">Communicate Efficiently</h2>
<div class="oe_span6">
<div class="oe_bg_img">
<img class="oe_picture oe_screenshot" src="sale_price.png">
</div>
</div>
<div class="oe_span6">
<p class='oe_mt32'>
Have clear pricing strategies.
</p>
</div>
</div>
</section>

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

@ -0,0 +1,77 @@
/*
jSignature v2 "2012-11-01T22:48" "commit ID 1c15dfafecc75925c3b7d529356a558b59220edb"
Copyright (c) 2012 Willow Systems Corp http://willow-systems.com
Copyright (c) 2010 Brinley Ang http://www.unbolt.net
MIT License <http://www.opensource.org/licenses/mit-license.php>
Simplify.js BSD
(c) 2012, Vladimir Agafonkin
mourner.github.com/simplify-js
base64 encoder
MIT, GPL
http://phpjs.org/functions/base64_encode
+ original by: Tyler Akins (http://rumkin.com)
+ improved by: Bayron Guevara
+ improved by: Thunder.m
+ improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ bugfixed by: Pellentesque Malesuada
+ improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ improved by: Rafal Kukawski (http://kukawski.pl)
jSignature v2 jSignature's Undo Button and undo functionality plugin
jSignature v2 jSignature's custom "base30" format export and import plugins.
jSignature v2 SVG export plugin.
*/
(function(){function q(b){for(var j,s=b.css("color"),g,b=b[0],c=!1;b&&!g&&!c;){try{j=$(b).css("background-color")}catch(d){j="transparent"}"transparent"!==j&&"rgba(0, 0, 0, 0)"!==j&&(g=j);c=b.body;b=b.parentNode}var b=/rgb[a]*\((\d+),\s*(\d+),\s*(\d+)/,c=/#([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})/,a;j=void 0;(j=s.match(b))?a={r:parseInt(j[1],10),g:parseInt(j[2],10),b:parseInt(j[3],10)}:(j=s.match(c))&&(a={r:parseInt(j[1],16),g:parseInt(j[2],16),b:parseInt(j[3],16)});var f;g?
(j=void 0,(j=g.match(b))?f={r:parseInt(j[1],10),g:parseInt(j[2],10),b:parseInt(j[3],10)}:(j=g.match(c))&&(f={r:parseInt(j[1],16),g:parseInt(j[2],16),b:parseInt(j[3],16)})):f=a?127<Math.max.apply(null,[a.r,a.g,a.b])?{r:0,g:0,b:0}:{r:255,g:255,b:255}:{r:255,g:255,b:255};j=function(b){return"rgb("+[b.r,b.g,b.b].join(", ")+")"};a&&f?(b=Math.max.apply(null,[a.r,a.g,a.b]),a=Math.max.apply(null,[f.r,f.g,f.b]),a=Math.round(a+-0.75*(a-b)),a={r:a,g:a,b:a}):a?(a=Math.max.apply(null,[a.r,a.g,a.b]),b=1,127<a&&
(b=-1),a=Math.round(a+96*b),a={r:a,g:a,b:a}):a={r:191,g:191,b:191};return{color:s,"background-color":f?j(f):g,"decor-color":j(a)}}function m(b,j){this.x=b;this.y=j;this.reverse=function(){return new this.constructor(-1*this.x,-1*this.y)};this._length=null;this.getLength=function(){this._length||(this._length=Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2)));return this._length};var s=function(b){return Math.round(b/Math.abs(b))};this.resizeTo=function(b){if(0===this.x&&0===this.y)this._length=0;else if(0===
this.x)this._length=b,this.y=b*s(this.y);else if(0===this.y)this._length=b,this.x=b*s(this.x);else{var j=Math.abs(this.y/this.x),a=Math.sqrt(Math.pow(b,2)/(1+Math.pow(j,2))),j=j*a;this._length=b;this.x=a*s(this.x);this.y=j*s(this.y)}return this};this.angleTo=function(b){var j=this.getLength()*b.getLength();return 0===j?0:Math.acos(Math.min(Math.max((this.x*b.x+this.y*b.y)/j,-1),1))/Math.PI}}function k(b,j){this.x=b;this.y=j;this.getVectorToCoordinates=function(b,j){return new m(b-this.x,j-this.y)};
this.getVectorFromCoordinates=function(b,j){return this.getVectorToCoordinates(b,j).reverse()};this.getVectorToPoint=function(b){return new m(b.x-this.x,b.y-this.y)};this.getVectorFromPoint=function(b){return this.getVectorToPoint(b).reverse()}}function t(b,j,a,g,c){this.data=b;this.context=j;if(b.length)for(var d=b.length,f,l,i=0;i<d;i++){f=b[i];l=f.x.length;a.call(j,f);for(var e=1;e<l;e++)g.call(j,f,e);c.call(j,f)}this.changed=function(){};this.startStrokeFn=a;this.addToStrokeFn=g;this.endStrokeFn=
c;this.inStroke=!1;this._stroke=this._lastPoint=null;this.startStroke=function(b){if(b&&"number"==typeof b.x&&"number"==typeof b.y){this._stroke={x:[b.x],y:[b.y]};this.data.push(this._stroke);this._lastPoint=b;this.inStroke=!0;var j=this._stroke,a=this.startStrokeFn,g=this.context;setTimeout(function(){a.call(g,j)},3);return b}return null};this.addToStroke=function(b){if(this.inStroke&&"number"===typeof b.x&&"number"===typeof b.y&&4<Math.abs(b.x-this._lastPoint.x)+Math.abs(b.y-this._lastPoint.y)){var j=
this._stroke.x.length;this._stroke.x.push(b.x);this._stroke.y.push(b.y);this._lastPoint=b;var a=this._stroke,g=this.addToStrokeFn,s=this.context;setTimeout(function(){g.call(s,a,j)},3);return b}return null};this.endStroke=function(){var b=this.inStroke;this.inStroke=!1;this._lastPoint=null;if(b){var j=this._stroke,a=this.endStrokeFn,g=this.context,s=this.changed;setTimeout(function(){a.call(g,j);s.call(g)},3);return!0}return null}}function r(b,j,a){var g=this.$parent=$(b),b=this.eventTokens={};this.events=
new v(this);var c=$.fn[e]("globalEvents"),d={width:"ratio",height:"ratio",sizeRatio:4,color:"#000","background-color":"#fff","decor-color":"#eee",lineWidth:0,minFatFingerCompensation:-10,showUndoButton:!1,data:[]};$.extend(d,q(g));j&&$.extend(d,j);this.settings=d;for(var f in a)a.hasOwnProperty(f)&&a[f].call(this,f);this.events.publish(e+".initializing");this.$controlbarUpper=$('<div style="padding:0 !important;margin:0 !important;width: 100% !important; height: 0 !important;margin-top:-1em !important;margin-bottom:1em !important;"></div>').appendTo(g);
this.isCanvasEmulator=!1;a=this.canvas=this.initializeCanvas(d);j=$(a);this.$controlbarLower=$('<div style="padding:0 !important;margin:0 !important;width: 100% !important; height: 0 !important;margin-top:-1.5em !important;margin-bottom:1.5em !important;"></div>').appendTo(g);this.canvasContext=a.getContext("2d");j.data(e+".this",this);g=(g=d.lineWidth)?g:Math.max(Math.round(a.width/400),2);d.lineWidth=g;this.lineCurveThreshold=3*d.lineWidth;d.cssclass&&""!=$.trim(d.cssclass)&&j.addClass(d.cssclass);
this.fatFingerCompensation=0;var g=function(b){var j,a,g=function(g){g=g.changedTouches&&0<g.changedTouches.length?g.changedTouches[0]:g;return new k(Math.round(g.pageX+j),Math.round(g.pageY+a)+b.fatFingerCompensation)},d=new y(750,function(){b.dataEngine.endStroke()});this.drawEndHandler=function(j){try{j.preventDefault()}catch(a){}d.clear();b.dataEngine.endStroke()};this.drawStartHandler=function(c){c.preventDefault();var s=$(b.canvas).offset();j=-1*s.left;a=-1*s.top;b.dataEngine.startStroke(g(c));
d.kick()};this.drawMoveHandler=function(j){j.preventDefault();b.dataEngine.inStroke&&(b.dataEngine.addToStroke(g(j)),d.kick())};return this}.call({},this),l=g.drawEndHandler,i=g.drawStartHandler,p=g.drawMoveHandler,h=this.canvas,j=$(h);this.isCanvasEmulator?(j.bind("mousemove."+e,p),j.bind("mouseup."+e,l),j.bind("mousedown."+e,i)):(h.ontouchstart=function(b){h.onmousedown=void 0;h.onmouseup=void 0;h.onmousemove=void 0;this.fatFingerCompensation=d.minFatFingerCompensation&&-3*d.lineWidth>d.minFatFingerCompensation?
-3*d.lineWidth:d.minFatFingerCompensation;i(b);h.ontouchend=l;h.ontouchstart=i;h.ontouchmove=p},h.onmousedown=function(b){h.ontouchstart=void 0;h.ontouchend=void 0;h.ontouchmove=void 0;i(b);h.onmousedown=i;h.onmouseup=l;h.onmousemove=p});b[e+".windowmouseup"]=c.subscribe(e+".windowmouseup",g.drawEndHandler);this.events.publish(e+".attachingEventHandlers");var n=this,b=d.width.toString(10),w=e;if("ratio"===b||"%"===b.split("")[b.length-1])this.eventTokens[w+".parentresized"]=c.subscribe(w+".parentresized",
function(b,j,a){return function(){var g=j.width();if(g!==a){for(var d in b)b.hasOwnProperty(d)&&(c.unsubscribe(b[d]),delete b[d]);var s=n.settings;n.$parent.children().remove();for(d in n)n.hasOwnProperty(d)&&delete n[d];d=s.data;var g=1*g/a,f=[],l,i,e,h,k,p;i=0;for(e=d.length;i<e;i++){p=d[i];l={x:[],y:[]};h=0;for(k=p.x.length;h<k;h++)l.x.push(p.x[h]*g),l.y.push(p.y[h]*g);f.push(l)}s.data=f;j[w](s)}}}(this.eventTokens,this.$parent,this.$parent.width(),1*this.canvas.width/this.canvas.height));this.resetCanvas(d.data);
this.events.publish(e+".initialized");return this}var e="jSignature",y=function(b,j){var a;this.kick=function(){clearTimeout(a);a=setTimeout(j,b)};this.clear=function(){clearTimeout(a)};return this},v=function(b){this.topics={};this.context=b?b:this;this.publish=function(b,a,g,d){if(this.topics[b]){var c=this.topics[b],f=Array.prototype.slice.call(arguments,1),l=[],i,e,h,p;e=0;for(h=c.length;e<h;e++)p=c[e],i=p[0],p[1]&&(p[0]=function(){},l.push(e)),i.apply(this.context,f);e=0;for(h=l.length;e<h;e++)c.splice(l[e],
1)}};this.subscribe=function(b,a,g){this.topics[b]?this.topics[b].push([a,g]):this.topics[b]=[[a,g]];return{topic:b,callback:a}};this.unsubscribe=function(b){if(this.topics[b.topic])for(var a=this.topics[b.topic],g=0,d=a.length;g<d;g++)a[g][0]===b.callback&&a.splice(g,1)}},z=function(b){var a=this.canvasContext,d=b.x[0],b=b.y[0],g=this.settings.lineWidth,c=a.fillStyle;a.fillStyle=a.strokeStyle;a.fillRect(d+g/-2,b+g/-2,g,g);a.fillStyle=c},u=function(b,a){var d=new k(b.x[a-1],b.y[a-1]),g=new k(b.x[a],
b.y[a]),c=d.getVectorToPoint(g);if(1<a){var f=new k(b.x[a-2],b.y[a-2]),l=f.getVectorToPoint(d),i;if(l.getLength()>this.lineCurveThreshold){i=2<a?(new k(b.x[a-3],b.y[a-3])).getVectorToPoint(f):new m(0,0);var e=0.35*l.getLength(),h=l.angleTo(i.reverse()),p=c.angleTo(l.reverse());i=(new m(i.x+l.x,i.y+l.y)).resizeTo(Math.max(0.05,h)*e);var n=(new m(l.x+c.x,l.y+c.y)).reverse().resizeTo(Math.max(0.05,p)*e),l=this.canvasContext,e=f.x,p=f.y,h=d.x,w=d.y,A=f.x+i.x,f=f.y+i.y;i=d.x+n.x;n=d.y+n.y;l.beginPath();
l.moveTo(e,p);l.bezierCurveTo(A,f,i,n,h,w);l.stroke()}}c.getLength()<=this.lineCurveThreshold&&(c=this.canvasContext,f=d.x,d=d.y,i=g.x,g=g.y,c.beginPath(),c.moveTo(f,d),c.lineTo(i,g),c.stroke())},x=function(b){var a=b.x.length-1;if(0<a){var d=new k(b.x[a],b.y[a]),g=new k(b.x[a-1],b.y[a-1]),c=g.getVectorToPoint(d);if(c.getLength()>this.lineCurveThreshold){if(1<a){var b=(new k(b.x[a-2],b.y[a-2])).getVectorToPoint(g),f=(new m(b.x+c.x,b.y+c.y)).resizeTo(c.getLength()/2),c=this.canvasContext,b=g.x,a=g.y,
l=d.x,i=d.y,e=g.x+f.x,g=g.y+f.y,f=d.x,d=d.y;c.beginPath();c.moveTo(b,a);c.bezierCurveTo(e,g,f,d,l,i)}else c=this.canvasContext,b=g.x,g=g.y,a=d.x,d=d.y,c.beginPath(),c.moveTo(b,g),c.lineTo(a,d);c.stroke()}}};r.prototype.resetCanvas=function(b){var a=this.canvas,d=this.settings,c=this.canvasContext,f=this.isCanvasEmulator,l=a.width,i=a.height;c.clearRect(0,0,l+30,i+30);c.shadowColor=c.fillStyle=d["background-color"];f&&c.fillRect(0,0,l+30,i+30);c.lineWidth=Math.ceil(parseInt(d.lineWidth,10));c.lineCap=
c.lineJoin="round";c.strokeStyle=d["decor-color"];c.shadowOffsetX=0;c.shadowOffsetY=0;var h=Math.round(i/5),p=1.5*h,k=i-h,l=l-1.5*h,i=i-h;c.beginPath();c.moveTo(p,k);c.lineTo(l,i);c.stroke();c.strokeStyle=d.color;f||(c.shadowColor=c.strokeStyle,c.shadowOffsetX=0.5*c.lineWidth,c.shadowOffsetY=-0.6*c.lineWidth,c.shadowBlur=0);b||(b=[]);c=this.dataEngine=new t(b,this,z,u,x);d.data=b;$(a).data(e+".data",b).data(e+".settings",d);var n=this.$parent,w=this.events,A=e;c.changed=function(){w.publish(A+".change");
n.trigger("change")};c.changed();return!0};r.prototype.initializeCanvas=function(b){var a=document.createElement("canvas"),c=$(a);b.width===b.height&&"ratio"===b.height&&(b.width="100%");c.css("margin",0).css("padding",0).css("border","none").css("height","ratio"===b.height||!b.height?1:b.height.toString(10)).css("width","ratio"===b.width||!b.width?1:b.width.toString(10));c.appendTo(this.$parent);"ratio"===b.height?c.css("height",Math.round(c.width()/b.sizeRatio)):"ratio"===b.width&&c.css("width",
Math.round(c.height()*b.sizeRatio));c.addClass(e);a.width=c.width();a.height=c.height();b=a;if(b.getContext)b=!1;else{var c=b.ownerDocument.parentWindow,d=c.FlashCanvas?b.ownerDocument.parentWindow.FlashCanvas:"undefined"===typeof FlashCanvas?void 0:FlashCanvas;if(d){b=d.initElement(b);d=1;c&&(c.screen&&c.screen.deviceXDPI&&c.screen.logicalXDPI)&&(d=1*c.screen.deviceXDPI/c.screen.logicalXDPI);if(1!==d)try{$(b).children("object").get(0).resize(Math.ceil(b.width*d),Math.ceil(b.height*d)),b.getContext("2d").scale(d,
d)}catch(f){}b=!0}else throw Error("Canvas element does not support 2d context. jSignature cannot proceed.");}this.isCanvasEmulator=b;a.onselectstart=function(b){b&&b.preventDefault&&b.preventDefault();b&&b.stopPropagation&&b.stopPropagation();return!1};return a};var p=window,h=function(b,a){var c=new Image,d=this;c.onload=function(){d.getContext("2d").drawImage(c,0,0,c.width<d.width?c.width:d.width,c.height<d.height?c.height:d.height)};c.src="data:"+a+","+b},a=function(b){this.find("canvas."+e).add(this.filter("canvas."+
e)).data(e+".this").resetCanvas(b);return this},f=function(b,a){if(void 0===a&&("string"===typeof b&&"data:"===b.substr(0,5))&&(a=b.slice(5).split(",")[0],b=b.slice(6+a.length),a===b))return;var c=this.find("canvas."+e).add(this.filter("canvas."+e));if(n.hasOwnProperty(a))0!==c.length&&n[a].call(c[0],b,a,function(b){return function(){return b.resetCanvas.apply(b,arguments)}}(c.data(e+".this")));else throw Error(e+" is unable to find import plugin with for format '"+String(a)+"'");return this},d=new v,
c=e,l,i=function(){d.publish(c+".parentresized")};$(p).bind("resize."+c,function(){l&&clearTimeout(l);l=setTimeout(i,500)}).bind("mouseup."+c,function(){d.publish(c+".windowmouseup")});var w={},A={"default":function(){return this.toDataURL()},"native":function(b){return b},image:function(){var b=this.toDataURL();if("string"===typeof b&&4<b.length&&"data:"===b.slice(0,5)&&-1!==b.indexOf(",")){var a=b.indexOf(",");return[b.slice(5,a),b.substr(a+1)]}return[]}},n={"native":function(b,a,c){c(b)},image:h,
"image/png;base64":h,"image/jpeg;base64":h,"image/jpg;base64":h},B={"export":A,"import":n,instance:w},C={init:function(b){return this.each(function(){var a,c=!1;for(a=this.parentNode;a&&!c;)c=a.body,a=a.parentNode;!c||new r(this,b,w)})},getSettings:function(){return this.find("canvas."+e).add(this.filter("canvas."+e)).data(e+".this").settings},clear:a,reset:a,addPlugin:function(b,a,c){B.hasOwnProperty(b)&&(B[b][a]=c);return this},listPlugins:function(b){var a=[];if(B.hasOwnProperty(b)){var b=B[b],
c;for(c in b)b.hasOwnProperty(c)&&a.push(c)}return a},getData:function(b){var a=this.find("canvas."+e).add(this.filter("canvas."+e));void 0===b&&(b="default");if(0!==a.length&&A.hasOwnProperty(b))return A[b].call(a.get(0),a.data(e+".data"))},importData:f,setData:f,globalEvents:function(){return d},events:function(){return this.find("canvas."+e).add(this.filter("canvas."+e)).data(e+".this").events}};$.fn[e]=function(b){if(!b||"object"===typeof b)return C.init.apply(this,arguments);if("string"===typeof b&&
C[b])return C[b].apply(this,Array.prototype.slice.call(arguments,1));$.error("Method "+String(b)+" does not exist on jQuery."+e)}})();
(function(){$.fn.jSignature("addPlugin","instance","UndoButton",function(q){this.events.subscribe("jSignature.attachingEventHandlers",function(){if(this.settings[q]){var m=this.settings[q];"function"!==typeof m&&(m=function(){var e=$('<input type="button" value="Undo last stroke" style="position:absolute;display:none;margin:0 !important;top:auto" />').appendTo(this.$controlbarLower),k=e.width();e.css("left",Math.round((this.canvas.width-k)/2));k!==e.width()&&e.width(k);return e});var k=m.call(this),
t=this;t.events.subscribe("jSignature.change",function(){t.dataEngine.data.length?k.show():k.hide()});var r=this,e=(this.events.topics.hasOwnProperty("jSignature.undo")?q:"jSignature")+".undo";k.bind("click",function(){r.events.publish(e)});r.events.subscribe(e,function(){var e=r.dataEngine.data;e.length&&(e.pop(),r.resetCanvas(e))})}})})})();
(function(){for(var q={},m={},k="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX".split(""),t=k.length/2,r=t-1;-1<r;r--)q[k[r]]=k[r+t],m[k[r+t]]=k[r];var e=function(e){for(var e=e.split(""),h=e.length,a=1;a<h;a++)e[a]=q[e[a]];return e.join("")},y=function(k){for(var h=[],a=0,f=1,d=k.length,c,l,i=0;i<d;i++)c=Math.round(k[i]),l=c-a,a=c,0>l&&0<f?(f=-1,h.push("Z")):0<l&&0>f&&(f=1,h.push("Y")),c=Math.abs(l),c>=t?h.push(e(c.toString(t))):h.push(c.toString(t));return h.join("")},v=function(e){for(var h=
[],e=e.split(""),a=e.length,f,d=1,c=[],l=0,i=0;i<a;i++)f=e[i],f in q||"Z"===f||"Y"===f?(0!==c.length&&(c=parseInt(c.join(""),t)*d+l,h.push(c),l=c),"Z"===f?(d=-1,c=[]):"Y"===f?(d=1,c=[]):c=[f]):c.push(m[f]);h.push(parseInt(c.join(""),t)*d+l);return h},z=function(e){for(var h=[],a=e.length,f,d=0;d<a;d++)f=e[d],h.push(y(f.x)),h.push(y(f.y));return h.join("_")},u=function(e){for(var h=[],e=e.split("_"),a=e.length/2,f=0;f<a;f++)h.push({x:v(e[2*f]),y:v(e[2*f+1])});return h},k=function(e){return["image/jsignature;base30",
z(e)]},r=function(e,h,a){"string"===typeof e&&("image/jsignature;base30"===e.substring(0,23).toLowerCase()&&(e=e.substring(24)),a(u(e)))};if(null==this.jQuery)throw Error("We need jQuery for some of the functionality. jQuery is not detected. Failing to initialize...");var x=this.jQuery.fn.jSignature;x("addPlugin","export","base30",k);x("addPlugin","export","image/jsignature;base30",k);x("addPlugin","import","base30",r);x("addPlugin","import","image/jsignature;base30",r);this.jSignatureDebug&&(this.jSignatureDebug.base30=
{remapTailChars:e,compressstrokeleg:y,uncompressstrokeleg:v,compressstrokes:z,uncompressstrokes:u,charmap:q})}).call("undefined"!==typeof window?window:this);
(function(){function q(a,f){this.x=a;this.y=f;this.reverse=function(){return new this.constructor(-1*this.x,-1*this.y)};this._length=null;this.getLength=function(){this._length||(this._length=Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2)));return this._length};var d=function(a){return Math.round(a/Math.abs(a))};this.resizeTo=function(a){if(0===this.x&&0===this.y)this._length=0;else if(0===this.x)this._length=a,this.y=a*d(this.y);else if(0===this.y)this._length=a,this.x=a*d(this.x);else{var f=Math.abs(this.y/
this.x),e=Math.sqrt(Math.pow(a,2)/(1+Math.pow(f,2))),f=f*e;this._length=a;this.x=e*d(this.x);this.y=f*d(this.y)}return this};this.angleTo=function(a){var d=this.getLength()*a.getLength();return 0===d?0:Math.acos(Math.min(Math.max((this.x*a.x+this.y*a.y)/d,-1),1))/Math.PI}}function m(a,f){this.x=a;this.y=f;this.getVectorToCoordinates=function(a,c){return new q(a-this.x,c-this.y)};this.getVectorFromCoordinates=function(a,c){return this.getVectorToCoordinates(a,c).reverse()};this.getVectorToPoint=function(a){return new q(a.x-
this.x,a.y-this.y)};this.getVectorFromPoint=function(a){return this.getVectorToPoint(a).reverse()}}function k(a,f){var d=Math.pow(10,f);return Math.round(a*d)/d}function t(a,f,d){var f=f+1,c=new m(a.x[f-1],a.y[f-1]),e=new m(a.x[f],a.y[f]),e=c.getVectorToPoint(e),i=new m(a.x[f-2],a.y[f-2]),c=i.getVectorToPoint(c);return c.getLength()>d?(d=2<f?(new m(a.x[f-3],a.y[f-3])).getVectorToPoint(i):new q(0,0),a=0.35*c.getLength(),i=c.angleTo(d.reverse()),f=e.angleTo(c.reverse()),d=(new q(d.x+c.x,d.y+c.y)).resizeTo(Math.max(0.05,
i)*a),e=(new q(c.x+e.x,c.y+e.y)).reverse().resizeTo(Math.max(0.05,f)*a),e=new q(c.x+e.x,c.y+e.y),["c",k(d.x,2),k(d.y,2),k(e.x,2),k(e.y,2),k(c.x,2),k(c.y,2)]):["l",k(c.x,2),k(c.y,2)]}function r(a,f){var d=a.x.length-1,c=new m(a.x[d],a.y[d]),e=new m(a.x[d-1],a.y[d-1]),c=e.getVectorToPoint(c);if(1<d&&c.getLength()>f){var d=(new m(a.x[d-2],a.y[d-2])).getVectorToPoint(e),e=c.angleTo(d.reverse()),i=0.35*c.getLength(),d=(new q(d.x+c.x,d.y+c.y)).resizeTo(Math.max(0.05,e)*i);return["c",k(d.x,2),k(d.y,2),k(c.x,
2),k(c.y,2),k(c.x,2),k(c.y,2)]}return["l",k(c.x,2),k(c.y,2)]}function e(a,e,d){for(var e=["M",k(a.x[0]-e,2),k(a.y[0]-d,2)],d=1,c=a.x.length-1;d<c;d++)e.push.apply(e,t(a,d,1));0<c?e.push.apply(e,r(a,d,1)):0===c&&e.push.apply(e,["l",1,1]);return e.join(" ")}function y(a){var f=['<?xml version="1.0" encoding="UTF-8" standalone="no"?>','<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'],d,c=a.length,l,i=[],h=[],k=l=d=0,n=0,p=[];if(0!==c){for(d=0;d<c;d++){k=
a[d];n=[];l={x:[],y:[]};for(var m=void 0,b=void 0,m=0,b=k.x.length;m<b;m++)n.push({x:k.x[m],y:k.y[m]});n=simplify(n,0.7,!0);m=0;for(b=n.length;m<b;m++)l.x.push(n[m].x),l.y.push(n[m].y);p.push(l);i=i.concat(l.x);h=h.concat(l.y)}a=Math.min.apply(null,i)-1;c=Math.max.apply(null,i)+1;i=Math.min.apply(null,h)-1;h=Math.max.apply(null,h)+1;k=0>a?0:a;n=0>i?0:i;d=c-a;l=h-i}f.push('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="'+d.toString()+'" height="'+l.toString()+'">');d=0;for(c=p.length;d<
c;d++)l=p[d],f.push('<path fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="'+e(l,k,n)+'"/>');f.push("</svg>");return f.join("")}function v(a){return[p,y(a)]}function z(a){return[h,x(y(a))]}var u=window;"use strict";("undefined"!=typeof exports?exports:u).simplify=function(a,e,d){e=void 0!==e?e*e:1;if(!d){for(var c=a.length,l,i=a[0],h=[i],d=1;d<c;d++){l=a[d];var k=l.x-i.x,n=l.y-i.y;k*k+n*n>e&&(h.push(l),i=l)}a=(i!==l&&h.push(l),h)}l=a;var d=l.length,
c=new ("undefined"!=typeof Uint8Array?Uint8Array:Array)(d),i=0,h=d-1,m,p,b=[],j=[],s=[];for(c[i]=c[h]=1;h;){n=0;for(k=i+1;k<h;k++){m=l[k];var g=l[i],r=l[h],q=g.x,t=g.y,g=r.x-q,u=r.y-t,v=void 0;if(0!==g||0!==u)v=((m.x-q)*g+(m.y-t)*u)/(g*g+u*u),1<v?(q=r.x,t=r.y):0<v&&(q+=g*v,t+=u*v);m=(g=m.x-q,u=m.y-t,g*g+u*u);m>n&&(p=k,n=m)}n>e&&(c[p]=1,b.push(i),j.push(p),b.push(p),j.push(h));i=b.pop();h=j.pop()}for(k=0;k<d;k++)c[k]&&s.push(l[k]);return a=s,a};if("function"!==typeof x)var x=function(a){var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split(""),
d,c,h,i,k=0,m=0,n="",n=[];do d=a.charCodeAt(k++),c=a.charCodeAt(k++),h=a.charCodeAt(k++),i=d<<16|c<<8|h,d=i>>18&63,c=i>>12&63,h=i>>6&63,i&=63,n[m++]=e[d]+e[c]+e[h]+e[i];while(k<a.length);n=n.join("");a=a.length%3;return(a?n.slice(0,a-3):n)+"===".slice(a||3)};var p="image/svg+xml",h="image/svg+xml;base64";if("undefined"===typeof $)throw Error("We need jQuery for some of the functionality. jQuery is not detected. Failing to initialize...");u=$.fn.jSignature;u("addPlugin","export","svg",v);u("addPlugin",
"export",p,v);u("addPlugin","export","svgbase64",z);u("addPlugin","export",h,z)})();

View File

@ -0,0 +1,56 @@
.bs-sidebar {
position: fixed;
z-index : 1;
background-color: #f7f5fa;
border-radius: 5px;
}
.bs-sidenav {
padding-top: 10px;
padding-bottom: 10px;
}
.bs-sidebar .nav > li > a {
display: block;
color: #716b7a;
padding: 5px 20px;
}
.bs-sidebar .nav > .active > a,
.bs-sidebar .nav > .active:hover > a,
.bs-sidebar .nav > .active:focus > a {
font-weight: bold;
color: #563d7c;
background-color: transparent;
border-right: 1px solid #563d7c;
}
.bs-sidebar .nav .nav {
display: none;
margin-bottom: 8px;
}
.bs-sidebar .nav .nav > li > a {
padding-top: 3px;
padding-bottom: 3px;
padding-left: 30px;
font-size: 90%;
}
@media (min-width: 992px) {
.bs-sidebar .nav > .active > ul {
display: block;
}
.bs-sidebar {
width: 213px;
}
}
@media (min-width: 1200px) {
.bs-sidebar {
width: 263px;
}
}
@media print {
body {
padding : 0 !important;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -0,0 +1,92 @@
$(document).ready(function () {
$('a.js_update_line_json').on('click', function (ev) {
ev.preventDefault();
var $link = $(ev.currentTarget);
var href = $link.attr("href");
var order_id = href.match(/order_id=([0-9]+)/);
var line_id = href.match(/update_line\/([0-9]+)/);
var token = href.match(/token=(.*)/);
openerp.jsonRpc("/quote/update_line/", 'call', {
'line_id': line_id[1],
'order_id': parseInt(order_id[1]),
'token': token[1],
'remove': $link.is('[href*="remove"]'),
'unlink': $link.is('[href*="unlink"]')
})
.then(function (data) {
if(!data){
location.reload();
}
$link.parents('.input-group:first').find('.js_quantity').val(data[0]);
$('[data-id="total_amount"]>span').html(data[1]);
});
return false;
});
var empty_sign = false;
$('#modelaccept').on('shown.bs.modal', function (e) {
$("#signature").empty().jSignature({'decor-color' : '#D1D0CE'});
empty_sign = $("#signature").jSignature("getData",'image');
});
$('#sign_clean').on('click', function (e) {
$("#signature").jSignature('reset');
});
$('form.js_accept_json').submit(function(ev){
ev.preventDefault();
var $link = $(ev.currentTarget);
var href = $link.attr("action");
var order_id = href.match(/accept\/([0-9]+)/);
var token = href.match(/token=(.*)/);
if (token)
token = token[1];
var signer_name = $("#name").val();
var sign = $("#signature").jSignature("getData",'image');
var is_empty = sign?empty_sign[1]==sign[1]:false;
$('#signer').toggleClass('has-error', ! signer_name);
$('#drawsign').toggleClass('panel-danger', is_empty).toggleClass('panel-default', ! is_empty);
if (is_empty || ! signer_name)
return false;
openerp.jsonRpc("/quote/accept/", 'call', {
'order_id': parseInt(order_id[1]),
'token': token,
'signer': signer_name,
'sign': sign?JSON.stringify(sign[1]):false,
}).then(function (data) {
$('#modelaccept').modal('hide');
window.location.href = '/quote/'+order_id[1]+'/'+token+'?message=3';
});
return false
});
// automatically generate a menu from h1 and h2 tag in content
var ul = $('[data-id="quote_sidebar"]');
var sub_li = null;
var sub_ul = null;
$("section h1, section h2").each(function() {
switch (this.tagName.toLowerCase()) {
case "h1":
id = _.uniqueId('quote_header_')
$(this.parentNode).attr('id',id);
sub_li = $("<li>").html('<a href="#'+id+'">'+$(this).text()+'</a>').appendTo(ul);
sub_ul = null;
break;
case "h2":
id = _.uniqueId('quote_')
if (sub_li) {
if (!sub_ul) {
sub_ul = $("<ul class='nav'>").appendTo(sub_li);
}
$(this.parentNode).attr('id',id)
$("<li>").html('<a href="#'+id+'">'+$(this).text()+'</a>').appendTo(sub_ul);
}
break;
}
});
});

View File

@ -0,0 +1,520 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<template id="pricing" name="Price">
<section data-snippet-id="title">
<h1 class="page-header">Pricing</h1>
</section>
<section id="quote">
<table class="table">
<thead>
<tr>
<th>Products</th>
<th>Quantity</th>
<th>Taxes</th>
<th></th>
<th class="text-right">Unit Price</th>
<th class="text-right">Price</th>
</tr>
</thead>
<tbody>
<tr t-foreach="quotation.order_line" t-as="line">
<td>
<div t-field="line.name"/>
</td>
<td>
<div id="quote_qty">
<span t-field="line.product_uom_qty"/>
<span t-field="line.product_uom"/>
</div>
</td>
<td>
<div t-foreach="line.tax_id" t-as="tax">
<t t-esc="tax.name"/>
</div>
</td>
<td>
<strong t-if="line.discount" class="text-info">
<t t-esc="((line.discount % 1) and '%s' or '%d') % line.discount"/>% discount
</strong>
</td>
<td>
<strong class="text-right">
<div t-field="line.price_unit"
t-field-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'
t-att-style="line.discount and 'text-decoration: line-through' or ''"
t-att-class="line.discount and 'text-danger' or ''"/>
<!-- TODO: apply monetary widget formating -->
<div t-if="line.discount">
<t t-esc="'%.2f' % ((1-line.discount / 100.0) * line.price_unit)"/>
</div>
</strong>
</td>
<td>
<div class="text-right"
t-field="line.price_subtotal"
t-field-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'/>
</td>
<td>
<a t-href="./update_line/#{ line.id }/?order_id=#{ quotation.id }&amp;unlink=True&amp;token=#{ quotation.access_token }" class="mb8 js_update_line_json pull-right hidden-print" t-if="line.option_line_id">
<span class="fa fa-trash-o"></span>
</a>
</td>
</tr>
<tr>
<td></td><td></td><td></td><td></td>
<td class="text-right"><strong>Subtotal:</strong></td>
<td class="text-right">
<strong data-id="total_amount" t-field="quotation.amount_untaxed" t-field-options='{"widget": "monetary","display_currency": "quotation.pricelist_id.currency_id"}'/>
</td>
</tr>
<tr>
<td></td><td></td><td></td><td></td>
<td class="text-right">Taxes:</td>
<td class="text-right">
<span data-id="total_amount" t-field="quotation.amount_tax" t-field-options='{"widget": "monetary","display_currency": "quotation.pricelist_id.currency_id"}'/>
</td>
</tr>
<tr>
<td></td><td></td><td></td><td></td>
<td class="text-right"><strong>Total:</strong></td>
<td class="text-right">
<strong data-id="total_amount" t-field="quotation.amount_total" t-field-options='{"widget": "monetary","display_currency": "quotation.pricelist_id.currency_id"}'/>
</td>
</tr>
</tbody>
</table>
</section>
<t t-call="website_quote.quotation_toolbar"/>
<section id="terms" class="container" t-if="quotation.note">
<h2 class="page-header">Terms &amp; Conditions</h2>
<p t-field="quotation.note"/>
</section>
</template>
<template id="change_quantity" inherit_option_id="website_quote.pricing" name="Change Quantity">
<xpath expr="//div[@id='quote_qty']" position="replace">
<div class="input-group">
<span class="input-group-addon hidden-print">
<a t-href="./update_line/#{ line.id }/?order_id=#{ quotation.id }&amp;remove=True&amp;token=#{ quotation.access_token }" class="mb8 js_update_line_json">
<span class="fa fa-minus"/>
</a>
</span>
<input type="text" class="js_quantity form-control" t-att-data-id="line.id" t-att-value="line.product_uom_qty"/>
<span class="input-group-addon hidden-print">
<a t-href="./update_line/#{ line.id }/?order_id=#{ quotation.id }&amp;token=#{ quotation.access_token }" class="mb8 js_update_line_json">
<span class="fa fa-plus"/>
</a>
</span>
</div>
</xpath>
</template>
<template id="chatter">
<h1 class="page-header hidden-print">History</h1>
<ul class="media-list hidden-print" id="comments-list">
<t t-foreach="quotation.message_ids" t-as="message">
<li class="media" t-if="message.type &lt;&gt; 'comment' or message.subtype_id">
<div class="media-body">
<img class="media-object pull-left" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(message.author_id.id)" style="width: 50px; margin-right: 10px;"/>
<div class="media-body">
<h5 class="media-heading">
<span t-field="message.author_id"/> <small>on <span t-field="message.date"/></small>
</h5>
<div t-field="message.body"/>
</div>
</div>
</li>
</t>
</ul>
</template>
<!-- Options:Quotation Chatter: user can reply -->
<template id="opt_quotation_chatter_post_complete_comment" name="Allow Comments" inherit_option_id="website_quote.chatter" inherit_id="website_quote.chatter">
<xpath expr="//h1" position="after">
<section class="mb32 css_editable_mode_hidden hidden-print">
<form id="comment" t-attf-action="/quote/#{quotation.id}/#{quotation.access_token}/post" method="POST">
<img class="img pull-left img-rounded" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(user_id.partner_id.id)" style="width: 50px; margin-right: 10px;"/>
<div class="pull-left mb32" style="width: 75%%">
<textarea rows="4" name="comment" class="form-control" placeholder="Send us a note..."></textarea>
<button type="submit" class="btn btn-primary mt8">Send</button>
</div>
</form>
</section>
<div class="clearfix"/>
</xpath>
</template>
<template id="quotation_toolbar">
<div class="text-center hidden-print" t-if="quotation.state in ('draft', 'sent', 'waiting_date')">
<a class="btn btn-success fa fa-check" data-toggle="modal" data-target="#modelaccept">
Accept
</a>
<a class="btn btn-info fa fa-comment" type="submit" href="#discussion">
Feedback
</a>
<a class="btn btn-danger fa fa-times" data-toggle="modal" data-target="#modeldecline">
Reject
</a>
</div>
</template>
<template id="so_quotation" name="Product Quotation">
<t t-call="website.layout">
<t t-set="head">
<script type="text/javascript" src="/website_quote/static/src/js/website_quotation.js"></script>
<script type="text/javascript" src="/website_quote/static/lib/jSignature/jSignature.min.js"></script>
<link rel='stylesheet' href='/website_quote/static/src/css/website_quotation.css'/>
<t t-raw="head or ''"/>
</t>
<body data-spy="scroll" data-target=".navspy">
<div class="container">
<div class="row mt16">
<div class="col-md-3">
<div class="bs-sidebar">
<div class="text-center hidden-print" t-if="quotation.state in ('draft', 'sent', 'waiting_date')" style="padding: 10px">
<a t-if="order_valid" class="btn btn-primary btn-block fa fa-check" data-toggle="modal" data-target="#modelaccept">
Accept Order
</a>
<a t-if="not order_valid" href="#discussion" class="btn btn-info btn-block">
<strong>This offer expired!</strong><br/>
Contact us for new quote.
</a>
<div class="mt8" t-if="order_valid">
<a type="submit" href="#discussion">
Ask Changes
</a> or
<a data-toggle="modal" data-target="#modeldecline">
Reject
</a>
</div>
</div>
<hr class="mt0 mb0"/>
<t t-call="website_quote.navigation_menu"/>
<hr class="mt0 mb0"/>
<div t-if="order_valid" class="text-center hidden-print">
<input type="hidden" t-att-value="quotation.validity_date" id="validity_date"/>
<div class="mt8" t-if="days_valid &gt; 0">
<strong>This offer expires in</strong>
<div class="fa fa-clock-o fa-2x day_counter mt8 mb16">
<t t-esc="days_valid"/>
<t t-if="days_valid &gt; 1">
days
</t>
<t t-if="days_valid &lt;= 1">
day
</t>
</div>
</div>
<div class="text-center mb16" t-if="quotation.amount_undiscounted &gt; quotation.amount_total">
<p class="text-muted mb8">Your advantage:</p>
<strong t-field="quotation.amount_total"
t-field-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'/>
<strong t-field="quotation.amount_undiscounted"
t-field-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'
style="text-decoration: line-through"
class="text-danger"/>
</div>
</div>
</div>
</div>
<div class="col-md-9">
<div class="alert alert-success alert-dismissable" t-if="message==1">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&amp;times;</button>
Your message has been successfully sent!
</div>
<div class="alert alert-warning alert-dismissable" t-if="message==2">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&amp;times;</button>
This quotation has been rejected. <a href="#discussion">Contact us</a> if you want a new one.
</div>
<div class="alert alert-warning alert-dismissable" t-if="message==3">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&amp;times;</button>
This order has been validated. Thanks for your trust
and do not hesitate to <a href="#discussion">contact us</a> for
any question.
</div>
<a id="introduction"/>
<h1 class="page-header mt16">
<span t-if="quotation.state in ('draft','sent','cancel')">Your Quotation</span>
<span t-if="quotation.state not in ('draft','sent','cancel')">Your Order</span>
<em t-esc="quotation.name"/>
<small t-field="quotation.state"/>
<div groups="base.group_website_publisher" t-ignore="true" class="pull-right css_editable_mode_hidden">
<a class="btn btn-info hidden-print" t-att-href="'/web#return_label=Website&amp;model=%s&amp;id=%s' % (quotation._name, quotation.id)">Update Quote</a>
</div>
</h1>
<div class="modal fade" id="modelaccept" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<form id="accept" method="POST" t-attf-action="/quote/accept/#{quotation.id}/?token=#{quotation.access_token}" class="js_accept_json modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&amp;times;</button>
<h4 class="modal-title">Validate Order</h4>
</div>
<div class="modal-body" id="sign-dialog">
<p>
I agree that by signing this proposal, I
accept it on the behalf of <b t-field="quotation.company_id"/>,
for an amount of
<b data-id="total_amount" t-field="quotation.amount_total"
t-field-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'/>
with payment terms: <b t-field="quotation.payment_term"/>.
</p>
<div id="signer" class="form-group">
<label class="control-label" for="name">Your Name:</label>
<input type="text" name="signer" id="name" class="form-control"/>
</div>
</div>
<div class="modal-footer">
<button type="submit" t-attf-class="btn btn-primary">Sign Order</button> or
<button type="button" class="btn btn-link" data-dismiss="modal" style="padding: 0">Cancel</button>
</div>
</form>
</div>
</div>
<div class="alert alert-warning alert-dismissable" t-if="quotation.state == 'cancel'">
<button type="button" class="close hidden-print" data-dismiss="alert" aria-hidden="true">&amp;times;</button>
<strong>This quotation has been canceled.</strong> Contact us to get a new quote.
</div>
<div class="modal fade" id="modeldecline" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<form id="decline" method="POST" t-attf-action="/quote/#{quotation.id}/#{quotation.access_token}/decline" class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&amp;times;</button>
<h4 class="modal-title">Reject This Quote</h4>
</div>
<div class="modal-body">
<p>
Tell us why you are refusing this quotation, this will help us improve our services.
</p>
<textarea rows="4" name="decline_message" placeholder="Your feedback....." class="form-control"/>
</div>
<div class="modal-footer">
<button type="submit" t-att-id="quotation.id" class="btn btn-primary">Reject</button> or
<button type="button" class="btn btn-link" data-dismiss="modal" style="padding: 0">Cancel</button>
</div>
</form>
</div>
</div>
<div class="row mt32">
<div class="col-md-6">
<div class="row">
<label class="col-sm-4 text-right">Customer:</label>
<div class="col-sm-8">
<div t-field="quotation.partner_id"/>
</div>
</div>
<div class="row">
<label class="col-sm-4 text-right">Bill To:</label>
<div class="col-sm-8">
<div t-field="quotation.partner_invoice_id" t-field-options='{
"widget": "contact",
"fields": ["address", "name", "phone", "email"]
}'/>
</div>
</div>
<div t-if="quotation.partner_shipping_id.id != quotation.partner_invoice_id.id" class="row">
<label class="col-sm-4 text-right">Ship To:</label>
<div class="col-sm-8">
<div t-field="quotation.partner_shipping_id" t-field-options='{
"widget": "contact",
"fields": ["address", "name", "phone"]
}'/>
</div>
</div>
</div>
<div class="col-md-6">
<div class="row">
<label class="col-sm-5 text-right">Your Contact:</label>
<div class="col-sm-7">
<div t-field="quotation.user_id" t-field-options='{
"widget": "contact",
"fields": ["name", "phone", "email"]
}'/>
</div>
</div>
<div class="row">
<label class="col-sm-5 text-right">Quote Date:</label>
<div class="col-sm-7">
<span t-field="quotation.date_order"/>
</div>
<div class="clearfix"/>
<div t-if="quotation.client_order_ref">
<label class="col-sm-5 text-right">Your Reference:</label>
<div class="col-sm-7">
<span t-field="quotation.client_order_ref"/>
</div>
</div>
</div>
</div>
</div>
<a id="offer"/>
<div t-field="quotation.website_description"/>
<t t-foreach="quotation.order_line" t-as="line">
<a t-att-id="line.id"/>
<div t-field="line.website_description"/>
</t>
<div class="oe_structure"/>
<a id="pricing"/>
<t t-call="website_quote.pricing"/>
<a id="options"/>
<t t-call="website_quote.optional_products"/>
<a id="discussion"/>
<t t-call="website_quote.chatter"/>
</div>
</div>
</div>
</body>
</t>
</template>
<template id="navigation_menu">
<div class="hidden-print navspy" data-editable="0" role="complementary">
<ul class="nav bs-sidenav" data-id="quote_sidebar">
<li><a href="#introduction">Introduction</a></li>
</ul>
</div>
</template>
<!-- Options:Quotation Signature -->
<template id="opt_quotation_signature" name="Ask Signature" inherit_option_id="website_quote.so_quotation" inherit_id="website_quote.so_quotation">
<xpath expr="//div[@id='sign-dialog']" position="inside">
<div class="panel panel-default mt16 mb0" id="drawsign">
<div class="panel-heading">
<div class="pull-right">
<a id="sign_clean" class="btn btn-xs">Clear</a>
</div>
<strong>Draw your signature</strong>
</div>
<div id="signature" class="panel-body" style="padding: 0"/>
</div>
</xpath>
</template>
<template id="optional_products">
<div class="container mt32" t-if="option">
<section data-snippet-id="title">
<h1 class="page-header">Options</h1>
</section>
<section id="options">
<table class="table table-hover">
<thead>
<tr>
<th>Products</th>
<th>Description</th>
<th></th>
<th class="text-right">Price</th>
</tr>
</thead>
<tbody>
<tr t-foreach="quotation.options" t-as="option">
<t t-if="not option.line_id">
<td>
<div t-field="option.product_id.name"/>
</td>
<td>
<div t-field="option.name"/>
</td>
<td>
<strong t-if="option.discount" class="text-info">
<t t-esc="((option.discount % 1) and '%s' or '%d') % option.discount"/>% discount
</strong>
</td>
<td>
<strong class="text-right">
<div t-field="option.price_unit"
t-field-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'
t-att-style="option.discount and 'text-decoration: line-through' or ''"
t-att-class="option.discount and 'text-danger' or ''"/>
<div t-if="option.discount">
<t t-esc="'%.2f' % ((1-option.discount / 100.0) * option.price_unit)"/>
</div>
</strong>
</td>
<td class="pull-right">
<a t-href="/quote/add_line/#{ option.id }/#{ quotation.id }/#{ quotation.access_token }" class="mb8 hidden-print">
<span class="fa fa-shopping-cart"/>
</a>
</td>
</t>
</tr>
</tbody>
</table>
</section>
</div>
</template>
<template id="so_template" name="SO Template">
<t t-call="website.layout">
<t t-set="head">
<script type="text/javascript" src="/website_quote/static/src/js/website_quotation.js"></script>
<link rel='stylesheet' href='/website_quote/static/src/css/website_quotation.css'/>
<t t-raw="head or ''"/>
</t>
<body data-spy="scroll" data-target=".navspy">
<div class="container">
<div class="row mt16">
<div class="col-md-3">
<div class="bs-sidebar">
<div class="hidden-print navspy" role="complementary">
<ul class="nav bs-sidenav" data-id="quote_sidebar"/>
</div>
</div>
</div>
<div class="col-md-9">
<div class="alert alert-info alert-dismissable" t-ignore="True">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&amp;times;</button>
<p>
<strong>Template Header:</strong> this content
will appear on all quotations using this
template.
</p>
<p class="text-muted">
Titles with style <i>Heading 1</i> and
<i>Heading 2</i> will be used to generate the
table of content automatically.
</p>
</div>
<div id="template_introduction" t-field="template.website_description"/>
<t t-foreach="template.quote_line" t-as="line">
<div class="alert alert-info alert-dismissable" t-ignore="True">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&amp;times;</button>
Product: <strong t-esc="line.product_id.name"/>:
this content will appear on the quotation only if this
product is put on the quote.
</div>
<div t-field="line.website_description"/>
</t>
<t t-foreach="template.options" t-as="option_line">
<div class="alert alert-info alert-dismissable" t-ignore="True">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&amp;times;</button>
Optional Product: <strong t-esc="option_line.product_id.name"/>:
this content will appear on the quotation only if this
product is used in the quote.
</div>
<div t-field="option_line.website_description"/>
</t>
<section id="terms" class="container" t-if="template.note">
<h1 class="page-header" t-ignore="True">Terms &amp; Conditions</h1>
<p t-field="template.note"/>
</section>
</div>
</div>
</div>
</body>
</t>
</template>
</data>
</openerp>

View File

@ -0,0 +1,138 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="ir.ui.view" id="sale_order_form_quote">
<field name="name">sale.order.form.payment</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//header/button[@name='invoice_corrected']" position="after">
<button name="open_quotation" string="View Quotation" type="object"
attrs="{'invisible': [('template_id','=',False)]}"/>
</xpath>
<xpath expr="//page[@string='Order Lines']" position="after">
<page string="Suggested Products">
<label string="Optional Products &amp; Services" for="options"/>
<field name="options">
<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="quantity"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="price_unit"/>
<field name="discount" groups="sale.group_discount_per_so_line"/>
<field name="website_description" invisible="1"/>
</tree>
</field>
</page>
</xpath>
<xpath expr="//field[@name='client_order_ref']" position="after">
<field name="template_id" on_change="onchange_template_id(template_id, partner_id, fiscal_position)"/>
<field name="validity_date"/>
<field name="website_description" invisible="1"/>
</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>
<button string="Edit Template" type="object" name="open_template" class="oe_link oe_right"/>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1>
<field name="name"/>
</h1>
</div>
<group>
<group>
<label for="number_of_days"/>
<div id="number_of_days">
<field name="number_of_days" class="oe_inline"/> days
</div>
</group>
</group>
<notebook>
<page string="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"/>
<label for="discount" groups="sale.group_discount_per_so_line"/>
<div groups="sale.group_discount_per_so_line">
<field name="discount" class="oe_inline"/> %%
</div>
</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="product_uom_id" groups="product.group_uom"/>
<field name="discount" groups="sale.group_discount_per_so_line"/>
<field name="price_unit"/>
<field name="website_description" invisible="1"/>
</tree>
</field>
</page>
<page string="Suggested Products">
<field name="options">
<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="quantity"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="price_unit"/>
<field name="discount" groups="sale.group_discount_per_so_line"/>
<field name="website_description" invisible="1"/>
</tree>
</field>
</page>
</notebook>
<field name="website_description" invisible="1"/>
<field name="note" placeholder="Terms and conditions..." nolable="1"/>
</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">Quotation Templates</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

@ -24,4 +24,4 @@
</record>
</data>
</openerp>
</openerp>

View File

@ -516,14 +516,14 @@
<div class="input-group">
<span class="input-group-addon">
<a t-href="../change_cart/#{ line.id }/?remove=True" class="mb8 js_add_cart_json">
<i class="fa fa-minus-square"></i>
<i class="fa fa-minus"></i>
</a>
</span>
<input type="text" class="js_quantity form-control"
t-att-data-id="line.id" t-att-value="int(line.product_uom_qty)"/>
<span class="input-group-addon">
<a t-href="../change_cart/#{ line.id }/" class="mb8 float_left js_add_cart_json">
<i class="fa fa-plus-square"></i>
<i class="fa fa-plus"></i>
</a>
</span>
</div>