2
0
mirror of https://github.com/frappe/books.git synced 2024-12-22 19:09:01 +00:00

incr: type frappe/utils/format

This commit is contained in:
18alantom 2022-04-18 13:31:41 +05:30
parent 9ec004184c
commit a8e208a68a
7 changed files with 168 additions and 117 deletions

View File

@ -1,5 +1,5 @@
import Doc from 'frappe/model/doc';
import { DocMap, ModelMap } from 'frappe/model/types';
import { DocMap, ModelMap, SinglesMap } from 'frappe/model/types';
import { coreModels } from 'frappe/models';
import { getRandomString } from 'frappe/utils';
import Observable from 'frappe/utils/observable';
@ -8,7 +8,7 @@ import { DocValue, DocValueMap } from './types';
export class DocHandler {
frappe: Frappe;
singles: DocMap = {};
singles: SinglesMap = {};
docs: Observable<DocMap> = new Observable();
models: ModelMap = {};

View File

@ -15,7 +15,6 @@ import { format } from './utils/format';
import { t, T } from './utils/translation';
import { ErrorLog } from './utils/types';
export class Frappe {
t = t;
T = T;
@ -36,6 +35,9 @@ export class Frappe {
methods?: Record<string, Function>;
temp?: Record<string, unknown>;
currencyFormatter?: Intl.NumberFormat;
currencySymbols: Record<string, string | undefined> = {};
constructor(DatabaseDemux?: DatabaseDemuxConstructor) {
/**
* `DatabaseManager` can be passed as the `DatabaseDemux` for
@ -84,6 +86,7 @@ export class Frappe {
await this.#initializeMoneyMaker();
this.doc.registerModels(models, regionalModels);
await this.doc.getSingle('SystemSettings');
this._initialized = true;
}

View File

@ -28,6 +28,7 @@ import {
import { setName } from './naming';
import {
Action,
CurrenciesMap,
DefaultMap,
DependsOnMap,
EmptyMessageMap,
@ -705,6 +706,7 @@ export default class Doc extends Observable<DocValue | Doc[]> {
required: RequiredMap = {};
hidden: HiddenMap = {};
dependsOn: DependsOnMap = {};
getCurrencies: CurrenciesMap = {};
static lists: ListsMap = {};
static filters: FiltersMap = {};

View File

@ -1,4 +1,5 @@
import { DocValue, DocValueMap } from 'frappe/core/types';
import SystemSettings from 'frappe/models/SystemSettings';
import { FieldType } from 'schemas/types';
import { QueryFilter } from 'utils/db/types';
import { Router } from 'vue-router';
@ -22,11 +23,13 @@ export type Default = () => DocValue;
export type Validation = (value: DocValue) => Promise<void>;
export type Required = () => boolean;
export type Hidden = () => boolean;
export type GetCurrency = () => string;
export type FormulaMap = Record<string, Formula | undefined>;
export type DefaultMap = Record<string, Default | undefined>;
export type ValidationMap = Record<string, Validation | undefined>;
export type RequiredMap = Record<string, Required | undefined>;
export type CurrenciesMap = Record<string, GetCurrency>;
export type HiddenMap = Record<string, Hidden>;
export type DependsOnMap = Record<string, string[]>;
@ -37,6 +40,11 @@ export type DependsOnMap = Record<string, string[]>;
export type ModelMap = Record<string, typeof Doc | undefined>;
export type DocMap = Record<string, Doc | undefined>;
export interface SinglesMap {
SystemSettings?: SystemSettings
[key: string]: Doc | undefined
}
// Static Config properties
export type FilterFunction = (doc: Doc) => QueryFilter;

View File

@ -1,8 +1,9 @@
export const DEFAULT_INTERNAL_PRECISION = 11;
export const DEFAULT_DISPLAY_PRECISION = 2;
export const DEFAULT_DATE_FORMAT = 'yyyy-MM-dd';
export const DEFAULT_LOCALE = 'en-IN';
export const DEFAULT_COUNTRY_CODE = 'in';
export const DEFAULT_CURRENCY = 'XXX';
export const DEFAULT_CURRENCY = 'INR';
export const DEFAULT_LANGUAGE = 'English';
export const DEFAULT_NUMBER_SERIES = {
SalesInvoice: 'SINV-',

View File

@ -1,113 +0,0 @@
import frappe from 'frappe';
import { DateTime } from 'luxon';
import { DEFAULT_DISPLAY_PRECISION, DEFAULT_LOCALE } from './consts';
export function format(value, df, doc) {
if (!df) {
return value;
}
if (typeof df === 'string') {
df = { fieldtype: df };
}
if (df.fieldtype === 'Currency') {
const currency = getCurrency(df, doc);
value = formatCurrency(value, currency);
} else if (df.fieldtype === 'Date') {
let dateFormat;
if (!frappe.SystemSettings) {
dateFormat = 'yyyy-MM-dd';
} else {
dateFormat = frappe.SystemSettings.dateFormat;
}
if (typeof value === 'string') {
// ISO String
value = DateTime.fromISO(value);
} else if (Object.prototype.toString.call(value) === '[object Date]') {
// JS Date
value = DateTime.fromJSDate(value);
}
value = value.toFormat(dateFormat);
if (value === 'Invalid DateTime') {
value = '';
}
} else if (df.fieldtype === 'Check') {
typeof parseInt(value) === 'number'
? (value = parseInt(value))
: (value = Boolean(value));
} else {
if (value === null || value === undefined) {
value = '';
} else {
value = value + '';
}
}
return value;
}
function formatCurrency(value, currency) {
let valueString;
try {
valueString = formatNumber(value);
} catch (err) {
err.message += ` value: '${value}', type: ${typeof value}`;
throw err;
}
const currencySymbol = frappe.currencySymbols[currency];
if (currencySymbol) {
return currencySymbol + ' ' + valueString;
}
return valueString;
}
function formatNumber(value) {
const numberFormatter = getNumberFormatter();
if (typeof value === 'number') {
return numberFormatter.format(value);
}
if (value.round) {
return numberFormatter.format(value.round());
}
const formattedNumber = numberFormatter.format(value);
if (formattedNumber === 'NaN') {
throw Error(
`invalid value passed to formatNumber: '${value}' of type ${typeof value}`
);
}
return formattedNumber;
}
function getNumberFormatter() {
if (frappe.currencyFormatter) {
return frappe.currencyFormatter;
}
const locale = frappe.SystemSettings.locale ?? DEFAULT_LOCALE;
const display =
frappe.SystemSettings.displayPrecision ?? DEFAULT_DISPLAY_PRECISION;
return (frappe.currencyFormatter = Intl.NumberFormat(locale, {
style: 'decimal',
minimumFractionDigits: display,
}));
}
function getCurrency(df, doc) {
if (!(doc && df.getCurrency)) {
return df.currency || frappe.AccountingSettings.currency || '';
}
if (doc.meta && doc.meta.isChild) {
return df.getCurrency(doc, doc.parentdoc);
}
return df.getCurrency(doc);
}

150
frappe/utils/format.ts Normal file
View File

@ -0,0 +1,150 @@
import frappe from 'frappe';
import { DocValue } from 'frappe/core/types';
import Doc from 'frappe/model/doc';
import { DateTime } from 'luxon';
import Money from 'pesa/dist/types/src/money';
import { Field, FieldType, FieldTypeEnum } from 'schemas/types';
import { getIsNullOrUndef } from 'utils';
import {
DEFAULT_CURRENCY,
DEFAULT_DATE_FORMAT,
DEFAULT_DISPLAY_PRECISION,
DEFAULT_LOCALE,
} from './consts';
export function format(
value: DocValue,
df?: string | Field,
doc?: Doc
): string {
if (!df) {
return String(value);
}
const field: Field = getField(df);
if (field.fieldtype === FieldTypeEnum.Currency) {
return formatCurrency(value, field, doc);
}
if (field.fieldtype === FieldTypeEnum.Date) {
return formatDate(value);
}
if (field.fieldtype === FieldTypeEnum.Check) {
return Boolean(value).toString();
}
if (getIsNullOrUndef(value)) {
return '';
}
return String(value);
}
function formatDate(value: DocValue): string {
const dateFormat =
(frappe.singles.SystemSettings?.dateFormat as string) ??
DEFAULT_DATE_FORMAT;
let dateValue: DateTime;
if (typeof value === 'string') {
dateValue = DateTime.fromISO(value);
} else if (value instanceof Date) {
dateValue = DateTime.fromJSDate(value);
} else {
dateValue = DateTime.fromSeconds(value as number);
}
const formattedDate = dateValue.toFormat(dateFormat);
if (value === 'Invalid DateTime') {
return '';
}
return formattedDate;
}
function formatCurrency(value: DocValue, field: Field, doc?: Doc): string {
const currency = getCurrency(field, doc);
let valueString;
try {
valueString = formatNumber(value);
} catch (err) {
(err as Error).message += ` value: '${value}', type: ${typeof value}`;
throw err;
}
const currencySymbol = frappe.currencySymbols[currency];
if (currencySymbol !== undefined) {
return currencySymbol + ' ' + valueString;
}
return valueString;
}
function formatNumber(value: DocValue): string {
const numberFormatter = getNumberFormatter();
if (typeof value === 'number') {
return numberFormatter.format(value);
}
if ((value as Money).round) {
const floatValue = parseFloat((value as Money).round());
return numberFormatter.format(floatValue);
}
const floatValue = parseFloat(value as string);
const formattedNumber = numberFormatter.format(floatValue);
if (formattedNumber === 'NaN') {
throw Error(
`invalid value passed to formatNumber: '${value}' of type ${typeof value}`
);
}
return formattedNumber;
}
function getNumberFormatter() {
if (frappe.currencyFormatter) {
return frappe.currencyFormatter;
}
const locale =
(frappe.singles.SystemSettings?.locale as string) ?? DEFAULT_LOCALE;
const display =
(frappe.singles.SystemSettings?.displayPrecision as number) ??
DEFAULT_DISPLAY_PRECISION;
return (frappe.currencyFormatter = Intl.NumberFormat(locale, {
style: 'decimal',
minimumFractionDigits: display,
}));
}
function getCurrency(field: Field, doc?: Doc): string {
if (doc && doc.getCurrencies[field.fieldname]) {
return doc.getCurrencies[field.fieldname]();
}
if (doc && doc.parentdoc?.getCurrencies[field.fieldname]) {
return doc.parentdoc.getCurrencies[field.fieldname]();
}
return (
(frappe.singles.SystemSettings?.currency as string) ?? DEFAULT_CURRENCY
);
}
function getField(df: string | Field): Field {
if (typeof df === 'string') {
return {
label: '',
fieldname: '',
fieldtype: df as FieldType,
};
}
return df;
}