2
0
mirror of https://github.com/frappe/books.git synced 2024-09-20 19:29:02 +00:00
books/models/helpers.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

300 lines
6.7 KiB
TypeScript
Raw Normal View History

import { Fyo, t } from 'fyo';
import { Doc } from 'fyo/model/doc';
2022-06-14 09:10:46 +00:00
import { Action, ColumnConfig, DocStatus, RenderData } from 'fyo/model/types';
import { DateTime } from 'luxon';
2022-05-23 05:30:54 +00:00
import { Money } from 'pesa';
import { safeParseFloat } from 'utils/index';
import { Router } from 'vue-router';
import {
AccountRootType,
AccountRootTypeEnum,
} from './baseModels/Account/types';
2022-10-12 09:29:43 +00:00
import {
Defaults,
numberSeriesDefaultsMap,
2022-10-12 09:29:43 +00:00
} from './baseModels/Defaults/Defaults';
import { InvoiceStatus, ModelNameEnum } from './types';
export function getInvoiceActions(
schemaName: ModelNameEnum.PurchaseInvoice | ModelNameEnum.SalesInvoice,
fyo: Fyo
): Action[] {
2022-04-14 08:01:33 +00:00
return [
{
label: fyo.t`Make Payment`,
2022-04-14 08:01:33 +00:00
condition: (doc: Doc) =>
doc.isSubmitted && !(doc.outstandingAmount as Money).isZero(),
2022-04-14 08:01:33 +00:00
action: async function makePayment(doc: Doc) {
const payment = fyo.doc.getNewDoc('Payment');
payment.once('afterSync', async () => {
2022-04-14 08:01:33 +00:00
await payment.submit();
});
2022-04-14 08:01:33 +00:00
const isSales = schemaName === 'SalesInvoice';
const party = doc.party as string;
2022-04-14 08:01:33 +00:00
const paymentType = isSales ? 'Receive' : 'Pay';
const hideAccountField = isSales ? 'account' : 'paymentAccount';
2022-04-20 06:38:47 +00:00
const { openQuickEdit } = await import('src/utils/ui');
2022-04-14 08:01:33 +00:00
await openQuickEdit({
schemaName: 'Payment',
name: payment.name as string,
hideFields: ['party', 'paymentType', 'for'],
2022-04-14 08:01:33 +00:00
defaults: {
party,
[hideAccountField]: doc.account,
date: new Date().toISOString().slice(0, 10),
paymentType,
for: [
{
2022-05-01 04:45:47 +00:00
referenceType: doc.schemaName,
2022-04-14 08:01:33 +00:00
referenceName: doc.name,
amount: doc.outstandingAmount,
},
],
},
});
},
},
getLedgerLinkAction(fyo),
2022-04-14 08:01:33 +00:00
];
}
export function getLedgerLinkAction(
fyo: Fyo,
isStock: boolean = false
): Action {
let label = fyo.t`Ledger Entries`;
let reportClassName = 'GeneralLedger';
if (isStock) {
label = fyo.t`Stock Entries`;
reportClassName = 'StockLedger';
}
return {
label,
condition: (doc: Doc) => doc.isSubmitted,
action: async (doc: Doc, router: Router) => {
router.push({
name: 'Report',
params: {
reportClassName,
2022-05-27 07:15:07 +00:00
defaultFilters: JSON.stringify({
referenceType: doc.schemaName,
referenceName: doc.name,
2022-05-27 07:15:07 +00:00
}),
},
});
},
};
}
export function getTransactionStatusColumn(): ColumnConfig {
2022-06-14 09:10:46 +00:00
const statusMap = getStatusMap();
2022-04-14 08:01:33 +00:00
return {
label: t`Status`,
2022-04-14 08:01:33 +00:00
fieldname: 'status',
fieldtype: 'Select',
2022-06-14 09:10:46 +00:00
render(doc) {
const status = getDocStatus(doc) as InvoiceStatus;
2022-04-14 08:01:33 +00:00
const color = statusColor[status];
const label = statusMap[status];
2022-06-14 09:10:46 +00:00
2022-04-14 08:01:33 +00:00
return {
template: `<Badge class="text-xs" color="${color}">${label}</Badge>`,
};
},
};
}
2022-06-14 09:10:46 +00:00
export const statusColor: Record<
DocStatus | InvoiceStatus,
string | undefined
> = {
'': 'gray',
2022-04-14 08:01:33 +00:00
Draft: 'gray',
Unpaid: 'orange',
Paid: 'green',
2022-06-14 09:10:46 +00:00
Saved: 'gray',
NotSaved: 'gray',
Submitted: 'green',
2022-04-14 08:01:33 +00:00
Cancelled: 'red',
};
2022-06-14 09:10:46 +00:00
export function getStatusMap(): Record<DocStatus | InvoiceStatus, string> {
return {
'': '',
Draft: t`Draft`,
Unpaid: t`Unpaid`,
Paid: t`Paid`,
Saved: t`Saved`,
NotSaved: t`Not Saved`,
Submitted: t`Submitted`,
Cancelled: t`Cancelled`,
};
}
export function getDocStatus(
doc?: RenderData | Doc
): DocStatus | InvoiceStatus {
if (!doc) {
return '';
}
if (doc.notInserted) {
return 'Draft';
2022-04-14 08:01:33 +00:00
}
2022-06-14 09:10:46 +00:00
if (doc.dirty) {
return 'NotSaved';
}
if (!doc.schema?.isSubmittable) {
return 'Saved';
}
return getSubmittableDocStatus(doc);
}
function getSubmittableDocStatus(doc: RenderData | Doc) {
if (
[ModelNameEnum.SalesInvoice, ModelNameEnum.PurchaseInvoice].includes(
doc.schema.name as ModelNameEnum
)
) {
return getInvoiceStatus(doc);
}
if (!!doc.submitted && !doc.cancelled) {
return 'Submitted';
}
if (!!doc.submitted && !!doc.cancelled) {
return 'Cancelled';
}
return 'Saved';
}
export function getInvoiceStatus(doc: RenderData | Doc): InvoiceStatus {
if (
doc.submitted &&
!doc.cancelled &&
(doc.outstandingAmount as Money).isZero()
) {
return 'Paid';
}
if (
doc.submitted &&
!doc.cancelled &&
(doc.outstandingAmount as Money).isPositive()
) {
return 'Unpaid';
2022-04-14 08:01:33 +00:00
}
if (doc.cancelled) {
2022-06-14 09:10:46 +00:00
return 'Cancelled';
2022-04-14 08:01:33 +00:00
}
2022-06-14 09:10:46 +00:00
return 'Saved';
2022-04-14 08:01:33 +00:00
}
export async function getExchangeRate({
fromCurrency,
toCurrency,
date,
}: {
fromCurrency: string;
toCurrency: string;
date?: string;
}) {
if (!fetch) {
return 1;
}
if (!date) {
date = DateTime.local().toISODate();
}
const cacheKey = `currencyExchangeRate:${date}:${fromCurrency}:${toCurrency}`;
let exchangeRate = 0;
if (localStorage) {
exchangeRate = safeParseFloat(
localStorage.getItem(cacheKey as string) as string
);
}
if (exchangeRate && exchangeRate !== 1) {
return exchangeRate;
}
try {
const res = await fetch(
`https://api.vatcomply.com/rates?date=${date}&base=${fromCurrency}&symbols=${toCurrency}`
);
const data = await res.json();
exchangeRate = data.rates[toCurrency];
} catch (error) {
console.error(error);
exchangeRate ??= 1;
}
if (localStorage) {
localStorage.setItem(cacheKey, String(exchangeRate));
}
return exchangeRate;
}
export function isCredit(rootType: AccountRootType) {
switch (rootType) {
case AccountRootTypeEnum.Asset:
return false;
case AccountRootTypeEnum.Liability:
return true;
case AccountRootTypeEnum.Equity:
return true;
case AccountRootTypeEnum.Expense:
return false;
case AccountRootTypeEnum.Income:
return true;
default:
return true;
}
}
2022-10-12 09:29:43 +00:00
export function getNumberSeries(schemaName: string, fyo: Fyo) {
const numberSeriesKey = numberSeriesDefaultsMap[schemaName];
if (!numberSeriesKey) {
return undefined;
}
const defaults = fyo.singles.Defaults as Defaults | undefined;
const field = fyo.getField(schemaName, 'numberSeries');
const value = defaults?.[numberSeriesKey] as string | undefined;
return value ?? (field?.default as string | undefined);
}
export function getDocStatusListColumn(): ColumnConfig {
return {
label: t`Status`,
fieldname: 'status',
fieldtype: 'Select',
size: 'small',
render(doc) {
const status = getDocStatus(doc);
const color = statusColor[status] ?? 'gray';
const label = getStatusMap()[status];
return {
template: `<Badge class="text-xs" color="${color}">${label}</Badge>`,
};
},
};
}