19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:32:12 +01:00
parent 79f83631d5
commit 73afc09215
6267 changed files with 1534193 additions and 1130106 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 3 KiB

Before After
Before After

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="70" height="70" viewBox="0 0 70 70"><defs><path id="a" d="M4 0h61c4 0 5 1 5 5v60c0 4-1 5-5 5H4c-3 0-4-1-4-5V5c0-4 1-5 4-5z"/><linearGradient id="c" x1="98.162%" x2="0%" y1="1.838%" y2="100%"><stop offset="0%" stop-color="#797DA5"/><stop offset="50.799%" stop-color="#6D7194"/><stop offset="100%" stop-color="#626584"/></linearGradient><path id="d" d="M50 31.035a3.5 3.5 0 1 0 0 6.93v3.06a1.633 1.633 0 0 1-1.588 1.286L26.974 43l.998 4h18.955c.998 0 .998 2 0 2h-20.95L19.99 25h-1.996v1c0 .667-.332 1-.997 1S16 26.667 16 26v-2c.066-.667.398-1 .998-1h3.99c.516 0 .848.333.998 1l.998 3.281 25.428.037c1.045 0 1.588.761 1.588 2.018v1.7zM45.43 55a2.497 2.497 0 0 1-2.493-2.5c0-1.38 1.116-2.5 2.494-2.5a2.497 2.497 0 0 1 2.494 2.5c0 1.38-1.117 2.5-2.494 2.5zm-18.955 0a2.497 2.497 0 0 1-2.494-2.5c0-1.38 1.117-2.5 2.494-2.5a2.497 2.497 0 0 1 2.495 2.5c0 1.38-1.117 2.5-2.495 2.5zm12.7-24.996a.51.51 0 0 0-.409.2l-7.668 9.056c-.233.31.004.738.41.738l.696-.002a.51.51 0 0 0 .409-.2l7.63-9.056c.234-.312-.005-.74-.41-.738l-.658.002zm-5.841 4.371c1.29 0 2.334-.979 2.334-2.188 0-1.208-1.044-2.187-2.334-2.187S31 30.979 31 32.188c0 1.208 1.044 2.187 2.334 2.187zm0-3.125c.552 0 1 .42 1 .938 0 .517-.448.937-1 .937s-1-.42-1-.938c0-.517.448-.937 1-.937zm4.668 4.375c-1.29 0-2.334.979-2.334 2.188 0 1.208 1.044 2.187 2.334 2.187s2.334-.979 2.334-2.188c0-1.208-1.044-2.187-2.334-2.187zm0 3.125c-.553 0-1-.42-1-.938 0-.517.447-.937 1-.937.552 0 1 .42 1 .938 0 .517-.448.937-1 .937z"/><path id="e" d="M50 30a3 3 0 0 0 0 6v3.025a1.633 1.633 0 0 1-1.588 1.286L26.974 41l.998 4h18.955c.998 0 .998 2 0 2h-20.95L19.99 23h-1.996v1c0 .667-.332 1-.997 1S16 24.667 16 24v-2c.066-.667.398-1 .998-1h3.99c.516 0 .848.333.998 1l.998 3.281 25.428.037c1.045 0 1.588.761 1.588 2.018V30zm-4.57 23a2.497 2.497 0 0 1-2.493-2.5c0-1.38 1.116-2.5 2.494-2.5a2.497 2.497 0 0 1 2.494 2.5c0 1.38-1.117 2.5-2.494 2.5zm-18.955 0a2.497 2.497 0 0 1-2.494-2.5c0-1.38 1.117-2.5 2.494-2.5a2.497 2.497 0 0 1 2.495 2.5c0 1.38-1.117 2.5-2.495 2.5zM48 26a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm0 11a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm-8.825-8.996a.51.51 0 0 0-.409.2l-7.668 9.056c-.233.31.004.738.41.738l.696-.002a.51.51 0 0 0 .409-.2l7.63-9.056c.234-.312-.005-.74-.41-.738l-.658.002zm-5.841 4.371c1.29 0 2.334-.979 2.334-2.188 0-1.208-1.044-2.187-2.334-2.187S31 28.979 31 30.188c0 1.208 1.044 2.187 2.334 2.187zm0-3.125c.552 0 1 .42 1 .938 0 .517-.448.937-1 .937s-1-.42-1-.938c0-.517.448-.937 1-.937zm4.668 4.375c-1.29 0-2.334.979-2.334 2.188 0 1.208 1.044 2.187 2.334 2.187s2.334-.979 2.334-2.188c0-1.208-1.044-2.187-2.334-2.187zm0 3.125c-.553 0-1-.42-1-.938 0-.517.447-.937 1-.937.552 0 1 .42 1 .938 0 .517-.448.937-1 .937z"/></defs><g fill="none" fill-rule="evenodd"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><g mask="url(#b)"><path fill="url(#c)" d="M0 0H70V70H0z"/><path fill="#FFF" fill-opacity=".383" d="M4 1h61c2.667 0 4.333.667 5 2V0H0v3c.667-1.333 2-2 4-2z"/><path fill="#393939" d="M33.317 69H4c-2 0-4-1-4-4V38.29l16.258-16.95L21 21l2 5h26.583l.371 3.987-3.862 4.883 3.764 1.445-.11 3.27L45.076 45H47l.507 1.61-1.993 1.943 1.815 3.434L33.317 69z" opacity=".324"/><path fill="#000" fill-opacity=".383" d="M4 69h61c2.667 0 4.333-1 5-3v4H0v-4c.667 2 2 3 4 3z"/><use fill="#000" fill-rule="nonzero" opacity=".3" xlink:href="#d"/><use fill="#FFF" fill-rule="nonzero" xlink:href="#e"/></g></g></svg>
<svg width="50" height="50" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M15.724 6.397C16.377 4.94 17.852 4 19.481 4h11.037c1.63 0 3.104.94 3.757 2.397L37.236 13H41.9c2.46 0 4.367 2.099 4.07 4.481l-3.106 25C42.613 44.49 40.866 46 38.793 46H11.207c-2.074 0-3.82-1.51-4.07-3.519l-3.107-25C3.734 15.1 5.64 13 8.1 13h4.663l2.961-6.603ZM32.917 13H17.082c0-.56.123-1.134.39-1.691l.956-2C19.102 7.9 20.551 7 22.144 7h5.711c1.593 0 3.042.9 3.716 2.308l.957 2c.266.558.39 1.132.39 1.692Z" fill="#F9464C"/><path fill-rule="evenodd" clip-rule="evenodd" d="M8.514 45.016a3.963 3.963 0 0 1-1.377-2.535l-3.107-25C3.734 15.1 5.64 13 8.1 13h4.663l2.961-6.603C16.377 4.94 17.852 4 19.481 4h11.037c1.63 0 3.104.94 3.757 2.397l2.59 5.777C35.5 28.256 23.848 41.405 8.515 45.016ZM17.082 13h15.835c0-.56-.123-1.134-.39-1.691l-.956-2C30.897 7.9 29.448 7 27.855 7h-5.711c-1.593 0-3.042.9-3.716 2.308l-.956 2a3.904 3.904 0 0 0-.39 1.692Z" fill="#FC868B"/><path d="M17.142 20.864a4 4 0 0 1 4.899-2.828l11.59 3.106a4 4 0 0 1 2.83 4.899l-3.107 11.59a4 4 0 0 1-4.899 2.83l-11.59-3.107a4 4 0 0 1-2.83-4.899l3.107-11.59Z" fill="#fff"/><path d="M23.135 28.615A2.828 2.828 0 1 1 26.28 26.8l3.3-1.906c.492-.284 1.056-.23 1.258.122l.919 1.59-5.188 2.996a2.828 2.828 0 1 1-2.726 1.574l-3.666 2.116c-.492.284-1.055.23-1.258-.122L18 31.58l5.135-2.965Z" fill="#F9464C"/></svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,33 @@
import { patch } from '@web/core/utils/patch';
import { Checkout } from '@website_sale/interactions/checkout';
patch(Checkout.prototype, {
/**
* @override method from `@website_sale/interactions/checkout`
*/
_updateCartSummary(result, targetEl) {
super._updateCartSummary(...arguments);
if (result.amount_delivery_discounted) {
// Update discount of the order
const cart_summary_shipping_reward = targetEl.querySelector(
'[data-reward-type="shipping"]'
);
if (cart_summary_shipping_reward) {
cart_summary_shipping_reward.innerHTML = result.amount_delivery_discounted;
}
}
if (result.discount_reward_amounts) {
const cart_summary_discount_rewards = targetEl.querySelectorAll(
'[data-reward-type=discount]'
);
if (cart_summary_discount_rewards.length !== result.discount_reward_amounts.length) {
// refresh cart summary to sync number of discount items
location.reload();
} else {
cart_summary_discount_rewards.forEach(
(el, i) => (el.innerHTML = result.discount_reward_amounts[i])
);
}
}
},
});

View file

@ -0,0 +1,34 @@
import { Interaction } from "@web/public/interaction";
import { registry } from "@web/core/registry";
export class CouponToaster extends Interaction {
static selector = ".coupon-message";
start() {
let options = {};
const titleEl = this.el.querySelector(".coupon-message-title");
const contentEl = this.el.querySelector(".coupon-message-content");
let message = null;
if (contentEl) {
message = contentEl.innerHTML;
if (titleEl) {
Object.assign(options, { title: titleEl.innerHTML });
}
} else if (titleEl) {
message = titleEl.innerHTML;
}
if (this.el.classList.contains("coupon-info-message")) {
this.services.notification.add(message, Object.assign({ type: "success" }, options));
} else if (this.el.classList.contains("coupon-error-message")) {
this.services.notification.add(message, Object.assign({ type: "danger" }, options));
} else if (this.el.classList.contains("coupon-warning-message")) {
this.services.notification.add(message, Object.assign({ type: "warning" }, options));
}
}
}
registry
.category("public.interactions")
.add("website_sale_loyalty.coupon_toaster", CouponToaster);

View file

@ -0,0 +1,23 @@
import { Interaction } from "@web/public/interaction";
import { registry } from "@web/core/registry";
import { browser } from "@web/core/browser/browser";
export class GiftCardCopy extends Interaction {
static selector = ".o_purchased_gift_card .copy-to-clipboard";
dynamicContent = {
_root: { "t-on-click": this.onClick },
};
/**
* @param {MouseEvent} ev
*/
onClick(ev) {
const textValue = ev.target.dataset.clipboardText;
browser.navigator.clipboard.writeText(textValue);
}
}
registry
.category("public.interactions")
.add("website_sale_loyalty.gift_card_copy", GiftCardCopy);

View file

@ -1,36 +0,0 @@
/** @odoo-module **/
import publicWidget from 'web.public.widget';
import {registry} from "@web/core/registry";
const CouponToasterWidget = publicWidget.Widget.extend({
start() {
let options = {};
const $content = this.$('.coupon-message-content');
const $title = this.$('.coupon-message-title');
if ($content.length) {
Object.assign(options, {message: $content[0].innerHTML});
}
if ($title.length) {
Object.assign(options, {title: $title[0].innerHTML});
}
if (this.$el.hasClass('coupon-info-message')) {
this.displayNotification(Object.assign({type: 'success'}, options));
} else if (this.$el.hasClass('coupon-error-message')) {
this.displayNotification(Object.assign({type: 'danger'}, options));
} else if (this.$el.hasClass('coupon-warning-message')) {
this.displayNotification(Object.assign({type: 'warning'}, options));
}
return this._super(...arguments);
},
});
registry.category("public_root_widgets").add("CouponToasterWidget", {
Widget: CouponToasterWidget,
selector: '.coupon-message',
});
export default CouponToasterWidget;

View file

@ -0,0 +1,56 @@
<templates>
<t t-inherit="loyalty.portal_loyalty_card_dialog" t-inherit-mode="extension">
<div name="history_lines" position="after">
<div
t-if="props.program.program_type == 'loyalty'"
t-foreach="props.rewards"
t-as="reward"
t-key="reward_index"
class="mb-2 mt-2"
>
<div class="d-flex border rounded align-items-center justify-content-center p-2">
<div class="flex-fill fs-6">
<t t-out="reward.description"/>
</div>
<span class="text-primary" t-out="reward.points"/>
</div>
</div>
<div t-if="props.rewards.length > 0" class="d-flex justify-content-center">
<a
t-if="props.program.program_type == 'loyalty'"
type="button"
href="/shop"
class="btn btn-primary"
>
Claim
</a>
</div>
<div
t-if="props.program.program_type == 'ewallet'
and props.program.trigger_products.length"
>
<form
method="post"
action="/wallet/top_up"
class="d-flex w-75 w-md-50 m-auto gap-1"
>
<input type="hidden" name="csrf_token" t-att-value="csrf_token"/>
<select name="trigger_product_id" class="form-select">
<t
t-foreach="props.program.trigger_products"
t-as="product"
t-key="product_index"
>
<option t-att-value="product.id">
<t t-esc="product.total_price"/>
</option>
</t>
</select>
<button type="submit" class="btn btn-primary text-nowrap">Top Up</button>
</form>
</div>
</div>
</t>
</templates>

View file

@ -1,13 +0,0 @@
/** @odoo-module **/
import publicWidget from 'web.public.widget';
publicWidget.registry.WebsiteSaleGiftCardCopy = publicWidget.Widget.extend({
selector: '.o_purchased_gift_card',
/**
* @override
*/
start: function () {
new ClipboardJS(this.$el.find('.copy-to-clipboard')[0]);
}
});

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="website_sale_loyalty.couponOption">
<BuilderRow label.translate="Show Discount in Subtotal">
<BuilderCheckbox action="'websiteConfig'" actionParam="{views: ['website_sale_loyalty.cart_discount']}"/>
</BuilderRow>
</t>
</templates>

View file

@ -0,0 +1,21 @@
import { Plugin } from "@html_editor/plugin";
import { registry } from "@web/core/registry";
import { _t } from "@web/core/l10n/translation";
import { BaseOptionComponent } from "@html_builder/core/utils";
export class CouponOption extends BaseOptionComponent {
static template = "website_sale_loyalty.couponOption";
static selector = "main:has(.oe_website_sale .wizard)";
static title = _t("Coupon Snippet Options");
static groups = ["website.group_website_designer"];
static editableOnly = false;
}
class CouponOptionPlugin extends Plugin {
static id = "couponOption";
resources = {
builder_options: [CouponOption],
};
}
registry.category("website-plugins").add(CouponOptionPlugin.id, CouponOptionPlugin);

View file

@ -0,0 +1,74 @@
import { registry } from "@web/core/registry";
import * as tourUtils from '@website_sale/js/tours/tour_utils';
registry.category("web_tour.tours").add('apply_discount_code_program_multi_rewards', {
url: '/shop?search=Super%20Chair',
steps: () => [
{
trigger: ".oe_search_found:not(:visible)",
},
{
content: 'select Super Chair',
trigger: '.oe_product_cart a:contains("Super Chair")',
run: "click",
expectUnloadPage: true,
},
{
content: 'Add Super Chair into cart',
trigger: 'a:contains(Add to cart)',
run: "click",
},
tourUtils.goToCart(),
{
trigger: "h4:contains(order summary)",
},
{
trigger: 'form[name="coupon_code"]',
},
{
content: 'insert discount code',
trigger: 'form[name="coupon_code"] input[name="promo"]',
run: "edit 12345",
},
{
content: 'validate the promo code',
trigger: 'form[name="coupon_code"] button[type="submit"]',
run: "click",
expectUnloadPage: true,
},
{
content: 'check reward',
trigger: '.alert:contains("10% on Super Chair")',
},
{
content: 'claim reward',
trigger: '.alert:contains("10% on Super Chair") .btn:contains("Claim")',
run: "click",
expectUnloadPage: true,
},
{
content: "check claimed reward",
trigger:
"#cart_products.js_cart_lines .o_cart_product h6:contains(10% on Super Chair)",
},
// Try to reapply the same promo code
{
trigger: 'form[name="coupon_code"]',
},
{
content: 'insert discount code',
trigger: 'form[name="coupon_code"] input[name="promo"]',
run: "edit 12345",
},
{
content: 'validate the promo code',
trigger: 'form[name="coupon_code"] button[type="submit"]',
run: "click",
expectUnloadPage: true,
},
{
content: 'check refused message',
trigger: '.alert-danger:contains("This promo code is already applied")',
},
],
});

View file

@ -0,0 +1,49 @@
import { registry } from "@web/core/registry";
import * as wsTourUtils from "@website_sale/js/tours/tour_utils";
registry.category("web_tour.tours").add("shop_sale_ewallet", {
url: "/shop",
steps: () => [
// Add a $50 gift card to the order
...wsTourUtils.addToCart({ productName: "TEST - Gift Card", expectUnloadPage: true }),
wsTourUtils.goToCart(),
{
trigger: 'button[name="o_loyalty_claim"]:contains("Use")',
async run(helpers) {
const rewards = document.querySelectorAll('form[name="claim_reward"]');
if (rewards.length === 1) {
await helpers.click();
} else {
console.error(`Expected 1 claimable reward, got: ${rewards.length}`);
}
},
expectUnloadPage: true,
},
{
content: "Checkout",
trigger: 'a[name="website_sale_main_button"]',
run: "click",
expectUnloadPage: true,
},
{
content: "Confirm Order",
trigger: 'button[name="o_payment_submit_button"]',
run: "click",
expectUnloadPage: true,
},
{
trigger: 'div h3:contains("Thank you for your order.")'
},
{
trigger: 'a[href="/shop/cart"]',
run: function () {
const cartQuantity = document.querySelector(".my_cart_quantity");
if (cartQuantity.textContent !== "0") {
console.error(
"cart should be empty and reset after an order is paid using ewallet"
);
}
},
},
],
});

View file

@ -1,90 +1,49 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import * as tourUtils from "@website_sale/js/tours/tour_utils";
import tour from 'web_tour.tour';
import tourUtils from 'website_sale.tour_utils';
tour.register('shop_sale_gift_card', {
test: true,
url: '/shop?search=Small%20Drawer'
},
[
registry.category("web_tour.tours").add('shop_sale_gift_card', {
url: '/shop',
steps: () => [
// Add a small drawer to the order (50$)
{
content: 'select Small Drawer',
extra_trigger: '.oe_search_found',
trigger: '.oe_product_cart a:contains("TEST - Small Drawer")',
},
{
content: 'Add Small Drawer into cart',
trigger: 'a:contains(ADD TO CART)',
},
...tourUtils.addToCart({ productName: "TEST - Small Drawer", expectUnloadPage: true }),
tourUtils.goToCart(),
{
content: 'Click on "I have a promo code"',
extra_trigger: '#cart_products',
trigger: '.show_coupon',
},
{
content: 'insert gift card code',
trigger: 'form[name="coupon_code"] input[name="promo"]',
run: 'text GIFT_CARD'
run: "edit GIFT_CARD",
},
{
content: 'validate the gift card',
trigger: 'form[name="coupon_code"] .a-submit',
trigger: 'form[name="coupon_code"] button[type="submit"]',
run: "click",
expectUnloadPage: true,
},
{
content: 'check gift card line',
trigger: '.td-product_name:contains("PAY WITH GIFT CARD")',
trigger: "#cart_products div>h6:contains(PAY WITH GIFT CARD)",
},
{
content: 'Click on "I have a promo code"',
trigger: '.show_coupon',
},
{
content: 'insert gift card code',
extra_trigger: 'form[name="coupon_code"]',
content: "Insert promo",
trigger: 'form[name="coupon_code"] input[name="promo"]',
run: 'text 10PERCENT'
run: "edit 10PERCENT",
},
{
content: 'validate the gift card',
trigger: 'form[name="coupon_code"] .a-submit',
content: "Validate the promo",
trigger: 'form[name="coupon_code"] button[type="submit"]',
run: "click",
expectUnloadPage: true,
},
{
content: 'check gift card amount',
trigger: '.oe_currency_value:contains("-45.00")',
trigger: '.oe_website_sale .oe_cart',
run: function () {}, // it's a check
content: "Check promo",
trigger: "#cart_products div>h6:contains(10% on your order)",
},
{
content: 'go to shop',
trigger: 'a:contains("Shop")',
},
{
content: "type Gift Card in search",
trigger: 'form input[name="search"]',
run: "text Gift Card",
},
{
content: "start search",
trigger: 'form:has(input[name="search"]) .oe_search_button',
},
{
content: "select Gift Card",
extra_trigger: '.oe_search_found', // Wait to be on search results or it sometimes throws concurent error (sent search form + click on product on /shop)
trigger: '.oe_product_cart a:containsExact("TEST - Gift Card")',
},
{
content: "click on 'Add to Cart' button",
trigger: "a:contains(ADD TO CART)",
},
tourUtils.goToCart({quantity: 2}),
{
content: 'check gift card amount',
trigger: '.oe_currency_value:contains("-45.00")',
trigger: '.oe_website_sale .oe_cart',
run: function () {}, // it's a check
content: "Click on Continue Shopping",
trigger: "div.card-body a:contains(Continue shopping)",
run: "click",
expectUnloadPage: true,
},
...tourUtils.addToCart({ productName: "TEST - Gift Card", expectUnloadPage: true }),
tourUtils.goToCart({quantity: 2}),
],
);
});

View file

@ -1,70 +1,68 @@
/** @odoo-module **/
import { rpc } from "@web/core/network/rpc";
import { registry } from "@web/core/registry";
import * as tourUtils from '@website_sale/js/tours/tour_utils';
import tour from 'web_tour.tour';
import ajax from 'web.ajax';
import tourUtils from 'website_sale.tour_utils';
tour.register('shop_sale_loyalty', {
test: true,
registry.category("web_tour.tours").add('shop_sale_loyalty', {
url: '/shop?search=Small%20Cabinet',
},
[
steps: () => [
/* 1. Buy 1 Small Cabinet, enable coupon code & insert 10% code */
{
trigger: ".oe_search_found:not(:visible)",
},
{
content: "select Small Cabinet",
extra_trigger: '.oe_search_found',
trigger: '.oe_product_cart a:contains("Small Cabinet")',
run: "click",
expectUnloadPage: true,
},
{
content: "add 2 Small Cabinet into cart",
trigger: '#product_details input[name="add_qty"]',
run: "text 2",
run: "edit 2",
},
{
content: "click on 'Add to Cart' button",
trigger: "a:contains(ADD TO CART)",
trigger: "a:contains(Add to cart)",
run: "click",
},
tourUtils.goToCart({quantity: 2}),
{
content: "click on 'I have a promo code'",
extra_trigger: '.show_coupon',
trigger: '.show_coupon',
trigger: 'form[name="coupon_code"]',
},
{
content: "insert promo code 'testcode'",
extra_trigger: 'form[name="coupon_code"]',
trigger: 'form[name="coupon_code"] input[name="promo"]',
run: "text testcode",
run: "edit testcode",
},
{
content: "validate the coupon",
trigger: 'form[name="coupon_code"] .a-submit',
trigger: 'form[name="coupon_code"] button[type="submit"]',
run: "click",
expectUnloadPage: true,
},
{
content: "check reward product",
trigger: '.td-product_name:contains("10.0% discount on total amount")',
run: function () {}, // it's a check
trigger: 'div>h6:contains("10.0% discount on total amount")',
},
{
content: "check loyalty points",
trigger: '.oe_website_sale_gift_card span:contains("372.03 Points")',
run: function () {}, // it's a check
trigger: '.oe_website_sale_gift_card strong[name="o_loyalty_points"]:contains("372.03")',
},
/* 2. Add some cabinet to get a free one, play with quantity */
{
content: "go to shop",
trigger: '.td-product_name:contains("10.0% discount on total amount")',
trigger: 'div>h6:contains("10.0% discount on total amount")',
run: function () {
ajax.jsonRpc('/web/dataset/call_kw', 'call', {
rpc('/web/dataset/call_kw/account.tax/create', {
model: 'account.tax',
method: 'create',
args: [{
'name':'15% tax incl ' + _.now(),
'name':'15% tax incl ' + new Date().getTime(),
'amount': 15,
}],
kwargs: {},
}).then(function (tax_id) {
ajax.jsonRpc('/web/dataset/call_kw', 'call', {
rpc('/web/dataset/call_kw/product.template/create', {
model: 'product.template',
method: 'create',
args: [{
@ -79,73 +77,54 @@ tour.register('shop_sale_loyalty', {
});
});
},
expectUnloadPage: true,
},
{
content: "type Taxed Product in search",
trigger: 'form input[name="search"]',
run: "text Taxed Product",
},
{
content: "start search",
trigger: 'form:has(input[name="search"]) .oe_search_button',
},
{
content: "select Taxed Product",
extra_trigger: '.oe_search_found', // Wait to be on search results or it sometimes throws concurent error (sent search form + click on product on /shop)
trigger: '.oe_product_cart a:containsExact("Taxed Product")',
},
{
content: "click on 'Add to Cart' button",
trigger: "a:contains(ADD TO CART)",
},
...tourUtils.addToCart({ productName: "Taxed Product", expectUnloadPage: true }),
tourUtils.goToCart({quantity: 3}),
{
trigger: ".oe_currency_value:contains(/74.00/):not(div[name='o_cart_total'])",
},
{
content: "check reduction amount got recomputed and merged both discount lines into one only",
extra_trigger: '.oe_currency_value:contains("-74.00"):not(#cart_total .oe_currency_value:contains("-74.00"))',
trigger: '.oe_website_sale .oe_cart',
run: function () {}, // it's a check
},
/* 3. Add some cabinet to get a free one, play with quantity */
{
content: "add one Small Cabinet",
trigger: '#cart_products input.js_quantity',
run: "text 3",
run: "edit 3 && click body",
},
{
content: "check reduction amount got recomputed when changing qty",
trigger: '.oe_currency_value:contains("-106.00")',
run: function () {}, // it's a check
trigger: '.oe_currency_value:contains("- 106.00")',
},
{
content: "add more Small Cabinet into cart",
trigger: '#cart_products input.js_quantity',
run: "text 4",
run: "edit 4 && click body",
},
{
content: "check free product is added",
trigger: '#wrap:has(.td-product_name:contains("Free Product - Small Cabinet"))',
run: function () {}, // it's a check
trigger: '#wrap:has(div h6:contains("Free Product - Small Cabinet"))',
},
{
content: "remove one cabinet from cart",
trigger: '#cart_products input.js_quantity[value="4"]',
run: "text 3",
run: "edit 3 && click body",
},
{
content: "check free product is removed",
trigger: '#wrap:not(:has(.td-product_name:contains("Free Product - Small Cabinet")))',
run: function () {}, // it's a check
trigger: '#wrap:not(:has(div h6:contains("Free Product - Small Cabinet")))',
},
/* 4. Check /shop/payment does not break the `merged discount lines split per tax` (eg: with _compute_tax_id) */
/* 4. Check /shop/payment does not break the `merged discount lines split per tax` (eg: with _compute_tax_ids) */
{
content: "go to checkout",
trigger: 'a[href="/shop/checkout?express=1"]',
},
{
content: "check total is unchanged once we land on payment page",
extra_trigger: '#payment_method h3:contains("Pay with")',
trigger: 'tr#order_total .oe_currency_value:contains("967.50")',
run: function () {}, // it's a check
trigger: 'a[href="/shop/checkout?try_skip_step=true"]',
run: "click",
expectUnloadPage: true,
},
...tourUtils.assertCartAmounts({
total: '967.50',
}),
]
);
});

View file

@ -0,0 +1,131 @@
import { registry } from "@web/core/registry";
import {
addToCart,
assertCartAmounts,
confirmOrder,
goToCart,
goToCheckout,
pay,
waitForInteractionToLoad,
} from "@website_sale/js/tours/tour_utils";
function assertRewardAmounts(rewards) {
const steps = [];
const currencyValue = `.oe_currency_value:visible`;
for (const [reward, amount] of Object.entries(rewards)) {
steps.push({
content: `check if ${reward} reward is correct`,
trigger: `[data-reward-type=${reward}] ${currencyValue}:text(${amount})`,
});
}
return steps;
}
function selectDelivery(provider) {
return {
content: `select ${provider} shipping`,
trigger: `li[name=o_delivery_method]:contains(${provider}) input`,
run: "click",
};
}
const webTours = registry.category("web_tour.tours");
webTours.add("check_shipping_discount", {
url: "/shop?search=Plumbus",
steps: () => [
{
content: "select Plumbus",
trigger: '.oe_product a:contains("Plumbus")',
run: "click",
expectUnloadPage: true,
},
{
content: "add 3 Plumbus into cart",
trigger: '#product_details input[name="add_qty"]',
run: "edit 3",
},
{
content: "click on 'Add to Cart' button",
trigger: '#product_detail form #add_to_cart',
run: "click",
},
goToCart({ quantity: 3 }),
goToCheckout(),
waitForInteractionToLoad(),
selectDelivery("delivery2"),
...assertCartAmounts({
delivery: "10.00", // delivery2 is $10, ignoring shipping discount
total: "304.00", // $100 per Plumbus, plus discounted delivery
}),
...assertRewardAmounts({ shipping: "- 6.00" }),
{
content: "pay with eWallet",
trigger: "form[name=claim_reward] button[name='o_loyalty_claim']:contains('Use')",
run: "click",
expectUnloadPage: true,
},
...assertRewardAmounts({ discount: "- 304.00" }),
waitForInteractionToLoad(),
selectDelivery("delivery1"),
...assertCartAmounts({ delivery: "5.00" }),
...assertRewardAmounts({ discount: "- 300.00", shipping: "- 5.00" }),
{
content: "confirm shipping method",
trigger: ".o_total_card a[name=website_sale_main_button]",
run: "click",
expectUnloadPage: true,
},
...pay({ expectUnloadPage: true }),
],
});
webTours.add("update_shipping_after_discount", {
url: "/shop",
steps: () => [
...addToCart({ productName: "Plumbus", expectUnloadPage: true }),
goToCart(),
{
content: "use eWallet to check it doesn't impact `free_over` shipping",
trigger: "button[name='o_loyalty_claim']:contains('Use')",
run: "click",
expectUnloadPage: true,
},
{
content: "Check pay with eWallet is applied",
trigger: ".o_cart_product [name=website_sale_cart_line_price]:contains(- 100.00)",
},
goToCheckout(),
selectDelivery("delivery1"),
...assertCartAmounts({
total: "0.00", // $100 total is covered by eWallet
delivery: "0.00", // $100 is over $75 `free_over` amount, so free shipping
}),
...assertRewardAmounts({ discount: "- 100.00" }),
confirmOrder(),
{
content: "enter discount code",
trigger: "form[name=coupon_code] input[name=promo]",
run: "edit test-50pc",
},
{
content: "apply discount code",
trigger: 'form[name="coupon_code"] button[type="submit"]',
run: "click",
expectUnloadPage: true,
},
...assertCartAmounts({
total: "0.00", // $50 total is covered by eWallet
delivery: "5.00", // $50 is below $75 `free_over` amount, so no free shipping
}),
{
content: "check discount code discount doesn't apply to shipping",
trigger: '[data-reward-type=discount] .oe_currency_value:text(- 50.00)',
},
{
content: "check eWallet discount applies to shipping ($50 for Plumbus + $5 for delivery)",
trigger: '[data-reward-type=discount] .oe_currency_value:text(- 55.00)',
},
],
});

View file

@ -0,0 +1,32 @@
import { registry } from "@web/core/registry";
import * as wsTourUtils from "@website_sale/js/tours/tour_utils";
registry.category("web_tour.tours").add('shop_sale_loyalty_delivery', {
url: '/shop',
steps: () => [
...wsTourUtils.addToCart({ productName: "Plumbus", expectUnloadPage: true }),
wsTourUtils.goToCart(1),
wsTourUtils.goToCheckout(),
{
content: "select delivery method 1",
trigger: "li label:contains(delivery1)",
run: "click",
},
{
content: "Enter gift card code",
trigger: "form[name='coupon_code'] input[name='promo']",
run: "edit 123456",
},
{
content: "click on 'Apply'",
trigger: 'form[name="coupon_code"] button[type="submit"]',
run: "click",
expectUnloadPage: true,
},
wsTourUtils.confirmOrder(),
...wsTourUtils.assertCartAmounts({
total: '0.00',
delivery: '5.00'
}),
]
});