mirror of
https://github.com/bringout/oca-ocb-pos.git
synced 2026-04-24 09:02:01 +02:00
19.0 vanilla
This commit is contained in:
parent
6e54c1af6c
commit
3ca647e428
1087 changed files with 132065 additions and 108499 deletions
|
|
@ -0,0 +1,48 @@
|
|||
import { test, expect } from "@odoo/hoot";
|
||||
import { mockDate } from "@odoo/hoot-mock";
|
||||
import { mountWithCleanup } from "@web/../tests/web_test_helpers";
|
||||
import { getFilledOrder, setupPosEnv } from "@point_of_sale/../tests/unit/utils";
|
||||
import { definePosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
import { ManageGiftCardPopup } from "@pos_loyalty/app/components/popups/manage_giftcard_popup/manage_giftcard_popup";
|
||||
|
||||
definePosModels();
|
||||
|
||||
test("addBalance", async () => {
|
||||
const store = await setupPosEnv();
|
||||
|
||||
// Freeze current date so luxon.DateTime.now() is fixed
|
||||
mockDate("2025-01-01");
|
||||
|
||||
let payloadResult = null;
|
||||
|
||||
const order = await getFilledOrder(store);
|
||||
const popup = await mountWithCleanup(ManageGiftCardPopup, {
|
||||
props: {
|
||||
line: order.lines[0],
|
||||
title: "Sell/Manage physical gift card",
|
||||
getPayload: (code, amount, expDate) => {
|
||||
payloadResult = { code, amount, expDate };
|
||||
},
|
||||
close: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
popup.state.inputValue = "";
|
||||
popup.state.amountValue = "";
|
||||
const valid = popup.validateCode();
|
||||
|
||||
expect(valid).toBe(false);
|
||||
expect(popup.state.error).toBe(true);
|
||||
|
||||
popup.state.inputValue = "101";
|
||||
popup.state.amountValue = "100";
|
||||
popup.state.error = false;
|
||||
popup.state.amountError = false;
|
||||
|
||||
await popup.addBalance();
|
||||
|
||||
expect(payloadResult.code).toBe("101");
|
||||
expect(payloadResult.amount).toBe(100);
|
||||
// expiration is +1 year
|
||||
expect(payloadResult.expDate).toBe("2026-01-01");
|
||||
});
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import { test, expect } from "@odoo/hoot";
|
||||
import { mountWithCleanup } from "@web/../tests/web_test_helpers";
|
||||
import { setupPosEnv } from "@point_of_sale/../tests/unit/utils";
|
||||
import { ControlButtons } from "@point_of_sale/app/screens/product_screen/control_buttons/control_buttons";
|
||||
import { definePosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
import { addProductLineToOrder } from "@pos_loyalty/../tests/unit/utils";
|
||||
|
||||
definePosModels();
|
||||
|
||||
test("_applyReward", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
// Get card #1 - belongs to a loyalty-type program
|
||||
const card = models["loyalty.card"].get(1);
|
||||
// Get reward #2 - belongs to the same loyalty program, type = discount reward
|
||||
const reward = models["loyalty.reward"].get(2);
|
||||
|
||||
await addProductLineToOrder(store, order);
|
||||
|
||||
// Total quantity in the order
|
||||
const potentialQty = order.getOrderlines().reduce((acc, line) => acc + line.qty, 0);
|
||||
|
||||
const component = await mountWithCleanup(ControlButtons, {});
|
||||
|
||||
const result = await component._applyReward(reward, card.id, potentialQty);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("getPotentialRewards", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
// Get loyalty program #1 - type = loyalty
|
||||
const loyaltyProgram = models["loyalty.program"].get(1);
|
||||
// Get card #1 - linked to loyalty program #1
|
||||
const card = models["loyalty.card"].get(1);
|
||||
|
||||
await addProductLineToOrder(store, order);
|
||||
order._code_activated_coupon_ids = [card];
|
||||
|
||||
const component = await mountWithCleanup(ControlButtons, {});
|
||||
|
||||
const rewards = component.getPotentialRewards();
|
||||
const reward = rewards[0].reward;
|
||||
|
||||
expect(reward).toEqual(models["loyalty.reward"].get(1));
|
||||
expect(reward.program_id).toEqual(loyaltyProgram);
|
||||
});
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import { test, expect } from "@odoo/hoot";
|
||||
import { mountWithCleanup } from "@web/../tests/web_test_helpers";
|
||||
import { OrderSummary } from "@point_of_sale/app/screens/product_screen/order_summary/order_summary";
|
||||
import { setupPosEnv } from "@point_of_sale/../tests/unit/utils";
|
||||
import { definePosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
import { addProductLineToOrder } from "@pos_loyalty/../tests/unit/utils";
|
||||
|
||||
definePosModels();
|
||||
|
||||
test("_updateGiftCardOrderline", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
const product = models["product.product"].get(1);
|
||||
// Program #3 - loyalty program for gift cards
|
||||
const program = models["loyalty.program"].get(3);
|
||||
// Card #3 - gift card which program type is gift_card
|
||||
const card = models["loyalty.card"].get(3);
|
||||
|
||||
await addProductLineToOrder(store, order);
|
||||
|
||||
const points = product.lst_price;
|
||||
|
||||
order.uiState.couponPointChanges[card.id] = {
|
||||
coupon_id: card.id,
|
||||
program_id: program.id,
|
||||
product_id: product.id,
|
||||
points: points,
|
||||
manual: false,
|
||||
};
|
||||
|
||||
const component = await mountWithCleanup(OrderSummary, {});
|
||||
|
||||
await component._updateGiftCardOrderline("ABC123", points);
|
||||
|
||||
const updatedLine = order.getSelectedOrderline();
|
||||
|
||||
expect(updatedLine.gift_code).toBe("ABC123");
|
||||
expect(updatedLine.product_id.id).toBe(product.id);
|
||||
expect(updatedLine.getQuantity()).toBe(1);
|
||||
expect(order.uiState.couponPointChanges[card.id]).toBe(undefined);
|
||||
});
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import { test, expect } from "@odoo/hoot";
|
||||
import { mountWithCleanup } from "@web/../tests/web_test_helpers";
|
||||
import { setupPosEnv } from "@point_of_sale/../tests/unit/utils";
|
||||
import { PartnerLine } from "@point_of_sale/app/screens/partner_list/partner_line/partner_line";
|
||||
import { definePosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
|
||||
definePosModels();
|
||||
|
||||
test("_getLoyaltyPointsRepr", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
|
||||
const partner = models["res.partner"].get(1);
|
||||
// Get first 3 loyalty cards and map them with program
|
||||
const loyaltyCards = models["loyalty.card"]
|
||||
.getAll()
|
||||
.slice(0, 3)
|
||||
.map((element) => ({
|
||||
id: element.id,
|
||||
points: element.points,
|
||||
partner_id: partner.id,
|
||||
program_id: models["loyalty.program"].get(element.id),
|
||||
}));
|
||||
|
||||
const component = await mountWithCleanup(PartnerLine, {
|
||||
props: {
|
||||
partner,
|
||||
close: () => {},
|
||||
isSelected: false,
|
||||
isBalanceDisplayed: true,
|
||||
onClickEdit: () => {},
|
||||
onClickUnselect: () => {},
|
||||
onClickPartner: () => {},
|
||||
onClickOrders: () => {},
|
||||
},
|
||||
env: {
|
||||
...store.env,
|
||||
utils: {
|
||||
formatCurrency: (val) => `$${val.toFixed(2)}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const results = loyaltyCards.map((card) => component._getLoyaltyPointsRepr(card));
|
||||
|
||||
expect(results[0]).toBe("10.00 Points");
|
||||
expect(results[1]).toBe("E-Wallet Program: $25.00");
|
||||
expect(results[2]).toMatch("15.00 Gift Card Points");
|
||||
});
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import { patch } from "@web/core/utils/patch";
|
||||
import { hootPosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
import { models } from "@web/../tests/web_test_helpers";
|
||||
|
||||
const { DateTime } = luxon;
|
||||
|
||||
export class LoyaltyCard extends models.ServerModel {
|
||||
_name = "loyalty.card";
|
||||
|
||||
_load_pos_data_fields() {
|
||||
return ["partner_id", "code", "points", "program_id", "expiration_date", "write_date"];
|
||||
}
|
||||
|
||||
_records = [
|
||||
{
|
||||
id: 1,
|
||||
code: "CARD001",
|
||||
points: 10,
|
||||
partner_id: 1,
|
||||
program_id: 1,
|
||||
expiration_date: DateTime.now().plus({ days: 1 }).toISODate(),
|
||||
write_date: DateTime.now().minus({ days: 1 }).toFormat("yyyy-MM-dd HH:mm:ss"),
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
code: "CARD002",
|
||||
points: 25,
|
||||
partner_id: 1,
|
||||
program_id: 2,
|
||||
expiration_date: DateTime.now().minus({ days: 1 }).toISODate(),
|
||||
write_date: DateTime.now().minus({ days: 2 }).toFormat("yyyy-MM-dd HH:mm:ss"),
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
code: "CARD003",
|
||||
points: 15,
|
||||
partner_id: 3,
|
||||
program_id: 3,
|
||||
write_date: DateTime.now().toFormat("yyyy-MM-dd HH:mm:ss"),
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
code: "CARD004",
|
||||
points: 3,
|
||||
partner_id: 1,
|
||||
program_id: 7,
|
||||
write_date: DateTime.now().minus({ days: 2 }).toFormat("yyyy-MM-dd HH:mm:ss"),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
patch(hootPosModels, [...hootPosModels, LoyaltyCard]);
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
import { patch } from "@web/core/utils/patch";
|
||||
import { hootPosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
import { models } from "@web/../tests/web_test_helpers";
|
||||
|
||||
const { DateTime } = luxon;
|
||||
|
||||
export class LoyaltyProgram extends models.ServerModel {
|
||||
_name = "loyalty.program";
|
||||
|
||||
_load_pos_data_fields() {
|
||||
return [
|
||||
"name",
|
||||
"trigger",
|
||||
"applies_on",
|
||||
"program_type",
|
||||
"pricelist_ids",
|
||||
"date_from",
|
||||
"date_to",
|
||||
"limit_usage",
|
||||
"max_usage",
|
||||
"is_nominative",
|
||||
"portal_visible",
|
||||
"portal_point_name",
|
||||
"trigger_product_ids",
|
||||
"rule_ids",
|
||||
"reward_ids",
|
||||
];
|
||||
}
|
||||
|
||||
_records = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Loyalty Program",
|
||||
trigger: "auto",
|
||||
applies_on: "both",
|
||||
program_type: "loyalty",
|
||||
pricelist_ids: [1],
|
||||
date_from: false,
|
||||
date_to: false,
|
||||
limit_usage: false,
|
||||
max_usage: 0,
|
||||
is_nominative: false,
|
||||
portal_visible: true,
|
||||
portal_point_name: "Points",
|
||||
trigger_product_ids: [],
|
||||
rule_ids: [1],
|
||||
reward_ids: [],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "E-Wallet Program",
|
||||
trigger: "auto",
|
||||
applies_on: "future",
|
||||
program_type: "ewallet",
|
||||
pricelist_ids: [],
|
||||
date_from: false,
|
||||
date_to: false,
|
||||
limit_usage: false,
|
||||
max_usage: 0,
|
||||
is_nominative: false,
|
||||
portal_visible: true,
|
||||
portal_point_name: "E-Wallet Points",
|
||||
trigger_product_ids: [],
|
||||
rule_ids: [],
|
||||
reward_ids: [],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Gift Card Program",
|
||||
trigger: "auto",
|
||||
applies_on: "future",
|
||||
program_type: "gift_card",
|
||||
pricelist_ids: [],
|
||||
date_from: false,
|
||||
date_to: false,
|
||||
limit_usage: false,
|
||||
max_usage: 0,
|
||||
is_nominative: false,
|
||||
portal_visible: true,
|
||||
portal_point_name: "Gift Card Points",
|
||||
trigger_product_ids: [],
|
||||
rule_ids: [1],
|
||||
reward_ids: [],
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "E-Wallet Program 2",
|
||||
trigger: "auto",
|
||||
applies_on: "future",
|
||||
program_type: "ewallet",
|
||||
pricelist_ids: [],
|
||||
date_from: false,
|
||||
date_to: false,
|
||||
limit_usage: false,
|
||||
max_usage: 0,
|
||||
is_nominative: false,
|
||||
portal_visible: true,
|
||||
portal_point_name: "E-Wallet Points 2",
|
||||
trigger_product_ids: [],
|
||||
rule_ids: [],
|
||||
reward_ids: [],
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Nominative Gift Card Program",
|
||||
trigger: "auto",
|
||||
applies_on: "future",
|
||||
program_type: "gift_card",
|
||||
pricelist_ids: [],
|
||||
date_from: false,
|
||||
date_to: false,
|
||||
limit_usage: false,
|
||||
max_usage: 0,
|
||||
is_nominative: true,
|
||||
portal_visible: true,
|
||||
portal_point_name: "Nominative Gift Card Points",
|
||||
trigger_product_ids: [],
|
||||
rule_ids: [1],
|
||||
reward_ids: [],
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "E-Wallet Program 2",
|
||||
trigger: "auto",
|
||||
applies_on: "future",
|
||||
program_type: "ewallet",
|
||||
pricelist_ids: [],
|
||||
date_from: DateTime.now().minus({ days: 10 }).toISODate(),
|
||||
date_to: DateTime.now().minus({ days: 1 }).toISODate(),
|
||||
limit_usage: false,
|
||||
max_usage: 0,
|
||||
is_nominative: false,
|
||||
portal_visible: true,
|
||||
portal_point_name: "E-Wallet Points",
|
||||
trigger_product_ids: [],
|
||||
rule_ids: [],
|
||||
reward_ids: [],
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Loyalty Program Future",
|
||||
trigger: "auto",
|
||||
applies_on: "future",
|
||||
program_type: "loyalty",
|
||||
pricelist_ids: [1],
|
||||
date_from: false,
|
||||
date_to: false,
|
||||
limit_usage: false,
|
||||
max_usage: 0,
|
||||
is_nominative: true,
|
||||
portal_visible: true,
|
||||
portal_point_name: "Points",
|
||||
trigger_product_ids: [],
|
||||
rule_ids: [4],
|
||||
reward_ids: [3],
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "100% Cheapest Discount Program",
|
||||
trigger: "auto",
|
||||
applies_on: "current",
|
||||
program_type: "promotion",
|
||||
pricelist_ids: [1],
|
||||
date_from: false,
|
||||
date_to: false,
|
||||
limit_usage: false,
|
||||
max_usage: 0,
|
||||
is_nominative: false,
|
||||
portal_visible: true,
|
||||
portal_point_name: "Points",
|
||||
trigger_product_ids: [],
|
||||
rule_ids: [5],
|
||||
reward_ids: [4],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
patch(hootPosModels, [...hootPosModels, LoyaltyProgram]);
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
import { patch } from "@web/core/utils/patch";
|
||||
import { hootPosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
import { models } from "@web/../tests/web_test_helpers";
|
||||
|
||||
export class LoyaltyReward extends models.ServerModel {
|
||||
_name = "loyalty.reward";
|
||||
|
||||
_load_pos_data_fields() {
|
||||
return [
|
||||
"description",
|
||||
"program_id",
|
||||
"reward_type",
|
||||
"required_points",
|
||||
"clear_wallet",
|
||||
"currency_id",
|
||||
"discount",
|
||||
"discount_mode",
|
||||
"discount_applicability",
|
||||
"all_discount_product_ids",
|
||||
"is_global_discount",
|
||||
"discount_max_amount",
|
||||
"discount_line_product_id",
|
||||
"reward_product_id",
|
||||
"multi_product",
|
||||
"reward_product_ids",
|
||||
"reward_product_qty",
|
||||
"reward_product_uom_id",
|
||||
"reward_product_domain",
|
||||
];
|
||||
}
|
||||
|
||||
_records = [
|
||||
{
|
||||
id: 1,
|
||||
description: "10% Discount",
|
||||
program_id: 1,
|
||||
reward_type: "discount",
|
||||
required_points: 10,
|
||||
clear_wallet: false,
|
||||
currency_id: 1,
|
||||
discount: 10,
|
||||
discount_mode: "percent",
|
||||
discount_applicability: "order",
|
||||
all_discount_product_ids: [],
|
||||
is_global_discount: true,
|
||||
discount_max_amount: 0,
|
||||
discount_line_product_id: false,
|
||||
reward_product_id: false,
|
||||
multi_product: false,
|
||||
reward_product_ids: [5],
|
||||
reward_product_qty: 1,
|
||||
reward_product_uom_id: false,
|
||||
reward_product_domain: "[]",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
description: "20% Discount",
|
||||
program_id: 2,
|
||||
reward_type: "product",
|
||||
required_points: 10,
|
||||
clear_wallet: false,
|
||||
currency_id: 1,
|
||||
discount: 0,
|
||||
discount_mode: "percent",
|
||||
discount_applicability: "order",
|
||||
all_discount_product_ids: [],
|
||||
is_global_discount: true,
|
||||
discount_max_amount: 0,
|
||||
discount_line_product_id: false,
|
||||
reward_product_id: false,
|
||||
multi_product: false,
|
||||
reward_product_ids: [5],
|
||||
reward_product_qty: 1,
|
||||
reward_product_uom_id: false,
|
||||
reward_product_domain: "[]",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
description: "Free Product - Whiteboard Pen",
|
||||
program_id: 7,
|
||||
reward_type: "product",
|
||||
required_points: 1,
|
||||
clear_wallet: false,
|
||||
currency_id: 1,
|
||||
discount: 0,
|
||||
discount_mode: "percent",
|
||||
discount_applicability: "order",
|
||||
all_discount_product_ids: [],
|
||||
is_global_discount: true,
|
||||
discount_max_amount: 0,
|
||||
discount_line_product_id: 18,
|
||||
reward_product_id: 10,
|
||||
multi_product: false,
|
||||
reward_product_ids: [10],
|
||||
reward_product_qty: 1,
|
||||
reward_product_uom_id: false,
|
||||
reward_product_domain: "[]",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
description: "100% Cheapest Discount",
|
||||
program_id: 8,
|
||||
reward_type: "discount",
|
||||
required_points: 1,
|
||||
clear_wallet: false,
|
||||
currency_id: 1,
|
||||
discount: 100,
|
||||
discount_mode: "percent",
|
||||
discount_applicability: "cheapest",
|
||||
all_discount_product_ids: [],
|
||||
is_global_discount: false,
|
||||
discount_max_amount: 0,
|
||||
discount_line_product_id: 5,
|
||||
reward_product_id: false,
|
||||
multi_product: false,
|
||||
reward_product_ids: [5],
|
||||
reward_product_qty: 1,
|
||||
reward_product_uom_id: false,
|
||||
reward_product_domain: "[]",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
patch(hootPosModels, [...hootPosModels, LoyaltyReward]);
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
import { patch } from "@web/core/utils/patch";
|
||||
import { hootPosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
import { models } from "@web/../tests/web_test_helpers";
|
||||
|
||||
export class LoyaltyRule extends models.ServerModel {
|
||||
_name = "loyalty.rule";
|
||||
|
||||
_load_pos_data_fields() {
|
||||
return [
|
||||
"program_id",
|
||||
"valid_product_ids",
|
||||
"any_product",
|
||||
"currency_id",
|
||||
"reward_point_amount",
|
||||
"reward_point_split",
|
||||
"reward_point_mode",
|
||||
"minimum_qty",
|
||||
"minimum_amount",
|
||||
"minimum_amount_tax_mode",
|
||||
"mode",
|
||||
"code",
|
||||
];
|
||||
}
|
||||
|
||||
_records = [
|
||||
{
|
||||
id: 1,
|
||||
program_id: 1,
|
||||
valid_product_ids: [5],
|
||||
any_product: true,
|
||||
currency_id: 1,
|
||||
reward_point_amount: 1,
|
||||
reward_point_split: true,
|
||||
reward_point_mode: "order",
|
||||
minimum_qty: 0,
|
||||
minimum_amount: 0,
|
||||
minimum_amount_tax_mode: "incl",
|
||||
mode: "auto",
|
||||
code: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
program_id: 2,
|
||||
valid_product_ids: [5],
|
||||
any_product: true,
|
||||
currency_id: 1,
|
||||
reward_point_amount: 1,
|
||||
reward_point_split: true,
|
||||
reward_point_mode: "order",
|
||||
minimum_qty: 3,
|
||||
minimum_amount: 40,
|
||||
minimum_amount_tax_mode: "excl",
|
||||
mode: "auto",
|
||||
code: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
program_id: 6,
|
||||
valid_product_ids: [5],
|
||||
any_product: true,
|
||||
currency_id: 1,
|
||||
reward_point_amount: 1,
|
||||
reward_point_split: true,
|
||||
reward_point_mode: "order",
|
||||
minimum_qty: 3,
|
||||
minimum_amount: 40,
|
||||
minimum_amount_tax_mode: "excl",
|
||||
mode: "with_code",
|
||||
code: "EXPIRED",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
program_id: 7,
|
||||
any_product: true,
|
||||
currency_id: 1,
|
||||
reward_point_amount: 1,
|
||||
reward_point_split: false,
|
||||
reward_point_mode: "unit",
|
||||
minimum_qty: 1,
|
||||
minimum_amount: 0,
|
||||
minimum_amount_tax_mode: "incl",
|
||||
mode: "auto",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
program_id: 8,
|
||||
any_product: true,
|
||||
currency_id: 1,
|
||||
reward_point_amount: 1,
|
||||
reward_point_split: false,
|
||||
reward_point_mode: "unit",
|
||||
minimum_qty: 1,
|
||||
minimum_amount: 0,
|
||||
minimum_amount_tax_mode: "incl",
|
||||
mode: "auto",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
patch(hootPosModels, [...hootPosModels, LoyaltyRule]);
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
import { patch } from "@web/core/utils/patch";
|
||||
import { PosOrder } from "@point_of_sale/../tests/unit/data/pos_order.data";
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
|
||||
patch(PosOrder.prototype, {
|
||||
validate_coupon_programs(self, point_changes) {
|
||||
const couponIdsFromPos = new Set(Object.keys(point_changes).map((id) => parseInt(id)));
|
||||
|
||||
const coupons = this.env["loyalty.card"]
|
||||
.browse([...couponIdsFromPos])
|
||||
.filter((c) => c && c.program_id);
|
||||
|
||||
const couponDifference = new Set(
|
||||
[...couponIdsFromPos].filter((id) => !coupons.find((c) => c.id === id))
|
||||
);
|
||||
|
||||
if (couponDifference.size > 0) {
|
||||
return {
|
||||
successful: false,
|
||||
payload: {
|
||||
message: _t(
|
||||
"Some coupons are invalid. The applied coupons have been updated. Please check the order."
|
||||
),
|
||||
removed_coupons: [...couponDifference],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
for (const coupon of coupons) {
|
||||
const needed = -point_changes[coupon.id];
|
||||
if (parseFloat(coupon.points.toFixed(2)) < parseFloat(needed.toFixed(2))) {
|
||||
return {
|
||||
successful: false,
|
||||
payload: {
|
||||
message: _t("There are not enough points for the coupon: %s.", coupon.code),
|
||||
updated_points: Object.fromEntries(coupons.map((c) => [c.id, c.points])),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
successful: true,
|
||||
payload: {},
|
||||
};
|
||||
},
|
||||
|
||||
confirm_coupon_programs(self, coupon_data) {
|
||||
const couponNewIdMap = {};
|
||||
for (const k of Object.keys(coupon_data)) {
|
||||
const id = parseInt(k);
|
||||
if (id > 0) {
|
||||
couponNewIdMap[id] = id;
|
||||
}
|
||||
}
|
||||
|
||||
const couponsToCreate = Object.fromEntries(
|
||||
Object.entries(coupon_data).filter(([k]) => parseInt(k) < 0)
|
||||
);
|
||||
|
||||
const couponCreateVals = Object.values(couponsToCreate).map((p) => ({
|
||||
program_id: p.program_id,
|
||||
partner_id: p.partner_id || false,
|
||||
code: p.code || p.barcode || `CODE${Math.floor(Math.random() * 10000)}`,
|
||||
points: p.points || 0,
|
||||
}));
|
||||
|
||||
const newCouponIds = this.env["loyalty.card"].create(couponCreateVals);
|
||||
const newCoupons = this.env["loyalty.card"].browse(newCouponIds);
|
||||
|
||||
for (let i = 0; i < Object.keys(couponsToCreate).length; i++) {
|
||||
const oldId = parseInt(Object.keys(couponsToCreate)[i], 10);
|
||||
const newCoupon = newCouponIds[i];
|
||||
couponNewIdMap[oldId] = newCoupon.id;
|
||||
}
|
||||
|
||||
const allCoupons = this.env["loyalty.card"].browse(Object.keys(couponNewIdMap).map(Number));
|
||||
for (const coupon of allCoupons) {
|
||||
const oldId = couponNewIdMap[coupon.id];
|
||||
if (oldId && coupon_data[oldId]) {
|
||||
coupon.points += coupon_data[oldId].points;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
coupon_updates: allCoupons.map((coupon) => ({
|
||||
old_id: couponNewIdMap[coupon.id],
|
||||
id: coupon.id,
|
||||
points: coupon.points,
|
||||
code: coupon.code,
|
||||
program_id: coupon.program_id,
|
||||
partner_id: coupon.partner_id,
|
||||
})),
|
||||
program_updates: [...new Set(allCoupons.map((c) => c.program_id))].map((program) => ({
|
||||
program_id: program,
|
||||
usages: this.env["loyalty.program"].browse(program)?.[0]?.total_order_count,
|
||||
})),
|
||||
new_coupon_info: newCoupons.map((c) => ({
|
||||
program_name: this.env["loyalty.program"].browse(c.program_id)?.[0]?.name || "",
|
||||
expiration_date: c.expiration_date || false,
|
||||
code: c.code,
|
||||
})),
|
||||
coupon_report: {},
|
||||
};
|
||||
},
|
||||
|
||||
add_loyalty_history_lines() {
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { patch } from "@web/core/utils/patch";
|
||||
import { PosOrderLine } from "@point_of_sale/../tests/unit/data/pos_order_line.data";
|
||||
|
||||
patch(PosOrderLine.prototype, {
|
||||
_load_pos_data_fields() {
|
||||
return [
|
||||
...super._load_pos_data_fields(),
|
||||
"is_reward_line",
|
||||
"reward_id",
|
||||
"coupon_id",
|
||||
"reward_identifier_code",
|
||||
"points_cost",
|
||||
];
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { patch } from "@web/core/utils/patch";
|
||||
import { PosSession } from "@point_of_sale/../tests/unit/data/pos_session.data";
|
||||
|
||||
patch(PosSession.prototype, {
|
||||
_load_pos_data_models() {
|
||||
return [
|
||||
...super._load_pos_data_models(),
|
||||
"loyalty.card",
|
||||
"loyalty.program",
|
||||
"loyalty.reward",
|
||||
"loyalty.rule",
|
||||
];
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import { patch } from "@web/core/utils/patch";
|
||||
import { ProductProduct } from "@point_of_sale/../tests/unit/data/product_product.data";
|
||||
|
||||
patch(ProductProduct.prototype, {
|
||||
_load_pos_data_fields() {
|
||||
return [...super._load_pos_data_fields(), "all_product_tag_ids"];
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
import { test, describe, expect } from "@odoo/hoot";
|
||||
import { tick } from "@odoo/hoot-mock";
|
||||
import { setupPosEnv, getFilledOrder } from "@point_of_sale/../tests/unit/utils";
|
||||
import { definePosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
import { addProductLineToOrder } from "@pos_loyalty/../tests/unit/utils";
|
||||
|
||||
definePosModels();
|
||||
|
||||
const { DateTime } = luxon;
|
||||
|
||||
describe("pos.order - loyalty", () => {
|
||||
test("_getIgnoredProductIdsTotalDiscount", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const order = store.addNewOrder();
|
||||
|
||||
const ignoredProductIds = order._getIgnoredProductIdsTotalDiscount();
|
||||
|
||||
expect(ignoredProductIds.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("getOrderlines, _get_reward_lines and _get_regular_order_lines", async () => {
|
||||
const store = await setupPosEnv();
|
||||
|
||||
const order = await getFilledOrder(store);
|
||||
const [line1, line2] = order.getOrderlines();
|
||||
line1.update({ is_reward_line: true });
|
||||
line2.update({ is_reward_line: false, refunded_orderline_id: 123 });
|
||||
|
||||
// Verify getOrderlines method
|
||||
const orderedLines = order.getOrderlines();
|
||||
|
||||
expect(orderedLines[0]).toBe(line2);
|
||||
expect(orderedLines[1]).toBe(line1);
|
||||
expect(orderedLines[0].is_reward_line).toBe(false);
|
||||
expect(orderedLines[1].is_reward_line).toBe(true);
|
||||
expect(order.getLastOrderline()).toBe(line2);
|
||||
|
||||
// Verify _get_reward_lines method
|
||||
const rewardLines = order._get_reward_lines();
|
||||
|
||||
expect(rewardLines).toEqual([line1]);
|
||||
expect(rewardLines[0].is_reward_line).toBe(true);
|
||||
|
||||
// Verify _get_regular_order_lines
|
||||
const regularLine = await addProductLineToOrder(store, order);
|
||||
|
||||
expect(order.getOrderlines().length).toBe(3);
|
||||
|
||||
const regularLines = order._get_regular_order_lines();
|
||||
|
||||
expect(regularLines.length).toBe(2);
|
||||
expect(regularLines[1].id).toBe(regularLine.id);
|
||||
});
|
||||
|
||||
test("setPricelist", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
const pricelist2 = models["product.pricelist"].get(2);
|
||||
|
||||
order.uiState.couponPointChanges = {
|
||||
key1: { program_id: 1, points: 100 },
|
||||
key2: { program_id: 2, points: 50 },
|
||||
};
|
||||
|
||||
order.setPricelist(pricelist2);
|
||||
|
||||
const remainingKeys = Object.keys(order.uiState.couponPointChanges);
|
||||
expect(remainingKeys.length).toBe(1);
|
||||
expect(order.uiState.couponPointChanges[remainingKeys[0]].program_id).toBe(2);
|
||||
});
|
||||
|
||||
test("_resetPrograms", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const order = store.addNewOrder();
|
||||
|
||||
order.uiState.disabledRewards = new Set(["reward1"]);
|
||||
order.uiState.codeActivatedProgramRules = ["rule1"];
|
||||
order.uiState.couponPointChanges = { key1: { points: 100 } };
|
||||
|
||||
await addProductLineToOrder(store, order, {
|
||||
is_reward_line: true,
|
||||
});
|
||||
|
||||
order._resetPrograms();
|
||||
|
||||
expect(order.uiState.disabledRewards.size).toBeEmpty();
|
||||
expect(order.uiState.codeActivatedProgramRules.length).toBeEmpty();
|
||||
expect(order.uiState.couponPointChanges).toMatchObject({});
|
||||
});
|
||||
|
||||
test("_programIsApplicable", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
// Get loyalty program #1 - type = "ewallet"
|
||||
const program = models["loyalty.program"].get(1);
|
||||
|
||||
expect(order._programIsApplicable(program)).toBe(true);
|
||||
|
||||
program.partner_id = false;
|
||||
program.is_nominative = true;
|
||||
|
||||
expect(order._programIsApplicable(program)).toBe(false);
|
||||
});
|
||||
|
||||
test("_getRealCouponPoints", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
// Get loyalty card #1 which program_id = 1 (loyalty)
|
||||
const card = models["loyalty.card"].get(1);
|
||||
|
||||
order.uiState.couponPointChanges = {
|
||||
1: {
|
||||
coupon_id: 1,
|
||||
program_id: 1,
|
||||
points: 25,
|
||||
},
|
||||
};
|
||||
|
||||
await addProductLineToOrder(store, order, {
|
||||
is_reward_line: true,
|
||||
coupon_id: card,
|
||||
points_cost: 5,
|
||||
});
|
||||
|
||||
expect(order._getRealCouponPoints(card.id)).toBe(30);
|
||||
});
|
||||
|
||||
test("processGiftCard", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
// Get loyalty program #3 - type = "gift_card"
|
||||
const giftProgram = models["loyalty.program"].get(3);
|
||||
|
||||
const line = await addProductLineToOrder(store, order, {
|
||||
price_unit: 10,
|
||||
eWalletGiftCardProgram: giftProgram,
|
||||
});
|
||||
|
||||
order.selected_orderline = line;
|
||||
|
||||
const expirationDate = DateTime.now().plus({ days: 1 }).toISODate();
|
||||
order.processGiftCard("GIFT9999", 100, expirationDate);
|
||||
|
||||
const couponChanges = Object.values(order.uiState.couponPointChanges);
|
||||
expect(couponChanges.length).toBe(1);
|
||||
expect(couponChanges[0].code).toBe("GIFT9999");
|
||||
expect(couponChanges[0].points).toBe(100);
|
||||
expect(couponChanges[0].expiration_date).toBe(expirationDate);
|
||||
expect(couponChanges[0].manual).toBe(true);
|
||||
});
|
||||
|
||||
test("_getDiscountableOnOrder", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
await addProductLineToOrder(store, order, {
|
||||
qty: 2,
|
||||
});
|
||||
|
||||
await addProductLineToOrder(store, order, {
|
||||
price_unit: 5,
|
||||
});
|
||||
|
||||
// Get loyalty reward #1 - type = "discount"
|
||||
const reward = models["loyalty.reward"].get(1);
|
||||
|
||||
const result = order._getDiscountableOnOrder(reward);
|
||||
expect(result.discountable).toBe(25);
|
||||
});
|
||||
|
||||
test("_computeNItems", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
|
||||
const order = await getFilledOrder(store);
|
||||
|
||||
// Get loyalty rule #1 - which program_id = 1 (loyalty)
|
||||
const rule = models["loyalty.rule"].get(1);
|
||||
|
||||
expect(order.getOrderlines().length).toBe(2);
|
||||
expect(order._computeNItems(rule)).toBe(5);
|
||||
});
|
||||
|
||||
test("_canGenerateRewards", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
await addProductLineToOrder(store, order, {
|
||||
qty: 5,
|
||||
});
|
||||
|
||||
// Get loyalty program #2 - type = "ewallet"
|
||||
const program = models["loyalty.program"].get(2);
|
||||
|
||||
expect(order._canGenerateRewards(program, 50, 50)).toBe(true);
|
||||
expect(order._canGenerateRewards(program, 30, 30)).toBe(false);
|
||||
});
|
||||
|
||||
test("isProgramsResettable", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const order = store.addNewOrder();
|
||||
|
||||
expect(order.isProgramsResettable()).toBe(false);
|
||||
|
||||
order.uiState.disabledRewards = [...new Set(["RULE1"])];
|
||||
expect(order.isProgramsResettable()).toBe(true);
|
||||
|
||||
order.uiState.disabledRewards = new Set();
|
||||
order.uiState.codeActivatedProgramRules.push("RULE2");
|
||||
expect(order.isProgramsResettable()).toBe(true);
|
||||
|
||||
order.uiState.codeActivatedProgramRules = [];
|
||||
order.uiState.couponPointChanges = { key1: { points: 10 } };
|
||||
expect(order.isProgramsResettable()).toBe(true);
|
||||
});
|
||||
|
||||
test("removeOrderline", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
// Get loyalty reward #1 - type = "discount"
|
||||
const reward = models["loyalty.reward"].get(1);
|
||||
// Get loyalty card #1 - which program_id = 1 (loyalty)
|
||||
const coupon = models["loyalty.card"].get(1);
|
||||
|
||||
const rewardLine = await addProductLineToOrder(store, order, {
|
||||
is_reward_line: true,
|
||||
reward_id: reward,
|
||||
coupon_id: coupon,
|
||||
reward_identifier_code: "ABC123",
|
||||
});
|
||||
|
||||
const normalLine = await addProductLineToOrder(store, order, {
|
||||
price_unit: 20,
|
||||
is_reward_line: false,
|
||||
});
|
||||
|
||||
expect(order.getOrderlines().length).toBe(2);
|
||||
|
||||
const result = order.removeOrderline(rewardLine);
|
||||
expect(result).toBe(true);
|
||||
expect(order.getOrderlines().length).toBe(1);
|
||||
|
||||
const remainingLines = order.getOrderlines();
|
||||
expect(remainingLines.length).toBe(1);
|
||||
expect(remainingLines[0].id).toBe(normalLine.id);
|
||||
expect(remainingLines[0].is_reward_line).toBe(false);
|
||||
});
|
||||
|
||||
test("isSaleDisallowed", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
// Get loyalty program #3 - type = "gift_card"
|
||||
const giftProgram = models["loyalty.program"].get(3);
|
||||
|
||||
const result = order.isSaleDisallowed({}, { eWalletGiftCardProgram: giftProgram });
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test("setPartner and getLoyaltyPoints", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
const partner1 = models["res.partner"].get(1);
|
||||
const partner2 = models["res.partner"].get(3);
|
||||
|
||||
order.setPartner(partner1);
|
||||
|
||||
order.uiState.couponPointChanges = {
|
||||
key1: { program_id: 5, points: 100 },
|
||||
key2: { program_id: 2, points: 50 },
|
||||
};
|
||||
|
||||
order.setPartner(partner2);
|
||||
|
||||
const remainingKeys = Object.keys(order.uiState.couponPointChanges);
|
||||
expect(remainingKeys.length).toBe(1);
|
||||
expect(order.uiState.couponPointChanges[remainingKeys[0]].program_id).toBe(2);
|
||||
|
||||
// Verify getLoyaltyPoints method
|
||||
order.uiState.couponPointChanges = {
|
||||
1: {
|
||||
coupon_id: 1,
|
||||
program_id: 1,
|
||||
points: 25,
|
||||
},
|
||||
};
|
||||
|
||||
const loyaltyStats = order.getLoyaltyPoints();
|
||||
expect(loyaltyStats.length).toBe(1);
|
||||
expect(loyaltyStats[0].points.name).toBe("Points");
|
||||
expect(loyaltyStats[0].points.won).toBe(25);
|
||||
expect(loyaltyStats[0].points.balance).toBe(10);
|
||||
});
|
||||
|
||||
test("getLoyaltyPoints adapts to qty decreasing", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
const partner1 = models["res.partner"].get(1);
|
||||
order.setPartner(partner1);
|
||||
await store.orderUpdateLoyaltyPrograms();
|
||||
const reward = models["loyalty.reward"].get(3);
|
||||
const loyalty_card = models["loyalty.card"].get(4);
|
||||
const line = await addProductLineToOrder(store, order, {
|
||||
productId: 10,
|
||||
templateId: 10,
|
||||
qty: 3,
|
||||
});
|
||||
await store.orderUpdateLoyaltyPrograms();
|
||||
order._applyReward(reward, loyalty_card.id);
|
||||
const loyaltyStats = order.getLoyaltyPoints();
|
||||
expect(loyaltyStats[0].points.won).toBe(0);
|
||||
expect(loyaltyStats[0].points.spent).toBe(3);
|
||||
expect(loyaltyStats[0].points.total).toBe(0);
|
||||
expect(loyaltyStats[0].points.balance).toBe(3);
|
||||
line.setQuantity(2);
|
||||
await store.updateRewards();
|
||||
await tick();
|
||||
const loyaltyStats2 = order.getLoyaltyPoints();
|
||||
expect(loyaltyStats2[0].points.won).toBe(0);
|
||||
expect(loyaltyStats2[0].points.spent).toBe(2);
|
||||
expect(loyaltyStats2[0].points.total).toBe(1);
|
||||
expect(loyaltyStats2[0].points.balance).toBe(3);
|
||||
});
|
||||
|
||||
test("reward amount tax included cheapest product", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const order = store.addNewOrder();
|
||||
|
||||
const line = await addProductLineToOrder(store, order, {
|
||||
productId: 24,
|
||||
templateId: 24,
|
||||
qty: 1,
|
||||
});
|
||||
expect(line.prices.total_included).toBe(10);
|
||||
expect(line.prices.total_excluded).toBe(8.7);
|
||||
await store.updateRewards();
|
||||
await tick();
|
||||
expect(order.getOrderlines().length).toBe(2);
|
||||
const rewardLine = order._get_reward_lines()[0];
|
||||
expect(rewardLine.prices.total_included).toBe(-10);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import { test, describe, expect } from "@odoo/hoot";
|
||||
import { setupPosEnv } from "@point_of_sale/../tests/unit/utils";
|
||||
import { definePosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
import { addProductLineToOrder } from "@pos_loyalty/../tests/unit/utils";
|
||||
|
||||
definePosModels();
|
||||
|
||||
describe("pos.order.line - loyalty", () => {
|
||||
test("getEWalletGiftCardProgramType", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
// Get loyalty program #2 - type = "ewallet"
|
||||
const program = models["loyalty.program"].get(2);
|
||||
|
||||
const line = await addProductLineToOrder(store, order, {
|
||||
_e_wallet_program_id: program,
|
||||
});
|
||||
|
||||
expect(line.getEWalletGiftCardProgramType()).toBe(`${program.program_type}`);
|
||||
});
|
||||
|
||||
test("ignoreLoyaltyPoints", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
// Get loyalty program #2 - type = "ewallet"
|
||||
const programA = models["loyalty.program"].get(2);
|
||||
// Get loyalty program #4 - type = "ewallet"
|
||||
const programB = models["loyalty.program"].get(4);
|
||||
|
||||
const line = await addProductLineToOrder(store, order);
|
||||
line.update({ _e_wallet_program_id: programB });
|
||||
|
||||
expect(line.ignoreLoyaltyPoints({ program: programA })).toBe(true);
|
||||
});
|
||||
|
||||
test("getGiftCardOrEWalletBalance", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
|
||||
// Get loyalty card #3 which program_id = 3 (gift_card)
|
||||
const card = models["loyalty.card"].get(3);
|
||||
|
||||
const order = store.addNewOrder();
|
||||
|
||||
const line = await addProductLineToOrder(store, order, {
|
||||
is_reward_line: true,
|
||||
coupon_id: card,
|
||||
});
|
||||
|
||||
const balance = line.getGiftCardOrEWalletBalance();
|
||||
|
||||
expect(balance).toBeOfType("string");
|
||||
expect(balance).toMatch(new RegExp(`${card.points}`));
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import { test, expect } from "@odoo/hoot";
|
||||
import { mountWithCleanup } from "@web/../tests/web_test_helpers";
|
||||
import { definePosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
import { setupPosEnv } from "@point_of_sale/../tests/unit/utils";
|
||||
import { addProductLineToOrder } from "@pos_loyalty/../tests/unit/utils";
|
||||
import { TicketScreen } from "@point_of_sale/app/screens/ticket_screen/ticket_screen";
|
||||
|
||||
definePosModels();
|
||||
|
||||
test("TicketScreen.setOrder keeps reward line and triggers pos.updateRewards", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
|
||||
const order = store.addNewOrder();
|
||||
const reward = models["loyalty.reward"].get(1);
|
||||
const coupon = models["loyalty.card"].get(1);
|
||||
|
||||
await addProductLineToOrder(store, order, {
|
||||
is_reward_line: true,
|
||||
reward_id: reward,
|
||||
coupon_id: coupon,
|
||||
reward_identifier_code: "LOAD-ORDER-REWARD",
|
||||
points_cost: 10,
|
||||
});
|
||||
order.uiState.couponPointChanges = {};
|
||||
store.selectedOrderUuid = null;
|
||||
|
||||
let updateRewardsCalled = false;
|
||||
const originalUpdateRewards = store.updateRewards.bind(store);
|
||||
store.updateRewards = () => {
|
||||
updateRewardsCalled = true;
|
||||
return originalUpdateRewards();
|
||||
};
|
||||
const comp = await mountWithCleanup(TicketScreen, {});
|
||||
await comp.setOrder(order);
|
||||
|
||||
expect(updateRewardsCalled).toBe(true);
|
||||
const currentOrder = store.getOrder();
|
||||
expect(currentOrder).toBe(order);
|
||||
const rewardLines = currentOrder.lines.filter((l) => l.is_reward_line);
|
||||
expect(rewardLines.length).toBe(1);
|
||||
expect(rewardLines[0].reward_id.id).toBe(reward.id);
|
||||
expect(rewardLines[0].coupon_id.id).toBe(coupon.id);
|
||||
});
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import { test, describe, expect } from "@odoo/hoot";
|
||||
import { setupPosEnv } from "@point_of_sale/../tests/unit/utils";
|
||||
import { definePosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
import { addProductLineToOrder } from "@pos_loyalty/../tests/unit/utils";
|
||||
import { onRpc } from "@web/../tests/web_test_helpers";
|
||||
|
||||
definePosModels();
|
||||
|
||||
describe("PosStore - loyalty essentials", () => {
|
||||
test("updatePrograms", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
|
||||
const partner = models["res.partner"].get(1);
|
||||
// Get loyalty program #5 with program_type = "gift_card" and is_nominative = true
|
||||
const program = models["loyalty.program"].get(5);
|
||||
|
||||
models["loyalty.program"].getAll = () => [program];
|
||||
|
||||
order.setPartner(partner);
|
||||
order._programIsApplicable = () => true;
|
||||
order._code_activated_coupon_ids = [];
|
||||
order.uiState.couponPointChanges = {};
|
||||
order.pricelist_id = { id: 1 };
|
||||
|
||||
const line = await addProductLineToOrder(store, order, {
|
||||
gift_code: "XYZ123",
|
||||
});
|
||||
|
||||
order.pointsForPrograms = () => ({
|
||||
[program.id]: [{ points: 10 }],
|
||||
});
|
||||
|
||||
await store.updatePrograms();
|
||||
|
||||
const changes = order.uiState.couponPointChanges;
|
||||
const changeList = Object.values(changes);
|
||||
|
||||
expect(changeList).toHaveLength(1);
|
||||
expect(changeList[0].program_id).toBe(program.id);
|
||||
expect(changeList[0].points).toBe(10);
|
||||
expect(changeList[0].code).toBe("XYZ123");
|
||||
expect(changeList[0].product_id).toBe(line.product_id.id);
|
||||
});
|
||||
|
||||
test("activateCode", async () => {
|
||||
onRpc("loyalty.card", "get_loyalty_card_partner_by_code", () => false);
|
||||
const store = await setupPosEnv();
|
||||
store.addNewOrder();
|
||||
const result = await store.activateCode("EXPIRED");
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("fetchLoyaltyCard", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
|
||||
// Get loyalty program #2 with program_type = "ewallet"
|
||||
const program = models["loyalty.program"].get(2);
|
||||
const partner = models["res.partner"].get(1);
|
||||
|
||||
const card = await store.fetchLoyaltyCard(program.id, partner.id);
|
||||
|
||||
expect(card.id).toBe(2);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import { test, expect } from "@odoo/hoot";
|
||||
import { setupPosEnv } from "@point_of_sale/../tests/unit/utils";
|
||||
import { definePosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
import { addProductLineToOrder } from "@pos_loyalty/../tests/unit/utils";
|
||||
import OrderPaymentValidation from "@point_of_sale/app/utils/order_payment_validation";
|
||||
|
||||
definePosModels();
|
||||
|
||||
test("validateOrder", async () => {
|
||||
const store = await setupPosEnv();
|
||||
const models = store.models;
|
||||
const order = store.addNewOrder();
|
||||
const fastPaymentMethod = order.config.fast_payment_method_ids[0];
|
||||
|
||||
// Get loyalty program #1 - type = "loyalty"
|
||||
const loyaltyProgram = models["loyalty.program"].get(1);
|
||||
// Get loyalty card #1 - linked to Partner #1
|
||||
const card = models["loyalty.card"].get(1);
|
||||
// Get loyalty reward #1 - type = "discount"
|
||||
const reward = models["loyalty.reward"].get(1);
|
||||
|
||||
order.uiState.couponPointChanges = {
|
||||
[card.id]: { coupon_id: card.id, program_id: loyaltyProgram.id, points: 100 },
|
||||
"-1": { coupon_id: -1, program_id: loyaltyProgram.id, points: 30, partner_id: 1 },
|
||||
};
|
||||
|
||||
await addProductLineToOrder(store, order, {
|
||||
coupon_id: card,
|
||||
is_reward_line: true,
|
||||
reward_id: reward,
|
||||
points_cost: 60,
|
||||
});
|
||||
|
||||
const validation = new OrderPaymentValidation({
|
||||
pos: store,
|
||||
orderUuid: store.getOrder().uuid,
|
||||
fastPaymentMethod: fastPaymentMethod,
|
||||
});
|
||||
|
||||
validation.isOrderValid = async () => true;
|
||||
|
||||
await validation.validateOrder();
|
||||
|
||||
expect(card.points).toBe(50);
|
||||
expect(loyaltyProgram.total_order_count).toBe(0);
|
||||
expect(order.new_coupon_info[0].code).toMatch(/^[A-Za-z0-9]+$/);
|
||||
expect(order.new_coupon_info[0].program_name).toBe(loyaltyProgram.name);
|
||||
});
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
export const addProductLineToOrder = async (
|
||||
store,
|
||||
order,
|
||||
{ templateId = 1, productId = 1, qty = 1, price_unit = 10, ...extraFields } = {}
|
||||
) => {
|
||||
const template = store.models["product.template"].get(templateId);
|
||||
const product = store.models["product.product"].get(productId);
|
||||
|
||||
const lineData = {
|
||||
product_tmpl_id: template,
|
||||
product_id: product,
|
||||
qty,
|
||||
price_unit,
|
||||
...extraFields,
|
||||
};
|
||||
|
||||
const line = await store.addLineToOrder(lineData, order);
|
||||
|
||||
return line;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue