[MERGE] [IMP] website_sale_delivery: make the module effectively works.

- added support in the front-end to choose and carrier and update the quotation
- 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
- added some wrappers on _amount_all method of the sale.order model, in order to be able to use inheritance for this method
- fixed shipping values management in website_sale + fixed shipping partner finding
- improved the cart to display only product lines, the delivery amount is displayed below the quotation total

[FIX] email_template: fixed a bug with pre-loading the template that was preventing to send a quotation by email

bzr revid: tde@openerp.com-20131219112219-kjvzfukcfur6f8g5
This commit is contained in:
Thibault Delavallée 2013-12-19 12:22:19 +01:00
commit 38e22d1808
18 changed files with 379 additions and 245 deletions

View File

@ -20,29 +20,69 @@
##############################################################################
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_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')
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_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),
},
multi='sums', help="The amount without tax.", track_visibility='always'
),
}
def onchange_partner_id(self, cr, uid, ids, part, context=None):
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):
@ -57,7 +97,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 +124,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)

View File

@ -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'),

View File

@ -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),

View File

@ -1 +1,2 @@
import event
import website

View File

@ -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)]

View File

@ -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)]

View File

@ -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,9 +410,9 @@ 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.get_number_of_products(),
order.amount_total,
request.website._render("website_sale.total", {'website_sale_order': order})]
@ -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')
@ -545,8 +553,8 @@ class Ecommerce(http.Controller):
'country_id': post['shipping_country_id'],
'state_id': post['shipping_state_id'],
}
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"]]
domain = [(key, '_id' in key and '=' or 'ilike', '_id' in key and value and int(value) or value)
for key, value in shipping_info.items() if key in info.mandatory_billing_fields + ["type", "parent_id"]]
shipping_ids = orm_parter.search(cr, SUPERUSER_ID, domain, context=context)
if shipping_ids:
@ -582,15 +590,14 @@ 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')
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
@ -645,7 +652,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/")
@ -712,7 +719,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)
@ -734,7 +741,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)

View File

@ -1,46 +1,36 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C)-2010 Tiny SPRL (<http://tiny.be>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import SUPERUSER_ID
from openerp.osv import osv, fields
class sale_order(osv.Model):
class SaleOrder(osv.Model):
_inherit = "sale.order"
_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):
return {}
return {
'partner': order.partner_id.id,
'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 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')

View File

@ -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)]

View File

@ -13,11 +13,11 @@
<template id="header" inherit_id="website.layout" name="Header Shop My Cart Link">
<xpath expr="//header//ul[@id='top_menu']/li" position="before">
<li t-att-class="(not website_sale_order or not website_sale_order.get_total_quantity()) and 'hidden' or ''">
<li t-att-class="(not website_sale_order or not website_sale_order.get_number_of_products()) and 'hidden' or ''">
<a href="/shop/mycart/">
<i class="fa fa-shopping-cart"></i>
My cart <sup t-attf-class="my_cart_quantity label label-primary"
t-esc="website_sale_order and website_sale_order.get_total_quantity() or ''"/>
t-esc="website_sale_order and website_sale_order.get_number_of_products() or ''"/>
</a>
</li>
</xpath>
@ -396,10 +396,10 @@
<h1 class="mb32">Shopping Cart</h1>
<div class="row">
<div class="col-md-8 col-sm-9 oe_mycart">
<div t-if="not website_sale_order or not website_sale_order.order_line" class="well well-lg">
<div t-if="not website_sale_order or not website_sale_order.website_order_line" class="well well-lg">
Your cart is empty!
</div>
<table class='table table-striped table-condensed' id="mycart_products" t-if="website_sale_order and website_sale_order.order_line">
<table class='table table-striped table-condensed' id="mycart_products" t-if="website_sale_order and website_sale_order.website_order_line">
<colgroup>
<col width="100"/>
<col/>
@ -414,7 +414,7 @@
</tr>
</thead>
<tbody>
<tr t-foreach="website_sale_order.order_line" t-as="line">
<tr t-foreach="website_sale_order.website_order_line" t-as="line">
<td colspan="2" t-if="not line.product_id.product_tmpl_id"></td>
<td align="center" t-if="line.product_id.product_tmpl_id">
<span t-field="line.product_id.image_small"
@ -467,13 +467,13 @@
<col width="120"/>
</colgroup>
<thead>
<tr style="border-top: 1px solid #000">
<tr style="border-top: 1px solid #000" id="order_total">
<th><h3>Total:</h3></th>
<th class="text-right">
<h3><t t-call="website_sale.total"/></h3>
</th>
</tr>
<tr class="text-muted">
<tr class="text-muted" id="order_total_taxes">
<td><abbr title="Taxes may be updated after providing shipping address">Taxes:</abbr></td>
<td class="text-right">
<span t-field="website_sale_order.amount_tax" t-field-options='{
@ -486,7 +486,7 @@
</table>
<div class="clearfix"/>
<a href="/shop" class="btn btn-default mb32"><span class="fa fa-long-arrow-left"/> Continue Shopping</a>
<a t-if="website_sale_order and website_sale_order.order_line" href="/shop/checkout/" class="btn btn-primary pull-right mb32">Process Checkout <span class="fa fa-long-arrow-right"/></a>
<a t-if="website_sale_order and website_sale_order.website_order_line" href="/shop/checkout/" class="btn btn-primary pull-right mb32">Process Checkout <span class="fa fa-long-arrow-right"/></a>
<div class="oe_structure"/>
</div>
<div class="col-lg-3 col-lg-offset-1 col-sm-3 text-muted" id="right_column">
@ -626,7 +626,7 @@
<p>
Have a coupon code? Fill in this field and apply.
</p>
<form t-if="website_sale_order and website_sale_order.order_line" action="/shop/mycart/" method="post" class="mb32">
<form t-if="website_sale_order and website_sale_order.website_order_line" action="/shop/mycart/" method="post" class="mb32">
<div class="input-group">
<input name="promo" class='form-control' type="text" placeholder="code..." t-att-value="website_sale_order.pricelist_id.code or ''"/>
<div class="input-group-btn">
@ -821,7 +821,7 @@
<h1 class="mb32">Validate Order</h1>
<div class="row">
<div class="col-lg-8 col-sm-9 oe_mycart">
<table class='table table-striped table-condensed' id="mycart_products" t-if="website_sale_order and website_sale_order.order_line">
<table class='table table-striped table-condensed' id="mycart_products" t-if="website_sale_order and website_sale_order.website_order_line">
<colgroup>
<col width="80"/>
<col/>
@ -836,7 +836,7 @@
</tr>
</thead>
<tbody>
<tr t-foreach="website_sale_order.order_line" t-as="line">
<tr t-foreach="website_sale_order.website_order_line" t-as="line">
<td colspan="2" t-if="not line.product_id.product_tmpl_id"></td>
<td t-if="line.product_id.product_tmpl_id">
<a t-href="/shop/product/#{ slug(line.product_id.product_tmpl_id) }/">
@ -865,14 +865,16 @@
<col width="120"/>
</colgroup>
<thead>
<tr style="border-top: 1px solid #000">
<tr style="border-top: 1px solid #000" id="order_total">
<th><h3>Total:</h3></th>
<th class="text-right"><h3><span t-field="website_sale_order.amount_total" t-field-options='{
<th class="text-right">
<h3><span t-field="website_sale_order.amount_total" t-field-options='{
"widget": "monetary",
"display_currency": "website.pricelist_id.currency_id"
}'/></h3></th>
}'/></h3>
</th>
</tr>
<tr class="text-muted">
<tr class="text-muted" id="order_total_taxes">
<td>Taxes:</td>
<td class="text-right"><span t-field="website_sale_order.amount_tax" t-field-options='{
"widget": "monetary",
@ -907,8 +909,8 @@
</div>
</div>
<div class="js_payment mb64" t-if="acquirers" id="payment_method">
<h3>Choose your payment method</h3>
<div class="js_payment mb64 row" t-if="acquirers" id="payment_method">
<h4>Choose your Payment Method</h4>
<div class="col-lg-5 col-sm-6">
<t t-foreach="acquirers or []" t-as="acquirer">
<label t-if="acquirer.button" class="oe_sale_acquirer_logo" style="display: block;">

View File

@ -1 +1,2 @@
import models
import controllers

View File

@ -0,0 +1 @@
import main

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from openerp.addons.website_sale.controllers.main import Ecommerce
from openerp.addons.web.http import request
from openerp.addons.website.models import website
class Ecommerce(Ecommerce):
@website.route(['/shop/payment/'], type='http', auth="public", multilang=True)
def payment(self, **post):
cr, uid, context = request.cr, request.uid, request.context
order = self.get_order()
carrier_id = post.get('carrier_id')
if order and carrier_id:
# recompute delivery costs
SaleOrder = request.registry['sale.order']
SaleOrder.write(cr, uid, [order.id], {'carrier_id': carrier_id}, context=context)
SaleOrder.delivery_set(cr, uid, [order.id], context=context)
res = super(Ecommerce, self).payment(**post)
return res

View File

@ -1 +1,2 @@
import sale_order
import website

View File

@ -2,6 +2,7 @@
from openerp.osv import orm, fields
class delivery_carrier(orm.Model):
_inherit = 'delivery.carrier'
_columns = {
@ -12,26 +13,36 @@ class delivery_carrier(orm.Model):
'website_published': True
}
class sale_order(orm.Model):
class SaleOrder(orm.Model):
_inherit = 'sale.order'
_columns = {
'website_order_line': fields.one2many(
'sale.order.line', 'order_id',
string='Order Lines displayed on Website', readonly=True,
domain=[('is_delivery', '=', False)],
help='Order Lines to be displayed on the website. They should not be used for computation purpose.',
),
}
def _add_delivery(self, cr, uid, order, context=None):
pass
def _get_website_data(self, cr, uid, order, context=None):
""" Override to add delivery-related website data. """
values = super(SaleOrder, self)._get_website_data(cr, uid, order, context=context)
# We need a delivery only if we have stockable products
todo = False
has_stockable_products = False
for line in order.order_line:
if line.product_id.type in ('consu','product'):
todo = True
if not todo: return {'deliveries': []}
if line.product_id.type in ('consu', 'product'):
has_stockable_products = True
if not has_stockable_products:
return values
carrier_obj = self.pool.get('delivery.carrier')
dids = carrier_obj.search(cr, uid, [], context=context)
context['order_id'] = order.id
deliveries = carrier_obj.browse(cr, uid, dids, context=context)
# By default, select the first carrier
if not order.carrier_id and dids:
self.pool.get('sale.order').write(cr, uid, [order.id], {'carrier_id': dids[0]}, context=context)
# recompute delivery costs
self.pool.get('sale.order').delivery_set(cr, uid, [order.id], context=context)
return {'deliveries': deliveries}
delivery_ctx = dict(context, order_id=order.id)
DeliveryCarrier = self.pool.get('delivery.carrier')
delivery_ids = DeliveryCarrier.search(cr, uid, [], context=context)
values['deliveries'] = DeliveryCarrier.browse(cr, uid, delivery_ids, context=delivery_ctx)
return values

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from openerp.osv import orm
class Website(orm.Model):
_inherit = 'website'
def _ecommerce_get_quotation_values(self, cr, uid, context=None):
""" Override the quotation values generation to add carrier_id data """
values = super(Website, self)._ecommerce_get_quotation_values(cr, uid, context=context)
DeliveryCarrier = self.pool.get('delivery.carrier')
carrier_ids = DeliveryCarrier.search(cr, uid, [], context=context, limit=1)
# By default, select the first carrier
if carrier_ids:
values['carrier_id'] = carrier_ids[0]
return values
def _ecommerce_create_quotation(self, cr, uid, context=None):
order_id = super(Website, self)._ecommerce_create_quotation(cr, uid, context=context)
self.pool['sale.order'].delivery_set(cr, uid, [order_id], context=context)
return order_id

View File

@ -1,24 +1,10 @@
$(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) {
var carrier_id = $(ev.currentTarget).val();
window.location.href = '/shop/payment?carrier_id=' + carrier_id;
});
});

View File

@ -1,29 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<template id="delivery" name="Delivery Costs" inherit_id="website_sale.payment">
<xpath expr="//t[@t-set='head']" position="inside">
<script type="text/javascript" src="/website_sale_delivery/static/src/js/website_sale_delivery.js"></script>
</xpath>
<xpath expr="//div[@id='payment_method']" position="before">
<t t-if="len(deliveries)">
<p>Choose Your Delivery method:</p>
<ul class="list-unstyled">
<li t-foreach="deliveries" t-as="delivery">
<label>
<input t-att-value="delivery.id" type="radio" name="delivery_type"/>
<span t-field="delivery.name"/>
<span class="badge" t-field="delivery.price"/>
<!-- TODO: add monetary widget -->
</label>
</li>
</ul>
</t>
<data>
<template id="mycart_delivery" name="Delivery Costs" inherit_id="website_sale.mycart">
<xpath expr="//tr[@id='order_total_taxes']" position="after">
<tr class="text-muted" id="order_delivery">
<td><abbr title="Delivery will be updated after choosing a new delivery method">Delivery:</abbr></td>
<td class="text-right">
<span t-field="website_sale_order.amount_delivery" t-field-options='{
"widget": "monetary",
"display_currency": "website.pricelist_id.currency_id"
}'/>
</td>
</tr>
</xpath>
</template>
</data>
<template id="payment_delivery" name="Delivery Costs" inherit_id="website_sale.payment">
<xpath expr="//t[@t-set='head']" position="inside">
<script type="text/javascript" src="/website_sale_delivery/static/src/js/website_sale_delivery.js"></script>
</xpath>
<xpath expr="//tr[@id='order_total_taxes']" position="after">
<tr class="text-muted" id="order_delivery">
<td><abbr title="Delivery will be updated after choosing a new delivery method">Delivery:</abbr></td>
<td class="text-right">
<span t-field="website_sale_order.amount_delivery" t-field-options='{
"widget": "monetary",
"display_currency": "website.pricelist_id.currency_id"
}'/>
</td>
</tr>
</xpath>
<xpath expr="//div[@id='payment_method']" position="before">
<div t-if="len(deliveries)" class="row" id="delivery_carrier">
<h4>Choose your Delivery Method</h4>
<div class="col-lg-5 col-sm-6">
<ul class="list-unstyled">
<li t-foreach="deliveries" t-as="delivery">
<label>
<input t-att-value="delivery.id" type="radio" name="delivery_type"
t-att-checked="website_sale_order.carrier_id and website_sale_order.carrier_id.id == delivery.id and 'checked' or False"/>
<span t-field="delivery.name"/>
<span class="badge" t-field="delivery.price"
t-field-options='{
"widget": "monetary",
"display_currency": "website.pricelist_id.currency_id"
}'/>
</label>
</li>
</ul>
</div>
</div>
</xpath>
</template>
</data>
</openerp>