200 lines
8.8 KiB
JavaScript
200 lines
8.8 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import { Order, Orderline } from "@point_of_sale/app/store/models";
|
|
import { patch } from "@web/core/utils/patch";
|
|
|
|
// Patch Order methods to handle line discounts
|
|
patch(Order.prototype, {
|
|
/**
|
|
* Override to calculate reward values as line discounts instead of order-level discounts
|
|
*/
|
|
_getRewardLineValues(args) {
|
|
const reward = args["reward"];
|
|
const coupon_id = args["coupon_id"];
|
|
|
|
// If config is not set to apply rewards as line discounts, use the original method
|
|
// Note: In JavaScript, we access the config through this.pos.config
|
|
if (!this.pos.config.apply_line_discount_on_rewards) {
|
|
return super._getRewardLineValues(...arguments);
|
|
}
|
|
|
|
// For discount rewards, we'll distribute the discount to order lines instead of creating reward lines
|
|
if (reward.reward_type === "discount") {
|
|
// Calculate the reward using the original method first to get the discount amount
|
|
const originalRewardLines = super._getRewardLineValues(...arguments);
|
|
|
|
// Get the total discount amount from the reward lines (tax-excluded)
|
|
let totalDiscountAmount = 0;
|
|
originalRewardLines.forEach(line => {
|
|
// Calculate tax-excluded amount from the reward line
|
|
// The line.price is tax-included, so we need to calculate the tax-excluded amount
|
|
// Reward lines are plain objects, not Orderline instances, so we calculate manually
|
|
let taxExcludedAmount = Math.abs(line.price * line.quantity);
|
|
|
|
// If the line has tax information, we need to calculate the tax-excluded amount
|
|
if (line.tax_ids && line.tax_ids.length > 0) {
|
|
// Calculate total tax rate (simplified approach for multiple taxes)
|
|
let totalTaxRate = 0;
|
|
for (const taxId of line.tax_ids) {
|
|
const tax = this.pos.taxes_by_id[taxId];
|
|
if (tax && tax.amount_type === 'percent') {
|
|
totalTaxRate += tax.amount / 100;
|
|
}
|
|
}
|
|
|
|
if (totalTaxRate > 0) {
|
|
taxExcludedAmount = Math.abs(line.price * line.quantity) / (1 + totalTaxRate);
|
|
}
|
|
}
|
|
|
|
totalDiscountAmount += taxExcludedAmount;
|
|
});
|
|
|
|
// Distribute the discount to order lines instead of creating reward lines
|
|
// Filter out reward lines by checking if they have a reward_id property
|
|
const orderLines = this.get_orderlines().filter(line => !line.is_reward_line);
|
|
const totalOrderAmount = orderLines.reduce((sum, line) => sum + line.get_price_without_tax(), 0);
|
|
|
|
if (totalOrderAmount > 0 && totalDiscountAmount > 0) {
|
|
// Apply the discount to each line proportionally based on its contribution to the total
|
|
orderLines.forEach(line => {
|
|
// Calculate the line's share of the total discount
|
|
const lineRatio = line.get_price_without_tax() / totalOrderAmount;
|
|
const lineDiscountAmount = totalDiscountAmount * lineRatio;
|
|
|
|
// Calculate the discount percentage for this line
|
|
// We want to set a discount that results in the lineDiscountAmount reduction
|
|
if (line.get_price_without_tax() > 0) {
|
|
const lineDiscountPercentage = (lineDiscountAmount / line.get_price_without_tax()) * 100;
|
|
// Set the discount percentage for this line (same as %Disc button)
|
|
// Ensure the discount percentage is within valid range [0, 100]
|
|
const validDiscountPercentage = Math.min(100, Math.max(0, lineDiscountPercentage));
|
|
// Mark this line as having a reward discount applied
|
|
if (line.originalDiscount === undefined) {
|
|
line.originalDiscount = line.discount;
|
|
}
|
|
// Apply the new discount on top of the original
|
|
line.discount = line.originalDiscount + validDiscountPercentage;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Return empty array since we're not creating reward lines
|
|
return [];
|
|
}
|
|
|
|
// For non-discount rewards, use the original method
|
|
return super._getRewardLineValues(...arguments);
|
|
},
|
|
|
|
/**
|
|
* Distribute an order-level discount to individual lines
|
|
*/
|
|
_distributeDiscountToLines(discountAmount, isPercentage = false) {
|
|
const lines = this.get_orderlines().filter(line => !line.reward_id);
|
|
if (lines.length === 0) return;
|
|
|
|
// Calculate total amount for proportional distribution
|
|
const totalAmount = lines.reduce((sum, line) => sum + line.get_price_without_tax(), 0);
|
|
if (totalAmount <= 0) return;
|
|
|
|
if (isPercentage) {
|
|
// Apply the same percentage discount to all lines
|
|
lines.forEach(line => {
|
|
const currentDiscount = line.get_discount();
|
|
// Combine discounts (this is a simplification)
|
|
// Store the original discount if not already stored
|
|
if (line.originalDiscount === undefined) {
|
|
line.originalDiscount = line.discount;
|
|
}
|
|
const newDiscount = Math.min(100, line.originalDiscount + discountAmount);
|
|
line.discount = newDiscount;
|
|
});
|
|
} else {
|
|
// Distribute fixed amount proportionally based on line subtotal
|
|
lines.forEach(line => {
|
|
const lineRatio = line.get_price_without_tax() / totalAmount;
|
|
const lineDiscountAmount = discountAmount * lineRatio;
|
|
// Calculate the additional discount percentage for this line
|
|
if (line.get_price_without_tax() > 0) {
|
|
const additionalDiscountPercentage = (lineDiscountAmount / line.get_price_without_tax()) * 100;
|
|
// Store the original discount if not already stored
|
|
if (line.originalDiscount === undefined) {
|
|
line.originalDiscount = line.discount;
|
|
}
|
|
const newDiscountPercentage = Math.min(100, line.originalDiscount + additionalDiscountPercentage);
|
|
line.discount = newDiscountPercentage;
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Apply a global discount to all order lines
|
|
*/
|
|
setGlobalDiscount(discountPercentage) {
|
|
this.get_orderlines().forEach(line => {
|
|
if (!line.is_reward_line) {
|
|
// Store the original discount if not already stored
|
|
if (line.originalDiscount === undefined) {
|
|
line.originalDiscount = line.discount;
|
|
}
|
|
line.discount = discountPercentage;
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Override to also reset line discounts applied by this module
|
|
*/
|
|
_resetPrograms() {
|
|
// First call the original reset method
|
|
super._resetPrograms(...arguments);
|
|
|
|
// Reset line discounts that were applied by this module
|
|
this.get_orderlines().forEach(line => {
|
|
if (!line.is_reward_line && line.originalDiscount !== undefined) {
|
|
// Reset to the original discount value
|
|
line.discount = line.originalDiscount;
|
|
delete line.originalDiscount;
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Clear all line discounts applied by this module
|
|
*/
|
|
clearLineDiscounts() {
|
|
this.get_orderlines().forEach(line => {
|
|
if (!line.is_reward_line && line.originalDiscount !== undefined) {
|
|
// Reset to the original discount value
|
|
line.discount = line.originalDiscount;
|
|
delete line.originalDiscount;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Patch Orderline methods to handle line discounts
|
|
patch(Orderline.prototype, {
|
|
/**
|
|
* Override to handle line discount changes
|
|
*/
|
|
set_discount(discount) {
|
|
// Call the original method
|
|
super.set_discount(...arguments);
|
|
|
|
// Store the original discount if not already stored and this is not a reward line
|
|
if (this.originalDiscount === undefined && !this.is_reward_line) {
|
|
this.originalDiscount = this.discount;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Get the discount amount for this line
|
|
*/
|
|
get_discount_amount() {
|
|
return this.get_price_without_tax() * (this.get_discount() / 100);
|
|
}
|
|
});
|