[IMP] website_sale, base: use invoice address at checkout
at checkout use the invoice address as billing address instead of main address if the invoice one is defined. Add the functionality to add new billings addresses at checkout. task-3258835 Part-of: odoo/odoo#118428
This commit is contained in:
committed by
Victor Feyens
parent
4015f90bde
commit
d9c7ff69a4
@@ -959,12 +959,11 @@ class WebsiteSale(payment_portal.PaymentPortal):
|
||||
# ------------------------------------------------------
|
||||
|
||||
def checkout_check_address(self, order):
|
||||
billing_fields_required = self._get_mandatory_fields_billing(order.partner_id.country_id.id)
|
||||
if not all(order.partner_id.read(billing_fields_required)[0].values()):
|
||||
return request.redirect('/shop/address?partner_id=%d' % order.partner_id.id)
|
||||
partner_invoice = order.partner_invoice_id
|
||||
if not self._check_billing_partner_mandatory_fields(partner_invoice):
|
||||
return request.redirect('/shop/address?partner_id=%d&is_invoice=1' % partner_invoice.id)
|
||||
|
||||
shipping_fields_required = self._get_mandatory_fields_shipping(order.partner_shipping_id.country_id.id)
|
||||
if not all(order.partner_shipping_id.read(shipping_fields_required)[0].values()):
|
||||
if not self._check_shipping_partner_mandatory_fields(order.partner_shipping_id):
|
||||
return request.redirect('/shop/address?partner_id=%d' % order.partner_shipping_id.id)
|
||||
|
||||
def checkout_redirection(self, order):
|
||||
@@ -987,29 +986,49 @@ class WebsiteSale(payment_portal.PaymentPortal):
|
||||
|
||||
def checkout_values(self, **kw):
|
||||
order = request.website.sale_get_order(force_create=True)
|
||||
shippings = []
|
||||
bill_partners = []
|
||||
ship_partners = []
|
||||
if order.partner_id != request.website.user_id.sudo().partner_id:
|
||||
Partner = order.partner_id.with_context(show_address=1).sudo()
|
||||
shippings = Partner.search([
|
||||
("id", "child_of", order.partner_id.commercial_partner_id.ids),
|
||||
'|', ("type", "in", ["delivery", "other"]), ("id", "=", order.partner_id.commercial_partner_id.id)
|
||||
commercial_partner = order.partner_id.commercial_partner_id
|
||||
bill_partners = Partner.search([
|
||||
("id", "child_of", commercial_partner.ids),
|
||||
'|', ("type", "in", ["invoice", "other"]), ("id", "=", commercial_partner.id)
|
||||
], order='id desc')
|
||||
if shippings:
|
||||
if kw.get('partner_id') or 'use_billing' in kw:
|
||||
if 'use_billing' in kw:
|
||||
partner_id = order.partner_id.id
|
||||
else:
|
||||
partner_id = int(kw.get('partner_id'))
|
||||
if partner_id in shippings.mapped('id'):
|
||||
order.partner_shipping_id = partner_id
|
||||
ship_partners = Partner.search([
|
||||
("id", "child_of", commercial_partner.ids),
|
||||
'|', ("type", "in", ["delivery", "other"]), ("id", "=", commercial_partner.id)
|
||||
], order='id desc')
|
||||
|
||||
# do not show commercial_partner_id if its mandatory fields are not complete to children
|
||||
# as children can not edit (fill) the commercial_partner_id
|
||||
if commercial_partner != order.partner_id:
|
||||
if not self._check_billing_partner_mandatory_fields(commercial_partner):
|
||||
bill_partners = bill_partners.filtered(lambda p: p.id != commercial_partner.id)
|
||||
|
||||
if not self._check_shipping_partner_mandatory_fields(commercial_partner):
|
||||
ship_partners = ship_partners.filtered(lambda p: p.id != commercial_partner.id)
|
||||
|
||||
partner_id = int(kw.get('partner_id', 0))
|
||||
if partner_id:
|
||||
if 'is_invoice' in kw and bill_partners and partner_id in bill_partners.mapped('id'):
|
||||
order.partner_invoice_id = partner_id
|
||||
elif ship_partners and partner_id in ship_partners.mapped('id'):
|
||||
order.partner_shipping_id = partner_id
|
||||
|
||||
values = {
|
||||
'order': order,
|
||||
'shippings': shippings,
|
||||
'shippings': ship_partners,
|
||||
'billings': bill_partners,
|
||||
'only_services': order and order.only_services or False
|
||||
}
|
||||
return values
|
||||
|
||||
def _check_billing_partner_mandatory_fields(self, partner_id):
|
||||
''' return True if all mandatory fields for billing address are complete '''
|
||||
billing_fields_required = self._get_mandatory_fields_billing(partner_id.country_id.id)
|
||||
return all(partner_id.read(billing_fields_required)[0].values())
|
||||
|
||||
def _get_mandatory_fields_billing(self, country_id=False):
|
||||
req = ["name", "email", "street", "city", "country_id"]
|
||||
if country_id:
|
||||
@@ -1020,6 +1039,11 @@ class WebsiteSale(payment_portal.PaymentPortal):
|
||||
req += ['zip']
|
||||
return req
|
||||
|
||||
def _check_shipping_partner_mandatory_fields(self, partner_id):
|
||||
''' return True if all mandatory fields for shipping address are complete '''
|
||||
shipping_fields_required = self._get_mandatory_fields_shipping(partner_id.country_id.id)
|
||||
return all(partner_id.read(shipping_fields_required)[0].values())
|
||||
|
||||
def _get_mandatory_fields_shipping(self, country_id=False):
|
||||
req = ["name", "street", "city", "country_id"]
|
||||
if country_id:
|
||||
@@ -1037,13 +1061,16 @@ class WebsiteSale(payment_portal.PaymentPortal):
|
||||
error = dict()
|
||||
error_message = []
|
||||
|
||||
if data.get('partner_id'):
|
||||
if mode == ('edit', 'billing') and data.get('partner_id'):
|
||||
partner_su = request.env['res.partner'].sudo().browse(int(data['partner_id'])).exists()
|
||||
name_change = partner_su and 'name' in data and data['name'] != partner_su.name
|
||||
email_change = partner_su and 'email' in data and data['email'] != partner_su.email
|
||||
name_change = partner_su and partner_su.name and 'name' in data and data['name'] != partner_su.name
|
||||
email_change = partner_su and 'email' in data and data['email'] != partner_su.email and partner_su.email
|
||||
invoices = request.env['account.move'].sudo().search(
|
||||
[('partner_id', '=', partner_su.id)], limit=1
|
||||
) if partner_su else False
|
||||
|
||||
# Prevent changing the billing partner name if invoices have been issued.
|
||||
if mode[1] == 'billing' and name_change and not partner_su.can_edit_vat():
|
||||
if name_change and not partner_su.can_edit_vat() and invoices:
|
||||
error['name'] = 'error'
|
||||
error_message.append(_(
|
||||
"Changing your name is not allowed once documents have been issued for your"
|
||||
@@ -1151,20 +1178,23 @@ class WebsiteSale(payment_portal.PaymentPortal):
|
||||
if request.website.specific_user_account:
|
||||
new_values['website_id'] = request.website.id
|
||||
|
||||
commercial_partner_id = order.partner_id.commercial_partner_id
|
||||
if mode[0] == 'new':
|
||||
lang = request.lang.code if request.lang.code in request.website.mapped('language_ids.code') else None
|
||||
if lang:
|
||||
new_values['lang'] = lang
|
||||
new_values['company_id'] = request.website.company_id.id
|
||||
new_values['team_id'] = request.website.salesteam_id and request.website.salesteam_id.id
|
||||
new_values['user_id'] = request.website.salesperson_id.id
|
||||
|
||||
lang = request.lang.code if request.lang.code in request.website.mapped('language_ids.code') else None
|
||||
if lang:
|
||||
new_values['lang'] = lang
|
||||
if mode == ('edit', 'billing') and order.partner_id.type == 'contact':
|
||||
new_values['type'] = 'other'
|
||||
if mode[1] == 'shipping':
|
||||
new_values['parent_id'] = order.partner_id.commercial_partner_id.id
|
||||
new_values['type'] = 'delivery'
|
||||
|
||||
# define type of the new partner
|
||||
if mode[1] == 'billing':
|
||||
new_values['type'] = 'other' if 'use_same' in values else 'invoice'
|
||||
# for public user avoid linking to default archived 'Public user' partner
|
||||
if commercial_partner_id.active:
|
||||
new_values['parent_id'] = commercial_partner_id.id
|
||||
elif mode[1] == 'shipping':
|
||||
new_values['type'] = 'delivery'
|
||||
new_values['parent_id'] = commercial_partner_id.id
|
||||
return new_values, errors, error_msg
|
||||
|
||||
@http.route(['/shop/address'], type='http', methods=['GET', 'POST'], auth="public", website=True, sitemap=False)
|
||||
@@ -1176,35 +1206,36 @@ class WebsiteSale(payment_portal.PaymentPortal):
|
||||
if redirection:
|
||||
return redirection
|
||||
|
||||
mode = (False, False)
|
||||
can_edit_vat = False
|
||||
values, errors = {}, {}
|
||||
|
||||
partner_id = int(kw.get('partner_id', -1))
|
||||
|
||||
# IF PUBLIC ORDER
|
||||
if order.partner_id.id == request.website.user_id.sudo().partner_id.id:
|
||||
if order._is_public_order():
|
||||
mode = ('new', 'billing')
|
||||
can_edit_vat = True
|
||||
# IF ORDER LINKED TO A PARTNER
|
||||
else:
|
||||
else: # IF ORDER LINKED TO A PARTNER
|
||||
if partner_id > 0:
|
||||
if partner_id == order.partner_id.id:
|
||||
mode = ('edit', 'billing')
|
||||
can_edit_vat = order.partner_id.can_edit_vat()
|
||||
partners = Partner.search(
|
||||
[('id', 'child_of', order.partner_id.commercial_partner_id.ids)]
|
||||
)
|
||||
if 'is_invoice' in kw:
|
||||
bill_partners_ids = partners.filtered(lambda p: p.type != 'delivery').ids
|
||||
if partner_id in bill_partners_ids or order.partner_id.id == partner_id:
|
||||
mode = ('edit', 'billing')
|
||||
can_edit_vat = order.partner_id.can_edit_vat()
|
||||
else:
|
||||
return Forbidden()
|
||||
else:
|
||||
shippings = Partner.search([('id', 'child_of', order.partner_id.commercial_partner_id.ids)])
|
||||
if order.partner_id.commercial_partner_id.id == partner_id:
|
||||
mode = ('new', 'shipping')
|
||||
partner_id = -1
|
||||
elif partner_id in shippings.mapped('id'):
|
||||
ship_partners_ids = partners.filtered(lambda p: p.type != 'invoice').ids
|
||||
if partner_id in ship_partners_ids:
|
||||
mode = ('edit', 'shipping')
|
||||
else:
|
||||
return Forbidden()
|
||||
if mode and partner_id != -1:
|
||||
values = Partner.browse(partner_id)
|
||||
elif partner_id == -1:
|
||||
mode = ('new', 'shipping')
|
||||
mode = ('new', 'billing') if 'is_invoice' in kw else ('new', 'shipping')
|
||||
else: # no mode - refresh without post?
|
||||
return request.redirect('/shop/checkout')
|
||||
|
||||
@@ -1218,29 +1249,53 @@ class WebsiteSale(payment_portal.PaymentPortal):
|
||||
errors['error_message'] = error_msg
|
||||
values = kw
|
||||
else:
|
||||
update_mode, address_mode = mode
|
||||
partner_id = self._checkout_form_save(mode, post, kw)
|
||||
# We need to validate _checkout_form_save return, because when partner_id not in shippings
|
||||
# it returns Forbidden() instead the partner_id
|
||||
if isinstance(partner_id, Forbidden):
|
||||
return partner_id
|
||||
|
||||
fpos_before = order.fiscal_position_id
|
||||
if mode[1] == 'billing':
|
||||
order.partner_id = partner_id
|
||||
# This is the *only* thing that the front end user will see/edit anyway when choosing billing address
|
||||
order.partner_invoice_id = partner_id
|
||||
if not kw.get('use_same'):
|
||||
kw['callback'] = kw.get('callback') or \
|
||||
(not order.only_services and (mode[0] == 'edit' and '/shop/checkout' or '/shop/address'))
|
||||
# We need to update the pricelist(by the one selected by the customer), because onchange_partner reset it
|
||||
# We only need to update the pricelist when it is not redirected to /confirm_order
|
||||
if kw.get('callback', False) != '/shop/confirm_order':
|
||||
request.website.sale_get_order(update_pricelist=True)
|
||||
elif mode[1] == 'shipping':
|
||||
order.partner_shipping_id = partner_id
|
||||
update_values = {}
|
||||
if update_mode == 'new': # New address
|
||||
if order._is_public_order():
|
||||
update_values['partner_id'] = partner_id
|
||||
|
||||
if address_mode == 'billing':
|
||||
update_values['partner_invoice_id'] = partner_id
|
||||
if kw.get('use_same'):
|
||||
update_values['partner_shipping_id'] = partner_id
|
||||
elif not kw.get('callback') and not order.only_services:
|
||||
# Now that the billing is set, if shipping is necessary
|
||||
# request the user to fill the shipping address
|
||||
kw['callback'] = '/shop/address'
|
||||
elif address_mode == 'shipping':
|
||||
update_values['partner_shipping_id'] = partner_id
|
||||
elif update_mode == 'edit': # Updating an existing address
|
||||
if order.partner_id.id == partner_id:
|
||||
# Editing the main partner of the SO --> also trigger a partner update to
|
||||
# recompute fpos & any partner-related fields
|
||||
update_values['partner_id'] = partner_id
|
||||
|
||||
if address_mode == 'billing':
|
||||
update_values['partner_invoice_id'] = partner_id
|
||||
if not kw.get('callback') and not order.only_services:
|
||||
kw['callback'] = '/shop/checkout'
|
||||
elif address_mode == 'shipping':
|
||||
update_values['partner_shipping_id'] = partner_id
|
||||
|
||||
order.write(update_values)
|
||||
|
||||
if order.fiscal_position_id != fpos_before:
|
||||
# Recompute taxes on fpos change
|
||||
# TODO recompute all prices too to correctly manage price_include taxes ?
|
||||
order._recompute_taxes()
|
||||
|
||||
if 'partner_id' in update_values:
|
||||
# Force recomputation of pricelist on main customer address update
|
||||
request.website.sale_get_order(update_pricelist=True)
|
||||
|
||||
# TDE FIXME: don't ever do this
|
||||
# -> TDE: you are the guy that did what we should never do in commit e6f038a
|
||||
order.message_partner_ids = [(4, partner_id), (3, request.website.partner_id.id)]
|
||||
@@ -1438,8 +1493,7 @@ class WebsiteSale(payment_portal.PaymentPortal):
|
||||
order = render_values['website_sale_order']
|
||||
|
||||
def_country_id = order.partner_id.country_id
|
||||
# IF PUBLIC ORDER
|
||||
if order.partner_id.id == request.website.user_id.sudo().partner_id.id:
|
||||
if order._is_public_order():
|
||||
if request.geoip.country_code:
|
||||
def_country_id = request.env['res.country'].search([('code', '=', request.geoip.country_code)], limit=1)
|
||||
else:
|
||||
@@ -1463,7 +1517,7 @@ class WebsiteSale(payment_portal.PaymentPortal):
|
||||
if redirection:
|
||||
return redirection
|
||||
|
||||
if order.partner_id.id == request.website.user_id.sudo().partner_id.id:
|
||||
if order._is_public_order():
|
||||
return request.redirect('/shop/address')
|
||||
|
||||
redirection = self.checkout_check_address(order)
|
||||
|
||||
@@ -622,3 +622,9 @@ class SaleOrder(models.Model):
|
||||
]).available_carriers(
|
||||
self.partner_shipping_id
|
||||
).filtered(_is_carrier_available)
|
||||
|
||||
#=== TOOLING ===#
|
||||
|
||||
def _is_public_order(self):
|
||||
self.ensure_one()
|
||||
return self.partner_id.id == request.website.user_id.sudo().partner_id.id
|
||||
|
||||
@@ -393,7 +393,6 @@ class Website(models.Model):
|
||||
# change the partner, and trigger the computes (fpos)
|
||||
sale_order_sudo.write({
|
||||
'partner_id': partner_sudo.id,
|
||||
'partner_invoice_id': partner_sudo.id,
|
||||
'payment_term_id': self.sale_get_payment_term(partner_sudo),
|
||||
# Must be specified to ensure it is not recomputed when it shouldn't
|
||||
'pricelist_id': pricelist_id,
|
||||
@@ -418,7 +417,7 @@ class Website(models.Model):
|
||||
|
||||
def _prepare_sale_order_values(self, partner_sudo):
|
||||
self.ensure_one()
|
||||
addr = partner_sudo.address_get(['delivery'])
|
||||
addr = partner_sudo.address_get(['delivery', 'invoice'])
|
||||
if not request.website.is_public_user():
|
||||
# FIXME VFE why not use last_website_so_id field ?
|
||||
last_sale_order = self.env['sale.order'].sudo().search(
|
||||
@@ -426,8 +425,11 @@ class Website(models.Model):
|
||||
limit=1,
|
||||
order="date_order desc, id desc",
|
||||
)
|
||||
if last_sale_order and last_sale_order.partner_shipping_id.active: # first = me
|
||||
addr['delivery'] = last_sale_order.partner_shipping_id.id
|
||||
if last_sale_order:
|
||||
if last_sale_order.partner_shipping_id.active: # first = me
|
||||
addr['delivery'] = last_sale_order.partner_shipping_id.id
|
||||
if last_sale_order.partner_invoice_id.active:
|
||||
addr['invoice'] = last_sale_order.partner_invoice_id.id
|
||||
|
||||
affiliate_id = request.session.get('affiliate_id')
|
||||
salesperson_user_sudo = self.env['res.users'].sudo().browse(affiliate_id).exists()
|
||||
@@ -439,7 +441,7 @@ class Website(models.Model):
|
||||
|
||||
'fiscal_position_id': self.fiscal_position_id.id,
|
||||
'partner_id': partner_sudo.id,
|
||||
'partner_invoice_id': partner_sudo.id,
|
||||
'partner_invoice_id': addr['invoice'],
|
||||
'partner_shipping_id': addr['delivery'],
|
||||
|
||||
'pricelist_id': self.pricelist_id.id,
|
||||
|
||||
@@ -796,6 +796,7 @@ publicWidget.registry.WebsiteSaleLayout = publicWidget.Widget.extend({
|
||||
publicWidget.registry.websiteSaleCart = publicWidget.Widget.extend({
|
||||
selector: '.oe_website_sale .oe_cart',
|
||||
events: {
|
||||
'click .js_change_billing': '_onClickChangeBilling',
|
||||
'click .js_change_shipping': '_onClickChangeShipping',
|
||||
'click .js_edit_address': '_onClickEditAddress',
|
||||
'click .js_delete_product': '_onClickDeleteProduct',
|
||||
@@ -805,19 +806,33 @@ publicWidget.registry.websiteSaleCart = publicWidget.Widget.extend({
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onClickChangeBilling: function (ev) {
|
||||
this._onClickChangeAddress(ev, 'all_billing', 'js_change_billing');
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onClickChangeShipping: function (ev) {
|
||||
var $old = $('.all_shipping').find('.card.border.border-primary');
|
||||
$old.find('.btn-ship').toggle();
|
||||
$old.addClass('js_change_shipping');
|
||||
this._onClickChangeAddress(ev, 'all_shipping', 'js_change_shipping');
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onClickChangeAddress: function (ev, rowAddrClass, cardClass) {
|
||||
var $old = $(`.${rowAddrClass}`).find('.card.border.border-primary');
|
||||
$old.find('.btn-addr').toggle();
|
||||
$old.addClass(cardClass);
|
||||
$old.removeClass('border border-primary');
|
||||
|
||||
var $new = $(ev.currentTarget).parent('div.one_kanban').find('.card');
|
||||
$new.find('.btn-ship').toggle();
|
||||
$new.removeClass('js_change_shipping');
|
||||
$new.find('.btn-addr').toggle();
|
||||
$new.removeClass(cardClass);
|
||||
$new.addClass('border border-primary');
|
||||
|
||||
var $form = $(ev.currentTarget).parent('div.one_kanban').find('form.d-none');
|
||||
|
||||
@@ -464,7 +464,7 @@ tr#empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.js_change_shipping {
|
||||
.js_change_shipping, .js_change_billing {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@
|
||||
},
|
||||
{
|
||||
content: "Add new shipping address",
|
||||
trigger: '.one_kanban form[action^="/shop/address"] .btn',
|
||||
trigger: '.all_shipping .one_kanban form[action^="/shop/address"] .btn',
|
||||
},
|
||||
{
|
||||
content: "Fulfill shipping address form",
|
||||
|
||||
@@ -146,6 +146,7 @@ class TestWebsiteSaleCheckoutAddress(TransactionCaseWithUserDemo, HttpCaseWithUs
|
||||
'name': 'a res.partner address', 'email': 'email@email.email', 'street': 'ooo',
|
||||
'city': 'ooo', 'zip': '1200', 'country_id': self.country_id, 'submitted': 1,
|
||||
}
|
||||
self.default_billing_address_values = self.default_address_values | {'is_invoice': 1}
|
||||
|
||||
def _create_so(self, partner_id=None, company_id=None):
|
||||
values = {
|
||||
@@ -165,7 +166,7 @@ class TestWebsiteSaleCheckoutAddress(TransactionCaseWithUserDemo, HttpCaseWithUs
|
||||
return self.env['sale.order'].create(values)
|
||||
|
||||
def _get_last_address(self, partner):
|
||||
''' Useful to retrieve the last created shipping address '''
|
||||
""" Useful to retrieve the last created address (shipping or billing) """
|
||||
return partner.child_ids.sorted('id', reverse=True)[0]
|
||||
|
||||
# TEST WEBSITE
|
||||
@@ -233,7 +234,7 @@ class TestWebsiteSaleCheckoutAddress(TransactionCaseWithUserDemo, HttpCaseWithUs
|
||||
|
||||
# 2. Logged in user/internal user, should not edit name or email address of billing
|
||||
self.default_address_values['partner_id'] = self.demo_partner.id
|
||||
self.WebsiteSaleController.address(**self.default_address_values)
|
||||
self.WebsiteSaleController.address(**self.default_billing_address_values)
|
||||
self.assertEqual(self.demo_partner.company_id, self.company_c, "Logged in user edited billing (the partner itself) should not get its company modified.")
|
||||
self.assertNotEqual(self.demo_partner.name, self.default_address_values['name'], "Employee cannot change their name during the checkout process.")
|
||||
self.assertNotEqual(self.demo_partner.email, self.default_address_values['email'], "Employee cannot change their email during the checkout process.")
|
||||
@@ -257,7 +258,7 @@ class TestWebsiteSaleCheckoutAddress(TransactionCaseWithUserDemo, HttpCaseWithUs
|
||||
|
||||
# 2. Public user, edit billing
|
||||
self.default_address_values['partner_id'] = new_partner.id
|
||||
self.WebsiteSaleController.address(**self.default_address_values)
|
||||
self.WebsiteSaleController.address(**self.default_billing_address_values)
|
||||
self.assertEqual(new_partner.company_id, self.website.company_id, "Public user edited billing (the partner itself) should not get its company modified.")
|
||||
|
||||
def test_04_apply_empty_pl(self):
|
||||
@@ -353,7 +354,7 @@ class TestWebsiteSaleCheckoutAddress(TransactionCaseWithUserDemo, HttpCaseWithUs
|
||||
|
||||
# 2. Portal user, edit billing
|
||||
self.default_address_values['partner_id'] = self.portal_partner.id
|
||||
self.WebsiteSaleController.address(**self.default_address_values)
|
||||
self.WebsiteSaleController.address(**self.default_billing_address_values)
|
||||
# Name cannot be changed if there are issued invoices
|
||||
self.assertNotEqual(self.portal_partner.name, self.default_address_values['name'], "Portal User should not be able to change the name if they have invoices under their name.")
|
||||
|
||||
@@ -383,7 +384,7 @@ class TestWebsiteSaleCheckoutAddress(TransactionCaseWithUserDemo, HttpCaseWithUs
|
||||
{'name': 'Tax 20% excl', 'amount': 20, 'price_include': False},
|
||||
{'name': 'Tax 15% incl', 'amount': 15, 'price_include': True},
|
||||
])
|
||||
self.env['account.fiscal.position'].create([
|
||||
fpos_be, fpos_nl = self.env['account.fiscal.position'].create([
|
||||
{
|
||||
'sequence': 1,
|
||||
'name': 'BE',
|
||||
@@ -426,13 +427,64 @@ class TestWebsiteSaleCheckoutAddress(TransactionCaseWithUserDemo, HttpCaseWithUs
|
||||
req.httprequest.method = "POST"
|
||||
|
||||
self.WebsiteSaleController.address(**be_address_POST)
|
||||
self.assertEqual(
|
||||
so.fiscal_position_id,
|
||||
fpos_be,
|
||||
)
|
||||
self.assertEqual(
|
||||
[so.amount_untaxed, so.amount_tax, so.amount_total],
|
||||
[90.91, 18.18, 109.09] # (100 : (1 + 10%)) * (1 + 20%) = 109.09
|
||||
)
|
||||
|
||||
self.WebsiteSaleController.address(**nl_address_POST)
|
||||
self.assertEqual(
|
||||
so.fiscal_position_id,
|
||||
fpos_nl,
|
||||
)
|
||||
self.assertEqual(
|
||||
[so.amount_untaxed, so.amount_tax, so.amount_total],
|
||||
[90.91, 13.64, 104.55] # (100 : (1 + 10%)) * (1 + 15%) = 104.55
|
||||
)
|
||||
|
||||
def test_08_new_user_address_state(self):
|
||||
''' Test the billing and shipping addresses creation values. '''
|
||||
self._setUp_multicompany_env()
|
||||
so = self._create_so(self.demo_partner.id)
|
||||
|
||||
env = api.Environment(self.env.cr, self.demo_user.id, {})
|
||||
# change also website env for `sale_get_order` to not change order partner_id
|
||||
with MockRequest(env, website=self.website.with_env(env), sale_order_id=so.id) as req:
|
||||
req.httprequest.method = "POST"
|
||||
|
||||
# check the default values
|
||||
self.assertEqual(self.demo_partner, so.partner_invoice_id)
|
||||
self.assertEqual(self.demo_partner, so.partner_shipping_id)
|
||||
|
||||
# 1. Logged-in user, new shipping
|
||||
self.WebsiteSaleController.address(**self.default_address_values)
|
||||
new_shipping = self._get_last_address(self.demo_partner)
|
||||
msg = "New shipping address should have its type set as 'delivery'"
|
||||
self.assertTrue(new_shipping.type == 'delivery', msg)
|
||||
|
||||
# 2. Logged-in user, new billing
|
||||
self.WebsiteSaleController.address(**self.default_billing_address_values)
|
||||
new_billing = self._get_last_address(self.demo_partner)
|
||||
msg = "New billing should have its type set as 'invoice'"
|
||||
self.assertTrue(new_billing.type == 'invoice', msg)
|
||||
self.assertNotEqual(new_billing, self.demo_partner)
|
||||
|
||||
# 3. Check that invoice/shipping address of so changed
|
||||
self.assertEqual(new_billing, so.partner_invoice_id)
|
||||
self.assertEqual(new_shipping, so.partner_shipping_id)
|
||||
|
||||
# 4. Logged-in user, new billing use same
|
||||
use_same = self.default_billing_address_values | {'use_same': 1}
|
||||
self.WebsiteSaleController.address(**use_same)
|
||||
new_billing_use_same = self._get_last_address(self.demo_partner)
|
||||
msg = "New billing use same should have its type set as 'other'"
|
||||
self.assertTrue(new_billing_use_same.type == 'other', msg)
|
||||
self.assertNotEqual(new_billing, self.demo_partner)
|
||||
|
||||
# 5. Check that invoice/shipping address of so changed
|
||||
self.assertEqual(new_billing_use_same, so.partner_invoice_id)
|
||||
self.assertEqual(new_billing_use_same, so.partner_shipping_id)
|
||||
|
||||
@@ -1735,7 +1735,7 @@
|
||||
<t t-set="no_footer" t-value="1"/>
|
||||
<div id="wrap">
|
||||
<div class="container oe_website_sale py-2">
|
||||
<t t-set="same_shipping" t-value="bool(order.partner_shipping_id==order.partner_id or only_services)" />
|
||||
<t t-set="same_shipping" t-value="bool(order.partner_shipping_id==order.partner_invoice_id or only_services)" />
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<t t-call="website_sale.wizard_checkout">
|
||||
@@ -1752,47 +1752,23 @@
|
||||
<div class="col-lg-12">
|
||||
<h3 class="o_page_header mt8">Billing Address</h3>
|
||||
</div>
|
||||
<div class="col-lg-6 one_kanban">
|
||||
<t t-call="website_sale.address_kanban">
|
||||
<t t-set='contact' t-value="order.partner_id"/>
|
||||
<t t-set='selected' t-value="1"/>
|
||||
<t t-set='readonly' t-value="1"/>
|
||||
<t t-set='allow_edit' t-value="1"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<t t-call="website_sale.row_addresses">
|
||||
<t t-set="order" t-value="order"/>
|
||||
<t t-set="is_invoice" t-value="1"/>
|
||||
<t t-set="addresses" t-value="billings"/>
|
||||
</t>
|
||||
<t t-if="not only_services" groups="account.group_delivery_invoice_address">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h3 class="o_page_header mt16 mb4">Shipping Address</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row all_shipping">
|
||||
<div class="col-lg-12">
|
||||
<div class="row mt8">
|
||||
<div class="col-md-12 col-lg-12 one_kanban">
|
||||
<form action="/shop/address" method="post" class=''>
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" t-nocache="The csrf token must always be up to date."/>
|
||||
<a role="button" href="#" class='a-submit btn btn-secondary mb-2 btn-block'>
|
||||
<i class="fa fa-plus-square"/>
|
||||
<span>Add an address</span>
|
||||
</a>
|
||||
</form>
|
||||
</div>
|
||||
<t t-foreach="shippings" t-as="ship">
|
||||
<div class="col-md-12 col-lg-6 one_kanban mb-2">
|
||||
<t t-call="website_sale.address_kanban">
|
||||
<t t-set='contact' t-value="ship"/>
|
||||
<t t-set='selected' t-value="order.partner_shipping_id==ship"/>
|
||||
<t t-set='readonly' t-value="bool(len(shippings)==1)"/>
|
||||
<t t-set='edit_billing' t-value="bool(ship==order.partner_id)"/>
|
||||
<t t-set="allow_edit" t-value="not order.partner_id or (ship.id in order.partner_id.child_ids.ids)" />
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<t t-call="website_sale.row_addresses">
|
||||
<t t-set="order" t-value="order"/>
|
||||
<t t-set="is_invoice" t-value="0"/>
|
||||
<t t-set="addresses" t-value="shippings"/>
|
||||
</t>
|
||||
</t>
|
||||
<div class="d-flex justify-content-between mt-3">
|
||||
<a role="button" href="/shop/cart" class="btn btn-secondary mb32">
|
||||
@@ -1810,29 +1786,71 @@
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="row_addresses">
|
||||
<div t-attf-class="row {{'all_billing' if is_invoice else 'all_shipping'}}">
|
||||
<div class="col-lg-12">
|
||||
<div class="row mt8">
|
||||
<div class="col-md-12 col-lg-12 one_kanban">
|
||||
<form action="/shop/address" method="post" class="">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" t-nocache="The csrf token must always be up to date."/>
|
||||
<t t-if="is_invoice">
|
||||
<input type="hidden" name="is_invoice" t-att-value="is_invoice" />
|
||||
<input type="hidden" name="callback" value="/shop/checkout" />
|
||||
</t>
|
||||
<a role="button" href="#" class="a-submit btn btn-secondary mb-2 btn-block">
|
||||
<i class="fa fa-plus-square"/>
|
||||
<span>Add an address</span>
|
||||
</a>
|
||||
</form>
|
||||
</div>
|
||||
<div t-foreach="addresses" t-as="addr"
|
||||
class="col-md-12 col-lg-6 one_kanban mb-2">
|
||||
<t t-call="website_sale.address_kanban">
|
||||
<t t-set="is_invoice" t-value="is_invoice"/>
|
||||
<t t-set="contact" t-value="addr"/>
|
||||
<t t-set="order" t-value="order"/>
|
||||
<t t-if="not is_invoice" t-set="selected" t-value="order.partner_shipping_id==addr or len(addresses)==1"/>
|
||||
<t t-if="is_invoice" t-set="selected" t-value="order.partner_invoice_id==addr or len(addresses)==1"/>
|
||||
<t t-set="readonly" t-value="bool(len(addresses)==1)"/>
|
||||
<t t-set="allow_edit"
|
||||
t-value="(website.is_public_user() or order.partner_id.id == addr.id or (addr.id in order.partner_id.commercial_partner_id.child_ids.ids))
|
||||
and is_invoice or addr.type=='delivery'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template id="address_kanban" name="Kanban address">
|
||||
<form action="/shop/checkout" method="POST" class="d-none">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" t-nocache="The csrf token must always be up to date."/>
|
||||
<input type="hidden" name="partner_id" t-att-value="contact.id" />
|
||||
<t t-if='edit_billing'>
|
||||
<input type="hidden" name="callback" value="/shop/checkout?use_billing" />
|
||||
<t t-if="is_invoice">
|
||||
<input type="hidden" name="is_invoice" t-att-value="is_invoice"/>
|
||||
<input type="hidden" name="callback" value="/shop/checkout"/>
|
||||
</t>
|
||||
<input type='submit'/>
|
||||
<input type="submit"/>
|
||||
</form>
|
||||
<div t-attf-class="card #{selected and 'border border-primary' or 'js_change_shipping'}">
|
||||
<div class='card-body' style='min-height: 130px;'>
|
||||
<div t-attf-class="card #{selected and 'border border-primary' or is_invoice and 'js_change_billing' or 'js_change_shipping'}">
|
||||
<div class="card-body" style="min-height: 130px;">
|
||||
<a t-if="allow_edit" href="#" class="btn btn-link float-end p-0 js_edit_address no-decoration" role="button" title="Edit this address" aria-label="Edit this address"><i class='fa fa-edit'/></a>
|
||||
<t t-esc="contact" t-options="dict(widget='contact', fields=['name', 'address'], no_marker=True)"/>
|
||||
</div>
|
||||
<div class='card-footer' t-if='not readonly'>
|
||||
<span class='btn-ship' t-att-style="'' if selected else 'display:none;'">
|
||||
<a role="button" href='#' class="btn btn-block btn-primary">
|
||||
<i class='fa fa-check'></i> Ship to this address
|
||||
<div class="card-footer" t-if="not readonly">
|
||||
<span class="btn-addr" t-att-style="'' if selected else 'display:none;'">
|
||||
<a role="button" href="#" class="btn btn-block btn-primary">
|
||||
<i class="fa fa-check"/>
|
||||
<t t-if="is_invoice">
|
||||
Bill to this address
|
||||
</t>
|
||||
<t t-else="">
|
||||
Ship to this address
|
||||
</t>
|
||||
</a>
|
||||
</span>
|
||||
<span class='btn-ship' t-att-style="'' if not selected else 'display:none;'">
|
||||
<a role="button" href='#' class="btn btn-block btn-secondary">
|
||||
<span class="btn-addr" t-att-style="'' if not selected else 'display:none;'">
|
||||
<a role="button" href="#" class="btn btn-block btn-secondary">
|
||||
Select this address
|
||||
</a>
|
||||
</span>
|
||||
@@ -1861,14 +1879,14 @@
|
||||
</div>
|
||||
<div class="col-12 col-xl order-xl-1 oe_cart">
|
||||
<div>
|
||||
<t t-if="mode == ('new', 'billing')">
|
||||
<t t-if="mode == ('new', 'billing') and is_public_user">
|
||||
<h2 class="o_page_header mt8">
|
||||
<span t-if="account_on_checkout != 'mandatory'">Fill in your address</span>
|
||||
<small class="text-muted" t-if="account_on_checkout == 'optional'"> or </small>
|
||||
<a t-if="account_on_checkout != 'disabled'" role="button" href='/web/login?redirect=/shop/checkout' style="margin-top: -11px"> Sign in</a>
|
||||
</h2>
|
||||
</t>
|
||||
<t t-if="mode == ('edit', 'billing')">
|
||||
<t t-elif="mode == ('edit', 'billing') or mode[1] == 'billing' and not is_public_user">
|
||||
<h2 class="o_page_header mt8">Your Address</h2>
|
||||
</t>
|
||||
<t t-if="mode[1] == 'shipping'">
|
||||
@@ -1975,8 +1993,10 @@
|
||||
<div class="col-lg-12">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id='shipping_use_same' class="mr8" name='use_same' value="1" checked='checked'/>Ship to the same address
|
||||
<span class='ship_to_other text-muted' style="display: none">&nbsp;(<i>Your shipping address will be requested later) </i></span>
|
||||
<input type="checkbox" id='shipping_use_same' class="mr8" name='use_same' value="1" t-att-checked='website.is_public_user()'/>Ship to the same address
|
||||
<t t-if='website.is_public_user()'>
|
||||
<span class='ship_to_other text-muted' style="display: none">&nbsp;(<i>Your shipping address will be requested later) </i></span>
|
||||
</t>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1987,6 +2007,9 @@
|
||||
<input type="hidden" name="submitted" value="1" />
|
||||
<input type="hidden" name="partner_id" t-att-value="partner_id or '0'" />
|
||||
<input type="hidden" name="callback" t-att-value="callback" />
|
||||
<t t-if="mode[1] == 'billing'">
|
||||
<input type="hidden" name="is_invoice" value="1" />
|
||||
</t>
|
||||
<!-- Example -->
|
||||
<input type="hidden" name="field_required" t-att-value="'phone,name'" />
|
||||
|
||||
@@ -2015,10 +2038,10 @@
|
||||
<div class="card">
|
||||
<div class="card-body" id="shipping_and_billing">
|
||||
<a class="float-end no-decoration" href="/shop/checkout"><i class="fa fa-edit"/> Edit</a>
|
||||
<t t-set="same_shipping" t-value="bool(order.partner_shipping_id==order.partner_id or only_services)" />
|
||||
<t t-set="same_shipping" t-value="bool(order.partner_shipping_id==order.partner_invoice_id or only_services)" />
|
||||
<div>
|
||||
<b>Billing<t t-if="same_shipping and not only_services"> & Shipping</t>: </b>
|
||||
<span t-esc="order.partner_id" t-options="dict(widget='contact', fields=['address'], no_marker=True, separator=', ')" class="address-inline"/>
|
||||
<span t-esc="order.partner_invoice_id" t-options="dict(widget='contact', fields=['address'], no_marker=True, separator=', ')" class="address-inline"/>
|
||||
</div>
|
||||
<div t-if="not same_shipping and not only_services" groups="account.group_delivery_invoice_address">
|
||||
<b>Shipping: </b>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="website_sale_mondialrelay_checkout" inherit_id="website_sale.checkout">
|
||||
<template id="website_sale_mondialrelay_checkout" inherit_id="website_sale.row_addresses">
|
||||
<xpath expr="//t[@t-set='allow_edit']" position="after">
|
||||
<t t-set="allow_edit" t-value="allow_edit and not contact.is_mondialrelay"/>
|
||||
</xpath>
|
||||
|
||||
@@ -1760,12 +1760,12 @@ actual arch.
|
||||
pass
|
||||
elif node.tag == 'input' and node.get('type') in ('button', 'submit', 'reset'):
|
||||
pass
|
||||
elif any(klass in classes for klass in ('btn-group', 'btn-toolbar', 'btn-ship')):
|
||||
elif any(klass in classes for klass in ('btn-group', 'btn-toolbar', 'btn-addr')):
|
||||
pass
|
||||
else:
|
||||
msg = ("A simili button must be in tag a/button/select or tag `input` "
|
||||
"with type button/submit/reset or have class in "
|
||||
"btn-group/btn-toolbar/btn-ship")
|
||||
"btn-group/btn-toolbar/btn-addr")
|
||||
self._log_view_warning(msg, node)
|
||||
|
||||
def _validate_fa_class_accessibility(self, node, description):
|
||||
|
||||
@@ -3040,7 +3040,7 @@ class TestViews(ViewCase):
|
||||
self.assertValid('<form><input type="reset" class="btn" role="button"/></form>')
|
||||
self.assertValid('<form><div type="reset" class="btn btn-group" role="button"/></form>')
|
||||
self.assertValid('<form><div type="reset" class="btn btn-toolbar" role="button"/></form>')
|
||||
self.assertValid('<form><div type="reset" class="btn btn-ship" role="button"/></form>')
|
||||
self.assertValid('<form><div type="reset" class="btn btn-addr" role="button"/></form>')
|
||||
self.assertWarning('<form><div class="btn" role="button"/></form>')
|
||||
self.assertWarning('<form><input type="email" class="btn" role="button"/></form>')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user