mirror of
https://github.com/frappe/books.git
synced 2026-08-24 02:24:17 -05:00
Merge pull request #1260 from frappe/fix-add-loyaltyamount-in-gt
fix: include converted loyalty points in grand total
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Fyo } from 'fyo';
|
||||
import { Fyo, t } from 'fyo';
|
||||
import { DocValueMap } from 'fyo/core/types';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import {
|
||||
@@ -193,6 +193,21 @@ export abstract class Invoice extends Transactional {
|
||||
await this._validatePricingRule();
|
||||
}
|
||||
|
||||
async beforeSubmit() {
|
||||
const partyDoc = (await this.fyo.doc.getDoc(
|
||||
ModelNameEnum.Party,
|
||||
this.party
|
||||
)) as Party;
|
||||
|
||||
if ((this.loyaltyPoints as number) > (partyDoc?.loyaltyPoints || 0)) {
|
||||
throw new ValidationError(
|
||||
t`${this.party as string} only has ${
|
||||
partyDoc.loyaltyPoints as number
|
||||
} points`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async afterSubmit() {
|
||||
await super.afterSubmit();
|
||||
if (this.isReturn) {
|
||||
@@ -446,6 +461,9 @@ export abstract class Invoice extends Transactional {
|
||||
const totalDiscount = this.getTotalDiscount();
|
||||
|
||||
if (!this.taxes!.length) {
|
||||
if (this.redeemLoyaltyPoints) {
|
||||
return this.getLPAddedBaseGrandTotal();
|
||||
}
|
||||
return (this.netTotal as Money).sub(totalDiscount);
|
||||
}
|
||||
|
||||
@@ -880,6 +898,10 @@ export abstract class Invoice extends Transactional {
|
||||
this.loyaltyPoints as number
|
||||
);
|
||||
|
||||
if (this.isReturn) {
|
||||
return this.grandTotal;
|
||||
}
|
||||
|
||||
return this.initialGrandTotal?.sub(totalLotaltyAmount);
|
||||
}
|
||||
formulas: FormulaMap = {
|
||||
@@ -937,7 +959,10 @@ export abstract class Invoice extends Transactional {
|
||||
},
|
||||
netTotal: { formula: () => this.getSum('items', 'amount', false) },
|
||||
taxes: { formula: async () => await this.getTaxSummary() },
|
||||
grandTotal: { formula: () => this.getGrandTotal() },
|
||||
grandTotal: {
|
||||
formula: async () => await this.getGrandTotal(),
|
||||
dependsOn: ['loyaltyPoints'],
|
||||
},
|
||||
baseGrandTotal: {
|
||||
formula: () => (this.grandTotal as Money).mul(this.exchangeRate! ?? 1),
|
||||
dependsOn: ['grandTotal', 'exchangeRate'],
|
||||
@@ -955,7 +980,7 @@ export abstract class Invoice extends Transactional {
|
||||
if (sinvreturnedDoc.outstandingAmount?.isZero()) {
|
||||
return this.grandTotal?.abs();
|
||||
} else {
|
||||
const totalPaid = this.grandTotal
|
||||
const totalPaid = sinvreturnedDoc.grandTotal
|
||||
?.abs()
|
||||
.sub(sinvreturnedDoc.outstandingAmount as Money);
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import { LoyaltyProgram } from '../LoyaltyProgram/LoyaltyProgram';
|
||||
import { DocValue } from 'fyo/core/types';
|
||||
import { Party } from '../Party/Party';
|
||||
import { ValidationError } from 'fyo/utils/errors';
|
||||
import { Money } from 'pesa';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
|
||||
export class SalesInvoice extends Invoice {
|
||||
items?: SalesInvoiceItem[];
|
||||
@@ -81,7 +83,7 @@ export class SalesInvoice extends Invoice {
|
||||
|
||||
validations: ValidationMap = {
|
||||
loyaltyPoints: async (value: DocValue) => {
|
||||
if (!this.redeemLoyaltyPoints || this.isSubmitted) {
|
||||
if (!this.redeemLoyaltyPoints || this.isSubmitted || this.isReturn) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -116,7 +118,24 @@ export class SalesInvoice extends Invoice {
|
||||
((loyaltyProgramDoc?.conversionFactor as number) || 0);
|
||||
|
||||
if (!this.isReturn) {
|
||||
if (this.grandTotal?.lt(loyaltyPoint)) {
|
||||
const totalDiscount = this.getTotalDiscount();
|
||||
let baseGrandTotal;
|
||||
|
||||
if (!this.taxes!.length) {
|
||||
baseGrandTotal = (this.netTotal as Money).sub(totalDiscount);
|
||||
} else {
|
||||
baseGrandTotal = ((this.taxes ?? []) as Doc[])
|
||||
.map((doc) => doc.amount as Money)
|
||||
.reduce((a, b) => {
|
||||
if (this.isReturn) {
|
||||
return a.abs().add(b.abs()).neg();
|
||||
}
|
||||
return a.add(b.abs());
|
||||
}, (this.netTotal as Money).abs())
|
||||
.sub(totalDiscount);
|
||||
}
|
||||
|
||||
if (baseGrandTotal?.lt(loyaltyPoint)) {
|
||||
throw new ValidationError(
|
||||
t`no need ${value as number} points to purchase this item`
|
||||
);
|
||||
|
||||
@@ -231,10 +231,18 @@ export class StockMovementItem extends TransferItem {
|
||||
);
|
||||
}
|
||||
},
|
||||
batch: () => {
|
||||
if (this.fyo.singles.InventorySettings?.enableBatches && !this.batch) {
|
||||
batch: async () => {
|
||||
if (!this.item || !this.batch) return;
|
||||
|
||||
const batchDoc = await this.fyo.doc.getDoc(
|
||||
ModelNameEnum.Batch,
|
||||
this.batch
|
||||
);
|
||||
if (!batchDoc) return;
|
||||
|
||||
if (batchDoc.item !== this.item) {
|
||||
throw new ValidationError(
|
||||
t`Batch is required for Item ${this.item as string}`
|
||||
t`Batch ${this.batch} does not belong to Item ${this.item}`
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -305,6 +305,9 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$watch('row.quantity', (newVal: number) => {
|
||||
this.setQuantity(newVal);
|
||||
});
|
||||
const posProfileName = this.fyo.singles.POSSettings?.posProfile;
|
||||
|
||||
if (posProfileName) {
|
||||
|
||||
@@ -40,6 +40,14 @@ export default defineComponent({
|
||||
return this.t`Partly Paid ${outstandingPayment}`;
|
||||
}
|
||||
|
||||
if (this.status === 'Outstanding') {
|
||||
const outstandingPayment = this.fyo.format(
|
||||
this.doc.outstandingAmount as Money,
|
||||
'Currency'
|
||||
);
|
||||
return this.t`Unpaid ${outstandingPayment}`;
|
||||
}
|
||||
|
||||
return {
|
||||
Draft: this.t`Draft`,
|
||||
Cancelled: this.t`Cancelled`,
|
||||
|
||||
@@ -146,7 +146,6 @@ import {
|
||||
getItemQtyMap,
|
||||
getPricingRule,
|
||||
removeFreeItems,
|
||||
getAddedLPWithGrandTotal,
|
||||
getItemRateFromPriceList,
|
||||
} from 'models/helpers';
|
||||
import {
|
||||
@@ -611,19 +610,6 @@ export default defineComponent({
|
||||
async setLoyaltyPoints(value: number) {
|
||||
this.appliedLoyaltyPoints = value;
|
||||
await this.sinvDoc.set('redeemLoyaltyPoints', true);
|
||||
|
||||
const totalLotaltyAmount = await getAddedLPWithGrandTotal(
|
||||
this.fyo,
|
||||
this.loyaltyProgram,
|
||||
value
|
||||
);
|
||||
|
||||
const total = totalLotaltyAmount
|
||||
.sub(this.sinvDoc.baseGrandTotal as Money)
|
||||
.abs();
|
||||
|
||||
this.sinvDoc.grandTotal = total;
|
||||
this.sinvDoc.outstandingAmount = total;
|
||||
},
|
||||
async selectedInvoiceName(doc: SalesInvoice) {
|
||||
const salesInvoiceDoc = (await this.fyo.doc.getDoc(
|
||||
@@ -704,50 +690,6 @@ export default defineComponent({
|
||||
)) as number;
|
||||
|
||||
if (item.hasBatch) {
|
||||
const isTrackItem =
|
||||
this.fyo.singles.POSSettings?.itemVisibility == 'Inventory Items';
|
||||
|
||||
for (const invItem of existingItems) {
|
||||
const itemQty = invItem.quantity ?? 0;
|
||||
|
||||
if (!isTrackItem) {
|
||||
invItem.quantity = quantity
|
||||
? (invItem.quantity as number) + quantity
|
||||
: (invItem.quantity as number) + 1;
|
||||
} else {
|
||||
const qtyInBatch =
|
||||
this.itemQtyMap[invItem.item as string][
|
||||
invItem.batch as string
|
||||
] ?? 0;
|
||||
|
||||
if (itemQty < qtyInBatch) {
|
||||
invItem.quantity = quantity
|
||||
? (invItem.quantity as number) + quantity
|
||||
: (invItem.quantity as number) + 1;
|
||||
invItem.rate = item.rate as Money;
|
||||
}
|
||||
await this.applyPricingRule();
|
||||
await this.sinvDoc.runFormulas();
|
||||
await validateQty(
|
||||
this.sinvDoc as SalesInvoice,
|
||||
item as Item,
|
||||
existingItems as InvoiceItem[]
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await this.applyPricingRule();
|
||||
await this.sinvDoc.runFormulas();
|
||||
await validateQty(
|
||||
this.sinvDoc as SalesInvoice,
|
||||
item as Item,
|
||||
existingItems as InvoiceItem[]
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await this.sinvDoc.append('items', {
|
||||
rate: item.rate as Money,
|
||||
item: item.name,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<main class="h-auto w-auto m-2.5 p-0.5 pb-5 bg-white border border-gray-500">
|
||||
<main class="h-auto w-auto m-1 p-0.5 pb-5 bg-white">
|
||||
<!-- Invoice Header -->
|
||||
<header class="pt-4 flex flex-col items-center">
|
||||
<!-- Company Logo & Name -->
|
||||
|
||||
Reference in New Issue
Block a user