2022-04-19 05:59:36 +00:00
|
|
|
import { DocValue } from 'fyo/core/types';
|
2022-04-24 06:48:44 +00:00
|
|
|
import { Doc } from 'fyo/model/doc';
|
2022-04-19 05:59:36 +00:00
|
|
|
import { DefaultMap, FiltersMap, FormulaMap } from 'fyo/model/types';
|
2022-04-19 06:05:39 +00:00
|
|
|
import { getExchangeRate } from 'models/helpers';
|
|
|
|
import { LedgerPosting } from 'models/ledgerPosting/ledgerPosting';
|
2022-04-14 08:01:33 +00:00
|
|
|
import Money from 'pesa/dist/types/src/money';
|
|
|
|
import { Party } from '../Party/Party';
|
|
|
|
import { Payment } from '../Payment/Payment';
|
|
|
|
import { Tax } from '../Tax/Tax';
|
2022-04-14 09:22:45 +00:00
|
|
|
import { TaxSummary } from '../TaxSummary/TaxSummary';
|
2022-04-14 08:01:33 +00:00
|
|
|
|
2022-04-14 09:22:45 +00:00
|
|
|
export abstract class Invoice extends Doc {
|
2022-04-14 08:01:33 +00:00
|
|
|
_taxes: Record<string, Tax> = {};
|
2022-04-14 09:22:45 +00:00
|
|
|
taxes?: TaxSummary[];
|
2022-04-14 08:01:33 +00:00
|
|
|
|
2022-04-14 09:22:45 +00:00
|
|
|
party?: string;
|
|
|
|
account?: string;
|
|
|
|
currency?: string;
|
|
|
|
netTotal?: Money;
|
|
|
|
baseGrandTotal?: Money;
|
2022-04-20 06:38:47 +00:00
|
|
|
outstandingAmount?: Money;
|
2022-04-14 09:22:45 +00:00
|
|
|
exchangeRate?: number;
|
|
|
|
|
2022-04-20 06:38:47 +00:00
|
|
|
submitted?: boolean;
|
|
|
|
cancelled?: boolean;
|
|
|
|
|
2022-04-14 09:22:45 +00:00
|
|
|
abstract getPosting(): Promise<LedgerPosting>;
|
|
|
|
|
|
|
|
get isSales() {
|
|
|
|
return this.schemaName === 'SalesInvoice';
|
|
|
|
}
|
2022-04-14 08:01:33 +00:00
|
|
|
|
|
|
|
async getPayments() {
|
2022-04-19 05:59:36 +00:00
|
|
|
const payments = await this.fyo.db.getAll('PaymentFor', {
|
2022-04-14 08:01:33 +00:00
|
|
|
fields: ['parent'],
|
2022-04-14 09:22:45 +00:00
|
|
|
filters: { referenceName: this.name! },
|
2022-04-14 08:01:33 +00:00
|
|
|
orderBy: 'name',
|
|
|
|
});
|
|
|
|
|
|
|
|
if (payments.length != 0) {
|
|
|
|
return payments;
|
|
|
|
}
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2022-04-26 10:12:33 +00:00
|
|
|
async beforeSync() {
|
2022-04-14 08:01:33 +00:00
|
|
|
const entries = await this.getPosting();
|
|
|
|
await entries.validateEntries();
|
|
|
|
}
|
|
|
|
|
|
|
|
async afterSubmit() {
|
|
|
|
// post ledger entries
|
|
|
|
const entries = await this.getPosting();
|
|
|
|
await entries.post();
|
|
|
|
|
|
|
|
// update outstanding amounts
|
2022-04-19 05:59:36 +00:00
|
|
|
await this.fyo.db.update(this.schemaName, {
|
2022-04-14 08:01:33 +00:00
|
|
|
name: this.name as string,
|
2022-04-14 09:22:45 +00:00
|
|
|
outstandingAmount: this.baseGrandTotal!,
|
2022-04-14 08:01:33 +00:00
|
|
|
});
|
|
|
|
|
2022-04-19 05:59:36 +00:00
|
|
|
const party = (await this.fyo.doc.getDoc('Party', this.party!)) as Party;
|
2022-04-14 08:01:33 +00:00
|
|
|
await party.updateOutstandingAmount();
|
|
|
|
}
|
|
|
|
|
|
|
|
async afterRevert() {
|
|
|
|
const paymentRefList = await this.getPayments();
|
|
|
|
for (const paymentFor of paymentRefList) {
|
|
|
|
const paymentReference = paymentFor.parent;
|
2022-04-19 05:59:36 +00:00
|
|
|
const payment = (await this.fyo.doc.getDoc(
|
2022-04-14 08:01:33 +00:00
|
|
|
'Payment',
|
|
|
|
paymentReference as string
|
|
|
|
)) as Payment;
|
|
|
|
|
|
|
|
const paymentEntries = await payment.getPosting();
|
|
|
|
for (const entry of paymentEntries) {
|
|
|
|
await entry.postReverse();
|
|
|
|
}
|
|
|
|
|
|
|
|
// To set the payment status as unsubmitted.
|
2022-04-19 05:59:36 +00:00
|
|
|
await this.fyo.db.update('Payment', {
|
2022-04-14 08:01:33 +00:00
|
|
|
name: paymentReference,
|
|
|
|
submitted: false,
|
|
|
|
cancelled: true,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
const entries = await this.getPosting();
|
|
|
|
await entries.postReverse();
|
|
|
|
}
|
|
|
|
|
|
|
|
async getExchangeRate() {
|
|
|
|
if (!this.currency) return 1.0;
|
|
|
|
|
2022-04-19 05:59:36 +00:00
|
|
|
const accountingSettings = await this.fyo.doc.getSingle(
|
2022-04-18 11:29:20 +00:00
|
|
|
'AccountingSettings'
|
|
|
|
);
|
2022-04-14 08:01:33 +00:00
|
|
|
const companyCurrency = accountingSettings.currency;
|
|
|
|
if (this.currency === companyCurrency) {
|
|
|
|
return 1.0;
|
|
|
|
}
|
|
|
|
return await getExchangeRate({
|
2022-04-14 09:22:45 +00:00
|
|
|
fromCurrency: this.currency!,
|
2022-04-14 08:01:33 +00:00
|
|
|
toCurrency: companyCurrency as string,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async getTaxSummary() {
|
|
|
|
const taxes: Record<
|
|
|
|
string,
|
2022-04-14 09:22:45 +00:00
|
|
|
{
|
|
|
|
account: string;
|
|
|
|
rate: number;
|
|
|
|
amount: Money;
|
|
|
|
baseAmount: Money;
|
|
|
|
[key: string]: DocValue;
|
|
|
|
}
|
2022-04-14 08:01:33 +00:00
|
|
|
> = {};
|
|
|
|
|
|
|
|
for (const row of this.items as Doc[]) {
|
|
|
|
if (!row.tax) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const tax = await this.getTax(row.tax as string);
|
|
|
|
for (const d of tax.details as Doc[]) {
|
|
|
|
const account = d.account as string;
|
|
|
|
const rate = d.rate as number;
|
|
|
|
|
|
|
|
taxes[account] = taxes[account] || {
|
|
|
|
account,
|
|
|
|
rate,
|
2022-04-19 05:59:36 +00:00
|
|
|
amount: this.fyo.pesa(0),
|
|
|
|
baseAmount: this.fyo.pesa(0),
|
2022-04-14 08:01:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const amount = (row.amount as Money).mul(rate).div(100);
|
|
|
|
taxes[account].amount = taxes[account].amount.add(amount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Object.keys(taxes)
|
|
|
|
.map((account) => {
|
|
|
|
const tax = taxes[account];
|
2022-04-14 09:22:45 +00:00
|
|
|
tax.baseAmount = tax.amount.mul(this.exchangeRate!);
|
2022-04-14 08:01:33 +00:00
|
|
|
return tax;
|
|
|
|
})
|
|
|
|
.filter((tax) => !tax.amount.isZero());
|
|
|
|
}
|
|
|
|
|
|
|
|
async getTax(tax: string) {
|
|
|
|
if (!this._taxes![tax]) {
|
2022-04-19 05:59:36 +00:00
|
|
|
this._taxes[tax] = await this.fyo.doc.getDoc('Tax', tax);
|
2022-04-14 08:01:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return this._taxes[tax];
|
|
|
|
}
|
|
|
|
|
|
|
|
async getGrandTotal() {
|
|
|
|
return ((this.taxes ?? []) as Doc[])
|
|
|
|
.map((doc) => doc.amount as Money)
|
2022-04-14 09:22:45 +00:00
|
|
|
.reduce((a, b) => a.add(b), this.netTotal!);
|
2022-04-14 08:01:33 +00:00
|
|
|
}
|
2022-04-14 09:22:45 +00:00
|
|
|
|
|
|
|
formulas: FormulaMap = {
|
|
|
|
account: async () =>
|
|
|
|
this.getFrom('Party', this.party!, 'defaultAccount') as string,
|
|
|
|
currency: async () =>
|
|
|
|
(this.getFrom('Party', this.party!, 'currency') as string) ||
|
2022-04-19 05:59:36 +00:00
|
|
|
(this.fyo.singles.AccountingSettings!.currency as string),
|
2022-04-14 09:22:45 +00:00
|
|
|
exchangeRate: async () => await this.getExchangeRate(),
|
|
|
|
netTotal: async () => this.getSum('items', 'amount', false),
|
|
|
|
baseNetTotal: async () => this.netTotal!.mul(this.exchangeRate!),
|
|
|
|
taxes: async () => await this.getTaxSummary(),
|
|
|
|
grandTotal: async () => await this.getGrandTotal(),
|
|
|
|
baseGrandTotal: async () =>
|
|
|
|
(this.grandTotal as Money).mul(this.exchangeRate!),
|
|
|
|
outstandingAmount: async () => {
|
|
|
|
if (this.submitted) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.baseGrandTotal!;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2022-04-25 06:33:31 +00:00
|
|
|
static defaults: DefaultMap = {
|
2022-04-14 09:22:45 +00:00
|
|
|
date: () => new Date().toISOString().slice(0, 10),
|
|
|
|
};
|
|
|
|
|
|
|
|
static filters: FiltersMap = {
|
|
|
|
account: (doc: Doc) => ({
|
|
|
|
isGroup: false,
|
|
|
|
accountType: doc.isSales ? 'Receivable' : 'Payable',
|
|
|
|
}),
|
|
|
|
numberSeries: (doc: Doc) => ({ referenceType: doc.schemaName }),
|
|
|
|
};
|
2022-04-14 08:01:33 +00:00
|
|
|
}
|