From 3805a863023838a4a4419a5a276603b79513d494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 17 Dec 2013 16:26:05 +0100 Subject: [PATCH 1/9] [IMP] website_sale_delivery: make the module effectively works. - added JS / HTML to handle the choice of the carrier - added override of payment controller in website_sale_delivery to handle the carrier and recompute the amount and details - some code cleaning - added amount_delivery field in sale.order, that is the amount related to the delivery bzr revid: tde@openerp.com-20131217152605-0xoulnnlo0nn7j9o --- addons/delivery/sale.py | 54 ++++++++++++++----- addons/website_sale/controllers/main.py | 3 +- addons/website_sale/models/sale_order.py | 11 ++-- addons/website_sale/views/website_sale.xml | 18 ++++--- addons/website_sale_delivery/__init__.py | 1 + .../models/sale_order.py | 19 ++++--- .../static/src/js/website_sale_delivery.js | 9 ++++ .../views/website_sale_delivery.xml | 54 +++++++++++++------ 8 files changed, 120 insertions(+), 49 deletions(-) diff --git a/addons/delivery/sale.py b/addons/delivery/sale.py index f43eb1c0a30..5b9f6fbf595 100644 --- a/addons/delivery/sale.py +++ b/addons/delivery/sale.py @@ -20,22 +20,55 @@ ############################################################################## import time -from openerp.osv import fields,osv + +from openerp.addons import decimal_precision +from openerp.addons.sale.sale import sale_order as OriginalSaleOrder +from openerp.osv import fields, osv from openerp.tools.translate import _ + class sale_order_line(osv.osv): _inherit = 'sale.order.line' + _columns = { - 'is_delivery':fields.boolean("Is a Delivery"), + 'is_delivery': fields.boolean("Is a Delivery"), } + _defaults = { 'is_delivery': False } -class sale_order(osv.osv): + +class sale_order(osv.Model): _inherit = 'sale.order' + + def _amount_all(self, cr, uid, ids, field_name, arg, context=None): + res = super(sale_order, self)._amount_all(cr, uid, ids, field_name, arg, context=context) + Currency = self.pool.get('res.currency') + for order in self.browse(cr, uid, ids, context=context): + line_amount = sum([line.price_subtotal for line in order.order_line if line.is_delivery]) + currency = order.pricelist_id.currency_id + res[order.id]['amount_delivery'] = Currency.round(cr, uid, currency, line_amount) + return res + + def _get_order(self, cr, uid, ids, context=None): + result = {} + for line in self.pool.get('sale.order.line').browse(cr, uid, ids, context=context): + result[line.order_id.id] = True + return result.keys() + _columns = { - 'carrier_id':fields.many2one("delivery.carrier", "Delivery Method", help="Complete this field if you plan to invoice the shipping based on picking."), + 'carrier_id': fields.many2one( + "delivery.carrier", string="Delivery Method", + help="Complete this field if you plan to invoice the shipping based on picking."), + 'amount_delivery': fields.function( + _amount_all, type='float', digits_compute=decimal_precision.get_precision('Account'), string='Delivery Amount', + store={ + 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10), + 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), + }, + multi='sums', help="The amount without tax.", track_visibility='always' + ), } def onchange_partner_id(self, cr, uid, ids, part, context=None): @@ -57,7 +90,6 @@ class sale_order(osv.osv): return True def delivery_set(self, cr, uid, ids, context=None): - order_obj = self.pool.get('sale.order') line_obj = self.pool.get('sale.order.line') grid_obj = self.pool.get('delivery.grid') carrier_obj = self.pool.get('delivery.carrier') @@ -85,14 +117,10 @@ class sale_order(osv.osv): 'product_uom': grid.carrier_id.product_id.uom_id.id, 'product_id': grid.carrier_id.product_id.id, 'price_unit': grid_obj.get_price(cr, uid, grid.id, order, time.strftime('%Y-%m-%d'), context), - 'tax_id': [(6,0,taxes_ids)], + 'tax_id': [(6, 0, taxes_ids)], 'type': 'make_to_stock', 'is_delivery': True }) - #remove the value of the carrier_id field on the sale order - return self.write(cr, uid, ids, {'carrier_id': False}, context=context) - #return {'type': 'ir.actions.act_window_close'} action reload? - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - + # remove the value of the carrier_id field on the sale order + # TDE NOTE: why removing it ?? seems weird + # return self.write(cr, uid, ids, {'carrier_id': False}, context=context) diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 0e3fedb1814..914a736abce 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -589,8 +589,7 @@ class Ecommerce(http.Controller): # alread a transaction: forward to confirmation tx = context.get('website_sale_transaction') if tx and not tx.state == 'draft': - print 'embetatn' - # return request.redirect('/shop/confirmation/%s' % order.id) + return request.redirect('/shop/confirmation/%s' % order.id) partner_id = False shipping_partner_id = False diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index 1b783dd9a83..7a899774598 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -22,7 +22,8 @@ from openerp import SUPERUSER_ID from openerp.osv import osv, fields -class sale_order(osv.Model): + +class SaleOrder(osv.Model): _inherit = "sale.order" _columns = { @@ -30,17 +31,21 @@ class sale_order(osv.Model): } def _get_website_data(self, cr, uid, order, context): - return {} + return { + 'partner': order.partner_id.id, + 'order': order + } def get_total_quantity(self, cr, uid, ids, context=None): order = self.browse(cr, uid, ids[0], context=context) return int(sum(l.product_uom_qty for l in (order.order_line or []))) -class sale_order_line(osv.Model): +class SaleOrderLine(osv.Model): _inherit = "sale.order.line" def _recalculate_product_values(self, cr, uid, ids, product_id=0, context=None): + # TDE FIXME: seems to be defined several times -> fix me ? if context is None: context = {} user_obj = self.pool.get('res.users') diff --git a/addons/website_sale/views/website_sale.xml b/addons/website_sale/views/website_sale.xml index 1e69db2c684..5aee67697cf 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -467,13 +467,13 @@ - +

Total:

- + Taxes: +

+ }'/> + - + Taxes: + + + + Delivery: + + + + + + - - -

Choose Your Delivery method:

-
    -
  • - -
  • -
-
- +
+

Choose your Delivery Method

+
+
    +
  • + +
  • +
+
+
+ +
+
- + From e701544aa4520b5d63aa5769296d25ddffd32207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 17 Dec 2013 17:39:12 +0100 Subject: [PATCH 2/9] [CLEAN] website_sale_delivery: removed dead JS code bzr revid: tde@openerp.com-20131217163912-4br0aar371qvr5cs --- .../static/src/js/website_sale_delivery.js | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/addons/website_sale_delivery/static/src/js/website_sale_delivery.js b/addons/website_sale_delivery/static/src/js/website_sale_delivery.js index 037847c0625..43a2eeb4c1a 100644 --- a/addons/website_sale_delivery/static/src/js/website_sale_delivery.js +++ b/addons/website_sale_delivery/static/src/js/website_sale_delivery.js @@ -1,26 +1,5 @@ $(document).ready(function () { - // $('.js_nav_month a:first').on('click', function (e) { - // e.preventDefault(); - // var $ul = $(this).next("ul"); - // if (!$ul.find('li').length) { - // // TODO: Why POST? (to pass the domain) A GET would be more appropriate... - // // This should be done server side anyway... - // $.post('/blog/nav', {'domain': $(this).data("domain")}, function (result) { - // var blog_id = +window.location.pathname.split("/").pop(); - // $(JSON.parse(result)).each(function () { - // var $li = $($.parseHTML(this.fragment)); - // if (blog_id == this.id) $li.addClass("active"); - // if (!this.website_published) $li.find('a').css("color", "red"); - // $ul.append($li); - // }); - - // }); - // } else { - // $ul.toggle(); - // } - // }); - // When choosing an delivery carrier, update the quotation and the acquirers var $carrier = $("#delivery_carrier"); $carrier.find("input[name='delivery_type']").click(function (ev) { From ba73588c6ce399e502febd9c20b385fdaeb1e7e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 17 Dec 2013 17:59:31 +0100 Subject: [PATCH 3/9] [FIX] email_template: fixed default_get, not possible ot render when we do not have any data about the record used for rendering bzr revid: tde@openerp.com-20131217165931-muxsi0hwhihcvo1f --- addons/email_template/wizard/mail_compose_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/email_template/wizard/mail_compose_message.py b/addons/email_template/wizard/mail_compose_message.py index 4c1e19abbe9..db6485d19ae 100644 --- a/addons/email_template/wizard/mail_compose_message.py +++ b/addons/email_template/wizard/mail_compose_message.py @@ -46,7 +46,7 @@ class mail_compose_message(osv.TransientModel): if context is None: context = {} res = super(mail_compose_message, self).default_get(cr, uid, fields, context=context) - if res.get('composition_mode') != 'mass_mail' and context.get('default_template_id'): + if res.get('composition_mode') != 'mass_mail' and context.get('default_template_id') and res.get('model') and res.get('res_id'): res.update( self.onchange_template_id( cr, uid, [], context['default_template_id'], res.get('composition_mode'), From e883b15c485a3bbf535e709dbf69848b6ab82188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Dec 2013 14:19:42 +0100 Subject: [PATCH 4/9] [FIX] sale, delivery: _amount_all function used in function field is now wrapper. Indeed as we give the function pointer when creating the fields, inheritance is not taken into account. This is done by adding a wrapper that calls _amount_all, using this time correct inheritance. bzr revid: tde@openerp.com-20131218131942-9lzrxbscb4lu4rj8 --- addons/delivery/sale.py | 11 +++++++++-- addons/sale/sale.py | 10 +++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/addons/delivery/sale.py b/addons/delivery/sale.py index 5b9f6fbf595..f60e37c4ebf 100644 --- a/addons/delivery/sale.py +++ b/addons/delivery/sale.py @@ -42,6 +42,10 @@ class sale_order_line(osv.osv): class sale_order(osv.Model): _inherit = 'sale.order' + def _amount_all_wrapper(self, cr, uid, ids, field_name, arg, context=None): + """ Wrapper because of direct method passing as parameter for function fields """ + return self._amount_all(cr, uid, ids, field_name, arg, context=context) + def _amount_all(self, cr, uid, ids, field_name, arg, context=None): res = super(sale_order, self)._amount_all(cr, uid, ids, field_name, arg, context=context) Currency = self.pool.get('res.currency') @@ -62,7 +66,8 @@ class sale_order(osv.Model): "delivery.carrier", string="Delivery Method", help="Complete this field if you plan to invoice the shipping based on picking."), 'amount_delivery': fields.function( - _amount_all, type='float', digits_compute=decimal_precision.get_precision('Account'), string='Delivery Amount', + _amount_all_wrapper, type='float', digits_compute=decimal_precision.get_precision('Account'), + string='Delivery Amount', store={ 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10), 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), @@ -75,7 +80,9 @@ class sale_order(osv.Model): result = super(sale_order, self).onchange_partner_id(cr, uid, ids, part, context=context) if part: dtype = self.pool.get('res.partner').browse(cr, uid, part, context=context).property_delivery_carrier.id - result['value']['carrier_id'] = dtype + # TDE NOTE: not sure the aded 'if dtype' is valid + if dtype: + result['value']['carrier_id'] = dtype return result def _prepare_order_picking(self, cr, uid, order, context=None): diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 88cd20bd786..14e5cae3ad6 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -58,6 +58,10 @@ class sale_order(osv.osv): val += c.get('amount', 0.0) return val + def _amount_all_wrapper(self, cr, uid, ids, field_name, arg, context=None): + """ Wrapper because of direct method passing as parameter for function fields """ + return self._amount_all(cr, uid, ids, field_name, arg, context=context) + def _amount_all(self, cr, uid, ids, field_name, arg, context=None): cur_obj = self.pool.get('res.currency') res = {} @@ -199,19 +203,19 @@ class sale_order(osv.osv): fnct_search=_invoiced_search, type='boolean', help="It indicates that sales order has at least one invoice."), 'note': fields.text('Terms and conditions'), - 'amount_untaxed': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Untaxed Amount', + 'amount_untaxed': fields.function(_amount_all_wrapper, digits_compute=dp.get_precision('Account'), string='Untaxed Amount', store={ 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10), 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), }, multi='sums', help="The amount without tax.", track_visibility='always'), - 'amount_tax': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Taxes', + 'amount_tax': fields.function(_amount_all_wrapper, digits_compute=dp.get_precision('Account'), string='Taxes', store={ 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10), 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), }, multi='sums', help="The tax amount."), - 'amount_total': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Total', + 'amount_total': fields.function(_amount_all_wrapper, digits_compute=dp.get_precision('Account'), string='Total', store={ 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10), 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), From 7a564f261ed67c4317aaac62b3d58c700c26b02f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Dec 2013 14:21:50 +0100 Subject: [PATCH 5/9] [IMP] [FIX] [ADD] website_sale, website_sale_delivery: delivery + ecommerce code cleaning - added a website_order_line on sale.order model, that are the lines to display and use in the front-end. This allows to display only products (and not delivery costs), and to have a correct badge for the number od products. - cleaned ecommerce website code and method name. Still some cleaning to do I think. - fixed some issues in partner and shipping info management, now using CheckoutInfo more cleanly - fixed the process of choosing the carrier bzr revid: tde@openerp.com-20131218132150-f4br0099p2caf43i --- addons/website_event/models/__init__.py | 1 + addons/website_event/models/event.py | 8 - addons/website_event/models/website.py | 10 + addons/website_sale/controllers/main.py | 78 ++++---- addons/website_sale/models/sale_order.py | 29 +-- addons/website_sale/models/website.py | 177 ++++++++++-------- addons/website_sale/views/website_sale.xml | 18 +- .../controllers/__init__.py | 1 + .../website_sale_delivery/controllers/main.py | 22 +++ .../website_sale_delivery/models/__init__.py | 1 + .../models/sale_order.py | 34 ++-- .../website_sale_delivery/models/website.py | 21 +++ 12 files changed, 233 insertions(+), 167 deletions(-) create mode 100644 addons/website_event/models/website.py create mode 100644 addons/website_sale_delivery/controllers/__init__.py create mode 100644 addons/website_sale_delivery/controllers/main.py create mode 100644 addons/website_sale_delivery/models/website.py diff --git a/addons/website_event/models/__init__.py b/addons/website_event/models/__init__.py index e7c003730cf..cb598ddaa69 100644 --- a/addons/website_event/models/__init__.py +++ b/addons/website_event/models/__init__.py @@ -1 +1,2 @@ import event +import website diff --git a/addons/website_event/models/event.py b/addons/website_event/models/event.py index d970823944c..5d42243a053 100644 --- a/addons/website_event/models/event.py +++ b/addons/website_event/models/event.py @@ -139,11 +139,3 @@ class sale_order_line(osv.osv): res.update({'price_unit': order_line.event_ticket_id.price}) return res - - -class Website(orm.Model): - _inherit = 'website' - - def get_website_sale_domain(self): - # remove product event from the website content grid and list view (not removed in detail view) - return super(Website, self).get_website_sale_domain() + [('event_ok', '=', False)] \ No newline at end of file diff --git a/addons/website_event/models/website.py b/addons/website_event/models/website.py new file mode 100644 index 00000000000..e41a7092404 --- /dev/null +++ b/addons/website_event/models/website.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +from openerp.osv import orm + + +class Website(orm.Model): + _inherit = 'website' + + def ecommerce_get_product_domain(self): + # remove product event from the website content grid and list view (not removed in detail view) + return ['&'] + super(Website, self).ecommerce_get_product_domain() + [('event_ok', '=', False)] diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 914a736abce..e97b5ff649f 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -11,13 +11,15 @@ from openerp.addons.website.models import website PPG = 20 # Products Per Page PPR = 4 # Products Per Row + class CheckoutInfo(object): mandatory_billing_fields = ["name", "phone", "email", "street", "city", "country_id", "zip"] optional_billing_fields = ["company", "state_id"] string_billing_fields = ["name", "phone", "email", "street", "city", "zip"] + mandatory_shipping_fields = ["shipping_name", "shipping_phone", "shipping_street", "shipping_city", "shipping_country_id", "shipping_zip"] - string_shipping_fields = ["shipping_name", "shipping_phone", "shipping_street", "shipping_city", "shipping_zip"] optional_shipping_field = ["shipping_state_id"] + string_shipping_fields = ["shipping_name", "shipping_phone", "shipping_street", "shipping_city", "shipping_zip"] def mandatory_fields(self): return self.mandatory_billing_fields + self.mandatory_shipping_fields @@ -31,11 +33,16 @@ class CheckoutInfo(object): def empty(self): return dict.fromkeys(self.all_fields(), '') - def from_partner(self, partner): - result = dict((field_name, getattr(partner, field_name)) for field_name in self.string_billing_fields if getattr(partner, field_name)) - result['state_id'] = partner.state_id and partner.state_id.id or '' - result['country_id'] = partner.country_id and partner.country_id.id or '' - result['company'] = partner.parent_id and partner.parent_id.name or '' + def from_partner(self, partner, address_type='billing'): + assert address_type in ('billing', 'shipping') + if address_type == 'billing': + prefix = '' + else: + prefix = 'shipping_' + result = dict((prefix + field_name, getattr(partner, field_name)) for field_name in self.string_billing_fields if getattr(partner, field_name)) + result[prefix + 'state_id'] = partner.state_id and partner.state_id.id or '' + result[prefix + 'country_id'] = partner.country_id and partner.country_id.id or '' + result[prefix + 'company'] = partner.parent_id and partner.parent_id.name or '' return result def from_post(self, post): @@ -101,17 +108,23 @@ class table_compute(object): rows[col] = map(lambda x: x[1], cols) return filter(bool, rows) + class Ecommerce(http.Controller): _order = 'website_published desc, website_sequence desc' def get_attribute_ids(self): - attributes_obj = request.registry.get('product.attribute') + attributes_obj = request.registry['product.attribute'] attributes_ids = attributes_obj.search(request.cr, request.uid, [], context=request.context) return attributes_obj.browse(request.cr, request.uid, attributes_ids, context=request.context) def get_pricelist(self): - return request.registry.get('website').get_pricelist_id(request.cr, request.uid, None, context=request.context) + """ Shortcut to get the pricelist from the website model """ + return request.registry['website'].ecommerce_get_pricelist_id(request.cr, request.uid, None, context=request.context) + + def get_order(self): + """ Shortcut to get the current ecommerce quotation from the website model """ + return request.registry['website'].ecommerce_get_current_order(request.cr, request.uid, context=request.context) def get_products(self, product_ids): product_obj = request.registry.get('product.template') @@ -179,7 +192,7 @@ class Ecommerce(http.Controller): @website.route(['/shop/pricelist'], type='http', auth="public", multilang=True) def shop_promo(self, code, **post): assert code, 'No pricelist code provided' - request.registry.get('website').change_pricelist_id(request.cr, request.uid, code, context=request.context) + request.registry['website']._ecommerce_change_pricelist(request.cr, request.uid, code=code, context=request.context) return request.redirect("/shop") @website.route([ @@ -191,7 +204,7 @@ class Ecommerce(http.Controller): def shop(self, category=0, page=0, filters='', search='', **post): cr, uid, context = request.cr, request.uid, request.context product_obj = request.registry.get('product.template') - domain = request.registry.get('website').get_website_sale_domain() + domain = request.registry.get('website').ecommerce_get_product_domain() if search: domain += ['|', ('name', 'ilike', "%%%s%%" % search), @@ -286,9 +299,9 @@ class Ecommerce(http.Controller): order_line_obj = request.registry.get('sale.order.line') order_obj = request.registry.get('sale.order') - order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context) + order = self.get_order() if not order: - order = request.registry['website']._get_order(request.cr, request.uid, context=request.context) + order = request.registry['website'].ecommerce_get_new_order(request.cr, request.uid, context=request.context) request.context = dict(request.context, pricelist=self.get_pricelist()) @@ -300,8 +313,9 @@ class Ecommerce(http.Controller): else: order_line_id = None else: - order_line_ids = order_line_obj.search(request.cr, SUPERUSER_ID, - [('order_id', '=', order.id),('product_id', '=', product_id)], context=request.context) + order_line_ids = order_line_obj.search( + request.cr, SUPERUSER_ID, + [('order_id', '=', order.id), ('product_id', '=', product_id)], context=request.context) if order_line_ids: order_line_id = order_line_ids[0] @@ -349,7 +363,7 @@ class Ecommerce(http.Controller): prod_obj = request.registry.get('product.product') # must have a draft sale order with lines at this point, otherwise reset - order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context) + order = self.get_order() if order and order.state != 'draft': request.registry['website'].sale_reset_order(cr, uid, context=context) return request.redirect('/shop/') @@ -396,7 +410,7 @@ class Ecommerce(http.Controller): @website.route(['/shop/add_cart_json/'], type='json', auth="public") def add_cart_json(self, product_id=None, order_line_id=None, remove=None): quantity = self.add_product_to_cart(product_id=product_id, order_line_id=order_line_id, number=(remove and -1 or 1)) - order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context) + order = self.get_order() return [quantity, order.get_total_quantity(), order.amount_total, @@ -411,15 +425,15 @@ class Ecommerce(http.Controller): 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 - order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context) + order = self.get_order() if not order or order.state != 'draft' or not order.order_line: - request.registry['website'].sale_reset_order(cr, uid, context=context) + request.registry['website'].ecommerce_reset(cr, uid, context=context) return request.redirect('/shop/') # if transaction pending / done: redirect to confirmation tx = context.get('website_sale_transaction') if tx and tx.state != 'draft': return request.redirect('/shop/payment/confirmation/%s' % order.id) - + self.get_pricelist() orm_partner = registry.get('res.partner') @@ -452,17 +466,11 @@ class Ecommerce(http.Controller): if partner: partner_info = info.from_partner(partner) checkout.update(partner_info) - shipping_ids = orm_partner.search(cr, SUPERUSER_ID, [("parent_id", "=", partner.id), ('type', "=", 'delivery')], context=context) + shipping_ids = orm_partner.search(cr, SUPERUSER_ID, [("parent_id", "=", partner.id), ('type', "=", 'delivery')], limit=1, context=context) if shipping_ids: values['shipping'] = "true" shipping_partner = orm_partner.browse(cr, SUPERUSER_ID, shipping_ids[0], context) - checkout['shipping_name'] = getattr(shipping_partner, 'name') - checkout['shipping_phone'] = getattr(shipping_partner, 'phone') - checkout['shipping_street'] = getattr(shipping_partner, 'street') - checkout['shipping_zip'] = getattr(shipping_partner, 'zip') - checkout['shipping_city'] = getattr(shipping_partner, 'city') - checkout['shipping_country_id'] = getattr(shipping_partner, 'country_id') - checkout['shipping_state_id'] = getattr(shipping_partner, 'state_id') + checkout.update(info.from_partner(shipping_partner, address_type='shipping')) for field_name in info.mandatory_fields(): if not checkout[field_name]: @@ -476,9 +484,9 @@ class Ecommerce(http.Controller): order_line_obj = request.registry.get('sale.order') # must have a draft sale order with lines at this point, otherwise redirect to shop - order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context) + order = self.get_order() if not order or order.state != 'draft' or not order.order_line: - request.registry['website'].sale_reset_order(cr, uid, context=context) + request.registry['website'].ecommerce_reset(cr, uid, context=context) return request.redirect('/shop/') # if transaction pending / done: redirect to confirmation tx = context.get('website_sale_transaction') @@ -533,6 +541,7 @@ class Ecommerce(http.Controller): shipping_id = None if post.get('shipping_different'): + print post shipping_info = { 'phone': post['shipping_phone'], 'zip': post['shipping_zip'], @@ -547,6 +556,7 @@ class Ecommerce(http.Controller): } domain = [(key, '_id' in key and '=' or 'ilike', '_id' in key and value and int(value) or False) for key, value in shipping_info.items() if key in info.mandatory_billing_fields + ["type", "parent_id"]] + print domain shipping_ids = orm_parter.search(cr, SUPERUSER_ID, domain, context=context) if shipping_ids: @@ -582,9 +592,9 @@ class Ecommerce(http.Controller): payment_obj = request.registry.get('payment.acquirer') # if no sale order at this stage: back to checkout beginning - order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context) + order = self.get_order() if not order or not order.state == 'draft' or not order.order_line: - request.registry['website'].sale_reset_order(cr, uid, context=context) + request.registry['website'].ecommerce_reset(cr, uid, context=context) return request.redirect("/shop/") # alread a transaction: forward to confirmation tx = context.get('website_sale_transaction') @@ -644,7 +654,7 @@ class Ecommerce(http.Controller): cr, uid, context = request.cr, request.uid, request.context payment_obj = request.registry.get('payment.acquirer') transaction_obj = request.registry.get('payment.transaction') - order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context) + order = self.get_order() if not order or not order.order_line or acquirer_id is None: return request.redirect("/shop/checkout/") @@ -711,7 +721,7 @@ class Ecommerce(http.Controller): tx = request.registry['payment.transaction'].browse(cr, uid, transaction_id, context=context) if sale_order_id is None: - order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context) + order = self.get_order() else: order = request.registry['sale.order'].browse(cr, uid, sale_order_id, context=context) @@ -733,7 +743,7 @@ class Ecommerce(http.Controller): request.registry['mail.compose.message'].send_mail(cr, uid, [compose_id], context=create_ctx) # clean context and session, then redirect to the confirmation page - request.registry['website'].sale_reset_order(cr, uid, context=context) + request.registry['website'].ecommerce_reset(cr, uid, context=context) return request.redirect('/shop/confirmation/%s' % order.id) diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index 7a899774598..bc2ca838194 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -1,24 +1,4 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C)-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - from openerp import SUPERUSER_ID from openerp.osv import osv, fields @@ -28,6 +8,11 @@ class SaleOrder(osv.Model): _columns = { 'website_session_id': fields.char('Session UUID4'), + '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.', + ), } def _get_website_data(self, cr, uid, order, context): @@ -36,9 +21,9 @@ class SaleOrder(osv.Model): 'order': order } - def get_total_quantity(self, cr, uid, ids, context=None): + def get_number_of_products(self, cr, uid, ids, context=None): order = self.browse(cr, uid, ids[0], context=context) - return int(sum(l.product_uom_qty for l in (order.order_line or []))) + return int(sum(l.product_uom_qty for l in (order.website_order_line or []))) class SaleOrderLine(osv.Model): diff --git a/addons/website_sale/models/website.py b/addons/website_sale/models/website.py index 9e05be27f82..ba8db704753 100644 --- a/addons/website_sale/models/website.py +++ b/addons/website_sale/models/website.py @@ -10,7 +10,7 @@ class Website(orm.Model): def _get_pricelist(self, cr, uid, ids, field_name, arg, context=None): # FIXME: oh god kill me now - pricelist_id = self.get_pricelist_id(cr, uid, ids, context=context) + pricelist_id = self.ecommerce_get_pricelist_id(cr, uid, ids, context=context) return dict.fromkeys( ids, self.pool['product.pricelist'].browse( cr, uid, pricelist_id, context=context)) @@ -20,86 +20,16 @@ class Website(orm.Model): _get_pricelist, type='many2one', obj='product.pricelist') } - def _get_order(self, cr, uid, order_id=None, context=None): - order_obj = self.pool.get('sale.order') - # check if order allready exists and have access - if order_id: - if not order_id in order_obj.exists(cr, uid, [order_id], context=context): - return False - try: - order = order_obj.browse(cr, uid, order_id, context=context) - if order: - return order - except: - return False + # ************************************************************ + # Ecommerce pricelist management + # *********************************************************** - fields = [k for k, v in order_obj._columns.items()] - order_value = order_obj.default_get(cr, SUPERUSER_ID, fields, context=context) - if request.httprequest.session.get('ecommerce_pricelist'): - order_value['pricelist_id'] = request.httprequest.session['ecommerce_pricelist'] - order_value['partner_id'] = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context).partner_id.id - order_value.update(order_obj.onchange_partner_id(cr, SUPERUSER_ID, [], order_value['partner_id'], context=context)['value']) - - # add website_session_id key for access rules - if not request.httprequest.session.get('website_session_id'): - request.httprequest.session['website_session_id'] = str(uuid.uuid4()) - - order_value["website_session_id"] = request.httprequest.session['website_session_id'] - order_id = order_obj.create(cr, SUPERUSER_ID, order_value, context=context) - order = order_obj.browse(cr, SUPERUSER_ID, order_id, context=context) - request.httprequest.session['ecommerce_order_id'] = order.id - - return order_obj.browse(cr, uid, order_id, - context=dict(request.context, pricelist=order.pricelist_id.id)) - - def get_current_order(self, cr, uid, context=None): - if request.httprequest.session.get('ecommerce_order_id'): - order = self._get_order(cr, uid, order_id=request.httprequest.session['ecommerce_order_id'], context=context) - if not order: - request.httprequest.session['ecommerce_order_id'] = False - return order - return False - - def _get_transaction(self, cr, uid, tx_id=None, context=None): - transaction_obj = self.pool.get('payment.transaction') - if tx_id: - tx_ids = transaction_obj.search(cr, uid, [('id', '=', tx_id), ('state', 'not in', ['cancel'])], context=context) - if tx_ids: - return transaction_obj.browse(cr, uid, tx_ids[0], context=context) - return False - - def get_current_transaction(self, cr, uid, context=None): - if request.httprequest.session.get('website_sale_transaction_id'): - tx = self._get_transaction(cr, uid, tx_id=request.httprequest.session['website_sale_transaction_id'], context=context) - if not tx: - request.httprequest.session['website_sale_transaction_id'] = False - return tx - return False - - def sale_reset_order(self, cr, uid, context=None): - request.httprequest.session.update({ - 'ecommerce_order_id': False, - 'ecommerce_pricelist': False, - 'website_sale_transaction_id': False, - }) - request.context.update({ - 'website_sale_order': False, - 'website_sale_transaction': False, - }) - - def preprocess_request(self, cr, uid, ids, request, context=None): - request.context.update({ - 'website_sale_order': self.get_current_order(cr, uid, context=context), - 'website_sale_transaction': self.get_current_transaction(cr, uid, context=context) - }) - return super(Website, self).preprocess_request(cr, uid, ids, request, context=None) - - def get_pricelist_id(self, cr, uid, ids, context=None): + def ecommerce_get_pricelist_id(self, cr, uid, ids, context=None): if not request.httprequest.session.get('ecommerce_pricelist'): - self.change_pricelist_id(cr, uid, None, context=context) + self._ecommerce_change_pricelist(cr, uid, None, context=context) return request.httprequest.session.get('ecommerce_pricelist') - def change_pricelist_id(self, cr, uid, code=False, context=None): + def _ecommerce_change_pricelist(self, cr, uid, code=None, context=None): request.httprequest.session.setdefault('ecommerce_pricelist', False) pricelist_id = False @@ -115,7 +45,7 @@ class Website(orm.Model): request.httprequest.session['ecommerce_pricelist'] = pricelist_id - order = self.get_current_order(cr, uid, context=context) + order = self.ecommerce_get_current_order(cr, uid, context=context) if order: values = {'pricelist_id': pricelist_id} values.update(order.onchange_pricelist_id(pricelist_id, None)['value']) @@ -123,6 +53,95 @@ class Website(orm.Model): for line in order.order_line: self.add_product_to_cart(order_line_id=line.id, number=0) + # ************************************************************ + # Ecommerce quotation management + # ************************************************************ - def get_website_sale_domain(self): + def _ecommerce_get_quotation_values(self, cr, uid, context=None): + """ Generate the values for a new ecommerce quotation. """ + SaleOrder = self.pool.get('sale.order') + fields = [k for k, v in SaleOrder._columns.items()] + values = SaleOrder.default_get(cr, SUPERUSER_ID, fields, context=context) + if request.httprequest.session.get('ecommerce_pricelist'): + values['pricelist_id'] = request.httprequest.session['ecommerce_pricelist'] + values['partner_id'] = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context).partner_id.id + values.update(SaleOrder.onchange_partner_id(cr, SUPERUSER_ID, [], values['partner_id'], context=context)['value']) + values['website_session_id'] = request.httprequest.session['website_session_id'] + return values + + def _ecommerce_create_quotation(self, cr, uid, context=None): + """ Create a new quotation used in the ecommerce (event, sale) """ + SaleOrder = self.pool.get('sale.order') + quotation_values = self._ecommerce_get_quotation_values(cr, uid, context=context) + return SaleOrder.create(cr, SUPERUSER_ID, quotation_values, context=context) + + def ecommerce_get_new_order(self, cr, uid, context=None): + """ Create a new quotation for the ecommerce and update the session + accordingly: website_session_id if not set, ecommerce_order_id """ + SaleOrder = self.pool.get('sale.order') + + # add website_session_id key for access rules + if not request.httprequest.session.get('website_session_id'): + request.httprequest.session['website_session_id'] = str(uuid.uuid4()) + + order_id = self._ecommerce_create_quotation(cr, uid, context=context) + request.httprequest.session['ecommerce_order_id'] = order_id + order = SaleOrder.browse(cr, SUPERUSER_ID, order_id, context=context) + return SaleOrder.browse(cr, uid, order_id, context=dict(request.context, pricelist=order.pricelist_id.id)) + + def ecommerce_get_current_order(self, cr, uid, context=None): + SaleOrder = self.pool.get('sale.order') + order_id = request.httprequest.session.get('ecommerce_order_id') + if not order_id: + request.httprequest.session['ecommerce_order_id'] = False + return False + if not order_id in SaleOrder.exists(cr, uid, [order_id], context=context): + request.httprequest.session['ecommerce_order_id'] = False + return False + try: + order = SaleOrder.browse(cr, uid, order_id, context=context) + return order + except: + request.httprequest.session['ecommerce_order_id'] = False + return False + + # ************************************************************ + # Ecommerce transaction management + # ************************************************************ + + def _get_transaction(self, cr, uid, tx_id=None, context=None): + transaction_obj = self.pool.get('payment.transaction') + if tx_id: + tx_ids = transaction_obj.search(cr, uid, [('id', '=', tx_id), ('state', 'not in', ['cancel'])], context=context) + if tx_ids: + return transaction_obj.browse(cr, uid, tx_ids[0], context=context) + return False + + def ecommerce_get_current_transaction(self, cr, uid, context=None): + if request.httprequest.session.get('website_sale_transaction_id'): + tx = self._get_transaction(cr, uid, tx_id=request.httprequest.session['website_sale_transaction_id'], context=context) + if not tx: + request.httprequest.session['website_sale_transaction_id'] = False + return tx + return False + + def ecommerce_reset(self, cr, uid, context=None): + request.httprequest.session.update({ + 'ecommerce_order_id': False, + 'ecommerce_pricelist': False, + 'website_sale_transaction_id': False, + }) + request.context.update({ + 'website_sale_order': False, + 'website_sale_transaction': False, + }) + + def preprocess_request(self, cr, uid, ids, request, context=None): + request.context.update({ + 'website_sale_order': self.ecommerce_get_current_order(cr, uid, context=context), + 'website_sale_transaction': self.ecommerce_get_current_transaction(cr, uid, context=context) + }) + return super(Website, self).preprocess_request(cr, uid, ids, request, context=None) + + def ecommerce_get_product_domain(self): return [("sale_ok", "=", True)] diff --git a/addons/website_sale/views/website_sale.xml b/addons/website_sale/views/website_sale.xml index 5aee67697cf..14fd932bde5 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -13,11 +13,11 @@