[IMP] Merge Trunk + Deisgn Improvements
bzr revid: fp@tinyerp.com-20140120205229-b9726jvb5tfmj3fc
This commit is contained in:
commit
cd3be26606
|
@ -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
|
||||
|
@ -113,7 +113,6 @@ class mail_compose_message(osv.TransientModel):
|
|||
values['attachment_ids'].append(ir_attach_obj.create(cr, uid, data_attach, context=context))
|
||||
else:
|
||||
values = self.default_get(cr, uid, ['subject', 'body', 'email_from', 'email_to', 'email_cc', 'partner_to', 'reply_to', 'attachment_ids', 'mail_server_id'], context=context)
|
||||
|
||||
if values.get('body_html'):
|
||||
values['body'] = values.pop('body_html')
|
||||
return {'value': values}
|
||||
|
@ -168,7 +167,6 @@ class mail_compose_message(osv.TransientModel):
|
|||
# filter template values
|
||||
fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to', 'attachment_ids', 'attachments', 'mail_server_id']
|
||||
values = dict.fromkeys(res_ids, False)
|
||||
|
||||
template_values = self.pool.get('email.template').generate_email_batch(cr, uid, template_id, res_ids, context=context)
|
||||
for res_id in res_ids:
|
||||
res_id_values = dict((field, template_values[res_id][field]) for field in fields if template_values[res_id].get(field))
|
||||
|
|
|
@ -65,6 +65,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_quotation': fields.boolean("Allow online quotation.",
|
||||
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'
|
||||
|
|
|
@ -80,6 +80,10 @@
|
|||
</group>
|
||||
</group>
|
||||
<div name="Sale Features" position="inside">
|
||||
<div name="module_website_quotation">
|
||||
<field name="module_website_quotation" class="oe_inline"/>
|
||||
<label for="module_website_quotation"/>
|
||||
</div>
|
||||
<div name="module_sale_margin">
|
||||
<field name="module_sale_margin" class="oe_inline"/>
|
||||
<label for="module_sale_margin"/>
|
||||
|
|
|
@ -454,7 +454,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">
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
import controllers
|
||||
import models
|
|
@ -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', 'portal_sale', 'mail','document'],
|
||||
'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,
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
import main
|
||||
|
||||
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,139 @@
|
|||
# -*- 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
|
||||
|
||||
|
||||
class sale_quote(http.Controller):
|
||||
|
||||
@http.route(["/quote/<int:order_id>/<token>"], type='http', auth="public", website=True)
|
||||
def view(self, order_id, token, **post):
|
||||
# use SUPERUSER_ID allow to access/view order for public user
|
||||
order = request.registry.get('sale.order').browse(request.cr, SUPERUSER_ID, order_id)
|
||||
assert token == order.access_token, 'Access denied, wrong token!'
|
||||
# TODO: if not order.template_id: return to the URL of the portal view of SO
|
||||
values = {
|
||||
'quotation': order,
|
||||
'new_post' : request.httprequest.session.get('new_post',False)
|
||||
}
|
||||
return request.website.render('website_quotation.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)
|
||||
assert token == order.access_token, 'Access denied, wrong token!'
|
||||
attachment = {
|
||||
'name': 'sign.png',
|
||||
'datas':sign,
|
||||
'datas_fname': 'sign.png',
|
||||
'res_model': 'sale.order',
|
||||
'res_id': order_id,
|
||||
}
|
||||
request.registry['ir.attachment'].create(request.cr, request.uid, attachment, context=request.context)
|
||||
order_obj.write(request.cr, request.uid, [order_id], {'signer_name':signer,'state': 'manual'})
|
||||
return []
|
||||
|
||||
@http.route(['/quote/<int:order_id>/<token>/decline'], type='http', auth="public", website=True)
|
||||
def decline(self, order_id, token, **post):
|
||||
message = post.get('decline_message')
|
||||
request.registry.get('sale.order').write(request.cr, request.uid, [order_id], {'state': 'cancel'})
|
||||
if message:
|
||||
self.message_post(message, order_id)
|
||||
return werkzeug.utils.redirect("/quote/%s/%s" % (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')
|
||||
assert token == order.access_token, 'Access denied, wrong token!'
|
||||
if message:
|
||||
self.message_post(message, order_id)
|
||||
request.httprequest.session['new_post'] = True
|
||||
return werkzeug.utils.redirect("/quote/%s/%s" % (order_id, token))
|
||||
|
||||
def message_post(self , message, order_id):
|
||||
request.session.body = message
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
if 'body' in request.session and request.session.body:
|
||||
request.registry.get('sale.order').message_post(cr, uid, order_id,
|
||||
body=request.session.body,
|
||||
type='comment',
|
||||
subtype='mt_comment',
|
||||
context=context,
|
||||
)
|
||||
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))
|
||||
assert token == order.access_token, 'Access denied, wrong token!'
|
||||
if unlink:
|
||||
request.registry.get('sale.order.line').unlink(request.cr, SUPERUSER_ID, [int(line_id)], context=request.context)
|
||||
return False
|
||||
val = self._update_order_line(line_id=int(line_id), number=(remove and -1 or 1))
|
||||
return [str(val), str(order.amount_total)]
|
||||
|
||||
def _update_order_line(self, line_id, number):
|
||||
order_line_obj = request.registry.get('sale.order.line')
|
||||
order_line_val = order_line_obj.read(request.cr, SUPERUSER_ID, [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 quantity
|
||||
|
||||
@http.route(["/template/<model('sale.quote.template'):quote>"], type='http', auth="public", website=True)
|
||||
def template_view(self, quote, **post):
|
||||
values = {
|
||||
'template': quote,
|
||||
}
|
||||
return request.website.render('website_quotation.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)
|
||||
assert token == order.access_token, 'Access denied, wrong token!'
|
||||
option_obj = request.registry.get('sale.option.line')
|
||||
option = option_obj.browse(request.cr, SUPERUSER_ID, option_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_uom_qty': option.quantity,
|
||||
'product_uom_id': option.uom_id.id,
|
||||
'discount': option.discount,
|
||||
'is_optional': True,
|
||||
})
|
||||
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" % (order.id, token))
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<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: rgb(255, 255, 255); ">
|
||||
|
||||
<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>
|
||||
You can access this document and pay online via our Customer Portal:
|
||||
</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="${object.open_quotation()['url']}">View ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'}</a>
|
||||
</div>
|
||||
]]></field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,412 @@
|
|||
<?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="product_template_quote_4" model="product.template">
|
||||
<field name="name">Advanced Account Functional </field>
|
||||
<field name="categ_id" ref="product.product_category_6"/>
|
||||
<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_4" model="product.product">
|
||||
<field name="product_tmpl_id" ref="product_template_quote_4"/>
|
||||
<field name="default_code">QF14</field>
|
||||
</record>
|
||||
|
||||
<record id="website_quote_template_1" model="sale.quote.template">
|
||||
<field name="name">Partnership Contract</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>Enterprise commissions</h2>
|
||||
<p>
|
||||
As an official partner, you benefit from OpenERP Enterprise
|
||||
commissions, according to your partner grade, as well as
|
||||
dedicated services restricted to partners like the technical
|
||||
support.
|
||||
</p>
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th>Grade</th>
|
||||
<th>Your Commission</th>
|
||||
</tr><tr>
|
||||
<td>Ready</td>
|
||||
<td></td>
|
||||
</tr><tr>
|
||||
<td>Silver</td>
|
||||
<td></td>
|
||||
</tr><tr>
|
||||
<td>Gold</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</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_quotation/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_quotation/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_quotation/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_quotation/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">Fuctional Training</field>
|
||||
<field name="product_id" ref="product_product_quote_1"/>
|
||||
<field name="product_uom_qty">1</field>
|
||||
<field name="price_unit">12950.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&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="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="row">
|
||||
<div class="col-md-12 mt32">
|
||||
<h2>Technical Training</h2>
|
||||
</div>
|
||||
<div class="col-md-12 mb32">
|
||||
<p>This course is dedicated to partners, integrators and developers who need to have a grasp of the OpenERP development process. This course is for new developers or for IT professionals eager to learn more about the OpenERP's technical aspects.
|
||||
This 5-day training takes place in the Headquarters of OpenERP, in Grand-Rosière (Belgium).</p>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<h2>Objectives</h2>
|
||||
</div>
|
||||
<div class="col-md-12 mb32">
|
||||
<h3>Having attended this course, participants should be able to</h3>
|
||||
<ul>
|
||||
<li>Videos with detailed demos</li>
|
||||
<li>Hands-on exercises and their solutions</li>
|
||||
<li>Live Q&A sessions with a trainer</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<h3>Structure of the Training</h3>
|
||||
<h4>There are three components to the training:</h4>
|
||||
<ul>
|
||||
<li>Demo in class with the trainer</li>
|
||||
<li>Hands-on exercises and their corresponding solutions</li>
|
||||
<li>Q&A sessions</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale_option_line_1" model="sale.option.line">
|
||||
<field name="temp_option_id" ref="website_quote_template_1"/>
|
||||
<field name="name">Advanced CRM Functional</field>
|
||||
<field name="product_id" ref="product_product_quote_3"/>
|
||||
<field name="product_uom_qty">1</field>
|
||||
<field name="uom_id" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">9000.00</field>
|
||||
<field name="website_description" type="html">
|
||||
<section data-snippet-id="title">
|
||||
<h1>Advanced CRM Functional</h1>
|
||||
</section>
|
||||
<section data-snippet-id="text-image">
|
||||
</section>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale_option_line_2" model="sale.option.line">
|
||||
<field name="temp_option_id" ref="website_quote_template_1"/>
|
||||
<field name="name">Advanced Account Functional</field>
|
||||
<field name="product_id" ref="product_product_quote_4"/>
|
||||
<field name="product_uom_qty">1</field>
|
||||
<field name="uom_id" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">18000.00</field>
|
||||
<field name="website_description" type="html">
|
||||
<section data-snippet-id="title">
|
||||
<h1>Advanced Account Functional</h1>
|
||||
</section>
|
||||
<section data-snippet-id="text-image">
|
||||
</section>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1 @@
|
|||
import order
|
|
@ -0,0 +1,179 @@
|
|||
# -*- 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
|
||||
|
||||
|
||||
class sale_quote_template(osv.osv):
|
||||
_name = "sale.quote.template"
|
||||
_description = "Sale Quotation Template"
|
||||
_columns = {
|
||||
'name': fields.char('Quotation Template', size=256, required=True),
|
||||
'website_description': fields.html('Description'),
|
||||
'quote_line': fields.one2many('sale.quote.line', 'quote_id', 'Quote Template Lines'),
|
||||
'note': fields.text('Terms and conditions'),
|
||||
'options': fields.one2many('sale.option.line', 'temp_option_id', 'Optional Products Lines'),
|
||||
}
|
||||
|
||||
def open_template(self, cr, uid, quote_id, context=None):
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'target': 'self',
|
||||
'url': '/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),
|
||||
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True),
|
||||
'website_description': fields.html('Line Description'),
|
||||
'price_unit': fields.float('Unit Price', required=True),
|
||||
'product_uom_qty': fields.float('Quantity', required=True),
|
||||
}
|
||||
_defaults = {
|
||||
'product_uom_qty': 1,
|
||||
}
|
||||
def on_change_product_id(self, cr, uid, ids, product, context=None):
|
||||
vals = {}
|
||||
product_obj = self.pool.get('product.product').browse(cr, uid, product, context=context)
|
||||
vals.update({
|
||||
'price_unit': product_obj.list_price,
|
||||
'website_description': product_obj.website_description,
|
||||
'name': product_obj.name,
|
||||
})
|
||||
return {'value': vals}
|
||||
|
||||
|
||||
class sale_order_line(osv.osv):
|
||||
_inherit = "sale.order.line"
|
||||
_description = "Sales Order Line"
|
||||
_columns = {
|
||||
'website_description': fields.html('Line Description'),
|
||||
'is_optional': fields.boolean('Optional Product')
|
||||
}
|
||||
def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
|
||||
res = super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty, uom, qty_uos, uos, name, partner_id, lang, update_tax, date_order, packaging, fiscal_position, flag, context)
|
||||
if product:
|
||||
desc = self.pool.get('product.product').browse(cr, uid, product, context).website_description
|
||||
res.get('value').update({'website_description': desc})
|
||||
return res
|
||||
|
||||
|
||||
class sale_order(osv.osv):
|
||||
_inherit = 'sale.order'
|
||||
_columns = {
|
||||
'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.option.line', 'option_id', 'Optional Products Lines'),
|
||||
'signer_name': fields.char('Signer Name', size=256),
|
||||
}
|
||||
_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/%s' % (quote.id, quote.access_token)
|
||||
}
|
||||
|
||||
def _get_sale_order_line(self, cr, uid, template_id, context=None):
|
||||
"""create order line from selected quote template line."""
|
||||
|
||||
def onchange_template_id(self, cr, uid, ids, template_id, 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:
|
||||
lines.append((0, 0, {
|
||||
'name': line.name,
|
||||
'price_unit': line.price_unit,
|
||||
'product_uom_qty': line.product_uom_qty,
|
||||
'product_id': line.product_id.id,
|
||||
'product_uom_id': line.product_id.uom_id.id,
|
||||
'website_description': line.website_description,
|
||||
'state': 'draft',
|
||||
}))
|
||||
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,
|
||||
}))
|
||||
data = {'order_line': lines, 'website_description': quote_template.website_description, 'note': quote_template.note, 'options': options}
|
||||
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_option_line(osv.osv):
|
||||
_name = "sale.option.line"
|
||||
_description = "Sale Options"
|
||||
_columns = {
|
||||
'option_id': fields.many2one('sale.order', 'Sale Order Reference', ondelete='cascade', select=True),
|
||||
'temp_option_id': fields.many2one('sale.quote.template', 'Quotation Template 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)], change_default=True),
|
||||
'website_description': fields.html('Line Description'),
|
||||
'price_unit': fields.float('Unit Price', required=True),
|
||||
'discount': fields.float('Discount (%)'),
|
||||
'uom_id': fields.many2one('product.uom', 'Unit of Measure ', required=True),
|
||||
'quantity': fields.float('Quantity', required=True),
|
||||
}
|
||||
|
||||
_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'),
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_sale_order_portal,sale.order.portal,model_sale_order,base.group_portal,1,1,1,0
|
||||
access_sale_order_public,sale.order.public,model_sale_order,base.group_public,1,1,1,0
|
|
|
@ -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)})();
|
|
@ -0,0 +1,53 @@
|
|||
.bs-sidebar {
|
||||
position: fixed;
|
||||
top: 110px;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
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 |
|
@ -0,0 +1,80 @@
|
|||
$(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;
|
||||
});
|
||||
|
||||
$('#modelaccept').on('shown.bs.modal', function (e) {
|
||||
$("#signature").empty().jSignature();
|
||||
});
|
||||
|
||||
$('#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=(.*)/);
|
||||
var datapair = $("#signature").jSignature("getData",'image')
|
||||
openerp.jsonRpc("/quote/accept/", 'call', {
|
||||
'order_id': parseInt(order_id[1]),
|
||||
'token': token[1],
|
||||
'signer': $("#signer").val(),
|
||||
'sign': JSON.stringify(datapair[1]),
|
||||
})
|
||||
.then(function (data) {
|
||||
$('#modelaccept').modal('hide');
|
||||
location.reload();
|
||||
});
|
||||
return false
|
||||
});
|
||||
// automatically generate a menu from h1 and h1 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;
|
||||
}
|
||||
});
|
||||
|
||||
//vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|
||||
});
|
|
@ -0,0 +1,380 @@
|
|||
<?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 table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Products</th>
|
||||
<th>Quantity</th>
|
||||
<th>Discount</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">
|
||||
<t t-esc="line.product_uom_qty"/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div id="quote_discount" t-if="line.discount">
|
||||
<t t-esc="line.discount"/>
|
||||
</div>
|
||||
</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) * 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 }&unlink=True&token=#{ quotation.access_token }" class="mb8 js_update_line_json pull-right hidden-print" t-if="line.is_optional">
|
||||
<span class="fa fa-trash-o"></span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td><h3 class="text-right">Total</h3></td>
|
||||
<td class="text-right" colspan="2">
|
||||
<h3>
|
||||
<strong data-id="total_amount" t-field="quotation.amount_total" t-field-options='{"widget": "monetary","display_currency": "quotation.pricelist_id.currency_id"}'/>
|
||||
</h3>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<t t-call="website_quotation.quotation_toolbar"/>
|
||||
<section id="terms" class="container" t-if="quotation.note">
|
||||
<h2 class="page-header">Terms & Conditions</h2>
|
||||
<p t-field="quotation.note"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<template id="change_quantity" inherit_option_id="website_quotation.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 }&remove=True&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 }&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">History</h1>
|
||||
<ul class="media-list" id="comments-list">
|
||||
<li t-foreach="quotation.message_ids" t-as="message" class="media">
|
||||
<div class="media-body">
|
||||
<img class="media-object pull-left" t-att-src="'/website/image?model=res.partner&field=image_small&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>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<!-- Options:Quotation Chatter: user can reply -->
|
||||
<template id="opt_quotation_chatter_post_complete_comment" name="Allow Comments" inherit_option_id="website_quotation.chatter" inherit_id="website_quotation.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&field=image_small&id='+str(user_id.partner_id.id)" style="width: 50px; margin-right: 10px;"/>
|
||||
<div class="pull-left mb32" style="width: 75%%">
|
||||
<textarea rows="3" name="comment" class="form-control" placeholder="Write a comment..."></textarea>
|
||||
<button type="submit" class="btn btn-primary mt8">Post</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<div class="clearfix"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="quotation_toolbar">
|
||||
<div class="text-center hidden-print">
|
||||
<div class="btn-group btn-group-lg" 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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="so_quotation" name="Product Quotation">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<script type="text/javascript" src="/website_quotation/static/src/js/website_quotation.js"></script>
|
||||
<script type="text/javascript" src="/website_quotation/static/lib/jSignature/jSignature.min.js"></script>
|
||||
<link rel='stylesheet' href='/website_quotation/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 class="btn btn-primary btn-block fa fa-check" data-toggle="modal" data-target="#modelaccept">
|
||||
Accept Order
|
||||
</a>
|
||||
<a class="btn btn-link" type="submit" href="#discussion">
|
||||
Ask Changes
|
||||
</a> or
|
||||
<a class="btn btn-link" data-toggle="modal" data-target="#modeldecline">
|
||||
Reject
|
||||
</a>
|
||||
</div>
|
||||
<hr class="mt0 mb0"/>
|
||||
<div class="hidden-print navspy" role="complementary">
|
||||
<ul class="nav bs-sidenav" data-id="quote_sidebar">
|
||||
<li><a href="#introduction">Introduction</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<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" t-att-href="'/web#return_label=Website&model=%s&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">&times;</button>
|
||||
<h4 class="modal-title">Validate Order</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<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>
|
||||
<strong>Your Name:</strong><br/>
|
||||
<input type="text" id="signer" class="form-control"/>
|
||||
</div>
|
||||
<div class="panel panel-default mt16 mb0">
|
||||
<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>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" 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">&times;</button>
|
||||
<strong>This quotation has been canceled.</strong> Contact <span t-field="quotation.user_id"/> ( <span t-if="quotation.user_id.email"> <span class="fa fa-envelope" t-field="quotation.user_id.email" /></span> <span t-if="quotation.user_id.phone"> <span class="fa fa-phone" t-field="quotation.user_id.phone"/></span> ) in order to ask 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">&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>
|
||||
<label class="col-sm-3 text-right">Bill To:</label>
|
||||
<div class="col-sm-9">
|
||||
<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">
|
||||
<label class="col-sm-3 text-right">Ship To:</label>
|
||||
<div class="col-sm-9">
|
||||
<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>
|
||||
|
||||
<a id="pricing"/>
|
||||
<t t-call="website_quotation.pricing"/>
|
||||
|
||||
<a id="options"/>
|
||||
<t t-call="website_quotation.optional_products"/>
|
||||
|
||||
<a id="discussion"/>
|
||||
<t t-call="website_quotation.chatter"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="optional_products">
|
||||
<div class="container mt32" t-if="quotation.options">
|
||||
<h3>Options:</h3>
|
||||
<div class='row mt16'>
|
||||
<t t-foreach="quotation.options" t-as="option">
|
||||
<div class='col-md-2 thumbnail' style='width: 170px; margin-right: 16px;' t-if="not option.line_id">
|
||||
<div class='mt16 text-center'>
|
||||
<a href="#" t-field="option.product_id.name"/>
|
||||
<span t-field="option.product_id.image_small" t-field-options='{"widget": "image", "class": "img-rounded"}'/>
|
||||
</div>
|
||||
<a t-href="/quote/add_line/#{ option.id }/#{ quotation.id }/#{ quotation.access_token }" class="btn btn-primary btn-xs hidden-print">Add To Quotation
|
||||
</a>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="so_template" name="SO Template">
|
||||
<t t-call="website.layout">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4" id="sidebar">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li class="active"><a href="#template_introduction" data-toggle="tab">Contact Data</a></li>
|
||||
<li><a href="#template_introduction" data-toggle="tab">Our Offer</a></li>
|
||||
<t t-foreach="template.quote_line" t-as="line">
|
||||
<li><a t-att-href="'#%s'% line.id" data-toggle="tab"><t t-raw="line.product_id.name_template"/> </a></li>
|
||||
</t>
|
||||
<li><a href="#templateterms" data-toggle="tab">Terms & Conditions</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-8 tab-content">
|
||||
<div id="template_introduction" t-field="template.website_description"/>
|
||||
|
||||
<t t-foreach="template.quote_line" t-as="line">
|
||||
<section class="tab-pane" t-att-id="line.id">
|
||||
<div t-field="line.website_description" class="oe_structure"/>
|
||||
</section>
|
||||
</t>
|
||||
<section id="templateterms" class="tab-pane">
|
||||
<div class="container panel panel-default">
|
||||
<div class="row panel-body">
|
||||
<div class="text-center">
|
||||
<h2 class="page-header">Terms & Conditions</h2>
|
||||
<p class="lead" t-field="template.note"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,125 @@
|
|||
<?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 & 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"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="discount"/>
|
||||
<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)"/>
|
||||
<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">
|
||||
<header>
|
||||
<button string="Edit Template" type="object" name="open_template" class="oe_highlight"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
</div>
|
||||
<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"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="Description">
|
||||
<field name="name" />
|
||||
</page>
|
||||
<page string="Website Description">
|
||||
<field name="website_description" />
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
<tree string="Sales Quote Template Lines" editable="bottom">
|
||||
<field name="product_id" on_change="on_change_product_id(product_id)"/>
|
||||
<field name="name"/>
|
||||
<field name="product_uom_qty"/>
|
||||
<field name="price_unit"/>
|
||||
<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"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="discount"/>
|
||||
<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">Sales Template</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">sale.quote.template</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_sale_quotation_template" id="menu_sale_quote_template" parent="base.menu_base_config" sequence="6" groups="base.group_sale_salesman,base.group_sale_manager"/>
|
||||
</data>
|
||||
</openerp>
|
|
@ -24,4 +24,4 @@
|
|||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
</openerp>
|
||||
|
|
Loading…
Reference in New Issue