2024-01-30 12:55:16 +00:00
|
|
|
import { Doc } from 'fyo/model/doc';
|
|
|
|
import { Money } from 'pesa';
|
|
|
|
import { PricingRuleItem } from '../PricingRuleItem/PricingRuleItem';
|
|
|
|
import { getIsDocEnabledColumn } from 'models/helpers';
|
|
|
|
import {
|
|
|
|
HiddenMap,
|
|
|
|
ListViewSettings,
|
|
|
|
RequiredMap,
|
|
|
|
ValidationMap,
|
|
|
|
} from 'fyo/model/types';
|
2024-02-02 10:28:22 +00:00
|
|
|
import { DocValue } from 'fyo/core/types';
|
|
|
|
import { ValidationError } from 'fyo/utils/errors';
|
|
|
|
import { t } from 'fyo';
|
2024-01-30 12:55:16 +00:00
|
|
|
|
|
|
|
export class PricingRule extends Doc {
|
|
|
|
isEnabled?: boolean;
|
|
|
|
title?: string;
|
|
|
|
appliedItems?: PricingRuleItem[];
|
|
|
|
discountType?: 'Price Discount' | 'Product Discount';
|
|
|
|
|
|
|
|
priceDiscountType?: 'rate' | 'percentage' | 'amount';
|
|
|
|
discountRate?: Money;
|
|
|
|
discountPercentage?: number;
|
|
|
|
discountAmount?: Money;
|
|
|
|
|
|
|
|
forPriceList?: string;
|
|
|
|
|
|
|
|
freeItem?: string;
|
|
|
|
freeItemQuantity?: number;
|
|
|
|
freeItemUnit?: string;
|
|
|
|
freeItemRate?: Money;
|
|
|
|
roundFreeItemQty?: number;
|
|
|
|
roundingMethod?: string;
|
|
|
|
|
|
|
|
isRecursive?: boolean;
|
|
|
|
recurseEvery?: number;
|
|
|
|
recurseOver?: number;
|
|
|
|
|
|
|
|
minQuantity?: number;
|
|
|
|
maxQuantity?: number;
|
|
|
|
|
|
|
|
minAmount?: Money;
|
|
|
|
maxAmount?: Money;
|
|
|
|
|
|
|
|
validFrom?: Date;
|
|
|
|
validTo?: Date;
|
|
|
|
|
|
|
|
thresholdForSuggestion?: number;
|
|
|
|
priority?: number;
|
|
|
|
|
|
|
|
get isDiscountTypeIsPriceDiscount() {
|
|
|
|
return this.discountType === 'Price Discount';
|
|
|
|
}
|
|
|
|
|
2024-02-02 10:28:22 +00:00
|
|
|
validations: ValidationMap = {
|
|
|
|
minQuantity: (value: DocValue) => {
|
|
|
|
if (!value || !this.maxQuantity) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((value as number) > this.maxQuantity) {
|
|
|
|
throw new ValidationError(
|
|
|
|
t`Minimum Quantity should be less than the Maximum Quantity.`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
maxQuantity: (value: DocValue) => {
|
|
|
|
if (!this.minQuantity || !value) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((value as number) < this.minQuantity) {
|
|
|
|
throw new ValidationError(
|
|
|
|
t`Maximum Quantity should be greater than the Minimum Quantity.`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
minAmount: (value: DocValue) => {
|
|
|
|
if (!value || !this.maxAmount) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((value as Money).isZero() && this.maxAmount.isZero()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((value as Money).gte(this.maxAmount)) {
|
|
|
|
throw new ValidationError(
|
|
|
|
t`Minimum Amount should be less than the Maximum Amount.`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
maxAmount: (value: DocValue) => {
|
|
|
|
if (!this.minAmount || !value) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.minAmount.isZero() && (value as Money).isZero()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((value as Money).lte(this.minAmount)) {
|
|
|
|
throw new ValidationError(
|
|
|
|
t`Maximum Amount should be greater than the Minimum Amount.`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
validFrom: (value: DocValue) => {
|
|
|
|
if (!value || !this.validTo) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ((value as Date).toISOString() > this.validTo.toISOString()) {
|
|
|
|
throw new ValidationError(
|
|
|
|
t`Valid From Date should be less than Valid To Date.`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
validTo: (value: DocValue) => {
|
|
|
|
if (!this.validFrom || !value) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ((value as Date).toISOString() < this.validFrom.toISOString()) {
|
|
|
|
throw new ValidationError(
|
|
|
|
t`Valid To Date should be greater than Valid From Date.`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
2024-01-30 12:55:16 +00:00
|
|
|
|
|
|
|
required: RequiredMap = {
|
|
|
|
priceDiscountType: () => this.isDiscountTypeIsPriceDiscount,
|
|
|
|
};
|
|
|
|
|
|
|
|
static getListViewSettings(): ListViewSettings {
|
|
|
|
return {
|
|
|
|
columns: ['name', 'title', getIsDocEnabledColumn(), 'discountType'],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
hidden: HiddenMap = {
|
|
|
|
location: () => !this.fyo.singles.AccountingSettings?.enableInventory,
|
|
|
|
|
|
|
|
priceDiscountType: () => !this.isDiscountTypeIsPriceDiscount,
|
|
|
|
discountRate: () =>
|
|
|
|
!this.isDiscountTypeIsPriceDiscount || this.priceDiscountType !== 'rate',
|
|
|
|
discountPercentage: () =>
|
|
|
|
!this.isDiscountTypeIsPriceDiscount ||
|
|
|
|
this.priceDiscountType !== 'percentage',
|
|
|
|
discountAmount: () =>
|
|
|
|
!this.isDiscountTypeIsPriceDiscount ||
|
|
|
|
this.priceDiscountType !== 'amount',
|
|
|
|
forPriceList: () =>
|
|
|
|
!this.isDiscountTypeIsPriceDiscount || this.priceDiscountType === 'rate',
|
|
|
|
|
|
|
|
freeItem: () => this.isDiscountTypeIsPriceDiscount,
|
|
|
|
freeItemQuantity: () => this.isDiscountTypeIsPriceDiscount,
|
|
|
|
freeItemUnit: () => this.isDiscountTypeIsPriceDiscount,
|
|
|
|
freeItemRate: () => this.isDiscountTypeIsPriceDiscount,
|
|
|
|
roundFreeItemQty: () => this.isDiscountTypeIsPriceDiscount,
|
|
|
|
roundingMethod: () =>
|
|
|
|
this.isDiscountTypeIsPriceDiscount || !this.roundFreeItemQty,
|
|
|
|
isRecursive: () => this.isDiscountTypeIsPriceDiscount,
|
|
|
|
recurseEvery: () => this.isDiscountTypeIsPriceDiscount || !this.isRecursive,
|
|
|
|
recurseOver: () => this.isDiscountTypeIsPriceDiscount || !this.isRecursive,
|
|
|
|
};
|
|
|
|
}
|