diff --git a/addons/website_sale/controllers/__init__.py b/addons/website_sale/controllers/__init__.py index 271986667ac..df4afa315ec 100644 --- a/addons/website_sale/controllers/__init__.py +++ b/addons/website_sale/controllers/__init__.py @@ -1,4 +1,5 @@ -# -*- coding: utf-8 -* +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. import backend import main import website_mail diff --git a/addons/website_sale/controllers/backend.py b/addons/website_sale/controllers/backend.py index 066bab79283..7c8c255803a 100644 --- a/addons/website_sale/controllers/backend.py +++ b/addons/website_sale/controllers/backend.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from openerp import http -from openerp.http import request +from odoo import http +from odoo.http import request from dateutil.relativedelta import relativedelta from datetime import datetime, date, timedelta from math import floor @@ -8,8 +8,8 @@ import time import operator import babel -from openerp.addons.website.controllers.backend import WebsiteBackend -from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT +from odoo.addons.website.controllers.backend import WebsiteBackend +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT class WebsiteSaleBackend(WebsiteBackend): diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 55e2febf0a8..d54818bf68b 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -1,17 +1,15 @@ # -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. import werkzeug -from openerp import SUPERUSER_ID -from openerp import http -from openerp import tools -from openerp.http import request -from openerp.tools.translate import _ -from openerp.addons.website.models.website import slug +from odoo import http, tools, _ +from odoo.http import request +from odoo.addons.website.models.website import slug -PPG = 20 # Products Per Page -PPR = 4 # Products Per Row +PPG = 20 # Products Per Page +PPR = 4 # Products Per Row -class table_compute(object): +class TableCompute(object): def __init__(self): self.table = {} @@ -19,15 +17,15 @@ class table_compute(object): res = True for y in range(sizey): for x in range(sizex): - if posx+x>=PPR: + if posx + x >= PPR: res = False break - row = self.table.setdefault(posy+y, {}) - if row.setdefault(posx+x) is not None: + row = self.table.setdefault(posy + y, {}) + if row.setdefault(posx + x) is not None: res = False break for x in range(PPR): - self.table[posy+y].setdefault(x, None) + self.table[posy + y].setdefault(x, None) return res def process(self, products, ppg=PPG): @@ -38,11 +36,11 @@ class table_compute(object): for p in products: x = min(max(p.website_size_x, 1), PPR) y = min(max(p.website_size_y, 1), PPR) - if index>=ppg: + if index >= ppg: x = y = 1 pos = minpos - while not self._check_place(pos%PPR, pos/PPR, x, y): + while not self._check_place(pos % PPR, pos / PPR, x, y): pos += 1 # if 21st products (index 20) and the last line is full (PPR products in it), break # (pos + 1.0) / PPR is the line where the product would be inserted @@ -52,18 +50,18 @@ class table_compute(object): if index >= ppg and ((pos + 1.0) / PPR) > maxy: break - if x==1 and y==1: # simple heuristic for CPU optimization - minpos = pos/PPR + if x == 1 and y == 1: # simple heuristic for CPU optimization + minpos = pos / PPR for y2 in range(y): for x2 in range(x): - self.table[(pos/PPR)+y2][(pos%PPR)+x2] = False - self.table[pos/PPR][pos%PPR] = { - 'product': p, 'x':x, 'y': y, + self.table[(pos / PPR) + y2][(pos % PPR) + x2] = False + self.table[pos / PPR][pos % PPR] = { + 'product': p, 'x': x, 'y': y, 'class': " ".join(map(lambda x: x.html_class or '', p.website_style_ids)) } - if index<=ppg: - maxy=max(maxy,y+(pos/PPR)) + if index <= ppg: + maxy = max(maxy, y + (pos / PPR)) index += 1 # Format table according to HTML needs @@ -74,7 +72,7 @@ class table_compute(object): cols = rows[col].items() cols.sort() x += len(cols) - rows[col] = [c for c in map(lambda x: x[1], cols) if c != False] + rows[col] = [c for c in map(lambda x: x[1], cols) if c] return rows @@ -89,49 +87,51 @@ class QueryURL(object): def __call__(self, path=None, **kw): if not path: path = self.path - for k,v in self.args.items(): - kw.setdefault(k,v) + for k, v in self.args.items(): + kw.setdefault(k, v) l = [] - for k,v in kw.items(): + for k, v in kw.items(): if v: if isinstance(v, list) or isinstance(v, set): - l.append(werkzeug.url_encode([(k,i) for i in v])) + l.append(werkzeug.url_encode([(k, i) for i in v])) else: - l.append(werkzeug.url_encode([(k,v)])) + l.append(werkzeug.url_encode([(k, v)])) if l: path += '?' + '&'.join(l) return path -class website_sale(http.Controller): +class WebsiteSale(http.Controller): 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 = [] - visible_attrs = set(l.attribute_id.id - for l in product.attribute_line_ids - if len(l.value_ids) > 1) - if request.website.pricelist_id.id != context['pricelist']: - website_currency_id = request.website.currency_id.id - currency_id = request.website.get_current_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, [v.id for v in p.attribute_value_ids if v.attribute_id.id in visible_attrs], p.price, price]) + """ list of selectable attributes of a product + + :return: list of product variant description + (variant id, [visible attribute ids], variant price, variant sale price) + """ + # product attributes with at least two choices + visible_attrs_ids = product.mapped('attribute_line_ids.attribute_id').filtered(lambda attr: len(attr.value_ids) > 1).ids + if request.website.pricelist_id.id != request.env.context.get('pricelist'): + attribute_value_ids = [] + to_currency = request.website.get_current_pricelist().currency_id + for variant in product.product_variant_ids: + pricelist_price = request.website.currency_id.compute(variant.lst_price, to_currency) + visible_attribute_ids = [v.id for v in variant.attribute_value_ids if v.attribute_id.id in visible_attrs_ids] + attribute_value_ids.append([variant.id, visible_attribute_ids, variant.price, pricelist_price]) else: - attribute_value_ids = [[p.id, [v.id for v in p.attribute_value_ids if v.attribute_id.id in visible_attrs], p.price, p.lst_price] - for p in product.product_variant_ids] + visible_attribute_ids = [v.id for v in variant.attribute_value_ids if v.attribute_id.id in visible_attrs_ids] + attribute_value_ids = [[variant.id, visible_attribute_ids, variant.price, variant.lst_price] + for variant in product.product_variant_ids] return attribute_value_ids @http.route(['/shop/change_pricelist/'], type='http', auth="public", website=True) def pricelist_change(self, pl_id, **post): - if request.website.is_pricelist_available(pl_id.id, context=request.context): + if request.website.is_pricelist_available(pl_id.id): request.session['website_sale_current_pl'] = pl_id.id - request.website.sale_get_order(force_pricelist=pl_id.id, context=request.context) + request.website.sale_get_order(force_pricelist=pl_id.id) return request.redirect(request.httprequest.referrer or '/shop') - def _get_search_domain(self, search, category, attrib_values): domain = request.website.sale_product_domain() if search: @@ -168,8 +168,6 @@ class website_sale(http.Controller): '/shop/category//page/' ], type='http', auth="public", website=True) def shop(self, page=0, category=None, search='', ppg=False, **post): - cr, uid, context, pool = request.cr, request.uid, request.context, request.registry - if ppg: try: ppg = int(ppg) @@ -188,26 +186,23 @@ class website_sale(http.Controller): keep = QueryURL('/shop', category=category and int(category), search=search, attrib=attrib_list) - pricelist_context = dict(context) + pricelist_context = dict(request.env.context) if not pricelist_context.get('pricelist'): pricelist = request.website.get_current_pricelist() - pricelist_context['pricelist'] = int(pricelist) + pricelist_context['pricelist'] = pricelist.id else: - pricelist = pool.get('product.pricelist').browse(cr, uid, pricelist_context['pricelist'], context) + pricelist = request.env['product.pricelist'].browse(pricelist_context['pricelist']) url = "/shop" if search: post["search"] = search if category: - category = pool['product.public.category'].browse(cr, uid, int(category), context=context) + category = request.env['product.public.category'].browse(int(category)) url = "/shop/category/%s" % slug(category) if attrib_list: post['attrib'] = attrib_list - category_obj = pool['product.public.category'] - category_ids = category_obj.search(cr, uid, [('parent_id', '=', False)], context=context) - categs = category_obj.browse(cr, uid, category_ids, context=context) - - product_obj = pool.get('product.template') + categs = request.env['product.public.category'].search([('parent_id', '=', False)]) + Product = request.env['product.template'] parent_category_ids = [] if category: @@ -217,19 +212,19 @@ class website_sale(http.Controller): parent_category_ids.append(current_category.parent_id.id) current_category = current_category.parent_id - product_count = product_obj.search_count(cr, uid, domain, context=context) + product_count = Product.search_count(domain) pager = request.website.pager(url=url, total=product_count, page=page, step=ppg, scope=7, url_args=post) - product_ids = product_obj.search(cr, uid, domain, limit=ppg, offset=pager['offset'], order='website_published desc, website_sequence desc', context=context) - products = product_obj.browse(cr, uid, product_ids, context=pricelist_context) + products = Product.with_context(pricelist_context).search(domain, limit=ppg, offset=pager['offset'], order='website_published desc, website_sequence desc') - attributes_obj = request.registry['product.attribute'] - if product_ids: - attributes_ids = attributes_obj.search(cr, uid, [('attribute_line_ids.product_tmpl_id', 'in', product_ids)], context=context) - attributes = attributes_obj.browse(cr, uid, attributes_ids, context=context) + ProductAttribute = request.env['product.attribute'] + if products: + attributes = ProductAttribute.search([('attribute_line_ids.product_tmpl_id', 'in', products.ids)]) + else: + attributes = ProductAttribute.browse(attributes_ids) - from_currency = pool['res.users'].browse(cr, uid, uid, context=context).company_id.currency_id + from_currency = request.env.user.company_id.currency_id to_currency = pricelist.currency_id - compute_currency = lambda price: pool['res.currency']._compute(cr, uid, from_currency, to_currency, price, context=context) + compute_currency = lambda price: from_currency.compute(price, to_currency) values = { 'search': search, @@ -239,7 +234,7 @@ class website_sale(http.Controller): 'pager': pager, 'pricelist': pricelist, 'products': products, - 'bins': table_compute().process(products, ppg), + 'bins': TableCompute().process(products, ppg), 'rows': PPR, 'categories': categs, 'attributes': attributes, @@ -253,41 +248,35 @@ class website_sale(http.Controller): @http.route(['/shop/product/'], type='http', auth="public", website=True) 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'] - - product_context = dict(context, active_id=product.id) + product_context = dict(request.env.context, active_id=product.id) + ProductCategory = request.env['product.public.category'] + Rating = request.env['rating.rating'] if category: - category = category_obj.browse(cr, uid, int(category), context=context) - category = category if category.exists() else False + category = ProductCategory.browse(int(category)).exists() attrib_list = request.httprequest.args.getlist('attrib') - attrib_values = [map(int,v.split("-")) for v in attrib_list if v] + 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_list) - category_ids = category_obj.search(cr, uid, [('parent_id', '=', False)], context=context) - categs = category_obj.browse(cr, uid, category_ids, context=context) + categs = ProductCategory.search([('parent_id', '=', False)]) pricelist = request.website.get_current_pricelist() - from_currency = pool['res.users'].browse(cr, uid, uid, context=context).company_id.currency_id + from_currency = request.env.user.company_id.currency_id to_currency = pricelist.currency_id - compute_currency = lambda price: pool['res.currency']._compute(cr, uid, from_currency, to_currency, price, context=context) + compute_currency = lambda price: from_currency.compute(price, to_currency) # get the rating attached to a mail.message, and the rating stats of the product - Rating = pool['rating.rating'] - rating_ids = Rating.search(cr, uid, [('message_id', 'in', product.website_message_ids.ids)], context=context) - ratings = Rating.browse(cr, uid, rating_ids, context=context) + ratings = Rating.search([('message_id', 'in', product.website_message_ids.ids)]) rating_message_values = dict([(record.message_id.id, record.rating) for record in ratings]) rating_product = product.rating_get_stats([('website_published', '=', True)]) if not product_context.get('pricelist'): - product_context['pricelist'] = int(self.get_pricelist()) - product = template_obj.browse(cr, uid, int(product), context=product_context) + product_context['pricelist'] = pricelist.id + product = product.with_context(product_context) values = { 'search': search, @@ -301,30 +290,27 @@ class website_sale(http.Controller): 'main_object': product, 'product': product, 'get_attribute_value_ids': self.get_attribute_value_ids, - 'rating_message_values' : rating_message_values, - 'rating_product' : rating_product + 'rating_message_values': rating_message_values, + 'rating_product': rating_product } return request.website.render("website_sale.product", values) @http.route(['/shop/pricelist'], type='http', auth="public", website=True) def pricelist(self, promo, **post): - cr, uid, pool, context = request.cr, request.uid, request.registry, request.context - pl = pool['product.pricelist'].search(cr, SUPERUSER_ID, [('code', '=', promo)], context=context) - if pl: - if not request.website.is_pricelist_available(pl[0], context=context): - return request.redirect("/shop/cart?code_not_available=1") + pricelist = request.env['product.pricelist'].sudo().search([('code', '=', promo)], limit=1) + if pricelist and not request.website.is_pricelist_available(pricelist.id): + return request.redirect("/shop/cart?code_not_available=1") - request.website.sale_get_order(code=promo, context=context) + request.website.sale_get_order(code=promo) 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() if order: from_currency = order.company_id.currency_id to_currency = order.pricelist_id.currency_id - compute_currency = lambda price: pool['res.currency']._compute(cr, uid, from_currency, to_currency, price, context=context) + compute_currency = lambda price: from_currency.compute(price, to_currency) else: compute_currency = lambda price: price @@ -335,7 +321,7 @@ class website_sale(http.Controller): } if order: _order = order - if not context.get('pricelist'): + if not request.env.context.get('pricelist'): _order = order.with_context(pricelist=order.pricelist_id.id) values['suggested_products'] = _order._cart_accessories() @@ -368,20 +354,20 @@ class website_sale(http.Controller): order = request.website.sale_get_order() value['cart_quantity'] = order.cart_quantity + from_currency = order.company_id.currency_id + to_currency = order.pricelist_id.currency_id value['website_sale.cart_lines'] = request.website._render("website_sale.cart_lines", { 'website_sale_order': order, - 'compute_currency': lambda price: request.registry['res.currency']._compute(request.cr, request.uid, - order.company_id.currency_id, order.pricelist_id.currency_id, price, context=request.context), + 'compute_currency': lambda price: from_currency.compute(price, to_currency), 'suggested_products': order.with_context(pricelist=order.pricelist_id.id)._cart_accessories() }) return value - #------------------------------------------------------ + # ------------------------------------------------------ # Checkout - #------------------------------------------------------ + # ------------------------------------------------------ def checkout_redirection(self, order): - cr, uid, context, registry = request.cr, request.uid, request.context, request.registry # must have a draft sale order with lines at this point, otherwise reset if not order or order.state != 'draft': @@ -390,37 +376,29 @@ class website_sale(http.Controller): return request.redirect('/shop') # if transaction pending / done: redirect to confirmation - tx = context.get('website_sale_transaction') + tx = request.env.context.get('website_sale_transaction') if tx and tx.state != 'draft': return request.redirect('/shop/payment/confirmation/%s' % order.id) def checkout_values(self, data=None): - cr, uid, context, registry = request.cr, request.uid, request.context, request.registry - orm_partner = registry.get('res.partner') - orm_user = registry.get('res.users') - orm_country = registry.get('res.country') - state_orm = registry.get('res.country.state') - - country_ids = orm_country.search(cr, SUPERUSER_ID, [], context=context) - countries = orm_country.browse(cr, SUPERUSER_ID, country_ids, context) - states_ids = state_orm.search(cr, SUPERUSER_ID, [], context=context) - states = state_orm.browse(cr, SUPERUSER_ID, states_ids, context) - partner = orm_user.browse(cr, SUPERUSER_ID, request.uid, context).partner_id + countries = request.env['res.country'].sudo().search([]) + states = request.env['res.country.state'].sudo().search([]) + partner = request.env.user.partner_id order = None shipping_id = data and data.get('shipping_id') or None - shipping_ids = [] + shippings = request.env['res.partner'] checkout = {} if not data: - if request.uid != request.website.user_id.id: - checkout.update( self.checkout_parse("billing", partner) ) - shipping_ids = orm_partner.search(cr, SUPERUSER_ID, [("parent_id", "=", partner.id), ('type', "=", 'delivery')], context=context) + if request.env.uid != request.website.user_id.id: + checkout.update(self.checkout_parse("billing", partner)) + shippings |= request.env['res.partner'].sudo().search([("parent_id", "=", partner.id), ('type', "=", 'delivery')]) else: - order = request.website.sale_get_order(force_create=1, context=context) + order = request.website.sale_get_order(force_create=1) if order.partner_id: domain = [("partner_id", "=", order.partner_id.id)] - user_ids = request.registry['res.users'].search(cr, SUPERUSER_ID, domain, context=dict(context or {}, active_test=False)) + user_ids = request.env['res.users'].with_context(active_test=False).sudo().search(domain).ids if not user_ids or request.website.user_id.id not in user_ids: checkout.update( self.checkout_parse("billing", order.partner_id) ) else: @@ -434,26 +412,22 @@ class website_sale(http.Controller): if shipping_id is None: if not order: - order = request.website.sale_get_order(context=context) + order = request.website.sale_get_order() if order and order.partner_shipping_id: shipping_id = order.partner_shipping_id.id - shipping_ids = list(set(shipping_ids) - set([partner.id])) + shippings -= partner if shipping_id == partner.id: shipping_id = 0 - elif shipping_id > 0 and shipping_id not in shipping_ids: - shipping_ids.append(shipping_id) - elif shipping_id is None and shipping_ids: - shipping_id = shipping_ids[0] + elif shipping_id > 0 and shipping_id not in shippings.ids: + shippings |= request.env['res.partner'].browse(shipping_id) + elif shipping_id is None and shippings: + shipping_id = shippings[0].id - ctx = dict(context, show_address=1) - shippings = [] - if shipping_ids: - shippings = shipping_ids and orm_partner.browse(cr, SUPERUSER_ID, list(shipping_ids), ctx) or [] if shipping_id > 0: - shipping = orm_partner.browse(cr, SUPERUSER_ID, shipping_id, ctx) - checkout.update( self.checkout_parse("shipping", shipping) ) + shipping = request.env['res.partner'].with_context(show_address=1).sudo().browse(shipping_id) + checkout.update(self.checkout_parse("shipping", shipping)) checkout['shipping_id'] = shipping_id @@ -461,18 +435,16 @@ class website_sale(http.Controller): if not checkout.get('country_id'): 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] + checkout['country_id'] = request.env['res.country'].search([('code', '=', country_code)], limit=1).id values = { 'countries': countries, 'states': states, 'checkout': checkout, 'shipping_id': partner.id != shipping_id and shipping_id or 0, - 'shippings': shippings, + 'shippings': shippings.with_context(show_address=1).sudo(), 'error': {}, - 'has_check_vat': hasattr(registry['res.partner'], 'check_vat'), + 'has_check_vat': hasattr(request.env['res.partner'], 'check_vat'), 'only_services': order and order.only_services or False } @@ -533,7 +505,7 @@ class website_sale(http.Controller): return dict((field_name, data[prefix + field_name]) for field_name in all_fields if prefix + field_name in data) def checkout_form_validate(self, data): - cr, uid, context, registry = request.cr, request.uid, request.context, request.registry + Partner = request.env['res.partner'] error = dict() error_message = [] @@ -549,15 +521,15 @@ class website_sale(http.Controller): error_message.append(_('Invalid Email! Please enter a valid email address.')) # vat validation - if data.get("vat") and hasattr(registry["res.partner"], "check_vat"): + if data.get("vat") and hasattr(Partner, "check_vat"): if request.website.company_id.vat_check_vies: # force full VIES online check - check_func = registry["res.partner"].vies_vat_check + check_func = Partner.vies_vat_check else: # quick and partial off-line checksum validation - check_func = registry["res.partner"].simple_vat_check - vat_country, vat_number = registry["res.partner"]._split_vat(data.get("vat")) - if not check_func(cr, uid, vat_country, vat_number, context=None): # simple_vat_check + check_func = Partner.simple_vat_check + vat_country, vat_number = Partner._split_vat(data.get("vat")) + if not check_func(vat_country, vat_number): # simple_vat_check error["vat"] = 'error' if data.get("shipping_id") == -1: @@ -579,15 +551,10 @@ class website_sale(http.Controller): return shipping_info def checkout_form_save(self, checkout): - cr, uid, context, registry = request.cr, request.uid, request.context, request.registry - order = request.website.sale_get_order(force_create=1, context=context) - - orm_partner = registry.get('res.partner') - orm_user = registry.get('res.users') - order_obj = request.registry.get('sale.order') - - partner_lang = request.lang if request.lang in [lang.code for lang in request.website.language_ids] else None + order = request.website.sale_get_order(force_create=1) + Partner = request.env['res.partner'] + partner_lang = request.lang if request.lang in request.website.mapped('language_ids.code') else None billing_info = {'customer': True} if partner_lang: @@ -595,47 +562,45 @@ class website_sale(http.Controller): billing_info.update(self.checkout_parse('billing', checkout, True)) # set partner_id - partner_id = None + partner = None if request.uid != request.website.user_id.id: - partner_id = orm_user.browse(cr, SUPERUSER_ID, uid, context=context).partner_id.id + partner = request.env.user.sudo().partner_id elif order.partner_id: - user_ids = request.registry['res.users'].search(cr, SUPERUSER_ID, - [("partner_id", "=", order.partner_id.id)], context=dict(context or {}, active_test=False)) + user_ids = request.env['res.users'].with_context(active_test=False).sudo().search([("partner_id", "=", order.partner_id.id)]).ids if not user_ids or request.website.user_id.id not in user_ids: - partner_id = order.partner_id.id + partner = order.partner_id # save partner informations - if partner_id and request.website.partner_id.id != partner_id: - orm_partner.write(cr, SUPERUSER_ID, [partner_id], billing_info, context=context) + if partner and request.website.partner_id.id != partner.id: + partner.write(billing_info) else: # create partner billing_info['team_id'] = request.website.salesteam_id.id - partner_id = orm_partner.create(cr, SUPERUSER_ID, billing_info, context=context) - order.write({'partner_id': partner_id, 'partner_invoice_id': partner_id}) - order_obj.onchange_partner_id(cr, SUPERUSER_ID, [order.id], context=context) + partner = Partner.sudo().create(billing_info) + order.write({'partner_id': partner.id, 'partner_invoice_id': partner.id}) + order.onchange_partner_id() # create a new shipping partner if checkout.get('shipping_id') == -1: shipping_info = self._get_shipping_info(checkout) if partner_lang: shipping_info['lang'] = partner_lang - shipping_info['parent_id'] = partner_id - checkout['shipping_id'] = orm_partner.create(cr, SUPERUSER_ID, shipping_info, context) + shipping_info['parent_id'] = partner.id + checkout['shipping_id'] = Partner.sudo().create(shipping_info).id order.write({'partner_shipping_id': checkout.get('shipping_id')}) - order_obj.onchange_partner_shipping_id(cr, SUPERUSER_ID, [order.id], context=context) + order.onchange_partner_shipping_id() order.order_line._compute_tax_id() order_info = { - 'message_partner_ids': [(4, partner_id), (3, request.website.partner_id.id)], + 'message_partner_ids': [(4, partner.id), (3, request.website.partner_id.id)], } - order_obj.write(cr, SUPERUSER_ID, [order.id], order_info, context=context) + order.write(order_info) @http.route(['/shop/checkout'], type='http', auth="public", website=True) def checkout(self, **post): - cr, uid, context = request.cr, request.uid, request.context - order = request.website.sale_get_order(force_create=1, context=context) + order = request.website.sale_get_order(force_create=1) redirection = self.checkout_redirection(order) if redirection: @@ -647,9 +612,8 @@ class website_sale(http.Controller): @http.route(['/shop/confirm_order'], type='http', auth="public", website=True) def confirm_order(self, **post): - cr, uid, context, registry = request.cr, request.uid, request.context, request.registry - order = request.website.sale_get_order(context=context) + order = request.website.sale_get_order() if not order: return request.redirect("/shop") @@ -670,28 +634,27 @@ class website_sale(http.Controller): request.session['sale_last_order_id'] = order.id - request.website.sale_get_order(update_pricelist=True, context=context) + request.website.sale_get_order(update_pricelist=True) - extra_step = registry['ir.model.data'].xmlid_to_object(cr, uid, 'website_sale.extra_info_option', raise_if_not_found=True) + extra_step = request.env.ref('website_sale.extra_info_option') if extra_step.active: return request.redirect("/shop/extra_info") return request.redirect("/shop/payment") - #------------------------------------------------------ + # ------------------------------------------------------ # Extra step - #------------------------------------------------------ + # ------------------------------------------------------ @http.route(['/shop/extra_info'], type='http', auth="public", website=True) def extra_info(self, **post): - cr, uid, context, registry = request.cr, request.uid, request.context, request.registry # Check that this option is activated - extra_step = registry['ir.model.data'].xmlid_to_object(cr, uid, 'website_sale.extra_info_option', raise_if_not_found=True) + extra_step = request.env.ref('website_sale.extra_info_option') if not extra_step.active: return request.redirect("/shop/payment") # check that cart is valid - order = request.website.sale_get_order(context=context) + order = request.website.sale_get_order() redirection = self.checkout_redirection(order) if redirection: return redirection @@ -700,7 +663,7 @@ class website_sale(http.Controller): if 'post_values' in post: values = {} for field_name, field_value in post.items(): - if field_name in request.registry['sale.order']._fields and field_name.startswith('x_'): + if field_name in request.env['sale.order']._fields and field_name.startswith('x_'): values[field_name] = field_value if values: order.write(values) @@ -710,14 +673,13 @@ class website_sale(http.Controller): 'website_sale_order': order } - sale_order_obj = request.registry.get('sale.order') - values.update(sale_order_obj._get_website_data(cr, uid, order, context)) + values.update(request.env['sale.order']._get_website_data(order)) return request.website.render("website_sale.extra_info", values) - #------------------------------------------------------ + # ------------------------------------------------------ # Payment - #------------------------------------------------------ + # ------------------------------------------------------ @http.route(['/shop/payment'], type='http', auth="public", website=True) def payment(self, **post): @@ -730,11 +692,9 @@ class website_sale(http.Controller): did go to a payment.acquirer website but closed the tab without paying / canceling """ - cr, uid, context = request.cr, request.uid, request.context - payment_obj = request.registry.get('payment.acquirer') - sale_order_obj = request.registry.get('sale.order') + SaleOrder = request.env['sale.order'] - order = request.website.sale_get_order(context=context) + order = request.website.sale_get_order() redirection = self.checkout_redirection(order) if redirection: @@ -750,27 +710,27 @@ class website_sale(http.Controller): values = { 'website_sale_order': order } - values['errors'] = sale_order_obj._get_errors(cr, uid, order, context=context) - values.update(sale_order_obj._get_website_data(cr, uid, order, context)) - + values['errors'] = SaleOrder._get_errors(order) + values.update(SaleOrder._get_website_data(order)) if not values['errors']: # find an already existing transaction tx = request.website.sale_get_transaction() - acquirer_ids = payment_obj.search(cr, SUPERUSER_ID, [('website_published', '=', True), ('company_id', '=', order.company_id.id)], context=context) - values['acquirers'] = list(payment_obj.browse(cr, uid, acquirer_ids, context=context)) - render_ctx = dict(context, submit_class='btn btn-primary', submit_txt=_('Pay Now')) - for acquirer in values['acquirers']: - acquirer.button = payment_obj.render( - cr, SUPERUSER_ID, acquirer.id, - tx and tx.reference or request.env['payment.transaction'].get_next_reference(order.name), - order.amount_total, - order.pricelist_id.currency_id.id, - values={ - 'return_url': '/shop/payment/validate', - 'partner_id': shipping_partner_id, - 'billing_partner_id': order.partner_invoice_id.id, - }, - context=render_ctx) + acquirers = request.env['payment.acquirer'].search( + [('website_published', '=', True), ('company_id', '=', order.company_id.id)] + ) + values['acquirers'] = list(acquirers) + acquirer_buttons = acquirers.with_context(submit_class='btn btn-primary', submit_txt=_('Pay Now')).sudo().render( + tx and tx.reference or request.env['payment.transaction'].get_next_reference(order.name), + order.amount_total, + order.pricelist_id.currency_id.id, + values={ + 'return_url': '/shop/payment/validate', + 'partner_id': shipping_partner_id, + 'billing_partner_id': order.partner_invoice_id.id, + } + ) + for index, button in enumerate(acquirer_buttons): + values['acquirers'][index].button = button return request.website.render("website_sale.payment", values) @@ -784,9 +744,8 @@ class website_sale(http.Controller): :param int acquirer_id: id of a payment.acquirer record. If not set the user is redirected to the checkout page """ - cr, uid, context = request.cr, request.uid, request.context - transaction_obj = request.registry.get('payment.transaction') - order = request.website.sale_get_order(context=context) + Transaction = request.env['payment.transaction'] + order = request.website.sale_get_order() if not order or not order.order_line or acquirer_id is None: return request.redirect("/shop/checkout") @@ -796,46 +755,40 @@ class website_sale(http.Controller): # find an already existing transaction tx = request.website.sale_get_transaction() if tx: - tx_id = tx.id if tx.sale_order_id.id != order.id or tx.state in ['error', 'cancel'] or tx.acquirer_id.id != acquirer_id: tx = False - tx_id = False elif tx.state == 'draft': # button cliked but no more info -> rewrite on tx or create a new one ? tx.write({ 'amount': order.amount_total, }) if not tx: - tx_id = transaction_obj.create(cr, SUPERUSER_ID, { + tx = Transaction.sudo().create({ 'acquirer_id': acquirer_id, 'type': 'form', 'amount': order.amount_total, 'currency_id': order.pricelist_id.currency_id.id, 'partner_id': order.partner_id.id, 'partner_country_id': order.partner_id.country_id.id, - 'reference': request.env['payment.transaction'].get_next_reference(order.name), + 'reference': Transaction.get_next_reference(order.name), 'sale_order_id': order.id, - }, context=context) - request.session['sale_transaction_id'] = tx_id - tx = transaction_obj.browse(cr, SUPERUSER_ID, tx_id, context=context) + }) + request.session['sale_transaction_id'] = tx.id # update quotation - request.registry['sale.order'].write( - cr, SUPERUSER_ID, [order.id], { - 'payment_acquirer_id': acquirer_id, - 'payment_tx_id': request.session['sale_transaction_id'] - }, context=context) + order.write({ + 'payment_acquirer_id': acquirer_id, + 'payment_tx_id': request.session['sale_transaction_id'] + }) # confirm the quotation if tx.acquirer_id.auto_confirm == 'at_pay_now': - request.registry['sale.order'].action_confirm(cr, SUPERUSER_ID, [order.id], context=dict(request.context, send_email=True)) + order.with_context(send_email=True).action_confirm() - return tx_id + return tx.id @http.route('/shop/payment/get_status/', type='json', auth="public", website=True) def payment_get_status(self, sale_order_id, **post): - cr, uid, context = request.cr, request.uid, request.context - - order = request.registry['sale.order'].browse(cr, SUPERUSER_ID, sale_order_id, context=context) + order = request.env['sale.order'].sudo().browse(sale_order_id) assert order.id == request.session.get('sale_last_order_id') values = {} @@ -843,18 +796,16 @@ class website_sale(http.Controller): if not order: values.update({'not_order': True, 'state': 'error'}) else: - tx_ids = request.registry['payment.transaction'].search( - cr, SUPERUSER_ID, [ - '|', ('sale_order_id', '=', order.id), ('reference', '=', order.name) - ], context=context) + tx = request.env['payment.transaction'].sudo().search( + ['|', ('sale_order_id', '=', order.id), ('reference', '=', order.name)], limit=1 + ) - if not tx_ids: + if not tx: if order.amount_total: values.update({'tx_ids': False, 'state': 'error'}) else: values.update({'tx_ids': False, 'state': 'done', 'validation': None}) else: - tx = request.registry['payment.transaction'].browse(cr, SUPERUSER_ID, tx_ids[0], context=context) state = tx.state flag = state == 'pending' values.update({ @@ -874,19 +825,15 @@ class website_sale(http.Controller): - UDPATE ME """ - cr, uid, context = request.cr, request.uid, request.context - email_act = None - sale_order_obj = request.registry['sale.order'] - if transaction_id is None: tx = request.website.sale_get_transaction() else: - tx = request.registry['payment.transaction'].browse(cr, uid, transaction_id, context=context) + tx = request.env['payment.transaction'].browse(transaction_id) if sale_order_id is None: - order = request.website.sale_get_order(context=context) + order = request.website.sale_get_order() else: - order = request.registry['sale.order'].browse(cr, SUPERUSER_ID, sale_order_id, context=context) + order = request.env['sale.order'].sudo().browse(sale_order_id) assert order.id == request.session.get('sale_last_order_id') if not order or (order.amount_total and not tx): @@ -896,13 +843,13 @@ class website_sale(http.Controller): if (not order.amount_total and not tx): # Orders are confirmed by payment transactions, but there is none for free orders, # (e.g. free events), so confirm immediately - order.with_context(dict(context, send_email=True)).action_confirm() + order.with_context(send_email=True).action_confirm() elif tx and tx.state == 'cancel': # cancel the quotation - sale_order_obj.action_cancel(cr, SUPERUSER_ID, [order.id], context=request.context) + order.action_cancel() # clean context and session, then redirect to the confirmation page - request.website.sale_reset(context=context) + request.website.sale_reset() if tx and tx.state == 'draft': return request.redirect('/shop') @@ -917,46 +864,40 @@ class website_sale(http.Controller): - take a sale.order id, because we request a sale.order and are not session dependant anymore """ - cr, uid, context = request.cr, request.uid, request.context - sale_order_id = request.session.get('sale_last_order_id') if sale_order_id: - order = request.registry['sale.order'].browse(cr, SUPERUSER_ID, sale_order_id, context=context) + order = request.env['sale.order'].sudo().browse(sale_order_id) + return request.website.render("website_sale.confirmation", {'order': order}) else: return request.redirect('/shop') - return request.website.render("website_sale.confirmation", {'order': order}) - @http.route(['/shop/print'], type='http', auth="public", website=True) def print_saleorder(self): - cr, uid, context = request.cr, SUPERUSER_ID, request.context sale_order_id = request.session.get('sale_last_order_id') if sale_order_id: - pdf = request.registry['report'].get_pdf(cr, uid, [sale_order_id], 'sale.report_saleorder', data=None, context=context) + order = request.env['sale.order'].browse(sale_order_id) + pdf = request.env['report'].sudo().get_pdf(order, 'sale.report_saleorder', data=None) pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))] return request.make_response(pdf, headers=pdfhttpheaders) else: return request.redirect('/shop') - #------------------------------------------------------ + # ------------------------------------------------------ # Edit - #------------------------------------------------------ + # ------------------------------------------------------ @http.route(['/shop/add_product'], type='http', auth="user", methods=['POST'], website=True) def add_product(self, name=None, category=0, **post): - cr, uid, context, pool = request.cr, request.uid, request.context, request.registry - if not name: - name = _("New Product") - product_obj = request.registry.get('product.product') - product_id = product_obj.create(cr, uid, { 'name': name, 'public_categ_ids': category }, context=context) - product = product_obj.browse(cr, uid, product_id, context=context) + product = request.env['product.product'].create({ + 'name': name or _("New Product"), + 'public_categ_ids': category + }) return request.redirect("/shop/product/%s?enable_editor=1" % slug(product.product_tmpl_id)) @http.route(['/shop/change_styles'], type='json', auth="public") def change_styles(self, id, style_id): - product_obj = request.registry.get('product.template') - product = product_obj.browse(request.cr, request.uid, id, context=request.context) + product = request.env['product.template'].browse(id) remove = [] active = False @@ -967,7 +908,7 @@ class website_sale(http.Controller): active = True break - style = request.registry.get('product.style').browse(request.cr, request.uid, style_id, context=request.context) + style = request.env['product.style'].browse(style_id) if remove: product.write({'website_style_ids': [(3, rid) for rid in remove]}) @@ -978,20 +919,19 @@ class website_sale(http.Controller): @http.route(['/shop/change_sequence'], type='json', auth="public") def change_sequence(self, id, sequence): - product_obj = request.registry.get('product.template') + product_tmpl = request.env['product.template'].browse(id) if sequence == "top": - product_obj.set_sequence_top(request.cr, request.uid, [id], context=request.context) + product_tmpl.set_sequence_top() elif sequence == "bottom": - product_obj.set_sequence_bottom(request.cr, request.uid, [id], context=request.context) + product_tmpl.set_sequence_bottom() elif sequence == "up": - product_obj.set_sequence_up(request.cr, request.uid, [id], context=request.context) + product_tmpl.set_sequence_up() elif sequence == "down": - product_obj.set_sequence_down(request.cr, request.uid, [id], context=request.context) + product_tmpl.set_sequence_down() @http.route(['/shop/change_size'], type='json', auth="public") def change_size(self, id, x, y): - product_obj = request.registry.get('product.template') - product = product_obj.browse(request.cr, request.uid, id, context=request.context) + product = request.env['product.template'].browse(id) return product.write({'website_size_x': x, 'website_size_y': y}) def order_lines_2_google_api(self, order_lines): @@ -1000,10 +940,10 @@ class website_sale(http.Controller): for line in order_lines: product = line.product_id ret.append({ - 'id': line.order_id and line.order_id.id, + 'id': line.order_id.id, 'sku': product.barcode or product.id, 'name': product.name or '-', - 'category': product.categ_id and product.categ_id.name or '-', + 'category': product.categ_id.name or '-', 'price': line.price_unit, 'quantity': line.product_uom_qty, }) @@ -1025,22 +965,20 @@ class website_sale(http.Controller): @http.route(['/shop/tracking_last_order'], type='json', auth="public") def tracking_cart(self, **post): """ return data about order in JSON needed for google analytics""" - cr, context = request.cr, request.context ret = {} sale_order_id = request.session.get('sale_last_order_id') if sale_order_id: - order = request.registry['sale.order'].browse(cr, SUPERUSER_ID, sale_order_id, context=context) + order = request.env['sale.order'].sudo().browse(sale_order_id) ret = self.order_2_return_dict(order) return ret @http.route(['/shop/get_unit_price'], type='json', auth="public", methods=['POST'], website=True) def get_unit_price(self, product_ids, add_qty, use_order_pricelist=False, **kw): - cr, uid, context, pool = request.cr, request.uid, request.context, request.registry - products = pool['product.product'].browse(cr, uid, product_ids, context=context) - partner = pool['res.users'].browse(cr, uid, uid, context=context).partner_id + products = request.env['product.product'].browse(product_ids) + partner = request.env.user.partner_id if use_order_pricelist: - pricelist_id = request.website.get_current_pricelist(context=context).id + pricelist = request.website.get_current_pricelist() else: - pricelist_id = partner.property_product_pricelist.id - prices = pool['product.pricelist'].price_rule_get_multi(cr, uid, [pricelist_id], [(product, add_qty, partner) for product in products], context=context) - return {product_id: prices[product_id][pricelist_id][0] for product_id in product_ids} + pricelist = partner.property_product_pricelist + prices = pricelist.price_rule_get_multi([(product, add_qty, partner) for product in products]) + return {product_id: prices[product_id][pricelist.id][0] for product_id in product_ids} diff --git a/addons/website_sale/controllers/website_mail.py b/addons/website_sale/controllers/website_mail.py index 2fe6e5f5355..cf677e0a51a 100644 --- a/addons/website_sale/controllers/website_mail.py +++ b/addons/website_sale/controllers/website_mail.py @@ -2,11 +2,10 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import urlparse -from openerp import SUPERUSER_ID -from openerp import http -from openerp.addons.web.http import request +from odoo import http +from odoo.addons.web.http import request -from openerp.addons.website_mail.controllers.main import WebsiteMail +from odoo.addons.website_mail.controllers.main import WebsiteMail class WebsiteMailController(WebsiteMail): @@ -38,7 +37,7 @@ class WebsiteMailController(WebsiteMail): try: fragment = urlparse.urlparse(response.location).fragment message_id = int(fragment.replace('message-', '')) - rating = request.env['rating.rating'].create({ + request.env['rating.rating'].create({ 'rating': float(kw.get('rating')), 'res_model': res_model, 'res_id': res_id, diff --git a/addons/website_sale/data/data.xml b/addons/website_sale/data/data.xml index 798d2221c81..22f78cd55ea 100644 --- a/addons/website_sale/data/data.xml +++ b/addons/website_sale/data/data.xml @@ -1,6 +1,5 @@ - - + Shop @@ -44,5 +43,5 @@ - - + + diff --git a/addons/website_sale/data/demo.xml b/addons/website_sale/data/demo.xml index f1b11957d97..2d69e3c5d6b 100644 --- a/addons/website_sale/data/demo.xml +++ b/addons/website_sale/data/demo.xml @@ -1,6 +1,5 @@ - - + @@ -762,5 +761,5 @@ Weight: 31 grams - - + + diff --git a/addons/website_sale/models/ir_http.py b/addons/website_sale/models/ir_http.py index 1bb0d6e8bed..9655a7b6223 100644 --- a/addons/website_sale/models/ir_http.py +++ b/addons/website_sale/models/ir_http.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- -from openerp import models -from openerp.addons.web.http import request +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo import models +from odoo.http import request + class IrHttp(models.AbstractModel): _inherit = 'ir.http' diff --git a/addons/website_sale/models/payment.py b/addons/website_sale/models/payment.py index f632292e8f7..be9279598d3 100644 --- a/addons/website_sale/models/payment.py +++ b/addons/website_sale/models/payment.py @@ -1,30 +1,29 @@ # -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. -from openerp import SUPERUSER_ID -from openerp.osv import orm, fields +from odoo import api, fields, models -class PaymentTransaction(orm.Model): +class PaymentTransaction(models.Model): _inherit = 'payment.transaction' - _columns = { - # link with the sale order - 'sale_order_id': fields.many2one('sale.order', 'Sale Order'), - } + # link with the sale order + sale_order_id = fields.Many2one('sale.order', string='Sale Order') - def form_feedback(self, cr, uid, data, acquirer_name, context=None): + @api.model + def form_feedback(self, data, acquirer_name): """ Override to confirm the sale order, if defined, and if the transaction is done. """ tx = None - res = super(PaymentTransaction, self).form_feedback(cr, uid, data, acquirer_name, context=context) + res = super(PaymentTransaction, self).form_feedback(data, acquirer_name) # fetch the tx, check its state, confirm the potential SO tx_find_method_name = '_%s_form_get_tx_from_data' % acquirer_name if hasattr(self, tx_find_method_name): - tx = getattr(self, tx_find_method_name)(cr, uid, data, context=context) - if tx and tx.state == 'done' and tx.acquirer_id.auto_confirm == 'at_pay_confirm' and tx.sale_order_id and tx.sale_order_id.state in ['draft', 'sent']: - self.pool['sale.order'].action_confirm(cr, SUPERUSER_ID, [tx.sale_order_id.id], context=dict(context, send_email=True)) - elif tx and tx.state not in ['cancel', 'error'] and tx.sale_order_id and tx.sale_order_id.state in ['draft']: - self.pool['sale.order'].force_quotation_send(cr, SUPERUSER_ID, [tx.sale_order_id.id], context=context) + tx = getattr(self, tx_find_method_name)(data) + if tx and tx.state == 'done' and tx.acquirer_id.auto_confirm == 'at_pay_confirm' and tx.sale_order_id.state in ['draft', 'sent']: + tx.sale_order_id.with_context(send_email=True).action_confirm() + elif tx and tx.state not in ['cancel', 'error'] and tx.sale_order_id.state in ['draft']: + tx.sale_order_id.force_quotation_send() return res diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py index f17aa5a2767..17c1423835f 100644 --- a/addons/website_sale/models/product.py +++ b/addons/website_sale/models/product.py @@ -1,72 +1,49 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo import api, fields, models, tools, _ -import openerp -from openerp import tools -from openerp.osv import osv, fields -from openerp.tools.translate import html_translate -class product_style(osv.Model): +class ProductStyle(models.Model): _name = "product.style" - _columns = { - 'name' : fields.char('Style Name', required=True), - 'html_class': fields.char('HTML Classes'), - } -class product_pricelist(osv.Model): + name = fields.Char(string='Style Name', required=True) + html_class = fields.Char(string='HTML Classes') + + +class ProductPricelist(models.Model): _inherit = "product.pricelist" - _columns = { - 'code': fields.char('E-commerce Promotional Code'), - } + + code = fields.Char(string='E-commerce Promotional Code') -class product_public_category(osv.osv): +class ProductPublicCategory(models.Model): _name = "product.public.category" _inherit = ["website.seo.metadata"] _description = "Website Product Category" _order = "sequence, name" - _constraints = [ - (osv.osv._check_recursion, 'Error ! You cannot create recursive categories.', ['parent_id']) - ] - - def name_get(self, cr, uid, ids, context=None): - res = [] - for cat in self.browse(cr, uid, ids, context=context): - names = [cat.name] - pcat = cat.parent_id - while pcat: - names.append(pcat.name) - pcat = pcat.parent_id - res.append((cat.id, ' / '.join(reversed(names)))) - return res - - _columns = { - 'name': fields.char('Name', required=True, translate=True), - 'parent_id': fields.many2one('product.public.category','Parent Category', select=True), - 'child_id': fields.one2many('product.public.category', 'parent_id', string='Children Categories'), - 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of product categories."), - } - + name = fields.Char(required=True, translate=True) + parent_id = fields.Many2one('product.public.category', string='Parent Category', index=True) + child_id = fields.One2many('product.public.category', 'parent_id', string='Children Categories') + sequence = fields.Integer(help="Gives the sequence order when displaying a list of product categories.") # NOTE: there is no 'default image', because by default we don't show # thumbnails for categories. However if we have a thumbnail for at least one # category, then we display a default image on the other, so that the # buttons have consistent styling. # In this case, the default image is set by the js code. - image = openerp.fields.Binary("Image", attachment=True, - help="This field holds the image used as image for the category, limited to 1024x1024px.") - image_medium = openerp.fields.Binary("Medium-sized image", - compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True, - help="Medium-sized image of the category. It is automatically "\ - "resized as a 128x128px image, with aspect ratio preserved. "\ - "Use this field in form views or some kanban views.") - image_small = openerp.fields.Binary("Small-sized image", - compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True, - help="Small-sized image of the category. It is automatically "\ - "resized as a 64x64px image, with aspect ratio preserved. "\ - "Use this field anywhere a small image is required.") + image = fields.Binary(attachment=True, help="This field holds the image used as image for the category, limited to 1024x1024px.") + image_medium = fields.Binary(string='Medium-sized image', + compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True, + help="Medium-sized image of the category. It is automatically " + "resized as a 128x128px image, with aspect ratio preserved. " + "Use this field in form views or some kanban views.") + image_small = fields.Binary(string='Small-sized image', + compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True, + help="Small-sized image of the category. It is automatically " + "resized as a 64x64px image, with aspect ratio preserved. " + "Use this field anywhere a small image is required.") - @openerp.api.depends('image') + @api.depends('image') def _compute_images(self): for rec in self: rec.image_medium = tools.image_resize_image_medium(rec.image) @@ -80,103 +57,101 @@ class product_public_category(osv.osv): for rec in self: rec.image = tools.image_resize_image_big(rec.image_small) -class product_template(osv.Model): + @api.constrains('parent_id') + def check_parent_id(self): + if not self._check_recursion(): + raise ValueError(_('Error ! You cannot create recursive categories.')) + + @api.multi + def name_get(self): + res = [] + for category in self: + names = [category.name] + parent_category = category.parent_id + while parent_category: + names.append(parent_category.name) + parent_category = parent_category.parent_id + res.append((category.id, ' / '.join(reversed(names)))) + return res + + +class ProductTemplate(models.Model): _inherit = ["product.template", "website.seo.metadata", 'website.published.mixin', 'rating.mixin'] _order = 'website_published desc, website_sequence desc, name' _name = 'product.template' _mail_post_access = 'read' - def _website_url(self, cr, uid, ids, field_name, arg, context=None): - res = super(product_template, self)._website_url(cr, uid, ids, field_name, arg, context=context) - for product in self.browse(cr, uid, ids, context=context): + # TODO FIXME tde: when website_mail/mail_thread.py inheritance work -> this field won't be necessary + website_message_ids = fields.One2many( + 'mail.message', 'res_id', + domain=lambda self: ['&', ('model', '=', self._name), ('message_type', '=', 'comment')], + string='Website Comments', + ) + website_description = fields.Html('Description for the website', sanitize=False, translate=True) + alternative_product_ids = fields.Many2many('product.template', 'product_alternative_rel', 'src_id', 'dest_id', + string='Suggested Products', help='Appear on the product page') + accessory_product_ids = fields.Many2many('product.product', 'product_accessory_rel', 'src_id', 'dest_id', + string='Accessory Products', help='Appear on the shopping cart') + website_size_x = fields.Integer('Size X', default=1) + website_size_y = fields.Integer('Size Y', default=1) + website_style_ids = fields.Many2many('product.style', string='Styles') + website_sequence = fields.Integer('Sequence', help="Determine the display order in the Website E-commerce", + default=lambda self: self._default_website_sequence()) + public_categ_ids = fields.Many2many('product.public.category', string='Website Product Category', + help="Those categories are used to group similar products for e-commerce.") + + def _default_website_sequence(self): + min_sequence = self.sudo().search([], order='website_sequence', limit=1).website_sequence + return min_sequence and min_sequence - 1 or 10 + + def set_sequence_top(self): + self.website_sequence = self.sudo().search([], order='website_sequence desc', limit=1).website_sequence + 1 + + def set_sequence_bottom(self): + self.website_sequence = self.sudo().search([], order='website_sequence', limit=1).website_sequence - 1 + + def set_sequence_up(self): + previous_product_tmpl = self.sudo().search( + [('website_sequence', '>', self.website_sequence), ('website_published', '=', self.website_published)], + order='website_sequence', limit=1) + if previous_product_tmpl: + previous_product_tmpl.website_sequence, self.website_sequence = self.website_sequence, previous_product_tmpl.website_sequence + else: + self.set_sequence_top() + + def set_sequence_down(self): + next_prodcut_tmpl = self.search([('website_sequence', '<', self.website_sequence), ('website_published', '=', self.website_published)], order='website_sequence desc', limit=1) + if next_prodcut_tmpl: + next_prodcut_tmpl.website_sequence, self.website_sequence = self.website_sequence, next_prodcut_tmpl.website_sequence + else: + return self.set_sequence_bottom() + + @api.multi + def _website_url(self, field_name, arg): + res = super(ProductTemplate, self)._website_url(field_name, arg) + for product in self: res[product.id] = "/shop/product/%s" % (product.id,) return res - _columns = { - # TODO FIXME tde: when website_mail/mail_thread.py inheritance work -> this field won't be necessary - 'website_message_ids': fields.one2many( - 'mail.message', 'res_id', - domain=lambda self: [ - '&', ('model', '=', self._name), ('message_type', '=', 'comment') - ], - string='Website Comments', - ), - 'website_description': fields.html('Description for the website', sanitize=False, translate=html_translate), - 'alternative_product_ids': fields.many2many('product.template','product_alternative_rel','src_id','dest_id', string='Suggested Products', help='Appear on the product page'), - 'accessory_product_ids': fields.many2many('product.product','product_accessory_rel','src_id','dest_id', string='Accessory Products', help='Appear on the shopping cart'), - 'website_size_x': fields.integer('Size X'), - 'website_size_y': fields.integer('Size Y'), - 'website_style_ids': fields.many2many('product.style', string='Styles'), - 'website_sequence': fields.integer('Sequence', help="Determine the display order in the Website E-commerce"), - 'public_categ_ids': fields.many2many('product.public.category', string='Website Product Category', help="Those categories are used to group similar products for e-commerce."), - } - def _defaults_website_sequence(self, cr, uid, *l, **kwargs): - cr.execute('SELECT MIN(website_sequence)-1 FROM product_template') - next_sequence = cr.fetchone()[0] or 10 - return next_sequence - - _defaults = { - 'website_size_x': 1, - 'website_size_y': 1, - 'website_sequence': _defaults_website_sequence, - } - - def set_sequence_top(self, cr, uid, ids, context=None): - cr.execute('SELECT MAX(website_sequence) FROM product_template') - max_sequence = cr.fetchone()[0] or 0 - return self.write(cr, uid, ids, {'website_sequence': max_sequence + 1}, context=context) - - def set_sequence_bottom(self, cr, uid, ids, context=None): - cr.execute('SELECT MIN(website_sequence) FROM product_template') - min_sequence = cr.fetchone()[0] or 0 - return self.write(cr, uid, ids, {'website_sequence': min_sequence -1}, context=context) - - def set_sequence_up(self, cr, uid, ids, context=None): - product = self.browse(cr, uid, ids[0], context=context) - cr.execute(""" SELECT id, website_sequence FROM product_template - WHERE website_sequence > %s AND website_published = %s ORDER BY website_sequence ASC LIMIT 1""" % (product.website_sequence, product.website_published)) - prev = cr.fetchone() - if prev: - self.write(cr, uid, [prev[0]], {'website_sequence': product.website_sequence}, context=context) - return self.write(cr, uid, [ids[0]], {'website_sequence': prev[1]}, context=context) - else: - return self.set_sequence_top(cr, uid, ids, context=context) - - def set_sequence_down(self, cr, uid, ids, context=None): - product = self.browse(cr, uid, ids[0], context=context) - cr.execute(""" SELECT id, website_sequence FROM product_template - WHERE website_sequence < %s AND website_published = %s ORDER BY website_sequence DESC LIMIT 1""" % (product.website_sequence, product.website_published)) - next = cr.fetchone() - if next: - self.write(cr, uid, [next[0]], {'website_sequence': product.website_sequence}, context=context) - return self.write(cr, uid, [ids[0]], {'website_sequence': next[1]}, context=context) - else: - return self.set_sequence_bottom(cr, uid, ids, context=context) - - -class product_product(osv.Model): +class Product(models.Model): _inherit = "product.product" - def website_publish_button(self, cr, uid, ids, context=None): - template_id = self.browse(cr, uid, ids, context=context).product_tmpl_id.id - return self.pool['product.template'].website_publish_button(cr, uid, [template_id], context=context) + @api.multi + def website_publish_button(self): + self.ensure_one() + return self.product_tmpl_id.website_publish_button() -class product_attribute(osv.Model): +class ProductAttribute(models.Model): _inherit = "product.attribute" - _columns = { - 'type': fields.selection([('radio', 'Radio'), ('select', 'Select'), ('color', 'Color'), ('hidden', 'Hidden')], string="Type"), - } - _defaults = { - 'type': lambda *a: 'radio', - } + + type = fields.Selection([('radio', 'Radio'), ('select', 'Select'), ('color', 'Color'), ('hidden', 'Hidden')], default='radio') -class product_attribute_value(osv.Model): +class ProductAttributeValue(models.Model): _inherit = "product.attribute.value" - _columns = { - 'html_color': fields.char("HTML Color Index", oldname='color', 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'."), - } + + html_color = fields.Char(string='HTML Color Index', oldname='color', 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'.") diff --git a/addons/website_sale/models/rating.py b/addons/website_sale/models/rating.py index 05fb7e64989..673299e6df4 100644 --- a/addons/website_sale/models/rating.py +++ b/addons/website_sale/models/rating.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from openerp import fields, models +from odoo import fields, models class Rating(models.Model): diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index dadda193b4f..31319d3d0cf 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -1,59 +1,56 @@ # -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. import random -import openerp -from openerp import SUPERUSER_ID, tools -import openerp.addons.decimal_precision as dp -from openerp.osv import osv, orm, fields -from openerp.addons.web.http import request -from openerp.tools.translate import _ -from openerp.exceptions import UserError +from odoo import api, models, fields, tools, _ +from odoo.http import request +from odoo.exceptions import UserError +import odoo.addons.decimal_precision as dp -class sale_order(osv.Model): +class SaleOrder(models.Model): _inherit = "sale.order" - def _cart_info(self, cr, uid, ids, field_name, arg, context=None): - res = dict() - for order in self.browse(cr, uid, ids, context=context): - res[order.id] = { - 'cart_quantity': int(sum(l.product_uom_qty for l in (order.website_order_line or []))), - 'only_services': all(l.product_id and l.product_id.type in ('service', 'digital') for l in order.website_order_line) - } - return res + website_order_line = fields.One2many( + 'sale.order.line', 'order_id', + string='Order Lines displayed on Website', readonly=True, + help='Order Lines to be displayed on the website. They should not be used for computation purpose.', + ) + cart_quantity = fields.Integer(compute='_compute_cart_info', string='Cart Quantity') + payment_acquirer_id = fields.Many2one('payment.acquirer', string='Payment Acquirer', copy=False) + payment_tx_id = fields.Many2one('payment.transaction', string='Transaction', copy=False) + only_services = fields.Boolean(compute='_compute_cart_info', string='Only Services') - _columns = { - 'website_order_line': fields.one2many( - 'sale.order.line', 'order_id', - string='Order Lines displayed on Website', readonly=True, - help='Order Lines to be displayed on the website. They should not be used for computation purpose.', - ), - 'cart_quantity': fields.function(_cart_info, type='integer', string='Cart Quantity', multi='_cart_info'), - 'payment_acquirer_id': fields.many2one('payment.acquirer', 'Payment Acquirer', on_delete='set null', copy=False), - 'payment_tx_id': fields.many2one('payment.transaction', 'Transaction', on_delete='set null', copy=False), - 'only_services': fields.function(_cart_info, type='boolean', string='Only Services', multi='_cart_info'), - } + @api.multi + @api.depends('website_order_line.product_uom_qty', 'website_order_line.product_id') + def _compute_cart_info(self): + for order in self: + order.cart_quantity = int(sum(order.mapped('website_order_line.product_uom_qty'))) + order.only_services = all(l.product_id.type in ('service', 'digital') for l in order.website_order_line) - def _get_errors(self, cr, uid, order, context=None): + @api.model + def _get_errors(self, order): return [] - def _get_website_data(self, cr, uid, order, context): + @api.model + def _get_website_data(self, order): return { 'partner': order.partner_id.id, 'order': order } - 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): + @api.multi + def _cart_find_product_line(self, product_id=None, line_id=None, **kwargs): + for so in self: domain = [('order_id', '=', so.id), ('product_id', '=', product_id)] if line_id: domain += [('id', '=', line_id)] - return self.pool.get('sale.order.line').search(cr, SUPERUSER_ID, domain, context=context) + return self.env['sale.order.line'].sudo().search(domain).ids - def _website_product_id_change(self, cr, uid, ids, order_id, product_id, qty=0, context=None): - context = dict(context or {}) - order = self.pool['sale.order'].browse(cr, SUPERUSER_ID, order_id, context=context) - product_context = context.copy() + @api.multi + def _website_product_id_change(self, order_id, product_id, qty=0): + order = self.sudo().browse(order_id) + product_context = dict(self.env.context) product_context.update({ 'lang': order.partner_id.lang, 'partner': order.partner_id.id, @@ -61,7 +58,7 @@ class sale_order(osv.Model): 'date': order.date_order, 'pricelist': order.pricelist_id.id, }) - product = self.pool['product.product'].browse(cr, uid, product_id, context=product_context) + product = self.env['product.product'].with_context(product_context).browse(product_id) values = { 'product_id': product_id, @@ -72,28 +69,29 @@ class sale_order(osv.Model): 'price_unit': product.price, } if product.description_sale: - values['name'] += '\n' + product.description_sale + values['name'] += '\n %s' % (product.description_sale) return values - def _cart_update(self, cr, uid, ids, product_id=None, line_id=None, add_qty=0, set_qty=0, context=None, **kwargs): + @api.multi + def _cart_update(self, product_id=None, line_id=None, add_qty=0, set_qty=0, **kwargs): """ Add or set product quantity, add_qty can be negative """ - sol = self.pool.get('sale.order.line') - + SaleOrderLineSudo = self.env['sale.order.line'].sudo() quantity = 0 - for so in self.browse(cr, uid, ids, context=context): + order_line = False + for so in self: if so.state != 'draft': request.session['sale_order_id'] = None raise UserError(_('It is forbidden to modify a sale order which is not in draft status')) if line_id is not False: - line_ids = so._cart_find_product_line(product_id, line_id, context=context, **kwargs) + line_ids = so._cart_find_product_line(product_id, line_id, **kwargs) if line_ids: - line_id = line_ids[0] + order_line = SaleOrderLineSudo.browse(line_ids[0]) # Create line if no line with product_id can be located - if not line_id: - values = self._website_product_id_change(cr, uid, ids, so.id, product_id, qty=1, context=context) - line_id = sol.create(cr, SUPERUSER_ID, values, context=context) - sol._compute_tax_id(cr, SUPERUSER_ID, [line_id], context=context) + if not order_line: + values = self._website_product_id_change(so.id, product_id, qty=1) + order_line = SaleOrderLineSudo.create(values) + order_line._compute_tax_id() if add_qty: add_qty -= 1 @@ -101,145 +99,124 @@ class sale_order(osv.Model): if set_qty: quantity = set_qty elif add_qty is not None: - quantity = sol.browse(cr, SUPERUSER_ID, line_id, context=context).product_uom_qty + (add_qty or 0) + quantity = order_line.product_uom_qty + (add_qty or 0) # Remove zero of negative lines if quantity <= 0: - sol.unlink(cr, SUPERUSER_ID, [line_id], context=context) + order_line.unlink() else: # update line - values = self._website_product_id_change(cr, uid, ids, so.id, product_id, qty=quantity, context=context) - sol.write(cr, SUPERUSER_ID, [line_id], values, context=context) + values = self._website_product_id_change(so.id, product_id, qty=quantity) + order_line.write(values) - return {'line_id': line_id, 'quantity': quantity} + return {'line_id': order_line.id, 'quantity': quantity} - def _cart_accessories(self, cr, uid, ids, context=None): - for order in self.browse(cr, uid, ids, context=context): - s = set(j.id for l in (order.website_order_line or []) for j in (l.product_id.accessory_product_ids or []) if j.website_published) - s -= set(l.product_id.id for l in order.order_line) - product_ids = random.sample(s, min(len(s), 3)) - return self.pool['product.product'].browse(cr, uid, product_ids, context=context) + def _cart_accessories(self): + """ Suggest accessories based on 'Accessory Products' of products in cart """ + for order in self: + accessory_products = order.website_order_line.mapped('product_id.accessory_product_ids').filtered(lambda product: product.website_published) + accessory_products -= order.website_order_line.mapped('product_id') + return random.sample(accessory_products, min(len(accessory_products), 3)) -class sale_order_line(osv.Model): + +class SaleOrderLine(models.Model): _inherit = "sale.order.line" - def _fnct_get_discounted_price(self, cr, uid, ids, field_name, args, context=None): - res = dict.fromkeys(ids, False) - for line in self.browse(cr, uid, ids, context=context): - res[line.id] = (line.price_unit * (1.0 - (line.discount or 0.0) / 100.0)) - return res + discounted_price = fields.Float(compute='_compute_discounted_price', digits_compute=dp.get_precision('Product Price')) - _columns = { - 'discounted_price': fields.function(_fnct_get_discounted_price, string='Discounted price', type='float', digits_compute=dp.get_precision('Product Price')), - } + @api.depends('price_unit', 'discount') + def _compute_discounted_price(self): + for line in self: + line.discounted_price = line.price_unit * (1.0 - line.discount / 100.0) -class website(orm.Model): +class Website(models.Model): _inherit = 'website' - def _get_pricelist_id(self, cr, uid, ids, name, args, context=None): - res = {} - for data in self.browse(cr, uid, ids, context=context): - pricelist = self.get_current_pricelist(cr, uid, context=dict(context, website_id=data.id)) - res[data.id] = pricelist.id - return res + pricelist_id = fields.Many2one('product.pricelist', compute='_compute_pricelist_id', string='Default Pricelist') + currency_id = fields.Many2one('res.currency', related='pricelist_id.currency_id', string='Default Currency') + salesperson_id = fields.Many2one('res.users', string='Salesperson') + salesteam_id = fields.Many2one('crm.team', string='Sales Team') + website_pricelist_ids = fields.One2many('website_pricelist', 'website_id', + string='Price list available for this Ecommerce/Website') - _columns = { - 'pricelist_id': fields.function(_get_pricelist_id,\ - type='many2one', relation="product.pricelist", string='Default Pricelist'), - 'currency_id': fields.related( - 'pricelist_id', 'currency_id', - type='many2one', relation='res.currency', string='Default Currency'), - 'salesperson_id': fields.many2one('res.users', 'Salesperson'), - 'salesteam_id': fields.many2one('crm.team', 'Sales Team'), - 'website_pricelist_ids': fields.one2many('website_pricelist', 'website_id', - string='Price list available for this Ecommerce/Website'), - } + @api.multi + def _compute_pricelist_id(self): + for website in self: + website.pricelist_id = website.with_context(website_id=website.id).get_current_pricelist() - @tools.ormcache('uid', 'country_code', 'show_visible', 'website_pl', 'current_pl', 'all_pl') - def _get_pl(self, cr, uid, country_code, show_visible, website_pl, current_pl, all_pl): + @tools.ormcache('self.env.uid', 'country_code', 'show_visible', 'website_pl', 'current_pl', 'all_pl') + def _get_pl(self, country_code, show_visible, website_pl, current_pl, all_pl): """ Return the list of pricelists that can be used on website for the current user. - :param str country_code: code iso or False, If set, we search only price list available for this country :param bool show_visible: if True, we don't display pricelist where selectable is False (Eg: Code promo) :param int website_pl: The default pricelist used on this website :param int current_pl: The current pricelist used on the website (If not selectable but the current pricelist we had this pricelist anyway) :param list all_pl: List of all pricelist available for this website - :returns: list of pricelist ids """ - pcs = [] - + pricelists = self.env['product.pricelist'] if country_code: - groups = self.pool['res.country.group'].search(cr, uid, [('country_ids.code', '=', country_code)]) - for cgroup in self.pool['res.country.group'].browse(cr, uid, groups): - for pll in cgroup.website_pricelist_ids: - if not show_visible or pll.selectable or pll.pricelist_id.id == current_pl: - pcs.append(pll.pricelist_id) + for cgroup in self.env['res.country.group'].search([('country_ids.code', '=', country_code)]): + for group_pricelists in cgroup.website_pricelist_ids: + if not show_visible or group_pricelists.selectable or group_pricelists.pricelist_id.id == current_pl: + pricelists |= group_pricelists.pricelist_id - if not pcs: # no pricelist for this country, or no GeoIP - pcs = [pll.pricelist_id for pll in all_pl - if not show_visible or pll.selectable or pll.pricelist_id.id == current_pl] + if not pricelists: # no pricelist for this country, or no GeoIP + pricelists |= all_pl.filtered(lambda pl: not show_visible or pl.selectable or pl.pricelist_id.id == current_pl).mapped('pricelist_id') - partner = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid).partner_id - if not pcs or partner.property_product_pricelist.id != website_pl: - pcs.append(partner.property_product_pricelist) - # remove duplicates and sort by name - pcs = sorted(set(pcs), key=lambda pl: pl.name) - return [pl.id for pl in pcs] + partner = self.env.user.partner_id + if not pricelists or partner.property_product_pricelist.id != website_pl: + pricelists |= partner.property_product_pricelist - def get_pricelist_available(self, cr, uid, show_visible=False, context=None): + return pricelists.sorted(lambda pl: pl.name) + + def get_pricelist_available(self, show_visible=False): """ Return the list of pricelists that can be used on website for the current user. Country restrictions will be detected with GeoIP (if installed). - - :param str country_code: code iso or False, If set, we search only price list available for this country :param bool show_visible: if True, we don't display pricelist where selectable is False (Eg: Code promo) - :returns: pricelist recordset """ website = request.website if not request.website: - if context.get('website_id'): - website_id = context['website_id'] + if self.env.context.get('website_id'): + website = self.browse(self.env.context['website_id']) else: - website_id = self.search(cr, uid, [], context=context) - website = self.browse(cr, uid, website_id, context=context) + website = self.search([], limit=1) isocountry = request.session.geoip and request.session.geoip.get('country_code') or False - pl_ids = self._get_pl(cr, uid, isocountry, show_visible, - website.user_id.sudo().partner_id.property_product_pricelist.id, + pricelists = website._get_pl(isocountry, show_visible, + website.user_id.partner_id.property_product_pricelist.id, request.session.get('website_sale_current_pl'), website.website_pricelist_ids) - return self.pool['product.pricelist'].browse(cr, uid, pl_ids, context=context) + return pricelists - def is_pricelist_available(self, cr, uid, pl_id, context=None): + def is_pricelist_available(self, pl_id): """ Return a boolean to specify if a specific pricelist can be manually set on the website. Warning: It check only if pricelist is in the 'selectable' pricelists or the current pricelist. - :param int pl_id: The pricelist id to check - :returns: Boolean, True if valid / available """ - return pl_id in [ppl.id for ppl in self.get_pricelist_available(cr, uid, show_visible=False, context=context)] + return pl_id in self.get_pricelist_available(show_visible=False).ids - def get_current_pricelist(self, cr, uid, context=None): + def get_current_pricelist(self): """ :returns: The current pricelist record """ # The list of available pricelists for this user. # If the user is signed in, and has a pricelist set different than the public user pricelist # then this pricelist will always be considered as available - available_pricelists = self.get_pricelist_available(cr, uid, context=context) + available_pricelists = self.get_pricelist_available() if request.session.get('website_sale_current_pl'): # `website_sale_current_pl` is set only if the user specifically chose it: # - Either, he chose it from the pricelist selection # - Either, he entered a coupon code - return self.pool['product.pricelist'].browse(cr, uid, [request.session['website_sale_current_pl']], context=context)[0] + return self.env['product.pricelist'].browse(request.session['website_sale_current_pl']) else: - partner = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context).partner_id + partner = self.env.user.partner_id # If the user has a saved cart, it take the pricelist of this cart, except if # the order is no longer draft (It has already been confirmed, or cancelled, ...) - pl = partner.last_website_so_id and partner.last_website_so_id.state == 'draft' and partner.last_website_so_id.pricelist_id + pl = partner.last_website_so_id.state == 'draft' and partner.last_website_so_id.pricelist_id if not pl: # The pricelist of the user set on its partner form. # If the user is not signed in, it's the public user pricelist @@ -255,70 +232,62 @@ class website(orm.Model): return pl - def sale_product_domain(self, cr, uid, ids, context=None): + @api.multi + def sale_product_domain(self): return [("sale_ok", "=", True)] - def get_partner(self, cr, uid): - return self.pool['res.users'].browse(cr, SUPERUSER_ID, uid).partner_id - - def sale_get_order(self, cr, uid, ids, force_create=False, code=None, update_pricelist=False, force_pricelist=False, context=None): + @api.multi + def sale_get_order(self, force_create=False, code=None, update_pricelist=False, force_pricelist=False): """ Return the current sale order after mofications specified by params. - :param bool force_create: Create sale order if not already existing :param str code: Code to force a pricelist (promo code) If empty, it's a special case to reset the pricelist with the first available else the default. :param bool update_pricelist: Force to recompute all the lines from sale order to adapt the price with the current pricelist. :param int force_pricelist: pricelist_id - if set, we change the pricelist with this one - :returns: browse record for the current sale order """ - partner = self.get_partner(cr, uid) - sale_order_obj = self.pool['sale.order'] + self.ensure_one() + partner = self.env.user.partner_id sale_order_id = request.session.get('sale_order_id') if not sale_order_id: last_order = partner.last_website_so_id - available_pricelists = self.get_pricelist_available(cr, uid, context=context) + available_pricelists = self.get_pricelist_available() # Do not reload the cart of this user last visit if the cart is no longer draft or uses a pricelist no longer available. - sale_order_id = last_order and last_order.state == 'draft' and last_order.pricelist_id in available_pricelists and last_order.id + sale_order_id = last_order.state == 'draft' and last_order.pricelist_id in available_pricelists and last_order.id - sale_order = None # Test validity of the sale_order_id - if sale_order_id and sale_order_obj.exists(cr, SUPERUSER_ID, sale_order_id, context=context): - sale_order = sale_order_obj.browse(cr, SUPERUSER_ID, sale_order_id, context=context) - else: - sale_order_id = None - pricelist_id = request.session.get('website_sale_current_pl') or self.get_current_pricelist(cr, uid, context=context).id + sale_order = self.env['sale.order'].sudo().browse(sale_order_id).exists() if sale_order_id else None + pricelist_id = request.session.get('website_sale_current_pl') or self.get_current_pricelist().id - if force_pricelist and self.pool['product.pricelist'].search_count(cr, uid, [('id', '=', force_pricelist)], context=context): + if self.env['product.pricelist'].browse(force_pricelist).exists(): pricelist_id = force_pricelist request.session['website_sale_current_pl'] = pricelist_id update_pricelist = True # create so if needed - if not sale_order_id and (force_create or code): + if not sale_order and (force_create or code): # TODO cache partner_id session - user_obj = self.pool['res.users'] affiliate_id = request.session.get('affiliate_id') - salesperson_id = affiliate_id if user_obj.exists(cr, SUPERUSER_ID, affiliate_id, context=context) else request.website.salesperson_id.id - for w in self.browse(cr, uid, ids): - addr = partner.address_get(['delivery', 'invoice']) - values = { - 'partner_id': partner.id, - 'pricelist_id': pricelist_id, - 'payment_term_id': partner.property_payment_term_id.id if partner.property_payment_term_id else False, - 'team_id': w.salesteam_id.id, - 'partner_invoice_id': addr['invoice'], - 'partner_shipping_id': addr['delivery'], - 'user_id': salesperson_id or w.salesperson_id.id, - } - sale_order_id = sale_order_obj.create(cr, SUPERUSER_ID, values, context=context) - request.session['sale_order_id'] = sale_order_id - sale_order = sale_order_obj.browse(cr, SUPERUSER_ID, sale_order_id, context=context) + if self.env['res.users'].sudo().browse(affiliate_id).exists(): + salesperson_id = affiliate_id + else: + salesperson_id = request.website.salesperson_id.id + addr = partner.address_get(['delivery', 'invoice']) + sale_order = self.env['sale.order'].sudo().create({ + 'partner_id': partner.id, + 'pricelist_id': pricelist_id, + 'payment_term_id': partner.property_payment_term_id.id, + 'team_id': self.salesteam_id.id, + 'partner_invoice_id': addr['invoice'], + 'partner_shipping_id': addr['delivery'], + 'user_id': salesperson_id or self.salesperson_id.id, + }) + request.session['sale_order_id'] = sale_order.id - if request.website.partner_id.id != partner.id: - self.pool['res.partner'].write(cr, SUPERUSER_ID, partner.id, {'last_website_so_id': sale_order_id}) + if request.website.partner_id.id != partner.id: + partner.write({'last_website_so_id': sale_order_id}) - if sale_order_id: + if sale_order: # check for change of pricelist with a coupon pricelist_id = pricelist_id or partner.property_product_pricelist.id @@ -328,11 +297,11 @@ class website(orm.Model): flag_pricelist = False if pricelist_id != sale_order.pricelist_id.id: flag_pricelist = True - fiscal_position = sale_order.fiscal_position_id and sale_order.fiscal_position_id.id or False + fiscal_position = sale_order.fiscal_position_id.id # change the partner, and trigger the onchange - sale_order_obj.write(cr, SUPERUSER_ID, [sale_order_id], {'partner_id': partner.id}, context=context) - sale_order_obj.onchange_partner_id(cr, SUPERUSER_ID, [sale_order_id], context=context) + sale_order.write({'partner_id': partner.id}) + sale_order.onchange_partner_id() # check the pricelist : update it if the pricelist is not the 'forced' one values = {} @@ -347,17 +316,17 @@ class website(orm.Model): # if values, then make the SO update if values: - sale_order_obj.write(cr, SUPERUSER_ID, [sale_order_id], values, context=context) + sale_order.write(values) # check if the fiscal position has changed with the partner_id update - recent_fiscal_position = sale_order.fiscal_position_id and sale_order.fiscal_position_id.id or False + recent_fiscal_position = sale_order.fiscal_position_id.id if flag_pricelist or recent_fiscal_position != fiscal_position: update_pricelist = True if code and code != sale_order.pricelist_id.code: - pricelist_ids = self.pool['product.pricelist'].search(cr, uid, [('code', '=', code)], limit=1, context=context) - if pricelist_ids: - pricelist_id = pricelist_ids[0] + code_pricelist = self.env['product.pricelist'].search([('code', '=', code)], limit=1) + if code_pricelist: + pricelist_id = code_pricelist.id update_pricelist = True elif code is not None and sale_order.pricelist_id.code: # code is not None when user removes code and click on "Apply" @@ -379,18 +348,17 @@ class website(orm.Model): return sale_order - def sale_get_transaction(self, cr, uid, ids, context=None): - transaction_obj = self.pool.get('payment.transaction') + def sale_get_transaction(self): tx_id = request.session.get('sale_transaction_id') if tx_id: - tx_ids = transaction_obj.search(cr, SUPERUSER_ID, [('id', '=', tx_id), ('state', 'not in', ['cancel'])], context=context) - if tx_ids: - return transaction_obj.browse(cr, SUPERUSER_ID, tx_ids[0], context=context) + transaction = self.env['payment.transaction'].sudo().browse(tx_id) + if transaction.state != 'cancel': + return transaction else: request.session['sale_transaction_id'] = False return False - def sale_reset(self, cr, uid, ids, context=None): + def sale_reset(self): request.session.update({ 'sale_order_id': False, 'sale_transaction_id': False, @@ -398,57 +366,56 @@ class website(orm.Model): }) -class website_pricelist(osv.Model): +class WebsitePricelist(models.Model): _name = 'website_pricelist' _description = 'Website Pricelist' - def _get_display_name(self, cr, uid, ids, name, arg, context=None): - result = {} - for o in self.browse(cr, uid, ids, context=context): - result[o.id] = _("Website Pricelist for %s") % o.pricelist_id.name - return result - - _columns = { - 'name': fields.function(_get_display_name, string='Pricelist Name', type="char"), - 'website_id': fields.many2one('website', string="Website", required=True), - 'selectable': fields.boolean('Selectable', help="Allow the end user to choose this price list"), - 'pricelist_id': fields.many2one('product.pricelist', string='Pricelist'), - 'country_group_ids': fields.many2many('res.country.group', 'res_country_group_website_pricelist_rel', - 'website_pricelist_id', 'res_country_group_id', string='Country Groups'), - } + name = fields.Char('Pricelist Name', compute='_get_display_name', required=True) + website_id = fields.Many2one('website', string="Website", required=True) + selectable = fields.Boolean(help="Allow the end user to choose this price list") + pricelist_id = fields.Many2one('product.pricelist', string='Pricelist') + country_group_ids = fields.Many2many('res.country.group', 'res_country_group_website_pricelist_rel', + 'website_pricelist_id', 'res_country_group_id', string='Country Groups') def clear_cache(self): # website._get_pl() is cached to avoid to recompute at each request the # list of available pricelists. So, we need to invalidate the cache when # we change the config of website price list to force to recompute. - website = self.pool['website'] + website = self.env['website'] website._get_pl.clear_cache(website) - def create(self, cr, uid, data, context=None): - res = super(website_pricelist, self).create(cr, uid, data, context=context) + @api.multi + def _get_display_name(self): + for website_pl in self: + website_pl.name = _("Website Pricelist for %s") % website_pl.pricelist_id.name + + @api.model + def create(self, data): + res = super(WebsitePricelist, self).create(data) self.clear_cache() return res - def write(self, cr, uid, ids, data, context=None): - res = super(website_pricelist, self).write(cr, uid, ids, data, context=context) + @api.multi + def write(self, data): + res = super(WebsitePricelist, self).write(data) self.clear_cache() return res - def unlink(self, cr, uid, ids, context=None): - res = super(website_pricelist, self).unlink(cr, uid, ids, context=context) + @api.multi + def unlink(self): + res = super(WebsitePricelist, self).unlink() self.clear_cache() return res -class CountryGroup(osv.Model): +class ResCountryGroup(models.Model): _inherit = 'res.country.group' - _columns = { - 'website_pricelist_ids': fields.many2many('website_pricelist', 'res_country_group_website_pricelist_rel', - 'res_country_group_id', 'website_pricelist_id', string='Website Price Lists'), - } + + website_pricelist_ids = fields.Many2many('website_pricelist', 'res_country_group_website_pricelist_rel', + 'res_country_group_id', 'website_pricelist_id', string='Website Price Lists') -class res_partner(openerp.models.Model): +class ResPartner(models.Model): _inherit = 'res.partner' - last_website_so_id = openerp.fields.Many2one('sale.order', 'Last Online Sale Order') + last_website_so_id = fields.Many2one('sale.order', string='Last Online Sale Order') diff --git a/addons/website_sale/res_config.py b/addons/website_sale/res_config.py index dfdb2b510b6..0c386a2cbd2 100644 --- a/addons/website_sale/res_config.py +++ b/addons/website_sale/res_config.py @@ -1,7 +1,9 @@ -# -*- encoding: utf-8 -*- -from openerp import models, fields +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo import models, fields -class website_config_settings(models.TransientModel): + +class WebsiteConfigSettings(models.TransientModel): _inherit = 'website.config.settings' salesperson_id = fields.Many2one('res.users', related='website_id.salesperson_id', string='Salesperson') diff --git a/addons/website_sale/security/website_sale.xml b/addons/website_sale/security/website_sale.xml index 2abc25f925c..48769f8c34c 100644 --- a/addons/website_sale/security/website_sale.xml +++ b/addons/website_sale/security/website_sale.xml @@ -1,6 +1,6 @@ - - + + Public product template @@ -22,5 +22,5 @@ - - + + diff --git a/addons/website_sale/tests/test_sale_process.py b/addons/website_sale/tests/test_sale_process.py index 0d685bd4abb..b3ff03802b9 100644 --- a/addons/website_sale/tests/test_sale_process.py +++ b/addons/website_sale/tests/test_sale_process.py @@ -1,8 +1,11 @@ -import openerp.tests +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +import odoo.tests -@openerp.tests.common.at_install(False) -@openerp.tests.common.post_install(True) -class TestUi(openerp.tests.HttpCase): + +@odoo.tests.common.at_install(False) +@odoo.tests.common.post_install(True) +class TestUi(odoo.tests.HttpCase): def test_01_admin_shop_tour(self): self.phantom_js("/", "odoo.__DEBUG__.services['web.Tour'].run('shop', 'test')", "odoo.__DEBUG__.services['web.Tour'].tours.shop", login="admin") diff --git a/addons/website_sale/tests/test_website_sale_pricelist.py b/addons/website_sale/tests/test_website_sale_pricelist.py index 159d8f599e5..4ebfdc3fc44 100644 --- a/addons/website_sale/tests/test_website_sale_pricelist.py +++ b/addons/website_sale/tests/test_website_sale_pricelist.py @@ -1,29 +1,31 @@ -from openerp.tests.common import TransactionCase +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. from mock import patch +from odoo.tests.common import TransactionCase class TestWebsitePriceList(TransactionCase): # Mock nedded because request.session doesn't exist during test - def _get_pricelist_available(self, cr, uid, show_visible=False, context=None): - return self.get_pl(context.get('show'), context.get('current_pl'), context.get('country')) + def _get_pricelist_available(self, show_visible=False): + return self.get_pl(self.args.get('show'), self.args.get('current_pl'), self.args.get('country')) def setUp(self): super(TestWebsitePriceList, self).setUp() - self.website = self.registry('website').browse(self.cr, self.uid, 1) - self.website.pricelist_id = self.registry('ir.model.data').xmlid_to_res_id(self.cr, self.uid, 'product.list0') - self.patcher = patch('openerp.addons.website_sale.models.sale_order.website.get_pricelist_available', wraps=self._get_pricelist_available) + self.website = self.env['website'].browse(1) + self.website.pricelist_id = self.ref('product.list0') + self.patcher = patch('odoo.addons.website_sale.models.sale_order.Website.get_pricelist_available', wraps=self._get_pricelist_available) self.mock_get_pricelist_available = self.patcher.start() def get_pl(self, show, current_pl, country): - pl_ids = self.website._get_pl( + pls = self.website._get_pl( country, show, self.website.pricelist_id.id, current_pl, self.website.website_pricelist_ids ) - return self.env['product.pricelist'].browse(pl_ids) + return pls def test_get_pricelist_available_show(self): show = True @@ -57,8 +59,8 @@ class TestWebsitePriceList(TransactionCase): self.assertEquals(len(pls), result) def test_get_pricelist_available_promocode(self): - christmas_pl = self.registry('ir.model.data').xmlid_to_res_id(self.cr, self.uid, 'website_sale.list_christmas') - context = { + christmas_pl = self.ref('website_sale.list_christmas') + self.args = { 'show': True, 'current_pl': christmas_pl, } @@ -71,8 +73,9 @@ class TestWebsitePriceList(TransactionCase): } for country, result in country_list.items(): - context['country'] = country - available = self.website.with_context(context).is_pricelist_available(christmas_pl) + self.args['country'] = country + # mock patch method could not pass env context + available = self.website.is_pricelist_available(christmas_pl) if result: self.assertTrue(available) else: diff --git a/addons/website_sale/views/payment.xml b/addons/website_sale/views/payment.xml index f4bec2323bb..994ed13adad 100644 --- a/addons/website_sale/views/payment.xml +++ b/addons/website_sale/views/payment.xml @@ -1,6 +1,5 @@ - - + payment.transaction.form.website_sale @@ -13,5 +12,4 @@ - - + diff --git a/addons/website_sale/views/report_shop_saleorder.xml b/addons/website_sale/views/report_shop_saleorder.xml index aea2a8e5fae..823c16d32ae 100644 --- a/addons/website_sale/views/report_shop_saleorder.xml +++ b/addons/website_sale/views/report_shop_saleorder.xml @@ -1,6 +1,5 @@ - - + - - + diff --git a/addons/website_sale/views/sale_order.xml b/addons/website_sale/views/sale_order.xml index 2568ec21603..a77bc74de35 100644 --- a/addons/website_sale/views/sale_order.xml +++ b/addons/website_sale/views/sale_order.xml @@ -1,6 +1,5 @@ - - + sale.order.form.acquirer @@ -13,6 +12,4 @@ - - - + diff --git a/addons/website_sale/views/snippets.xml b/addons/website_sale/views/snippets.xml index e4a65d5e043..9c876c331b7 100644 --- a/addons/website_sale/views/snippets.xml +++ b/addons/website_sale/views/snippets.xml @@ -1,6 +1,5 @@ - - + - - \ No newline at end of file + \ No newline at end of file diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index c09870eb137..dce989710d8 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -1,6 +1,5 @@ - - + @@ -1494,5 +1493,4 @@ - - + diff --git a/addons/website_sale_delivery/controllers/main.py b/addons/website_sale_delivery/controllers/main.py index f03a524dfcb..5612c164fe0 100644 --- a/addons/website_sale_delivery/controllers/main.py +++ b/addons/website_sale_delivery/controllers/main.py @@ -2,15 +2,15 @@ import openerp from openerp import http from openerp.http import request -import openerp.addons.website_sale.controllers.main +from openerp.addons.website_sale.controllers.main import WebsiteSale -class website_sale(openerp.addons.website_sale.controllers.main.website_sale): +class website_sale(WebsiteSale): @http.route(['/shop/payment'], type='http', auth="public", website=True) def payment(self, **post): cr, uid, context = request.cr, request.uid, request.context - order = request.website.sale_get_order(context=context) + order = request.website.sale_get_order() carrier_id = post.get('carrier_id') if carrier_id: carrier_id = int(carrier_id) diff --git a/addons/website_sale_digital/controllers/main.py b/addons/website_sale_digital/controllers/main.py index 3f315dedb38..391f81c060c 100644 --- a/addons/website_sale_digital/controllers/main.py +++ b/addons/website_sale_digital/controllers/main.py @@ -4,12 +4,12 @@ import base64 from openerp.addons.web import http from openerp.addons.web.http import request from openerp.addons.website_portal.controllers.main import website_account -from openerp.addons.website_sale.controllers.main import website_sale +from openerp.addons.website_sale.controllers.main import WebsiteSale from cStringIO import StringIO from werkzeug.utils import redirect -class website_sale_digital_confirmation(website_sale): +class website_sale_digital_confirmation(WebsiteSale): @http.route([ '/shop/confirmation', diff --git a/addons/website_sale_options/controllers/main.py b/addons/website_sale_options/controllers/main.py index b564b48b536..f0325c683ee 100644 --- a/addons/website_sale_options/controllers/main.py +++ b/addons/website_sale_options/controllers/main.py @@ -3,9 +3,9 @@ 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 +from openerp.addons.website_sale.controllers.main import WebsiteSale -class website_sale_options(website_sale): +class website_sale_options(WebsiteSale): @http.route(['/shop/product/'], type='http', auth="public", website=True) def product(self, product, category='', search='', **kwargs):