From 2be56f9d81a279ecfb5235d7764a397dd2e247e3 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Fri, 24 Feb 2017 19:05:59 +0100 Subject: [PATCH] [ADD] website_sale_wishlist: add wishlist feature on eCommerce --- addons/website_sale/models/res_config.py | 1 + addons/website_sale/views/res_config_view.xml | 11 ++ addons/website_sale_wishlist/__init__.py | 5 + addons/website_sale_wishlist/__manifest__.py | 17 +++ .../controllers/__init__.py | 2 + .../website_sale_wishlist/controllers/main.py | 36 +++++ .../website_sale_wishlist/models/__init__.py | 2 + .../models/product_wishlist.py | 35 +++++ .../security/ir.model.access.csv | 5 + .../website_sale_wishlist_security.xml | 20 +++ .../static/src/js/website_sale_wishlist.js | 131 ++++++++++++++++++ .../src/less/website_sale_wishlist.less | 14 ++ .../views/website_sale_wishlist_template.xml | 91 ++++++++++++ 13 files changed, 370 insertions(+) create mode 100644 addons/website_sale_wishlist/__init__.py create mode 100644 addons/website_sale_wishlist/__manifest__.py create mode 100644 addons/website_sale_wishlist/controllers/__init__.py create mode 100644 addons/website_sale_wishlist/controllers/main.py create mode 100644 addons/website_sale_wishlist/models/__init__.py create mode 100644 addons/website_sale_wishlist/models/product_wishlist.py create mode 100644 addons/website_sale_wishlist/security/ir.model.access.csv create mode 100644 addons/website_sale_wishlist/security/website_sale_wishlist_security.xml create mode 100644 addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js create mode 100644 addons/website_sale_wishlist/static/src/less/website_sale_wishlist.less create mode 100644 addons/website_sale_wishlist/views/website_sale_wishlist_template.xml diff --git a/addons/website_sale/models/res_config.py b/addons/website_sale/models/res_config.py index aa280ab7626..67f73085efe 100644 --- a/addons/website_sale/models/res_config.py +++ b/addons/website_sale/models/res_config.py @@ -37,6 +37,7 @@ class WebsiteConfigSettings(models.TransientModel): module_website_sale_options = fields.Boolean("Optional Products", help='Installs *e-Commerce Optional Products*') module_website_sale_digital = fields.Boolean("Digital Content") + module_website_sale_wishlist = fields.Boolean("Wishlists ", help='Installs *e-Commerce Wishlist*') module_sale_stock = fields.Boolean("Delivery Orders") module_portal = fields.Boolean("Activate the customer portal", help="""Give your customers access to their documents.""") diff --git a/addons/website_sale/views/res_config_view.xml b/addons/website_sale/views/res_config_view.xml index a42498e63b7..2eaf895ca0f 100644 --- a/addons/website_sale/views/res_config_view.xml +++ b/addons/website_sale/views/res_config_view.xml @@ -146,6 +146,17 @@ +
+
+ +
+
+
+

Checkout Process

diff --git a/addons/website_sale_wishlist/__init__.py b/addons/website_sale_wishlist/__init__.py new file mode 100644 index 00000000000..5efbd253ebb --- /dev/null +++ b/addons/website_sale_wishlist/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import models +import controllers diff --git a/addons/website_sale_wishlist/__manifest__.py b/addons/website_sale_wishlist/__manifest__.py new file mode 100644 index 00000000000..6c346f73150 --- /dev/null +++ b/addons/website_sale_wishlist/__manifest__.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +{ + 'name': 'Website Wishlist', + 'description': 'Let returning shoppers save products in a wishlist', + 'author': 'Odoo SA', + 'website': 'https://www.odoo.com', + 'category': 'Website', + 'version': '1.0', + 'depends': ['website_sale'], + 'data': [ + 'security/website_sale_wishlist_security.xml', + 'security/ir.model.access.csv', + 'views/website_sale_wishlist_template.xml', + ], + 'installable': True, +} diff --git a/addons/website_sale_wishlist/controllers/__init__.py b/addons/website_sale_wishlist/controllers/__init__.py new file mode 100644 index 00000000000..a84d81a791b --- /dev/null +++ b/addons/website_sale_wishlist/controllers/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +import main diff --git a/addons/website_sale_wishlist/controllers/main.py b/addons/website_sale_wishlist/controllers/main.py new file mode 100644 index 00000000000..bf2d0014e59 --- /dev/null +++ b/addons/website_sale_wishlist/controllers/main.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +from odoo import http +from odoo.http import request +from odoo.addons.website_sale.controllers.main import WebsiteSale +import json + + +class WebsiteSaleWishlist(WebsiteSale): + + @http.route(['/shop/wishlist/add'], type='json', auth="user", website=True) + def add_to_wishlist(self, product_id, price=False, **kw): + if not price: + compute_currency, pricelist_context, pl = self._get_compute_currency_and_context() + p = request.env['product.product'].with_context(pricelist_context, display_default_code=False).browse(product_id) + price = p.website_price + + request.env['product.wishlist']._add_to_wishlist( + request.env.user.partner_id.id, + pl.id, + pl.currency_id.id, + request.website.id, + price, + product_id + ) + return True + + @http.route(['/shop/wishlist'], type='http', auth="user", website=True) + def get_wishlist(self, count=False, **kw): + values = request.env['product.wishlist'].with_context(display_default_code=False).search([('partner_id', '=', request.env.user.partner_id.id)]) + if count: + return request.make_response(json.dumps(values.mapped('product_id').ids)) + + if not len(values): + return request.redirect("/shop") + + return request.render("website_sale_wishlist.product_wishlist", dict(wishes=values)) diff --git a/addons/website_sale_wishlist/models/__init__.py b/addons/website_sale_wishlist/models/__init__.py new file mode 100644 index 00000000000..9f849c2393b --- /dev/null +++ b/addons/website_sale_wishlist/models/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +import product_wishlist \ No newline at end of file diff --git a/addons/website_sale_wishlist/models/product_wishlist.py b/addons/website_sale_wishlist/models/product_wishlist.py new file mode 100644 index 00000000000..54a31f5718e --- /dev/null +++ b/addons/website_sale_wishlist/models/product_wishlist.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +from odoo import api, fields, models + + +class ProductWishlist(models.Model): + _name = 'product.wishlist' + + partner_id = fields.Many2one('res.partner', string='Owner', required=True) + pricelist_id = fields.Many2one('product.pricelist', string='Pricelist', help='Pricelist when added') + currency_id = fields.Many2one('res.currency', related='pricelist_id.currency_id', readonly=True) + website_id = fields.Many2one('website', required=True) + price = fields.Monetary(digits=0, currency_field='currency_id', string='Price', help='Price of the product when it has been added in the wishlist') + price_new = fields.Float(compute='compute_new_price', string='Current price', help='Current price of this product, using same pricelist, ...') + product_id = fields.Many2one('product.product', string='Product', required=True) + active = fields.Boolean(default=True, required=True) + create_date = fields.Datetime('Added Date', readonly=True, required=True) + + @api.multi + @api.depends('pricelist_id', 'currency_id', 'product_id') + def compute_new_price(self): + for wish in self: + wish.price_new = wish.product_id.with_context(pricelist=wish.pricelist_id.id).website_price + + @api.model + def _add_to_wishlist(self, partner_id, pricelist_id, currency_id, website_id, price, product_id): + wish = self.env['product.wishlist'].create({ + 'partner_id': partner_id, + 'pricelist_id': pricelist_id, + 'currency_id': currency_id, + 'website_id': website_id, + 'price': price, + 'product_id': product_id, + }) + return wish diff --git a/addons/website_sale_wishlist/security/ir.model.access.csv b/addons/website_sale_wishlist/security/ir.model.access.csv new file mode 100644 index 00000000000..f8486a0afcd --- /dev/null +++ b/addons/website_sale_wishlist/security/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_product_wishlist_default,access_product_wishlist_default,model_product_wishlist,,0,0,0,0 +access_product_wishlist_public,access_product_wishlist_public,model_product_wishlist,base.group_public,1,0,0,0 +access_product_wishlist_portal,access_product_wishlist_portal,model_product_wishlist,base.group_portal,1,1,1,1 +access_product_wishlist_user,access_product_wishlist_user,model_product_wishlist,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/addons/website_sale_wishlist/security/website_sale_wishlist_security.xml b/addons/website_sale_wishlist/security/website_sale_wishlist_security.xml new file mode 100644 index 00000000000..cdca47c4f55 --- /dev/null +++ b/addons/website_sale_wishlist/security/website_sale_wishlist_security.xml @@ -0,0 +1,20 @@ + + + + + + See own Wishlist + + [('partner_id','=', user.partner_id.id)] + + + + + See all wishlist + + [(1, '=', 1)] + + + + + diff --git a/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js b/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js new file mode 100644 index 00000000000..f3fa749f13f --- /dev/null +++ b/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js @@ -0,0 +1,131 @@ +odoo.define('website_sale_wishlist.wishlist', function (require) { +"use strict"; + +var ajax = require('web.ajax'); +var core = require('web.core'); +var Model = require('web.Model'); +var Widget = require('web.Widget'); +var base = require('web_editor.base'); +var website_sale_utils = require('website_sale.utils'); + + +if(!$('.oe_website_sale').length) { + return $.Deferred().reject("DOM doesn't contain '.oe_website_sale'"); +} + +var ProductWishlist = Widget.extend({ + events: { + 'click #my_wish': 'display_wishlist', + }, + init: function(){ + var self = this; + this.wishlist_product_ids = []; + + if (!odoo.session_info.is_website_user) { + $.get('/shop/wishlist', {'count': 1}).then(function(res) { + self.wishlist_product_ids = JSON.parse(res); + self.update_wishlist_view(); + }); + } + $('.oe_website_sale .o_add_wishlist, .oe_website_sale .o_add_wishlist_dyn').click(function (e){ + self.add_new_products($(this), e); + }); + + if ($('.wishlist-section').length) { + $('.wishlist-section a.o_wish_rm').on('click', function (e){ self.wishlist_rm(e); }); + $('.wishlist-section a.o_wish_add').on('click', function (e){ self.wishlist_add(e); }); + $('.wishlist-section a.o_wish_mv').on('click', function (e){ self.wishlist_mv(e); }); + } + }, + add_new_products:function($el, e){ + var self = this; + var product_id = $el.data('product-product-id'); + if (e.currentTarget.classList.contains('o_add_wishlist_dyn')) { + product_id = parseInt($el.parent().find('.product_id').val()); + } + if (odoo.session_info.is_website_user){ + this.warning_not_logged(); + } else { + if (!_.contains(self.wishlist_product_ids, product_id)) { + return ajax.jsonRpc('/shop/wishlist/add', 'call', { + 'product_id': product_id + }).then(function () { + self.wishlist_product_ids.push(product_id); + self.update_wishlist_view(); + website_sale_utils.animate_clone($('#my_wish'), $el.closest('form'), 10, 10); + }); + } + } + }, + display_wishlist: function() { + if (odoo.session_info.is_website_user) { + this.warning_not_logged(); + } + else if (this.wishlist_product_ids.length === 0) { + this.update_wishlist_view(); + this.redirect_no_wish(); + } + else { + window.location = '/shop/wishlist'; + } + }, + update_wishlist_view: function() { + if (this.wishlist_product_ids.length > 0) { + $('#my_wish').show(); + $('.my_wish_quantity').text(this.wishlist_product_ids.length); + } + else { + $('#my_wish').hide(); + } + }, + wishlist_rm: function(e){ + var tr = $(e.currentTarget).parents('tr'); + var wish = tr.data('wish-id'); + var product = tr.data('product-id'); + + new Model('product.wishlist') + .call('write', [[wish], { active: false }, base.get_context()]) + .then(function(){ + $(tr).hide(); + }); + this.wishlist_product_ids = _.without(this.wishlist_product_ids, product); + if (this.wishlist_product_ids.length === 0) { + this.redirect_no_wish(); + } + this.update_wishlist_view(); + }, + wishlist_add: function(e){ + var tr = $(e.currentTarget).parents('tr'); + var product = tr.data('product-id'); + + // can be hidden if empty + $('#my_cart').removeClass('hidden'); + website_sale_utils.animate_clone($('#my_cart'), tr, 0, 0); + this.add_to_cart(product, tr.find('qty').val() || 1); + }, + wishlist_mv: function(e){ + var tr = $(e.currentTarget).parents('tr'); + var product = tr.data('product-id'); + + $('#my_cart').removeClass('hidden'); + website_sale_utils.animate_clone($('#my_cart'), tr, 0, 0); + this.add_to_cart(product, tr.find('qty').val() || 1); + this.wishlist_rm(e); + }, + add_to_cart: function(product_id, qty_id) { + ajax.jsonRpc("/shop/cart/update_json", 'call', { + 'product_id': parseInt(product_id, 10), + 'add_qty': parseInt(qty_id, 10) + }); + }, + redirect_no_wish: function() { + window.location = '/shop/cart'; + }, + warning_not_logged: function() { + window.location = '/shop/wishlist'; + } +}); + +new ProductWishlist(); + +}); diff --git a/addons/website_sale_wishlist/static/src/less/website_sale_wishlist.less b/addons/website_sale_wishlist/static/src/less/website_sale_wishlist.less new file mode 100644 index 00000000000..2bac6365fa7 --- /dev/null +++ b/addons/website_sale_wishlist/static/src/less/website_sale_wishlist.less @@ -0,0 +1,14 @@ +.oe_website_sale { + .td-wish-btn { + width: 140px; + } +} + +// XS size +@media (max-width: @screen-xs-max) { + .oe_website_sale { + .td-wish-btn { + width: 100px; + } + } +} \ No newline at end of file diff --git a/addons/website_sale_wishlist/views/website_sale_wishlist_template.xml b/addons/website_sale_wishlist/views/website_sale_wishlist_template.xml new file mode 100644 index 00000000000..db27cdc0e4e --- /dev/null +++ b/addons/website_sale_wishlist/views/website_sale_wishlist_template.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + +