[MERGE] website_sale optional-products
This commit is contained in:
commit
893aace122
|
@ -9,9 +9,10 @@
|
|||
<record model="ir.ui.view" id="membership_product_search_form_view">
|
||||
<field name="name">membership.product.search.form</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="priority">50</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Membership Products">
|
||||
<field name="name" filter_domain="['|',('name','ilike',self),('code','ilike',self)]" string="Membership Product"/>
|
||||
<field name="name" string="Membership Product"/>
|
||||
<filter string="Inactive" icon="terp-gdu-smart-failing" domain="[('active','=',0)]"/>
|
||||
<field name="categ_id" operator="child_of"/>
|
||||
<group expand='0' string='Group by...'>
|
||||
|
|
|
@ -816,10 +816,8 @@ class product_product(osv.osv):
|
|||
uom.id, product.list_price, context['uom'])
|
||||
else:
|
||||
res[product.id] = product.list_price
|
||||
price_extra = 0.0
|
||||
for variant_id in product.attribute_value_ids:
|
||||
price_extra += variant_id.price_extra
|
||||
res[product.id] = (res[product.id] or 0.0) + price_extra
|
||||
res[product.id] = res[product.id] + product.price_extra
|
||||
|
||||
return res
|
||||
|
||||
def _get_partner_code_name(self, cr, uid, ids, product, partner_id, context=None):
|
||||
|
|
|
@ -283,15 +283,30 @@
|
|||
<record id="product_kanban_view" model="ir.ui.view">
|
||||
<field name="name">Product Kanban</field>
|
||||
<field name="model">product.product</field>
|
||||
<field name="mode">primary</field>
|
||||
<field name="inherit_id" ref="product.product_template_kanban_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="name" position="after">
|
||||
<field name="attribute_value_ids"/>
|
||||
</field>
|
||||
<xpath expr="//img[@class='oe_kanban_image']" position="replace">
|
||||
<img t-att-src="kanban_image('product.product', 'image_small', record.id.value)" class="oe_kanban_image"/>
|
||||
</xpath>
|
||||
<kanban>
|
||||
<field name="image_small"/>
|
||||
<field name="lst_price"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div class="oe_kanban_vignette oe_semantic_html_override">
|
||||
<a type="open"><img t-att-src="kanban_image('product.product', 'image_small', record.id.value)" class="oe_kanban_image"/></a>
|
||||
<div class="oe_kanban_details">
|
||||
<h4>
|
||||
<a type="open">
|
||||
<field name="name"/>
|
||||
<field name="attribute_value_ids"/>
|
||||
</a>
|
||||
</h4>
|
||||
<div name="tags"/>
|
||||
<ul>
|
||||
<li>Price: <field name="lst_price"></field></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
@ -343,7 +358,7 @@
|
|||
<div class="oe_right">
|
||||
<button class="oe_inline oe_stat_button" string="Variant Prices" name="%(product.variants_template_action)d" type="action" icon="fa-strikethrough"/>
|
||||
<button class="oe_inline oe_stat_button" name="%(product.product_variant_action)d" type="action" icon="fa-sitemap">
|
||||
<field string="List of Variants" name="product_variant_count" widget="statinfo" />
|
||||
<field string="Variants" name="product_variant_count" widget="statinfo" />
|
||||
</button>
|
||||
</div>
|
||||
</field>
|
||||
|
@ -361,7 +376,7 @@
|
|||
<field name="product_variant_ids"/>
|
||||
</field>
|
||||
<h4 position="after">
|
||||
<a name="%(product.product_variant_action)d" type="action" t-if="!record.is_product_variant.raw_value & record.product_variant_count.raw_value>1">
|
||||
<a name="%(product.product_variant_action)d" type="action">
|
||||
<t t-esc="record.product_variant_count.value"/> Variants
|
||||
</a>
|
||||
</h4>
|
||||
|
|
|
@ -25,14 +25,13 @@
|
|||
</record>
|
||||
|
||||
<!-- Product -->
|
||||
<record id="product_normal_form_view" model="ir.ui.view">
|
||||
<field name="name">product.normal.form</field>
|
||||
<field name="model">product.product</field>
|
||||
<field name="inherit_id" ref="product.product_normal_form_view"/>
|
||||
<record id="product_template_form_view" model="ir.ui.view">
|
||||
<field name="name">product.template.form</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="product_manager" position="after">
|
||||
<field name="intrastat_id"
|
||||
attrs="{'readonly': [('is_product_variant', '=', False)]}"/>
|
||||
<field name="intrastat_id"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -62,7 +62,7 @@ class ir_http(orm.AbstractModel):
|
|||
self.geo_ip_resolver = GeoIP.open('/usr/share/GeoIP/GeoIP.dat', GeoIP.GEOIP_STANDARD)
|
||||
except ImportError:
|
||||
self.geo_ip_resolver = False
|
||||
if self.geo_ip_resolver:
|
||||
if self.geo_ip_resolver and request.httprequest.remote_addr:
|
||||
record = self.geo_ip_resolver.record_by_addr(request.httprequest.remote_addr) or {}
|
||||
request.session['geoip'] = record
|
||||
|
||||
|
|
|
@ -452,7 +452,6 @@
|
|||
openerp.jsonRpc('/website/customize_template_get', 'call', { 'xml_id': view_name }).then(
|
||||
function(result) {
|
||||
_.each(result, function (item) {
|
||||
if (item.xml_id === "website.debugger" && !window.location.search.match(/[&?]debug(&|$)/)) return;
|
||||
if (item.header) {
|
||||
menu.append('<li class="dropdown-header">' + item.name + '</li>');
|
||||
} else {
|
||||
|
|
|
@ -251,12 +251,6 @@
|
|||
<script type="text/javascript" src="/website/static/src/js/jQuery.transfo.js"></script>
|
||||
</template>
|
||||
|
||||
<template id="debugger" inherit_id="website.layout" optional="disabled" name="Debugger & Tests">
|
||||
<xpath expr='//t[@name="layout_head"]' position="after">
|
||||
<t t-set="debugger_hook" t-value="1" />
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="login_layout" inherit_id="web.login_layout" name="Website Login Layout">
|
||||
<xpath expr="t" position="replace">
|
||||
<t t-call="website.layout">
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="editor_head" inherit_id="website.assets_editor" name="Event Editor" groups="event.group_event_manager">
|
||||
<template id="assets_editor" inherit_id="website.assets_editor" name="Event Editor" groups="event.group_event_manager">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/website_event/static/src/js/website_event.editor.js"></script>
|
||||
<script type="text/javascript" src="/website_event/static/src/js/website.tour.event.js"></script>
|
||||
|
|
|
@ -7,21 +7,18 @@ from openerp.tools.translate import _
|
|||
class sale_order(osv.Model):
|
||||
_inherit = "sale.order"
|
||||
|
||||
def _cart_find_product_line(self, cr, uid, ids, product_id=None, line_id=None, context=None):
|
||||
def _cart_find_product_line(self, cr, uid, ids, product_id=None, line_id=None, context=None, **kwargs):
|
||||
line_ids = super(sale_order, self)._cart_find_product_line(cr, uid, ids, product_id, line_id, context=context)
|
||||
if line_id:
|
||||
return line_ids
|
||||
for so in self.browse(cr, uid, ids, context=context):
|
||||
order_line_id = None
|
||||
domain = [('order_id', '=', so.id), ('product_id', '=', product_id)]
|
||||
if line_id:
|
||||
domain += [('id', '=', line_id)]
|
||||
elif context.get("event_ticket_id"):
|
||||
domain = [('id', 'in', line_ids)]
|
||||
if context.get("event_ticket_id"):
|
||||
domain += [('event_ticket_id', '=', context.get("event_ticket_id"))]
|
||||
order_line_ids = self.pool.get('sale.order.line').search(cr, SUPERUSER_ID, domain, context=context)
|
||||
if order_line_ids:
|
||||
order_line_id = order_line_ids[0]
|
||||
return order_line_id
|
||||
return self.pool.get('sale.order.line').search(cr, SUPERUSER_ID, domain, context=context)
|
||||
|
||||
def _website_product_id_change(self, cr, uid, ids, order_id, product_id, line_id=None, context=None):
|
||||
values = super(sale_order,self)._website_product_id_change(cr, uid, ids, order_id, product_id, line_id=None, context=None)
|
||||
values = super(sale_order,self)._website_product_id_change(cr, uid, ids, order_id, product_id, line_id=line_id, context=None)
|
||||
|
||||
event_ticket_id = None
|
||||
if context.get("event_ticket_id"):
|
||||
|
|
|
@ -4,7 +4,6 @@ import openerp.tests
|
|||
|
||||
inject = [
|
||||
("openerp.Tour", os.path.join(os.path.dirname(__file__), '../../web/static/src/js/tour.js')),
|
||||
("openerp.Tour.ShopTest", os.path.join(os.path.dirname(__file__), "../static/src/js/website.tour.event_sale.js")),
|
||||
]
|
||||
|
||||
@openerp.tests.common.at_install(False)
|
||||
|
|
|
@ -2,13 +2,12 @@
|
|||
<openerp>
|
||||
<data>
|
||||
|
||||
<template id="debugger" inherit_id="website.debugger" name="Event Debugger">
|
||||
<xpath expr='//t[@t-set="debugger_hook"]' position="after">
|
||||
<script type="text/javascript" src="/website_event_sale/static/src/js/website.tour.event_sale.js"></script>
|
||||
</xpath>
|
||||
<template id="assets_editor" inherit_id="website.assets_frontend" name="Event Sale">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/website_event_sale/static/src/js/website.tour.event_sale.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
||||
<template id="index" inherit_id="website_event.index" name="Event's Ticket">
|
||||
<xpath expr="//li[@t-foreach='event_ids']/div/h4" position="before">
|
||||
<t t-if="event.state in ['draft', 'confirm'] and event.event_ticket_ids">
|
||||
|
|
|
@ -110,6 +110,21 @@ class website_sale(http.Controller):
|
|||
pricelist = partner.property_product_pricelist
|
||||
return pricelist
|
||||
|
||||
def get_attribute_value_ids(self, product):
|
||||
cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
|
||||
currency_obj = pool['res.currency']
|
||||
attribute_value_ids = []
|
||||
if request.website.pricelist_id.id != context['pricelist']:
|
||||
website_currency_id = request.website.currency_id.id
|
||||
currency_id = self.get_pricelist().currency_id.id
|
||||
for p in product.product_variant_ids:
|
||||
price = currency_obj.compute(cr, uid, website_currency_id, currency_id, p.lst_price)
|
||||
attribute_value_ids.append([p.id, map(int, p.attribute_value_ids), p.price, price])
|
||||
else:
|
||||
attribute_value_ids = [[p.id, map(int, p.attribute_value_ids), p.price, p.lst_price] for p in product.product_variant_ids]
|
||||
|
||||
return attribute_value_ids
|
||||
|
||||
@http.route(['/shop',
|
||||
'/shop/page/<int:page>',
|
||||
'/shop/category/<model("product.public.category"):category>',
|
||||
|
@ -125,7 +140,10 @@ class website_sale(http.Controller):
|
|||
if category:
|
||||
domain += [('product_variant_ids.public_categ_ids', 'child_of', int(category))]
|
||||
|
||||
attrib_values = [map(int,v.split(",")) for v in request.httprequest.args.getlist('attrib') if v]
|
||||
attrib_list = request.httprequest.args.getlist('attrib')
|
||||
attrib_values = [map(int,v.split("-")) for v in attrib_list if v]
|
||||
attrib_set = set([v[1] for v in attrib_values])
|
||||
|
||||
if attrib_values:
|
||||
attrib = None
|
||||
ids = []
|
||||
|
@ -142,8 +160,7 @@ class website_sale(http.Controller):
|
|||
if attrib:
|
||||
domain += [('attribute_line_ids.value_ids', 'in', ids)]
|
||||
|
||||
attrib_set = set([v[1] for v in attrib_values])
|
||||
keep = QueryURL('/shop', category=category and int(category), search=search, attrib=attrib_set)
|
||||
keep = QueryURL('/shop', category=category and int(category), search=search, attrib=attrib_list)
|
||||
|
||||
if not context.get('pricelist'):
|
||||
context['pricelist'] = int(self.get_pricelist())
|
||||
|
@ -191,16 +208,18 @@ class website_sale(http.Controller):
|
|||
def product(self, product, category='', search='', **kwargs):
|
||||
cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
|
||||
category_obj = pool['product.public.category']
|
||||
template_obj = pool['product.template']
|
||||
|
||||
context.update(active_id=product.id)
|
||||
|
||||
if category:
|
||||
category = category_obj.browse(request.cr, request.uid, int(category), context=request.context)
|
||||
category = category_obj.browse(cr, uid, int(category), context=context)
|
||||
|
||||
attrib_values = [map(int,v.split(",")) for v in request.httprequest.args.getlist('attrib') if v]
|
||||
attrib_list = request.httprequest.args.getlist('attrib')
|
||||
attrib_values = [map(int,v.split("-")) for v in attrib_list if v]
|
||||
attrib_set = set([v[1] for v in attrib_values])
|
||||
|
||||
keep = QueryURL('/shop', category=category and category.id, search=search, attrib=attrib_set)
|
||||
keep = QueryURL('/shop', category=category and category.id, search=search, attrib=attrib_list)
|
||||
|
||||
category_ids = category_obj.search(cr, uid, [], context=context)
|
||||
category_list = category_obj.name_get(cr, uid, category_ids, context=context)
|
||||
|
@ -208,9 +227,7 @@ class website_sale(http.Controller):
|
|||
|
||||
if not context.get('pricelist'):
|
||||
context['pricelist'] = int(self.get_pricelist())
|
||||
product = request.registry.get('product.template').browse(request.cr, request.uid, int(product), context=context)
|
||||
|
||||
variants = [[p.id, map(int, p.attribute_value_ids), p.price] for p in product.product_variant_ids]
|
||||
product = template_obj.browse(cr, uid, int(product), context=context)
|
||||
|
||||
values = {
|
||||
'search': search,
|
||||
|
@ -222,7 +239,7 @@ class website_sale(http.Controller):
|
|||
'category_list': category_list,
|
||||
'main_object': product,
|
||||
'product': product,
|
||||
'variants': variants,
|
||||
'get_attribute_value_ids': self.get_attribute_value_ids
|
||||
}
|
||||
return request.website.render("website_sale.product", values)
|
||||
|
||||
|
@ -238,36 +255,45 @@ class website_sale(http.Controller):
|
|||
context=dict(context, mail_create_nosubcribe=True))
|
||||
return werkzeug.utils.redirect(request.httprequest.referrer + "#comments")
|
||||
|
||||
@http.route(['/shop/pricelist'], type='http', auth="public", website=True)
|
||||
def pricelist(self, promo, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
request.website.sale_get_order(code=promo, context=context)
|
||||
return request.redirect("/shop/cart")
|
||||
|
||||
@http.route(['/shop/cart'], type='http', auth="public", website=True)
|
||||
def cart(self, **post):
|
||||
cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
|
||||
order = request.website.sale_get_order()
|
||||
|
||||
values = {
|
||||
'order': order,
|
||||
'suggested_products': [],
|
||||
}
|
||||
if order:
|
||||
if not request.context.get('pricelist'):
|
||||
request.context['pricelist'] = order.pricelist_id.id
|
||||
values['suggested_products'] = order._cart_accessories(context=request.context)
|
||||
if not context.get('pricelist'):
|
||||
context['pricelist'] = order.pricelist_id.id
|
||||
values['suggested_products'] = order._cart_accessories(context=context)
|
||||
|
||||
return request.website.render("website_sale.cart", values)
|
||||
|
||||
@http.route(['/shop/cart/update'], type='http', auth="public", methods=['POST'], website=True)
|
||||
def cart_update(self, product_id, add_qty=1, set_qty=0, **kw):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
request.website.sale_get_order(force_create=1)._cart_update(product_id=int(product_id), add_qty=add_qty, set_qty=set_qty)
|
||||
request.website.sale_get_order(force_create=1)._cart_update(product_id=int(product_id), add_qty=float(add_qty), set_qty=float(set_qty))
|
||||
return request.redirect("/shop/cart")
|
||||
|
||||
@http.route(['/shop/cart/update_json'], type='json', auth="public", methods=['POST'], website=True)
|
||||
def cart_update_json(self, product_id, line_id, add_qty=None, set_qty=None):
|
||||
def cart_update_json(self, product_id, line_id, add_qty=None, set_qty=None, display=True):
|
||||
order = request.website.sale_get_order(force_create=1)
|
||||
quantity = order._cart_update(product_id=product_id, line_id=line_id, add_qty=add_qty, set_qty=set_qty)
|
||||
return {
|
||||
'quantity': quantity,
|
||||
'cart_quantity': order.cart_quantity,
|
||||
'website_sale.total': request.website._render("website_sale.total", {
|
||||
'website_sale_order': request.website.sale_get_order()
|
||||
})
|
||||
}
|
||||
value = order._cart_update(product_id=product_id, line_id=line_id, add_qty=add_qty, set_qty=set_qty)
|
||||
if not display:
|
||||
return None
|
||||
value['cart_quantity'] = order.cart_quantity
|
||||
value['website_sale.total'] = request.website._render("website_sale.total", {
|
||||
'website_sale_order': request.website.sale_get_order()
|
||||
})
|
||||
return value
|
||||
|
||||
#------------------------------------------------------
|
||||
# Checkout
|
||||
|
@ -323,12 +349,20 @@ class website_sale(http.Controller):
|
|||
checkout.update(self.checkout_parse('shipping', data))
|
||||
checkout["shipping_different"] = True
|
||||
|
||||
# Default search by user country
|
||||
country_code = request.session['geoip'].get('country_code')
|
||||
if country_code:
|
||||
country_ids = request.registry.get('res.country').search(cr, uid, [('code', '=', country_code)], context=context)
|
||||
if country_ids:
|
||||
checkout['country_id'] = country_ids[0]
|
||||
|
||||
values = {
|
||||
'countries': countries,
|
||||
'states': states,
|
||||
'checkout': checkout,
|
||||
'shipping_different': checkout.get('shipping_different'),
|
||||
'error': {},
|
||||
'has_check_vat': hasattr(registry['res.partner'], 'check_vat')
|
||||
}
|
||||
return values
|
||||
|
||||
|
@ -445,7 +479,7 @@ class website_sale(http.Controller):
|
|||
|
||||
@http.route(['/shop/checkout'], type='http', auth="public", website=True)
|
||||
def checkout(self, **post):
|
||||
cr, uid, context, registry = request.cr, request.uid, request.context, request.registry
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
|
||||
order = request.website.sale_get_order(force_create=1, context=context)
|
||||
|
||||
|
|
|
@ -555,8 +555,6 @@ Weight: 1.1 ounces</field>
|
|||
<record model="crm.case.section" id="website.salesteam_website_sales">
|
||||
<field name="member_ids" eval="[(4, ref('base.user_root'), ref('base.user_demo'))]"/>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<!-- product.public.category -->
|
||||
|
||||
|
|
|
@ -201,7 +201,7 @@ class product_product(osv.Model):
|
|||
class product_attribute(osv.Model):
|
||||
_inherit = "product.attribute"
|
||||
_columns = {
|
||||
'type': fields.selection([('radio', 'Radio'), ('select', 'Select'), ('color', 'Color')], string="Type", type="char"),
|
||||
'type': fields.selection([('radio', 'Radio'), ('select', 'Select'), ('color', 'Color'), ('hidden', 'Hidden')], string="Type", type="char"),
|
||||
}
|
||||
_defaults = {
|
||||
'type': lambda *a: 'radio',
|
||||
|
@ -210,5 +210,5 @@ class product_attribute(osv.Model):
|
|||
class product_attribute_value(osv.Model):
|
||||
_inherit = "product.attribute.value"
|
||||
_columns = {
|
||||
'color': fields.char("Color for Color Attributes"),
|
||||
'color': fields.char("HTML Color Index", help="Here you can set a specific HTML color index (e.g. #ff0000) to display the color on the website if the attibute type is 'Color'."),
|
||||
}
|
||||
|
|
|
@ -43,16 +43,12 @@ class sale_order(osv.Model):
|
|||
'order': order
|
||||
}
|
||||
|
||||
def _cart_find_product_line(self, cr, uid, ids, product_id=None, line_id=None, context=None):
|
||||
def _cart_find_product_line(self, cr, uid, ids, product_id=None, line_id=None, context=None, **kwargs):
|
||||
for so in self.browse(cr, uid, ids, context=context):
|
||||
order_line_id = None
|
||||
domain = [('order_id', '=', so.id), ('product_id', '=', product_id)]
|
||||
if line_id:
|
||||
domain += [('id', '=', line_id)]
|
||||
order_line_ids = self.pool.get('sale.order.line').search(cr, SUPERUSER_ID, domain, context=context)
|
||||
if order_line_ids:
|
||||
order_line_id = order_line_ids[0]
|
||||
return order_line_id
|
||||
return self.pool.get('sale.order.line').search(cr, SUPERUSER_ID, domain, context=context)
|
||||
|
||||
def _website_product_id_change(self, cr, uid, ids, order_id, product_id, line_id=None, context=None):
|
||||
so = self.pool.get('sale.order').browse(cr, uid, order_id, context=context)
|
||||
|
@ -69,7 +65,7 @@ class sale_order(osv.Model):
|
|||
values['name'] = line.name
|
||||
else:
|
||||
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
|
||||
values['name'] = product.name_get()[0][1]
|
||||
values['name'] = product.description_sale or product.name
|
||||
|
||||
values['product_id'] = product_id
|
||||
values['order_id'] = order_id
|
||||
|
@ -77,13 +73,16 @@ class sale_order(osv.Model):
|
|||
values['tax_id'] = [(6, 0, values['tax_id'])]
|
||||
return values
|
||||
|
||||
def _cart_update(self, cr, uid, ids, product_id=None, line_id=None, add_qty=0, set_qty=0, context=None):
|
||||
def _cart_update(self, cr, uid, ids, product_id=None, line_id=None, add_qty=0, set_qty=0, context=None, **kwargs):
|
||||
""" Add or set product quantity, add_qty can be negative """
|
||||
sol = self.pool.get('sale.order.line')
|
||||
|
||||
quantity = 0
|
||||
for so in self.browse(cr, uid, ids, context=context):
|
||||
line_id = so._cart_find_product_line(product_id, line_id, context=context)
|
||||
if line_id != False:
|
||||
line_ids = so._cart_find_product_line(product_id, line_id, context=context, **kwargs)
|
||||
if line_ids:
|
||||
line_id = line_ids[0]
|
||||
|
||||
# Create line if no line with product_id can be located
|
||||
if not line_id:
|
||||
|
@ -107,7 +106,7 @@ class sale_order(osv.Model):
|
|||
values['product_uom_qty'] = quantity
|
||||
sol.write(cr, SUPERUSER_ID, [line_id], values, context=context)
|
||||
|
||||
return quantity
|
||||
return {'line_id': line_id, 'quantity': quantity}
|
||||
|
||||
def _cart_accessories(self, cr, uid, ids, context=None):
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
|
@ -121,7 +120,9 @@ class website(orm.Model):
|
|||
|
||||
_columns = {
|
||||
'pricelist_id': fields.related('user_id','partner_id','property_product_pricelist',
|
||||
type='many2one', relation='product.pricelist', string='Default pricelist')
|
||||
type='many2one', relation='product.pricelist', string='Default pricelist'),
|
||||
'currency_id': fields.related('pricelist_id','currency_id',
|
||||
type='many2one', relation='res.currency', string='Default pricelist'),
|
||||
}
|
||||
|
||||
def sale_product_domain(self, cr, uid, ids, context=None):
|
||||
|
@ -215,4 +216,9 @@ class website(orm.Model):
|
|||
'sale_order_code_pricelist_id': False,
|
||||
})
|
||||
|
||||
def compute_curency(self, cr, uid, ids, from_amount, from_currency_id=None, context=None):
|
||||
from_currency_id = from_currency_id or self.browse(cr, SUPERUSER_ID, ids[0]).currency_id.id
|
||||
to_currency_id = self.pool.get("res.users").browse(cr, uid, uid).partner_id.property_product_pricelist.currency_id.id
|
||||
return self.pool['res.currency'].compute(cr, uid, from_currency_id, to_currency_id, from_amount, context=context)
|
||||
|
||||
# vim:et:
|
||||
|
|
|
@ -298,14 +298,32 @@
|
|||
margin: 6px;
|
||||
}
|
||||
|
||||
.js_add_cart_variants option.css_not_available {
|
||||
.css_not_available_msg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.css_not_available.js_product > *:nth-child(4) > * {
|
||||
display: none;
|
||||
}
|
||||
.css_not_available.js_product .product_price, .css_not_available.js_product .css_quantity {
|
||||
display: none;
|
||||
}
|
||||
.css_not_available.js_product .css_not_available_msg {
|
||||
display: block;
|
||||
}
|
||||
|
||||
option.css_not_available {
|
||||
color: #cccccc;
|
||||
}
|
||||
.js_add_cart_variants label.css_not_available {
|
||||
opacity: 0.3;
|
||||
|
||||
label.css_not_available {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.js_add_cart_variants label.css_not_available input {
|
||||
opacity: 0;
|
||||
|
||||
label.css_attribute_color.css_not_available {
|
||||
opacity: 1;
|
||||
background-image: url("/website_sale/static/src/img/redcross.png");
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.product_detail_img {
|
||||
|
|
|
@ -257,13 +257,24 @@
|
|||
&.active input
|
||||
margin: 6px
|
||||
|
||||
.js_add_cart_variants
|
||||
option.css_not_available
|
||||
color: #ccc
|
||||
label.css_not_available
|
||||
opacity: 0.3
|
||||
input
|
||||
opacity: 0
|
||||
.css_not_available_msg
|
||||
display: none
|
||||
.css_not_available.js_product
|
||||
> *:nth-child(4) > *
|
||||
display: none
|
||||
.product_price, .css_quantity
|
||||
display: none
|
||||
.css_not_available_msg
|
||||
display: block
|
||||
|
||||
option.css_not_available
|
||||
color: #ccc
|
||||
label.css_not_available
|
||||
opacity: 0.6
|
||||
label.css_attribute_color.css_not_available
|
||||
opacity: 1
|
||||
background-image: url('/website_sale/static/src/img/redcross.png')
|
||||
background-size: cover
|
||||
|
||||
.product_detail_img
|
||||
margin-left: auto
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 669 B |
|
@ -1,38 +1,5 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
openerp.Tour.register({
|
||||
id: 'shop_customize',
|
||||
name: "Customize the page and search a product",
|
||||
path: '/shop',
|
||||
mode: 'test',
|
||||
steps: [
|
||||
{
|
||||
title: "open customize menu",
|
||||
element: '#customize-menu-button',
|
||||
},
|
||||
{
|
||||
title: "click on 'Product Attribute's Filters'",
|
||||
element: "#customize-menu a:contains(Product Attribute's Filters)",
|
||||
},
|
||||
{
|
||||
title: "select product attribute memory 16 Go",
|
||||
element: 'form.js_attributes label:contains(16 Go) input:not(:checked)',
|
||||
},
|
||||
{
|
||||
title: "check the selection",
|
||||
waitFor: 'form.js_attributes label:contains(16 Go) input:checked',
|
||||
},
|
||||
{
|
||||
title: "select ipod",
|
||||
waitNot: '.oe_website_sale .oe_product_cart:eq(2)',
|
||||
element: '.oe_product_cart a:contains("iPod")',
|
||||
},
|
||||
{
|
||||
title: "finish",
|
||||
waitFor: 'form[action="/shop/cart/update"] label:contains(32 Go) input',
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
openerp.Tour.register({
|
||||
id: 'shop_buy_product',
|
||||
|
@ -57,7 +24,7 @@
|
|||
{
|
||||
title: "add suggested",
|
||||
waitNot: '#cart_products:contains("[A8767] Apple In-Ear Headphones")',
|
||||
element: 'form[action="/shop/cart/update"] .btn-link:contains("Add to Cart")',
|
||||
element: '.oe_cart a:contains("Add to Cart")',
|
||||
},
|
||||
{
|
||||
title: "add one more iPod",
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
$(document).ready(function () {
|
||||
var $shippingDifferent = $(".oe_website_sale input[name='shipping_different']");
|
||||
$('.oe_website_sale').each(function () {
|
||||
var oe_website_sale = this;
|
||||
|
||||
var $shippingDifferent = $("input[name='shipping_different']", oe_website_sale);
|
||||
if ($shippingDifferent.is(':checked')) {
|
||||
$(".oe_website_sale .js_shipping").show();
|
||||
$(".js_shipping", oe_website_sale).show();
|
||||
}
|
||||
$shippingDifferent.change(function () {
|
||||
$(".oe_website_sale .js_shipping").toggle();
|
||||
$(".js_shipping", oe_website_sale).toggle();
|
||||
});
|
||||
|
||||
// change for css
|
||||
$(document).on('mouseup', '.js_publish', function (ev) {
|
||||
$(oe_website_sale).on('mouseup touchend', '.js_publish', function (ev) {
|
||||
$(ev.currentTarget).parents(".thumbnail").toggleClass("disabled");
|
||||
});
|
||||
|
||||
$(".oe_website_sale .oe_cart input.js_quantity").change(function () {
|
||||
$(oe_website_sale).on("change", ".oe_cart input.js_quantity", function () {
|
||||
var $input = $(this);
|
||||
var value = parseInt($input.val(), 10);
|
||||
var line_id = parseInt($input.data('line-id'),10);
|
||||
if (isNaN(value)) value = 0;
|
||||
openerp.jsonRpc("/shop/cart/update_json", 'call', {
|
||||
'line_id': parseInt($input.data('line-id'),10),
|
||||
'line_id': line_id,
|
||||
'product_id': parseInt($input.data('product-id'),10),
|
||||
'set_qty': value})
|
||||
.then(function (data) {
|
||||
|
@ -28,33 +32,38 @@ $(document).ready(function () {
|
|||
var $q = $(".my_cart_quantity");
|
||||
$q.parent().parent().removeClass("hidden", !data.quantity);
|
||||
$q.html(data.cart_quantity).hide().fadeIn(600);
|
||||
|
||||
$input.val(data.quantity);
|
||||
$('.js_quantity[data-line-id='+line_id+']').val(data.quantity).html(data.quantity);
|
||||
$("#cart_total").replaceWith(data['website_sale.total']);
|
||||
});
|
||||
});
|
||||
|
||||
// hack to add and rome from cart with json
|
||||
$('.oe_website_sale a.js_add_cart_json').on('click', function (ev) {
|
||||
$(oe_website_sale).on('click', 'a.js_add_cart_json', function (ev) {
|
||||
ev.preventDefault();
|
||||
var $link = $(ev.currentTarget);
|
||||
var $input = $link.parent().parent().find("input");
|
||||
$input.val(($link.has(".fa-minus").length ? -1 : 1) + parseFloat($input.val(),10));
|
||||
var min = parseFloat($input.data("min") || 0);
|
||||
var max = parseFloat($input.data("max") || Infinity);
|
||||
var quantity = ($link.has(".fa-minus").length ? -1 : 1) + parseFloat($input.val(),10);
|
||||
$input.val(quantity > min ? (quantity < max ? quantity : max) : min);
|
||||
$('input[name="'+$input.attr("name")+'"]').val(quantity > min ? (quantity < max ? quantity : max) : min);
|
||||
$input.change();
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.a-submit').on('click', function () {
|
||||
$('.a-submit', oe_website_sale).off('click').on('click', function () {
|
||||
$(this).closest('form').submit();
|
||||
});
|
||||
$('form.js_attributes input, form.js_attributes select').on('change', function () {
|
||||
$('.form.js_attributes input, form.js_attributes select', oe_website_sale).on('change', function () {
|
||||
$(this).closest("form").submit();
|
||||
});
|
||||
|
||||
// change price when they are variants
|
||||
var $price = $(".oe_price .oe_currency_value");
|
||||
$('form.js_add_cart_json label').on('mouseup', function (ev) {
|
||||
ev.preventDefault();
|
||||
var $label = $(ev.currentTarget);
|
||||
$('form.js_add_cart_json label', oe_website_sale).on('mouseup touchend', function (ev) {
|
||||
var $label = $(this);
|
||||
var $price = $label.parents("form:first").find(".oe_price .oe_currency_value");
|
||||
if (!$price.data("price")) {
|
||||
$price.data("price", parseFloat($price.text()));
|
||||
}
|
||||
|
@ -63,62 +72,85 @@ $(document).ready(function () {
|
|||
$price.html(value + (dec < 0.01 ? ".00" : (dec < 1 ? "0" : "") ));
|
||||
});
|
||||
// hightlight selected color
|
||||
$('.css_attribute_color input').on('change', function (ev) {
|
||||
$('.css_attribute_color input', oe_website_sale).on('change', function (ev) {
|
||||
$('.css_attribute_color').removeClass("active");
|
||||
$('.css_attribute_color:has(input:checked)').addClass("active");
|
||||
});
|
||||
|
||||
var $form_var = $('form.js_add_cart_variants');
|
||||
var variant_ids = $form_var.data("attribute_value_ids");
|
||||
$form_var.on('change', 'input, select', function (ev) {
|
||||
var values = [];
|
||||
$form_var.find("label").removeClass("text-muted css_not_available");
|
||||
$form_var.find(".a-submit").removeAttr("disabled");
|
||||
function price_to_str(price) {
|
||||
price = Math.round(price * 100) / 100;
|
||||
var dec = Math.round((price % 1) * 100);
|
||||
return price + (dec ? '' : '.0') + (dec%10 ? '' : '0');
|
||||
}
|
||||
|
||||
$form_var.find('input:checked, select').each(function () {
|
||||
$(oe_website_sale).on('change', 'input.js_variant_change, select.js_variant_change', function (ev) {
|
||||
var $ul = $(this).parents('ul.js_add_cart_variants:first');
|
||||
var $parent = $ul.closest('.js_product');
|
||||
var $product_id = $parent.find('input.product_id').first();
|
||||
var $price = $parent.find(".oe_price:first .oe_currency_value");
|
||||
var $default_price = $parent.find(".oe_default_price:first .oe_currency_value");
|
||||
var variant_ids = $ul.data("attribute_value_ids");
|
||||
var values = [];
|
||||
$parent.find('input.js_variant_change:checked, select.js_variant_change').each(function () {
|
||||
values.push(+$(this).val());
|
||||
});
|
||||
|
||||
$parent.find("label").removeClass("text-muted css_not_available");
|
||||
|
||||
var product_id = false;
|
||||
for (var k in variant_ids) {
|
||||
if (_.isEqual(variant_ids[k][1], values)) {
|
||||
var dec = variant_ids[k][2] % 1;
|
||||
$price.html(price_to_str(variant_ids[k][2]));
|
||||
$default_price.html(price_to_str(variant_ids[k][3]));
|
||||
$default_price.parent().toggle(variant_ids[k][3]-variant_ids[k][2]>0.2);
|
||||
product_id = variant_ids[k][0];
|
||||
$('input[name="product_id"]').val(product_id);
|
||||
$price.html(variant_ids[k][2] + (dec < 0.01 ? ".00" : (dec < 1 ? "0" : "") ));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (product_id) {
|
||||
$("#product_detail .product_detail_img").attr("src", "/website/image?field=image&model=product.product&id="+product_id);
|
||||
}
|
||||
|
||||
$form_var.find("input:radio, select").each(function () {
|
||||
var id = +$(this).val();
|
||||
$parent.find("input.js_variant_change:radio, select.js_variant_change").each(function () {
|
||||
var $input = $(this);
|
||||
var id = +$input.val();
|
||||
var values = [id];
|
||||
$form_var.find(">ul>li:not(:has(input[value='" + id + "'])) input:checked, select").each(function () {
|
||||
|
||||
$parent.find("ul:not(:has(input.js_variant_change[value='" + id + "'])) input.js_variant_change:checked, select").each(function () {
|
||||
values.push(+$(this).val());
|
||||
});
|
||||
|
||||
for (var k in variant_ids) {
|
||||
if (!_.difference(values, variant_ids[k][1]).length) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$(this).parents("label:not(.css_attribute_color):first").addClass("text-muted");
|
||||
$(this).parents("label.css_attribute_color:first").addClass("css_not_available");
|
||||
$(this).find("option[value='" + id + "']").addClass("css_not_available");
|
||||
$input.closest("label").addClass("css_not_available");
|
||||
$input.find("option[value='" + id + "']").addClass("css_not_available");
|
||||
});
|
||||
|
||||
if (product_id) {
|
||||
$(".oe_price_h4").removeClass("hidden");
|
||||
$(".oe_not_available").addClass("hidden");
|
||||
$parent.removeClass("css_not_available");
|
||||
$product_id.val(product_id);
|
||||
$parent.find(".js_check_product").removeAttr("disabled");
|
||||
} else {
|
||||
$(".oe_price_h4").addClass("hidden");
|
||||
$(".oe_not_available").removeClass("hidden");
|
||||
$form_var.find('input[name="product_id"]').val(0);
|
||||
$form_var.find(".a-submit").attr("disabled", "disabled");
|
||||
$parent.addClass("css_not_available");
|
||||
$product_id.val(0);
|
||||
$parent.find(".js_check_product").attr("disabled", "disabled");
|
||||
}
|
||||
});
|
||||
$form_var.find("input:first").trigger('change');
|
||||
$('ul.js_add_cart_variants', oe_website_sale).each(function () {
|
||||
$('input.js_variant_change, select.js_variant_change', this).first().trigger('change');
|
||||
});
|
||||
|
||||
$(oe_website_sale).on('change', "select[name='country_id']", function () {
|
||||
var $select = $("select[name='state_id']");
|
||||
$select.find("option:not(:first)").hide();
|
||||
var nb = $select.find("option[data-country_id="+($(this).val() || 0)+"]").show().size();
|
||||
$select.parent().toggle(nb>1);
|
||||
}).change();
|
||||
$(oe_website_sale).on('change', "select[name='shipping_country_id']", function () {
|
||||
var $select = $("select[name='shipping_state_id']");
|
||||
$select.find("option:not(:first)").hide();
|
||||
var nb = $select.find("option[data-country_id="+($(this).val() || 0)+"]").show().size();
|
||||
$select.parent().toggle(nb>1);
|
||||
}).change();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ $(document).ready(function () {
|
|||
.find("input[name='acquirer']:checked").click();
|
||||
|
||||
// When clicking on payment button: create the tx using json then continue to the acquirer
|
||||
$('button[type="submit"]').on("click", function (ev) {
|
||||
$payment.on("click", 'button[type="submit"]', function (ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
var $form = $(ev.currentTarget).parents('form');
|
||||
|
|
|
@ -4,7 +4,6 @@ import openerp.tests
|
|||
|
||||
inject = [
|
||||
("openerp.Tour", os.path.join(os.path.dirname(__file__), '../../web/static/src/js/tour.js')),
|
||||
("openerp.Tour.ShopTest", os.path.join(os.path.dirname(__file__), "../static/src/js/website.tour.sale.js")),
|
||||
]
|
||||
|
||||
@openerp.tests.common.at_install(False)
|
||||
|
@ -14,7 +13,6 @@ class TestUi(openerp.tests.HttpCase):
|
|||
self.phantom_js("/", "openerp.Tour.run('shop', 'test')", "openerp.Tour.tours.shop", login="admin")
|
||||
|
||||
def test_02_admin_checkout(self):
|
||||
self.phantom_js("/", "openerp.Tour.run('shop_customize', 'test')", "openerp.Tour.tours.shop_customize", login="admin", inject=inject)
|
||||
self.phantom_js("/", "openerp.Tour.run('shop_buy_product', 'test')", "openerp.Tour.tours.shop_buy_product", login="admin", inject=inject)
|
||||
|
||||
def test_03_demo_checkout(self):
|
||||
|
|
|
@ -4,12 +4,6 @@
|
|||
|
||||
<!-- Layout and common templates -->
|
||||
|
||||
<template id="debugger" inherit_id="website.debugger" name="Event Debugger">
|
||||
<xpath expr='//t[@t-set="debugger_hook"]' position="after">
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website.tour.sale.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_editor" inherit_id="website.assets_editor" name="Shop Editor" groups="base.group_sale_manager">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.editor.js"></script>
|
||||
|
@ -17,6 +11,17 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_frontend" inherit_id="website.assets_frontend" name="Shop">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website.tour.sale.js"></script>
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_mail.css'/>
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale_payment.js"></script>
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale_validate.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="header" inherit_id="website.layout" name="Header Shop My Cart Link">
|
||||
<xpath expr="//header//ul[@id='top_menu']/li" position="before">
|
||||
<t t-set="website_sale_order" t-value="website.sale_get_order()"/>
|
||||
|
@ -31,7 +36,7 @@
|
|||
<template id="search" name="Search hidden fields">
|
||||
<t t-if="attrib_values">
|
||||
<t t-foreach="attrib_values" t-as="a">
|
||||
<input type="hidden" name="attrib" t-att-value="'%s,%s' % (a[0], a[1])"/>
|
||||
<input type="hidden" name="attrib" t-att-value="'%s-%s' % (a[0], a[1])"/>
|
||||
</t>
|
||||
</t>
|
||||
<input t-if="category" type="hidden" name="category" t-att-value="int(category or 0)"/>
|
||||
|
@ -60,33 +65,39 @@
|
|||
<!-- Product item used by /shop and /shop/cart -->
|
||||
|
||||
<template id="products_item" name="Product item">
|
||||
<form action="/shop/cart/update" method="post" style="display: inline-block;">
|
||||
<div itemscope="itemscope" itemtype="http://schema.org/Product">
|
||||
<div class="ribbon-wrapper">
|
||||
<div class="ribbon btn btn-danger">Sale</div>
|
||||
</div>
|
||||
<div class="oe_product_image">
|
||||
<a itemprop="url" t-attf-href="/shop/product/{{ slug(product) }}/?{{ keep_query('search', 'attrib', category=(category and int(category)), page=(pager['page']['num'] if pager['page']['num']>1 else None)) }}">
|
||||
<a itemprop="url" t-att-href="keep('/shop/product/%s' % slug(product), page=(pager['page']['num'] if pager['page']['num']>1 else None))">
|
||||
<img itemprop="image" class="img img-responsive" t-attf-src="/website/image/product.template/#{product.id}/image#{'' if product_image_big else '?max_width=300&max_height=300'}"/>
|
||||
</a>
|
||||
</div>
|
||||
<section>
|
||||
<h5><strong><a itemprop="name" t-attf-href="/shop/product/{{ slug(product) }}/?{{ keep_query('search', 'attrib', category=(category and int(category)), page=(pager['page']['num'] if pager['page']['num']>1 else None)) }}" t-field="product.name"/></strong></h5>
|
||||
<h5><strong><a itemprop="name" t-att-href="keep('/shop/product/%s' % slug(product), page=(pager['page']['num'] if pager['page']['num']>1 else None))" t-field="product.name"/></strong></h5>
|
||||
<div itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer" class="product_price" t-if="product.product_variant_ids">
|
||||
<b>
|
||||
<t t-if="abs(product.lst_price - product.price) > 0.2">
|
||||
<del class="text-danger" t-field="product.lst_price" t-field-options='{ "widget": "monetary", "display_currency": "pricelist.currency_id" }'/>&nbsp;
|
||||
<t t-if="(website.compute_curency(product.lst_price) - product.price) > 0.1">
|
||||
<del class="text-danger" style="white-space: nowrap;" t-field="product.lst_price" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>&nbsp;
|
||||
</t>
|
||||
<span t-field="product.price" t-field-options='{
|
||||
<span t-field="product.price" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'>
|
||||
</span>
|
||||
<span itemprop="price" style="display:none;" t-esc="product.price"/>
|
||||
<span itemprop="priceCurrency" style="display:none;" t-esc="website.pricelist_id.currency_id.name"/>
|
||||
<span itemprop="priceCurrency" style="display:none;" t-esc="user_id.partner_id.property_product_pricelist.currency_id.name"/>
|
||||
</b>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<template id="products_description" inherit_id="website_sale.products_item" optional="disabled" name="Product Description">
|
||||
|
@ -99,10 +110,8 @@
|
|||
|
||||
<template id="products_add_to_cart" inherit_id="website_sale.products_item" optional="disabled" name="Add to Cart">
|
||||
<xpath expr="//div[@class='product_price']" position="inside">
|
||||
<form action="/shop/cart/update" method="post" style="display: inline-block;">
|
||||
<input name="product_id" t-att-value="product.product_variant_ids[0].id" type="hidden"/>
|
||||
<a class="btn btn-default btn-xs fa fa-shopping-cart a-submit"/>
|
||||
</form>
|
||||
<input name="product_id" t-att-value="product.product_variant_ids[0].id" type="hidden"/>
|
||||
<a class="btn btn-default btn-xs fa fa-shopping-cart a-submit"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
@ -110,22 +119,20 @@
|
|||
|
||||
<template id="products" name="Products">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<!--t t-set="head">
|
||||
<t t-call="web.jqueryui_conflict">
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script>
|
||||
</t>
|
||||
<link rel='stylesheet' href="/web/static/lib/jquery.ui/css/smoothness/jquery-ui-1.9.1.custom.css"/>
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
</t-->
|
||||
<t t-set="additional_title">Shop</t>
|
||||
<div id="wrap" class="js_sale">
|
||||
<div class="oe_structure"/>
|
||||
<div class="container oe_website_sale">
|
||||
<div class="products_pager">
|
||||
<div class="row">
|
||||
<form t-att-action="keep('/shop',search=0)" method="get" class="pagination form-inline col-md-3">
|
||||
<form t-att-action="keep('/shop',search='')" method="get" class="pagination form-inline col-md-3">
|
||||
<t t-call="website_sale.search"/>
|
||||
</form>
|
||||
<t t-call="website.pager"/>
|
||||
|
@ -273,13 +280,14 @@
|
|||
<input type="hidden" name="search" t-att-value="search"/>
|
||||
<ul class="nav nav-pills nav-stacked mt16">
|
||||
<t t-foreach="attributes" t-as="a">
|
||||
<t t-if="a.type != 'hidden'">
|
||||
<li t-if="a.value_ids and len(a.value_ids) > 1">
|
||||
<div><strong t-field="a.name"/></div>
|
||||
<t t-if="a.type == 'select'">
|
||||
<select class="form-control" name="attrib">
|
||||
<option value=""/>
|
||||
<t t-foreach="a.value_ids" t-as="v">
|
||||
<option t-att-value="'%s,%s' % (a.id,v.id)" t-field="v.name" t-att-selected="'selected' if v.id in attrib_set else ''"/>
|
||||
<option t-att-value="'%s-%s' % (a.id,v.id)" t-field="v.name" t-att-selected="'selected' if v.id in attrib_set else ''"/>
|
||||
</t>
|
||||
</select>
|
||||
</t>
|
||||
|
@ -288,7 +296,7 @@
|
|||
<t t-foreach="a.value_ids" t-as="v">
|
||||
<li t-att-class="'active' if v.id in attrib_set else ''">
|
||||
<label style="margin: 0 20px;">
|
||||
<input type="checkbox" name="attrib" t-att-value="'%s,%s' % (a.id,v.id)" t-att-checked="'checked' if v.id in attrib_set else ''"/>
|
||||
<input type="checkbox" name="attrib" t-att-value="'%s-%s' % (a.id,v.id)" t-att-checked="'checked' if v.id in attrib_set else ''"/>
|
||||
<span style="font-weight: normal" t-field="v.name"/>
|
||||
</label>
|
||||
</li>
|
||||
|
@ -301,13 +309,14 @@
|
|||
t-attf-class="css_attribute_color #{'active' if v.id in attrib_set else ''}">
|
||||
<input type="checkbox"
|
||||
name="attrib"
|
||||
t-att-value="'%s,%s' % (a.id,v.id)"
|
||||
t-att-value="'%s-%s' % (a.id,v.id)"
|
||||
t-att-checked="'checked' if v.id in attrib_set else ''"
|
||||
t-att-title="v.name"/>
|
||||
</label>
|
||||
</t>
|
||||
</t>
|
||||
</li>
|
||||
</t>
|
||||
</t>
|
||||
</ul>
|
||||
</form>
|
||||
|
@ -334,11 +343,6 @@
|
|||
|
||||
<template id="product" name="Product">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_mail.css'/>
|
||||
</t>
|
||||
<t t-set="additional_title" t-value="product.name"/>
|
||||
<div itemscope="itemscope" itemtype="http://schema.org/Product" id="wrap" class="js_sale">
|
||||
|
||||
|
@ -373,21 +377,23 @@
|
|||
</div><div class="col-sm-5 col-md-5 col-lg-4 col-lg-offset-1">
|
||||
<h1 itemprop="name" t-field="product.name">Product Name</h1>
|
||||
<span itemprop="url" style="display:none;" t-esc="'/shop/product/%s' % slug(product)"/>
|
||||
<form action="/shop/cart/update" class="js_add_cart_json" method="POST">
|
||||
<input type="hidden" t-if="len(product.product_variant_ids) == 1" name="product_id" t-att-value="product.product_variant_ids[0].id"/>
|
||||
<t t-if="len(product.product_variant_ids) > 1">
|
||||
<label label-default="label-default" class="radio" t-foreach="product.product_variant_ids" t-as="variant_id">
|
||||
<input type="radio" name="product_id" t-att-value="variant_id.id"/>
|
||||
<span t-esc="variant_id.name_get()[0][1]"/>
|
||||
<span class="badge" t-if="variant_id.price_extra">
|
||||
<t t-esc="variant_id.price_extra > 0 and '+' or ''"/><span t-field="variant_id.price_extra" t-field-options='{ "widget": "monetary", "display_currency": "pricelist.currency_id" }'/>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<form t-att-action="keep('/shop/cart/update')" class="js_add_cart_variants" method="POST">
|
||||
|
||||
<div class="js_product">
|
||||
<t t-placeholder="select">
|
||||
<input type="hidden" class="product_id" name="product_id" t-att-value="int(product.product_variant_ids[0]) if len(product.product_variant_ids) == 1 else '0'"/>
|
||||
<t t-call="website_sale.variants">
|
||||
<t t-set="ul_class" t-value="'nav-stacked'"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-call="website_sale.product_price"/>
|
||||
<p t-if="len(product.product_variant_ids) > 1" class="css_not_available_msg bg-danger" style="padding: 15px;">Product not available</p>
|
||||
|
||||
<a id="add_to_cart" class="btn btn-primary btn-lg mt8 js_check_product a-submit" href="#">Add to Cart</a>
|
||||
</div>
|
||||
|
||||
<a class="btn btn-primary btn-lg mt8 a-submit">Add to Cart</a>
|
||||
</form>
|
||||
|
||||
<hr t-if="product.description_sale"/>
|
||||
|
@ -402,29 +408,47 @@
|
|||
</div>
|
||||
</section>
|
||||
<div itemprop="description" t-field="product.website_description" class="oe_structure mt16" id="product_full_description"/>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="product_quantity" inherit_id="website_sale.product" optional="enabled" name="Select Quantity">
|
||||
<xpath expr="//a[@id='add_to_cart']" position="before">
|
||||
<div class="css_quantity input-group" style="width: 108px;">
|
||||
<span class="input-group-addon">
|
||||
<a t-attf-href="#" class="mb8 js_add_cart_json">
|
||||
<i class="fa fa-minus"></i>
|
||||
</a>
|
||||
</span>
|
||||
<input type="text" class="js_quantity form-control" data-min="1" name="add_qty" value="1"/>
|
||||
<span class="input-group-addon">
|
||||
<a t-attf-href="#" class="mb8 float_left js_add_cart_json">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="product_price">
|
||||
<div itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer" class="product_price mt16">
|
||||
<h4 class="oe_price_h4 css_editable_mode_hidden">
|
||||
<t t-if="product.lst_price != product.price">
|
||||
<span class="text-danger" style="text-decoration: line-through;"
|
||||
<span class="text-danger oe_default_price" t-att-style="'' if (website.compute_curency(product.lst_price) - product.price) > 0.1 else 'display: none;'" style="text-decoration: line-through; white-space: nowrap;"
|
||||
t-field="product.lst_price"
|
||||
t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
"widget": "monetary",
|
||||
"from_currency": "website.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/><br/>
|
||||
</t>
|
||||
<b class="oe_price"
|
||||
<b class="oe_price" style="white-space: nowrap;"
|
||||
t-field="product.price"
|
||||
t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>
|
||||
<span itemprop="price" style="display:none;" t-esc="product.price"/>
|
||||
<span itemprop="priceCurrency" style="display:none;" t-esc="website.pricelist_id.currency_id.name"/>
|
||||
<span itemprop="priceCurrency" style="display:none;" t-esc="user_id.partner_id.property_product_pricelist.currency_id.name"/>
|
||||
</h4>
|
||||
<h4 class="css_editable_mode_display" style="display: none;">
|
||||
<span t-field="product.lst_price"
|
||||
|
@ -437,81 +461,94 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<template id="product_variants" inherit_id="website_sale.product" optional="enabled" name="Product Variants">
|
||||
<xpath expr="//form[@action='/shop/cart/update']" position="replace">
|
||||
<form action="/shop/cart/update" class="js_add_cart_variants" method="POST" t-att-data-attribute_value_ids="variants">
|
||||
<input type="hidden" name="product_id" t-att-value="int(product.product_variant_ids[0]) if len(product.product_variant_ids) == 1 else '0'"/>
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<t t-foreach="product.attribute_line_ids" t-as="variant_id">
|
||||
<li t-if="len(variant_id.value_ids) > 1">
|
||||
|
||||
<strong t-field="variant_id.attribute_id.name"/>
|
||||
|
||||
<t t-if="variant_id.attribute_id.type == 'select'">
|
||||
<select class="form-control" t-att-name="'attribute-%s' % variant_id.attribute_id.id">
|
||||
<t t-foreach="variant_id.value_ids" t-as="value_id">
|
||||
<option t-att-value="value_id.id">
|
||||
<span t-field="value_id.name"/>
|
||||
<span t-if="value_id.price_extra">
|
||||
<t t-esc="value_id.price_extra > 0 and '+' or ''"/><span t-field="value_id.price_extra" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/>
|
||||
</span>
|
||||
</option>
|
||||
</t>
|
||||
</select>
|
||||
</t>
|
||||
|
||||
<t t-if="variant_id.attribute_id.type == 'radio'">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<t t-set="inc" t-value="0"/>
|
||||
<t t-foreach="variant_id.value_ids" t-as="value_id">
|
||||
<li t-if="value_id.product_ids" class="form-group js_attribute_value" style="margin: 0;">
|
||||
<label class="control-label" style="margin: 0 20px;">
|
||||
<input type="radio" t-att-checked="'checked' if not inc else ''" t-att-name="'attribute-%s' % variant_id.attribute_id.id" t-att-value="value_id.id" style="vertical-align: top; margin-right: 10px;"/>
|
||||
<span t-field="value_id.name"/>
|
||||
<span class="badge" t-if="value_id.price_extra">
|
||||
<t t-esc="value_id.price_extra > 0 and '+' or ''"/><span t-field="value_id.price_extra" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/>
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
<t t-set="inc" t-value="inc+1"/>
|
||||
</t>
|
||||
</ul>
|
||||
</t>
|
||||
|
||||
<t t-if="variant_id.attribute_id.type == 'color'">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<t t-set="inc" t-value="0"/>
|
||||
<t t-foreach="variant_id.value_ids" t-as="value_id">
|
||||
<label t-attf-style="background-color:#{value_id.color or value_id.name}"
|
||||
t-attf-class="css_attribute_color #{'active' if not inc else ''}">
|
||||
<input type="radio"
|
||||
t-att-checked="'checked' if not inc else ''"
|
||||
t-att-name="'attribute-%s' % variant_id.attribute_id.id"
|
||||
t-att-value="value_id.id"
|
||||
t-att-title="value_id.name"/>
|
||||
</label>
|
||||
<t t-set="inc" t-value="inc+1"/>
|
||||
</t>
|
||||
</ul>
|
||||
</t>
|
||||
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
|
||||
<t t-call="website_sale.product_price"/>
|
||||
|
||||
<a class="btn btn-primary btn-lg mt8 a-submit">Add to Cart</a>
|
||||
</form>
|
||||
<template id="product_variants" inherit_id="website_sale.product" optional="disabled" name="List View of Variants">
|
||||
<xpath expr="//t[@t-placeholder='select']" position="replace">
|
||||
<input type="hidden" t-if="len(product.product_variant_ids) == 1" name="product_id" t-att-value="product.product_variant_ids[0].id"/>
|
||||
<t t-if="len(product.product_variant_ids) > 1">
|
||||
<label label-default="label-default" class="radio" t-foreach="product.product_variant_ids" t-as="variant_id">
|
||||
<input type="radio" name="product_id" t-att-value="variant_id.id"/>
|
||||
<span t-esc="variant_id.name_get()[0][1]"/>
|
||||
<span class="badge" t-if="variant_id.price_extra">
|
||||
<t t-esc="variant_id.price_extra > 0 and '+' or ''"/><span t-field="variant_id.price_extra" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>
|
||||
</span>
|
||||
</label>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="variants">
|
||||
<t t-set="attribute_value_ids" t-value="get_attribute_value_ids(product)"/>
|
||||
<ul t-attf-class="list-unstyled js_add_cart_variants #{ul_class}" t-att-data-attribute_value_ids="attribute_value_ids">
|
||||
<t t-foreach="product.attribute_line_ids" t-as="variant_id">
|
||||
<li t-if="len(variant_id.value_ids) > 1">
|
||||
|
||||
<strong t-field="variant_id.attribute_id.name"/>
|
||||
|
||||
<t t-if="variant_id.attribute_id.type in ['select', 'hidden']">
|
||||
<select class="form-control js_variant_change" t-att-name="'attribute-%s-%s' % (product.id, variant_id.attribute_id.id)">
|
||||
<t t-foreach="variant_id.value_ids" t-as="value_id">
|
||||
<option t-att-value="value_id.id">
|
||||
<span t-field="value_id.name"/>
|
||||
<span t-if="value_id.price_extra">
|
||||
<t t-esc="value_id.price_extra > 0 and '+' or ''"/><span t-field="value_id.price_extra" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>
|
||||
</span>
|
||||
</option>
|
||||
</t>
|
||||
</select>
|
||||
</t>
|
||||
|
||||
<t t-if="variant_id.attribute_id.type == 'radio'">
|
||||
<ul class="list-unstyled">
|
||||
<t t-set="inc" t-value="0"/>
|
||||
<t t-foreach="variant_id.value_ids" t-as="value_id">
|
||||
<li t-if="value_id.product_ids" class="form-group js_attribute_value" style="margin: 0;">
|
||||
<label class="control-label" style="margin: 0 20px;">
|
||||
<input type="radio" class="js_variant_change" t-att-checked="'checked' if not inc else ''" t-att-name="'attribute-%s-%s' % (product.id, variant_id.attribute_id.id)" t-att-value="value_id.id" style="vertical-align: top; margin-right: 10px;"/>
|
||||
<span t-field="value_id.name"/>
|
||||
<span class="badge" t-if="value_id.price_extra">
|
||||
<t t-esc="value_id.price_extra > 0 and '+' or ''"/><span t-field="value_id.price_extra" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
<t t-set="inc" t-value="inc+1"/>
|
||||
</t>
|
||||
</ul>
|
||||
</t>
|
||||
|
||||
<t t-if="variant_id.attribute_id.type == 'color'">
|
||||
<ul class="list-inline">
|
||||
<t t-set="inc" t-value="0"/>
|
||||
<li t-foreach="variant_id.value_ids" t-as="value_id">
|
||||
<label t-attf-style="background-color:#{value_id.color or value_id.name}"
|
||||
t-attf-class="css_attribute_color #{'active' if not inc else ''}">
|
||||
<input type="radio" class="js_variant_change"
|
||||
t-att-checked="'checked' if not inc else ''"
|
||||
t-att-name="'attribute-%s-%s' % (product.id, variant_id.attribute_id.id)"
|
||||
t-att-value="value_id.id"
|
||||
t-att-title="value_id.name"/>
|
||||
</label>
|
||||
<t t-set="inc" t-value="inc+1"/>
|
||||
</li>
|
||||
</ul>
|
||||
</t>
|
||||
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<template id="recommended_products" inherit_id="website_sale.product" optional="enabled" name="Alternative Products">
|
||||
<xpath expr="//div[@id='product_full_description']" position="after">
|
||||
<div class="container mt32" t-if="product.alternative_product_ids">
|
||||
|
@ -611,11 +648,6 @@
|
|||
|
||||
<template id="cart" name="Shopping Cart">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<div id="wrap">
|
||||
<div class="container oe_website_sale">
|
||||
|
||||
|
@ -641,7 +673,8 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="website_sale_order.website_order_line" t-as="line">
|
||||
<t t-foreach="website_sale_order.website_order_line" t-as="line">
|
||||
<tr>
|
||||
<td colspan="2" t-if="not line.product_id.product_tmpl_id"></td>
|
||||
<td align="center" t-if="line.product_id.product_tmpl_id">
|
||||
<span t-field="line.product_id.image_small"
|
||||
|
@ -653,22 +686,23 @@
|
|||
<strong t-esc="line.product_id.name_get()[0][1]"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-muted" t-field="line.product_id.description_sale"/>
|
||||
<div class="text-muted" t-field="line.name"/>
|
||||
</td>
|
||||
<td class="text-center" name="price">
|
||||
<t t-if="abs(line.product_id.lst_price - line.price_unit) > 0.2">
|
||||
<del class="text-danger"
|
||||
<t t-if="(website.compute_curency(line.product_id.lst_price) - line.price_unit) > 0.1">
|
||||
<del class="text-danger" style="white-space: nowrap;"
|
||||
t-field="line.product_id.lst_price" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
"widget": "monetary",
|
||||
"from_currency": "website.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>&nbsp;
|
||||
</t>
|
||||
<span t-field="line.price_unit" t-field-options='{
|
||||
<span t-field="line.price_unit" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
<td>
|
||||
<td class="text-center">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">
|
||||
<a t-attf-href="#" class="mb8 js_add_cart_json" data-no-instant="">
|
||||
|
@ -685,14 +719,16 @@
|
|||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
<t t-call="website_sale.total"/>
|
||||
<div class="clearfix"/>
|
||||
<a t-if="website_sale_order and website_sale_order.website_order_line" href="/shop/checkout" class="btn btn-primary pull-right mb32">Process Checkout <span class="fa fa-long-arrow-right"/></a>
|
||||
|
||||
<a t-if="not optional_products and website_sale_order and website_sale_order.website_order_line" class="btn btn-primary pull-right mb32" href="/shop/checkout">Process Checkout <span class="fa fa-long-arrow-right"/></a>
|
||||
|
||||
<div class="oe_structure"/>
|
||||
</div>
|
||||
<div class="col-lg-3 col-lg-offset-1 col-sm-3 col-md-3 text-muted" id="right_column">
|
||||
|
@ -726,7 +762,7 @@
|
|||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">Suggested products</th>
|
||||
<th colspan="4">Suggested products:</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -747,16 +783,17 @@
|
|||
<div class="text-muted" t-field="product.description_sale"/>
|
||||
</td>
|
||||
<td>
|
||||
<t t-if="abs(product.lst_price - product.price) > 0.2">
|
||||
<del class="text-danger"
|
||||
<t t-if="(website.compute_curency(product.lst_price) - product.price) > 0.1">
|
||||
<del class="text-danger" style="white-space: nowrap;"
|
||||
t-field="product.lst_price" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
"widget": "monetary",
|
||||
"from_currency": "website.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>&nbsp;
|
||||
</t>
|
||||
<span t-field="product.price" t-field-options='{
|
||||
<span t-field="product.price" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
|
@ -798,11 +835,6 @@
|
|||
|
||||
<template id="checkout">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="additional_title">Shop - Checkout</t>
|
||||
<div id="wrap">
|
||||
<div class="container oe_website_sale">
|
||||
|
@ -827,12 +859,12 @@
|
|||
<label class="control-label" for="contact_name">Your Name</label>
|
||||
<input type="text" name="name" class="form-control" t-att-value="checkout.get('name')"/>
|
||||
</div>
|
||||
<div t-if="has_check_vat" class="clearfix"/>
|
||||
<div t-attf-class="form-group #{error.get('street2') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="street2" style="font-weight: normal">Company Name</label>
|
||||
<input type="text" name="street2" class="form-control" t-att-value="checkout.get('street2')"/>
|
||||
</div>
|
||||
<div class="col-lg-6"> </div>
|
||||
<div t-attf-class="form-group #{error.get('vat') and 'has-error' or ''} col-lg-6">
|
||||
<div t-if="has_check_vat" t-attf-class="form-group #{error.get('vat') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="vat" style="font-weight: normal">VAT Number</label>
|
||||
<input type="text" name="vat" class="form-control" t-att-value="checkout.get('vat')"/>
|
||||
</div>
|
||||
|
@ -859,21 +891,21 @@
|
|||
<label class="control-label" for="zip">Zip / Postal Code</label>
|
||||
<input type="text" name="zip" class="form-control" t-att-value="checkout.get('zip')"/>
|
||||
</div>
|
||||
<div t-attf-class="form-group #{error.get('country_id') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="country_id">Country</label>
|
||||
<select name="country_id" class="form-control">
|
||||
<option value="">Country...</option>
|
||||
<t t-foreach="countries or []" t-as="country">
|
||||
<option t-att-value="country.id" t-att-selected="country.id == checkout.get('country_id')"><t t-esc="country.name"/></option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div t-attf-class="form-group #{error.get('state_id') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="state_id" style="font-weight: normal">State / Province</label>
|
||||
<select name="state_id" class="form-control">
|
||||
<option value="">select...</option>
|
||||
<t t-foreach="states or []" t-as="state">
|
||||
<option t-att-value="state.id" t-att-selected="state.id == checkout.get('state_id')"><t t-esc="state.name"/></option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div t-attf-class="form-group #{error.get('country_id') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="contact_name">Country</label>
|
||||
<select name="country_id" class="form-control">
|
||||
<option value="">Country...</option>
|
||||
<t t-foreach="countries or []" t-as="country">
|
||||
<option t-att-value="country.id" t-att-selected="country.id == checkout.get('country_id')"><t t-esc="country.name"/></option>
|
||||
<option t-att-value="state.id" style="display:none;" t-att-data-country_id="state.country_id.id" t-att-selected="state.id == checkout.get('state_id')"><t t-esc="state.name"/></option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -891,37 +923,28 @@
|
|||
<h3 class="oe_shipping col-lg-12 mt16">Shipping Information</h3>
|
||||
|
||||
<div t-attf-class="form-group #{error.get('shipping_name') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="contact_name">Name (Shipping)</label>
|
||||
<label class="control-label" for="shipping_name">Name (Shipping)</label>
|
||||
<input type="text" name="shipping_name" class="form-control" t-att-value="checkout.get('shipping_name', '')"/>
|
||||
</div>
|
||||
<div t-attf-class="form-group #{error.get('shipping_phone') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="contact_name">Phone</label>
|
||||
<label class="control-label" for="shipping_phone">Phone</label>
|
||||
<input type="tel" name="shipping_phone" class="form-control" t-att-value="checkout.get('shipping_phone', '')"/>
|
||||
</div>
|
||||
<div t-attf-class="form-group #{error.get('shipping_street') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="contact_name">Street</label>
|
||||
<label class="control-label" for="shipping_street">Street</label>
|
||||
<input type="text" name="shipping_street" class="form-control" t-att-value="checkout.get('shipping_street', '')"/>
|
||||
</div>
|
||||
<div class="clearfix"/>
|
||||
<div t-attf-class="form-group #{error.get('shipping_city') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="contact_name">City</label>
|
||||
<label class="control-label" for="shipping_city">City</label>
|
||||
<input type="text" name="shipping_city" class="form-control" t-att-value="checkout.get('shipping_city', '')"/>
|
||||
</div>
|
||||
<div t-attf-class="form-group #{error.get('shipping_zip') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="contact_name">Zip / Postal Code</label>
|
||||
<label class="control-label" for="shipping_zip">Zip / Postal Code</label>
|
||||
<input type="text" name="shipping_zip" class="form-control" t-att-value="checkout.get('shipping_zip', '')"/>
|
||||
</div>
|
||||
<div t-attf-class="form-group #{error.get('shipping_state_id') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="contact_name" style="font-weight: normal">State / Province</label>
|
||||
<select name="shipping_state_id" class="form-control">
|
||||
<option value="">State / Province...</option>
|
||||
<t t-foreach="states or []" t-as="state">
|
||||
<option t-att-value="state.id" t-att-selected="state.id == checkout.get('shipping_state_id')"><t t-esc="state.name"/></option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div t-attf-class="form-group #{error.get('shipping_country_id') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="contact_name">Country</label>
|
||||
<label class="control-label" for="shipping_country_id">Country</label>
|
||||
<select name="shipping_country_id" class="form-control">
|
||||
<option value="">Country...</option>
|
||||
<t t-foreach="countries or []" t-as="country">
|
||||
|
@ -929,27 +952,37 @@
|
|||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div t-attf-class="form-group #{error.get('shipping_state_id') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="shipping_state_id" style="font-weight: normal">State / Province</label>
|
||||
<select name="shipping_state_id" class="form-control">
|
||||
<option value="">State / Province...</option>
|
||||
<t t-foreach="states or []" t-as="state">
|
||||
<option t-att-value="state.id" style="display:none;" t-att-data-country_id="state.country_id.id" t-att-selected="state.id == checkout.get('shipping_state_id')"><t t-esc="state.name"/></option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/shop/cart" class="btn btn-default mb32"><span class="fa fa-long-arrow-left"/> Return to Cart</a>
|
||||
<a class="btn btn-default btn-primary pull-right mb32 a-submit">Confirm <span class="fa fa-long-arrow-right"/></a>
|
||||
</div>
|
||||
<div class="col-lg-offset-1 col-lg-3 col-md-3 text-muted">
|
||||
<h3 class="page-header mt16">Your Order <small><a href="/shop/cart"><span class="fa fa-arrow-right"/> change</a></small></h3>
|
||||
<t t-set="website_sale_order" t-value="website.sale_get_order()"/>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 text-right">Subtotal:</div>
|
||||
<div class="col-sm-6"><span t-field="website_sale_order.amount_untaxed" t-field-options='{
|
||||
<div class="col-sm-6 text-right">Subtotal: </div>
|
||||
<div class="col-sm-6"><span style="white-space: nowrap;" t-field="website_sale_order.amount_untaxed" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/></div>
|
||||
<div class="col-sm-6 text-right">Taxes:</div>
|
||||
<div class="col-sm-6"><span t-field="website_sale_order.amount_tax" t-field-options='{
|
||||
<div class="col-sm-6 text-right">Taxes: </div>
|
||||
<div class="col-sm-6"><span style="white-space: nowrap;" t-field="website_sale_order.amount_tax" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/></div>
|
||||
<div class="col-sm-6 text-right"><h4>Total To Pay:</h4></div>
|
||||
<div class="col-sm-6"><h4><span t-field="website_sale_order.amount_total" t-field-options='{
|
||||
<div class="col-sm-6 text-right"><h4>Total To Pay: </h4></div>
|
||||
<div class="col-sm-6"><h4><span style="white-space: nowrap;" t-field="website_sale_order.amount_total" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/></h4></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -964,12 +997,6 @@
|
|||
|
||||
<template id="payment">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale_payment.js"></script>
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="additional_title">Shop - Select Payment Mode</t>
|
||||
<div id="wrap">
|
||||
<div class="container oe_website_sale">
|
||||
|
@ -1005,9 +1032,9 @@
|
|||
<strong t-field="line.product_id.name"/>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span t-field="line.price_unit" t-field-options='{
|
||||
<span t-field="line.price_unit" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -1088,11 +1115,6 @@
|
|||
|
||||
<template id="confirmation">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale_validate.js"></script>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="additional_title">Shop - Confirmed</t>
|
||||
<div id="wrap">
|
||||
<div class="container oe_website_sale">
|
||||
|
@ -1146,18 +1168,18 @@
|
|||
<tr width="100" style="border-top: 1px solid #000" id="order_total">
|
||||
<th><h3>Total: </h3></th>
|
||||
<th class="text-right">
|
||||
<h3><span t-field="website_sale_order.amount_total" t-field-options='{
|
||||
<h3><span t-field="website_sale_order.amount_total" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/></h3>
|
||||
</th>
|
||||
</tr>
|
||||
<tr width="120" class="text-muted" id="order_total_taxes">
|
||||
<td><abbr title="Taxes may be updated after providing shipping address">Taxes:</abbr></td>
|
||||
<td class="text-right">
|
||||
<span t-field="website_sale_order.amount_tax" t-field-options='{
|
||||
<span t-field="website_sale_order.amount_tax" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
import controllers
|
||||
import models
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
'name': 'eCommerce Optional Products',
|
||||
'category': 'Website',
|
||||
'version': '1.0',
|
||||
'description': """
|
||||
OpenERP E-Commerce
|
||||
==================
|
||||
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['website_sale'],
|
||||
'data': [
|
||||
'views/views.xml',
|
||||
'views/templates.xml',
|
||||
],
|
||||
'demo': [
|
||||
'data/demo.xml',
|
||||
],
|
||||
'qweb': ['static/src/xml/*.xml'],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
import main
|
|
@ -0,0 +1,64 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
from openerp.addons.website_sale.controllers.main import website_sale
|
||||
|
||||
class website_sale_options(website_sale):
|
||||
|
||||
@http.route(['/shop/product/<model("product.template"):product>'], type='http', auth="public", website=True)
|
||||
def product(self, product, category='', search='', **kwargs):
|
||||
r = super(website_sale_options, self).product(product, category, search, **kwargs)
|
||||
|
||||
cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
|
||||
template_obj = pool['product.template']
|
||||
|
||||
optional_product_ids = []
|
||||
for p in product.optional_product_ids:
|
||||
ctx = dict(context, active_id=p.id)
|
||||
optional_product_ids.append(template_obj.browse(cr, uid, p.id, context=ctx))
|
||||
|
||||
r.qcontext['optional_product_ids'] = optional_product_ids
|
||||
return r
|
||||
|
||||
@http.route(['/shop/cart/update_option'], type='http', auth="public", methods=['POST'], website=True)
|
||||
def cart_options_update_json(self, product_id, add_qty=1, set_qty=0, goto_shop=None, **kw):
|
||||
cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
|
||||
|
||||
order = request.website.sale_get_order(force_create=1)
|
||||
product = pool['product.product'].browse(cr, uid, int(product_id), context=context)
|
||||
|
||||
|
||||
option_ids = [p.id for tmpl in product.optional_product_ids for p in tmpl.product_variant_ids]
|
||||
optional_product_ids = []
|
||||
for k, v in kw.items():
|
||||
if "optional-product-" in k and int(kw.get(k.replace("product", "add"))) and int(v) in option_ids:
|
||||
optional_product_ids.append(int(v))
|
||||
|
||||
value = {}
|
||||
if add_qty or set_qty:
|
||||
value = order._cart_update(product_id=int(product_id),
|
||||
add_qty=int(add_qty), set_qty=int(set_qty),
|
||||
optional_product_ids=optional_product_ids)
|
||||
|
||||
# options have all time the same quantity
|
||||
for option_id in optional_product_ids:
|
||||
order._cart_update(product_id=option_id,
|
||||
set_qty=value.get('quantity'),
|
||||
linked_line_id=value.get('line_id'))
|
||||
|
||||
return str(order.cart_quantity)
|
||||
|
||||
@http.route(['/shop/modal'], type='json', auth="public", methods=['POST'], website=True)
|
||||
def modal(self, product_id, **kw):
|
||||
cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
|
||||
if not context.get('pricelist'):
|
||||
context['pricelist'] = int(self.get_pricelist())
|
||||
|
||||
product = pool['product.product'].browse(cr, uid, int(product_id), context=context)
|
||||
|
||||
return request.website._render("website_sale_options.modal", {
|
||||
'product': product,
|
||||
'get_attribute_value_ids': self.get_attribute_value_ids,
|
||||
})
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- add Warranty to ipad -->
|
||||
|
||||
<record id="product_attribute_1" model="product.attribute">
|
||||
<field name="name">Duration</field>
|
||||
<field name="type">hidden</field>
|
||||
</record>
|
||||
<record id="product_attribute_value_1" model="product.attribute.value">
|
||||
<field name="name">1 year</field>
|
||||
<field name="attribute_id" ref="product_attribute_1"/>
|
||||
</record>
|
||||
<record id="product_attribute_value_2" model="product.attribute.value">
|
||||
<field name="name">2 year</field>
|
||||
<field name="attribute_id" ref="product_attribute_1"/>
|
||||
</record>
|
||||
|
||||
<record id="product_product_1" model="product.product">
|
||||
<field name="name">Warranty</field>
|
||||
<field name="list_price">20.0</field>
|
||||
<field name="website_sequence">-1</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<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">Warranty, issued to the purchaser of an article by its manufacturer, promising to repair or replace it if necessary within a specified period of time.</field>
|
||||
<field name="default_code">W0001</field>
|
||||
<field name="attribute_value_ids" eval="[(6,0,[ref('product_attribute_value_1')])]"/>
|
||||
</record>
|
||||
<record id="product_product_1b" model="product.product">
|
||||
<field name="default_code">W0002</field>
|
||||
<field name="product_tmpl_id" ref="product_product_1_product_template"/>
|
||||
<field name="attribute_value_ids" eval="[(6,0,[ref('product_attribute_value_2')])]"/>
|
||||
</record>
|
||||
|
||||
<record id="product_attribute_line_1" model="product.attribute.line">
|
||||
<field name="product_tmpl_id" ref="product_product_1_product_template"/>
|
||||
<field name="attribute_id" ref="product_attribute_1"/>
|
||||
<field name="value_ids" eval="[(6,0,[ref('product_attribute_value_1'), ref('product_attribute_value_2')])]"/>
|
||||
</record>
|
||||
<record id="product_product_1_product_template" model="product.template">
|
||||
<field name="attribute_line_ids" eval="[(6,0,[ref('product_attribute_line_1')])]"/>
|
||||
</record>
|
||||
<record id="product.product_product_4_product_template" model="product.template">
|
||||
<field name="optional_product_ids" eval="[(6,0,[ref('product_product_1_product_template')])]"/>
|
||||
</record>
|
||||
<record id="product_attribute_price_1" model="product.attribute.price">
|
||||
<field name="product_tmpl_id" ref="product_product_1_product_template"/>
|
||||
<field name="value_id" ref="product_attribute_value_2"/>
|
||||
<field name="price_extra">18.00</field>
|
||||
</record>
|
||||
|
||||
<!-- -->
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,2 @@
|
|||
import product
|
||||
import sale_order
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from openerp import tools
|
||||
from openerp.osv import osv, fields
|
||||
|
||||
class product_template(osv.Model):
|
||||
_inherit = "product.template"
|
||||
|
||||
_columns = {
|
||||
'optional_product_ids': fields.many2many('product.template','product_optional_rel','src_id','dest_id',string='Optional Products', help="Products to propose when add to cart."),
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.osv import osv, orm, fields
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class sale_order_line(osv.Model):
|
||||
_inherit = "sale.order.line"
|
||||
_columns = {
|
||||
'linked_line_id': fields.many2one('sale.order.line', 'Linked Order Line', domain="[('order_id','!=',order_id)]", ondelete='cascade'),
|
||||
'option_line_ids': fields.one2many('sale.order.line', 'linked_line_id', string='Options Linked'),
|
||||
}
|
||||
|
||||
class sale_order(osv.Model):
|
||||
_inherit = "sale.order"
|
||||
|
||||
def _cart_find_product_line(self, cr, uid, ids, product_id=None, line_id=None, context=None, **kwargs):
|
||||
line_ids = super(sale_order, self)._cart_find_product_line(cr, uid, ids, product_id, line_id, context=context)
|
||||
if line_id:
|
||||
return line_ids
|
||||
linked_line_id = kwargs.get('linked_line_id')
|
||||
optional_product_ids = kwargs.get('optional_product_ids')
|
||||
for so in self.browse(cr, uid, ids, context=context):
|
||||
domain = [('id', 'in', line_ids)]
|
||||
domain += linked_line_id and [('linked_line_id', '=', linked_line_id)] or [('linked_line_id', '=', False)]
|
||||
if optional_product_ids:
|
||||
domain += [('option_line_ids.product_id', '=', pid) for pid in optional_product_ids]
|
||||
else:
|
||||
domain += [('option_line_ids', '=', False)]
|
||||
return self.pool.get('sale.order.line').search(cr, SUPERUSER_ID, domain, context=context)
|
||||
|
||||
def _cart_update(self, cr, uid, ids, product_id=None, line_id=None, add_qty=0, set_qty=0, context=None, **kwargs):
|
||||
""" Add or set product quantity, add_qty can be negative """
|
||||
value = super(sale_order, self)._cart_update(cr, uid, ids, product_id, line_id, add_qty, set_qty, context=context, **kwargs)
|
||||
|
||||
linked_line_id = kwargs.get('linked_line_id')
|
||||
sol = self.pool.get('sale.order.line')
|
||||
line = sol.browse(cr, SUPERUSER_ID, value.get('line_id'), context=context)
|
||||
|
||||
for so in self.browse(cr, uid, ids, context=context):
|
||||
|
||||
if linked_line_id and linked_line_id in map(int,so.order_line):
|
||||
linked = sol.browse(cr, SUPERUSER_ID, linked_line_id, context=context)
|
||||
line.write({
|
||||
"name": _("%s\nOption for: %s") % (line.name, linked.product_id.name_get()[0][1]),
|
||||
"linked_line_id": linked_line_id
|
||||
}, context=context)
|
||||
|
||||
# select linked product
|
||||
option_ids = [l.id for l in so.order_line if l.linked_line_id.id == line.id]
|
||||
|
||||
if option_ids:
|
||||
# update line
|
||||
sol.write(cr, SUPERUSER_ID, option_ids, {
|
||||
'product_uom_qty': value.get('quantity')
|
||||
}, context=context)
|
||||
|
||||
value['option_ids'] = option_ids
|
||||
return value
|
|
@ -0,0 +1,2 @@
|
|||
sass:
|
||||
sass -t expanded --unix-newlines --watch website_sale.sass:website_sale.css&
|
|
@ -0,0 +1,3 @@
|
|||
.css_not_available.js_product > *:nth-child(5) > * {
|
||||
display: none;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.css_not_available.js_product
|
||||
> *:nth-child(5) > *
|
||||
display: none
|
|
@ -0,0 +1,66 @@
|
|||
$(document).ready(function () {
|
||||
$('.oe_website_sale #add_to_cart, .oe_website_sale #products_grid .a-submit')
|
||||
.off('click')
|
||||
.removeClass('a-submit')
|
||||
.click(function (event) {
|
||||
var $form = $(this).closest('form');
|
||||
var quantity = parseFloat($form.find('input[name="add_qty"]').val() || 1);
|
||||
event.preventDefault();
|
||||
openerp.jsonRpc("/shop/modal", 'call', {
|
||||
'product_id': parseInt($form.find('input[name="product_id"]').val(),10),
|
||||
kwargs: {
|
||||
context: openerp.website.get_context()
|
||||
},
|
||||
}).then(function (modal) {
|
||||
var $modal = $(modal);
|
||||
|
||||
$modal.appendTo($form)
|
||||
.modal()
|
||||
.on('hidden.bs.modal', function () {
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
$modal.on('click', '.a-submit', function () {
|
||||
var $a = $(this);
|
||||
$form.ajaxSubmit({
|
||||
url: '/shop/cart/update_option',
|
||||
success: function (quantity) {
|
||||
if (!$a.hasClass('js_goto_shop')) {
|
||||
window.location.href = window.location.href.replace(/shop([\/?].*)?$/, "shop/cart");
|
||||
}
|
||||
var $q = $(".my_cart_quantity");
|
||||
$q.parent().parent().removeClass("hidden", !quantity);
|
||||
$q.html(quantity).hide().fadeIn(600);
|
||||
}
|
||||
});
|
||||
$modal.modal('hide');
|
||||
});
|
||||
|
||||
$modal.on("click", "a.js_add, a.js_remove", function (event) {
|
||||
event.preventDefault();
|
||||
var $parent = $(this).parents('.js_product:first');
|
||||
$parent.find("a.js_add, span.js_remove").toggleClass("hidden");
|
||||
$parent.find("input.js_optional_same_quantity").val( $(this).hasClass("js_add") ? 1 : 0 );
|
||||
var $remove = $parent.find(".js_remove");
|
||||
});
|
||||
|
||||
$modal.on("change", "input.js_quantity", function (event) {
|
||||
var qty = parseFloat($(this).val());
|
||||
if (qty === 1) {
|
||||
$(".js_remove .js_items").addClass("hidden");
|
||||
$(".js_remove .js_item").removeClass("hidden");
|
||||
} else {
|
||||
$(".js_remove .js_items").removeClass("hidden").text($(".js_remove .js_items").text().replace(/[0-9.,]+/, qty));
|
||||
$(".js_remove .js_item").addClass("hidden");
|
||||
}
|
||||
});
|
||||
|
||||
$modal.find('input[name="add_qty"]').val(quantity).change();
|
||||
$('ul.js_add_cart_variants').each(function () {
|
||||
$('input.js_variant_change, select.js_variant_change', this).first().trigger('change');
|
||||
});
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,95 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
var steps = openerp.Tour.tours.shop_buy_product.steps;
|
||||
for (var k=0; k<steps.length; k++) {
|
||||
if (steps[k].title === "click on add to cart") {
|
||||
steps.splice(k+1, 0, {
|
||||
title: "click in modal on 'Proceed to checkout' button",
|
||||
element: '.modal a:contains("Proceed to checkout")',
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
openerp.Tour.register({
|
||||
id: 'shop_customize',
|
||||
name: "Customize the page and search a product",
|
||||
path: '/shop',
|
||||
mode: 'test',
|
||||
steps: [
|
||||
{
|
||||
title: "open customize menu",
|
||||
element: '#customize-menu-button',
|
||||
},
|
||||
{
|
||||
title: "click on 'Product Attribute's Filters'",
|
||||
element: "#customize-menu a:contains(Product Attribute's Filters)",
|
||||
},
|
||||
{
|
||||
title: "select product attribute memory 16 Go",
|
||||
waitNot: '#customize-menu:visible',
|
||||
element: 'form.js_attributes label:contains(16 Go) input:not(:checked)',
|
||||
},
|
||||
{
|
||||
title: "check the selection",
|
||||
waitFor: 'form.js_attributes label:contains(16 Go) input:checked',
|
||||
},
|
||||
{
|
||||
title: "select iPad",
|
||||
waitNot: '.oe_website_sale .oe_product_cart:eq(2)',
|
||||
element: '.oe_product_cart a:contains("iPad")',
|
||||
},
|
||||
{
|
||||
title: "click on 'Add to Cart' button",
|
||||
waitNot: '#customize-menu:visible',
|
||||
element: "a:contains(Add to Cart)",
|
||||
},
|
||||
{
|
||||
title: "add an optional Warranty",
|
||||
element: ".js_product:contains(Warranty) a:contains(Add to Cart)",
|
||||
},
|
||||
{
|
||||
title: "click in modal on 'Proceed to checkout' button",
|
||||
waitFor: '.js_product:contains(Warranty) a:contains(Add to Cart):hidden',
|
||||
element: '.modal a:contains("Proceed to checkout")',
|
||||
},
|
||||
{
|
||||
title: "check quantity",
|
||||
waitFor: '.my_cart_quantity:containsExact(2)',
|
||||
},
|
||||
{
|
||||
title: "check optional product",
|
||||
waitFor: '.optional_product',
|
||||
},
|
||||
{
|
||||
title: "remove iPad from cart",
|
||||
element: '#cart_products a.js_add_cart_json:first',
|
||||
},
|
||||
{
|
||||
title: "check optional product is removed",
|
||||
waitNot: '.optional_product',
|
||||
},
|
||||
{
|
||||
title: "click on shop",
|
||||
element: "a:contains(Shop)",
|
||||
waitNot: '#products_grid_before .js_attributes',
|
||||
},
|
||||
{
|
||||
title: "open customize menu bis",
|
||||
waitFor: '#products_grid_before .js_attributes',
|
||||
element: '#customize-menu-button',
|
||||
},
|
||||
{
|
||||
title: "remove 'Product Attribute's Filters'",
|
||||
element: "#customize-menu a:contains(Product Attribute's Filters):has(.fa-check-square-o)",
|
||||
},
|
||||
{
|
||||
title: "finish",
|
||||
waitNot: '#products_grid_before .js_attributes',
|
||||
waitFor: 'li:has(.my_cart_quantity):hidden',
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
}());
|
|
@ -0,0 +1 @@
|
|||
import customize_test
|
|
@ -0,0 +1,13 @@
|
|||
import os
|
||||
|
||||
import openerp.tests
|
||||
|
||||
inject = [
|
||||
("openerp.Tour", os.path.join(os.path.dirname(__file__), '../../web/static/src/js/tour.js')),
|
||||
]
|
||||
|
||||
@openerp.tests.common.at_install(False)
|
||||
@openerp.tests.common.post_install(True)
|
||||
class TestUi(openerp.tests.HttpCase):
|
||||
def test_01_admin_shop_tour(self):
|
||||
self.phantom_js("/", "openerp.Tour.run('shop_customize', 'test')", "openerp.Tour.tours.shop_customize", login="admin", inject=inject)
|
|
@ -0,0 +1,149 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<template id="assets_frontend" inherit_id="website.assets_frontend" name="Shop Product Options">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/website_sale_options/static/src/js/website_sale.test.js"></script>
|
||||
<script type="text/javascript" src="/website_sale_options/static/src/js/website_sale.js"></script>
|
||||
<link rel='stylesheet' href='/website_sale_options/static/src/css/website_sale.css'/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="optional_products" inherit_id="website_sale.cart" name="Optional products">
|
||||
<xpath expr="//table[@id='cart_products']/tbody//td[last()]/div" position="attributes">
|
||||
<attribute name="t-if">not line.linked_line_id</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//table[@id='cart_products']/tbody//tr" position="attributes">
|
||||
<attribute name="t-att-class">'optional_product info' if line.linked_line_id else ''</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//table[@id='cart_products']/tbody//td[last()]" position="inside">
|
||||
<t t-if="line.linked_line_id">
|
||||
<span class="js_quantity text-muted" t-att-data-line-id="line.id" t-esc="int(line.product_uom_qty)"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="modal" name="Optional Products">
|
||||
<div id="modal_optional_products" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
|
||||
<h4 class="modal-title" id="myModalLabel">Product to add in your shopping cart</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">Product</th>
|
||||
<th style="min-width: 140px;"> </th>
|
||||
<th width="100">Price</th>
|
||||
<th width="120">Quantity</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr id="product_confirmation">
|
||||
<td width="100">
|
||||
<span t-field="product.image_medium" t-field-options='{"widget": "image" }'/>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
<strong t-field="product.name"/>
|
||||
<div class="text-muted">
|
||||
<div t-field="product.description_sale"/>
|
||||
<div class="js_attributes"/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span t-attf-class="text-danger oe_default_price" t-att-style="'' if (website.compute_curency(product.lst_price) - product.price) > 0.1 else 'display: none;'" style="text-decoration: line-through; white-space: nowrap;"
|
||||
t-field="product.lst_price"
|
||||
t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/><br/>
|
||||
<span class="oe_price" style="white-space: nowrap;"
|
||||
t-field="product.price"
|
||||
t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
<td>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">
|
||||
<a t-attf-href="#" class="mb8 js_add_cart_json">
|
||||
<i class="fa fa-minus"></i>
|
||||
</a>
|
||||
</span>
|
||||
<input type="text" class="js_quantity form-control" data-min="1" name="add_qty" value="1"/>
|
||||
<span class="input-group-addon">
|
||||
<a t-attf-href="#" class="mb8 float_left js_add_cart_json">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr t-if="product.optional_product_ids"><td colspan="5"><h4>Select Your Options:</h4></td></tr>
|
||||
<t t-set="option_inc" t-value="0"/>
|
||||
<tr class="js_product" t-foreach="product.optional_product_ids" t-as="product">
|
||||
<td width="100">
|
||||
<input type="hidden" class="product_id" t-attf-name="optional-product-#{option_inc}" t-att-value="int(product.product_variant_ids[0]) if len(product.product_variant_ids) == 1 else '0'"/>
|
||||
<span t-field="product.image_small" t-field-options='{"widget": "image"}'/>
|
||||
</td>
|
||||
<td>
|
||||
<div class="pull-left">
|
||||
<strong class="media-heading" t-field="product.name"/>
|
||||
<div class="text-muted" t-field="product.description_sale"/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="pull-right">
|
||||
<t t-call="website_sale.variants"/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<t t-if="(website.compute_curency(product.lst_price) - product.price) > 0.1">
|
||||
<span class="text-danger" style="text-decoration: line-through; white-space: nowrap;"
|
||||
t-field="product.lst_price"
|
||||
t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/><br/>
|
||||
</t>
|
||||
<span class="oe_price" style="white-space: nowrap;"
|
||||
t-field="product.price"
|
||||
t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>
|
||||
<p class="css_not_available_msg bg-danger" style="position:absolute; padding: 15px;">Product not available</p>
|
||||
</td>
|
||||
<td>
|
||||
<input type="hidden" class="js_optional_same_quantity" t-attf-name="optional-add-#{option_inc}" value="0"/>
|
||||
<a href="#" class="js_add"><strong>Add to Cart</strong></a>
|
||||
<span class="js_remove hidden">
|
||||
<span class="js_item">1 Item</span><span class="js_items hidden">5 Items</span><br/>
|
||||
<a href="#" class="js_remove"><small>Remove from cart</small></a>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="btn btn-default a-submit js_goto_shop"><i class="fa fa-chevron-left"></i> Continue shopping</a>
|
||||
<a class="btn btn-primary pull-right a-submit"><i class="fa fa-shopping-cart fa-fw"></i> Proceed to checkout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record model="ir.ui.view" id="product_template_form_view">
|
||||
<field name="name">product.template.product.website.form</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="website_sale.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="accessory_product_ids" position="after">
|
||||
<field name="optional_product_ids" widget="many2many_tags"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -761,7 +761,7 @@ class MonetaryConverter(osv.AbstractModel):
|
|||
if context is None:
|
||||
context = {}
|
||||
Currency = self.pool['res.currency']
|
||||
display = self.display_currency(cr, uid, options)
|
||||
display_currency = self.display_currency(cr, uid, options['display_currency'], options)
|
||||
|
||||
# lang.format mandates a sprintf-style format. These formats are non-
|
||||
# minimal (they have a default fixed precision instead), and
|
||||
|
@ -772,17 +772,23 @@ class MonetaryConverter(osv.AbstractModel):
|
|||
# The log10 of the rounding should be the number of digits involved if
|
||||
# negative, if positive clamp to 0 digits and call it a day.
|
||||
# nb: int() ~ floor(), we want nearest rounding instead
|
||||
precision = int(round(math.log10(display.rounding)))
|
||||
precision = int(round(math.log10(display_currency.rounding)))
|
||||
fmt = "%.{0}f".format(-precision if precision < 0 else 0)
|
||||
|
||||
from_amount = record[field_name]
|
||||
|
||||
if options.get('from_currency'):
|
||||
from_currency = self.display_currency(cr, uid, options['from_currency'], options)
|
||||
from_amount = Currency.compute(cr, uid, from_currency.id, display_currency.id, from_amount)
|
||||
|
||||
lang_code = context.get('lang') or 'en_US'
|
||||
lang = self.pool['res.lang']
|
||||
formatted_amount = lang.format(cr, uid, [lang_code],
|
||||
fmt, Currency.round(cr, uid, display, record[field_name]),
|
||||
fmt, Currency.round(cr, uid, display_currency, from_amount),
|
||||
grouping=True, monetary=True)
|
||||
|
||||
pre = post = u''
|
||||
if display.position == 'before':
|
||||
if display_currency.position == 'before':
|
||||
pre = u'{symbol} '
|
||||
else:
|
||||
post = u' {symbol}'
|
||||
|
@ -791,12 +797,12 @@ class MonetaryConverter(osv.AbstractModel):
|
|||
formatted_amount,
|
||||
pre=pre, post=post,
|
||||
).format(
|
||||
symbol=display.symbol,
|
||||
symbol=display_currency.symbol,
|
||||
))
|
||||
|
||||
def display_currency(self, cr, uid, options):
|
||||
def display_currency(self, cr, uid, currency, options):
|
||||
return self.qweb_object().eval_object(
|
||||
options['display_currency'], options['_qweb_context'])
|
||||
currency, options['_qweb_context'])
|
||||
|
||||
TIMEDELTA_UNITS = (
|
||||
('year', 3600 * 24 * 365),
|
||||
|
|
Loading…
Reference in New Issue