Hi Pascal
Due to that is a nested local function there is no way that you could extend that function using any kind of JS inheritance technique, you could only have at least 2 choices here.
1- You could disconnect that js file using Odoo xml extension like:
<template id="assets_frontend" inherit_id="website.assets_frontend" name="Shop">
<xpath expr="//script[@src='/website_sale/static/src/js/website_sale.js']" position="remove"/>
<xpath expr="." position="inside">
<script type="text/javascript" src="/your_website_sale_module/static/src/js/website_sale.js"></script>
</xpath>
</template>
And provide yours with the proper changes, that is a valid way to change JS in extreme scenarios in Odoo
2- Disconnect the events binding functions that use the price_to_str function in order to rebind it with a new version of the event binding using a new local nested version of the price_to_str function, using jquery.off
http://api.jquery.com/off/
Like:s
$(document).ready(function () {
function update_product_image(event_source, product_id) {
var $img = $(event_source).closest('tr.js_product, .oe_website_sale').find('span[data-oe-model^="product."][data-oe-type="image"] img:first, img.product_detail_img');
$img.attr("src", "/website/image/product.product/" + product_id + "/image");
$img.parent().attr('data-oe-model', 'product.product').attr('data-oe-id', product_id)
.data('oe-model', 'product.product').data('oe-id', product_id);
}
$('.oe_website_sale').each(function () {
var oe_website_sale = this;
//your new function
function price_to_str(price) {
price = Math.round(price * 100) / 100;
var dec = Math.round((price % 1) * 100);
return price + (dec ? '' : '.0') + (dec%10 ? '' : '0');
}
$(oe_website_sale).off('change', 'input.js_product_change');
$(oe_website_sale).on('change', 'input.js_product_change', function (ev) {
var $parent = $(this).closest('.js_product');
$parent.find(".oe_default_price:first .oe_currency_value").html( price_to_str(+$(this).data('lst_price')) );
$parent.find(".oe_price:first .oe_currency_value").html(price_to_str(+$(this).data('price')) );
update_product_image(this, +$(this).val());
});
$(oe_website_sale).off('change', 'input.js_variant_change, select.js_variant_change, ul[data-attribute_value_ids]');
$(oe_website_sale).on('change', 'input.js_variant_change, select.js_variant_change, ul[data-attribute_value_ids]', function (ev) {
var $ul = $(ev.target).closest('.js_add_cart_variants');
var $parent = $ul.closest('.js_product');
var $product_id = $parent.find('input.product_id').first();
var $price = $parent.find(".oe_price:first .oe_currency_value");
var $default_price = $parent.find(".oe_default_price:first .oe_currency_value");
var $optional_price = $parent.find(".oe_optional:first .oe_currency_value");
var variant_ids = $ul.data("attribute_value_ids");
var values = [];
$parent.find('input.js_variant_change:checked, select.js_variant_change').each(function () {
values.push(+$(this).val());
});
$parent.find("label").removeClass("text-muted css_not_available");
var product_id = false;
for (var k in variant_ids) {
if (_.isEmpty(_.difference(variant_ids[k][1], values))) {
$price.html(price_to_str(variant_ids[k][2]));
$default_price.html(price_to_str(variant_ids[k][3]));
if (variant_ids[k][3]-variant_ids[k][2]>0.2) {
$default_price.closest('.oe_website_sale').addClass("discount");
$optional_price.closest('.oe_optional').show().css('text-decoration', 'line-through');
} else {
$default_price.closest('.oe_website_sale').removeClass("discount");
$optional_price.closest('.oe_optional').hide();
}
product_id = variant_ids[k][0];
break;
}
}
if (product_id) {
update_product_image(this, product_id);
}
$parent.find("input.js_variant_change:radio, select.js_variant_change").each(function () {
var $input = $(this);
var id = +$input.val();
var values = [id];
$parent.find("ul:not(:has(input.js_variant_change[value='" + id + "'])) input.js_variant_change:checked, select").each(function () {
values.push(+$(this).val());
});
for (var k in variant_ids) {
if (!_.difference(values, variant_ids[k][1]).length) {
return;
}
}
$input.closest("label").addClass("css_not_available");
$input.find("option[value='" + id + "']").addClass("css_not_available");
});
if (product_id) {
$parent.removeClass("css_not_available");
$product_id.val(product_id);
$parent.find(".js_check_product").removeAttr("disabled");
} else {
$parent.addClass("css_not_available");
$product_id.val(0);
$parent.find(".js_check_product").attr("disabled", "disabled");
}
});
});
});
That I think that is the one choice you could do if you don't want to use the first choice to provide a new version of the whole file recommended in this case
Hope this help you