mirror of
https://github.com/frappe/books.git
synced 2025-01-09 09:50:27 +00:00
Merge pull request #429 from 18alantom/multi-currency-invoicing
feat: multi currency invoicing
This commit is contained in:
commit
24b6021606
@ -12,7 +12,7 @@ import {
|
||||
OptionField,
|
||||
RawValue,
|
||||
Schema,
|
||||
TargetField,
|
||||
TargetField
|
||||
} from 'schemas/types';
|
||||
import { getIsNullOrUndef, getMapFromList, getRandomString } from 'utils';
|
||||
import { markRaw } from 'vue';
|
||||
@ -23,7 +23,7 @@ import {
|
||||
getMissingMandatoryMessage,
|
||||
getPreDefaultValues,
|
||||
setChildDocIdx,
|
||||
shouldApplyFormula,
|
||||
shouldApplyFormula
|
||||
} from './helpers';
|
||||
import { setName } from './naming';
|
||||
import {
|
||||
@ -41,7 +41,7 @@ import {
|
||||
ReadOnlyMap,
|
||||
RequiredMap,
|
||||
TreeViewSettings,
|
||||
ValidationMap,
|
||||
ValidationMap
|
||||
} from './types';
|
||||
import { validateOptions, validateRequired } from './validationFunction';
|
||||
|
||||
@ -186,7 +186,8 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
// set value and trigger change
|
||||
async set(
|
||||
fieldname: string | DocValueMap,
|
||||
value?: DocValue | Doc[] | DocValueMap[]
|
||||
value?: DocValue | Doc[] | DocValueMap[],
|
||||
retriggerChildDocApplyChange: boolean = false
|
||||
): Promise<boolean> {
|
||||
if (typeof fieldname === 'object') {
|
||||
return await this.setMultiple(fieldname as DocValueMap);
|
||||
@ -216,7 +217,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
await this._applyChange(fieldname);
|
||||
await this.parentdoc._applyChange(this.parentFieldname as string);
|
||||
} else {
|
||||
await this._applyChange(fieldname);
|
||||
await this._applyChange(fieldname, retriggerChildDocApplyChange);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -259,8 +260,11 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
return !areDocValuesEqual(currentValue as DocValue, value as DocValue);
|
||||
}
|
||||
|
||||
async _applyChange(fieldname: string): Promise<boolean> {
|
||||
await this._applyFormula(fieldname);
|
||||
async _applyChange(
|
||||
fieldname: string,
|
||||
retriggerChildDocApplyChange?: boolean
|
||||
): Promise<boolean> {
|
||||
await this._applyFormula(fieldname, retriggerChildDocApplyChange);
|
||||
await this.trigger('change', {
|
||||
doc: this,
|
||||
changed: fieldname,
|
||||
@ -616,37 +620,62 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
}
|
||||
}
|
||||
|
||||
async _applyFormula(fieldname?: string): Promise<boolean> {
|
||||
async _applyFormula(
|
||||
fieldname?: string,
|
||||
retriggerChildDocApplyChange?: boolean
|
||||
): Promise<boolean> {
|
||||
const doc = this;
|
||||
let changed = false;
|
||||
let changed = await this._callAllTableFieldsApplyFormula(fieldname);
|
||||
changed = (await this._applyFormulaForFields(doc, fieldname)) || changed;
|
||||
|
||||
const childDocs = this.tableFields
|
||||
.map((f) => (this.get(f.fieldname) as Doc[]) ?? [])
|
||||
.flat();
|
||||
|
||||
// children
|
||||
for (const row of childDocs) {
|
||||
changed ||= (await row?._applyFormula()) ?? false;
|
||||
if (changed && retriggerChildDocApplyChange) {
|
||||
await this._callAllTableFieldsApplyFormula(fieldname);
|
||||
await this._applyFormulaForFields(doc, fieldname);
|
||||
}
|
||||
|
||||
// parent or child row
|
||||
return changed;
|
||||
}
|
||||
|
||||
async _callAllTableFieldsApplyFormula(
|
||||
changedFieldname?: string
|
||||
): Promise<boolean> {
|
||||
let changed = false;
|
||||
|
||||
for (const { fieldname } of this.tableFields) {
|
||||
const childDocs = this.get(fieldname) as Doc[];
|
||||
if (!childDocs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
changed =
|
||||
(await this._callChildDocApplyFormula(childDocs, changedFieldname)) ||
|
||||
changed;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
async _callChildDocApplyFormula(
|
||||
childDocs: Doc[],
|
||||
fieldname?: string
|
||||
): Promise<boolean> {
|
||||
let changed: boolean = false;
|
||||
for (const childDoc of childDocs) {
|
||||
if (!childDoc._applyFormula) {
|
||||
continue;
|
||||
}
|
||||
|
||||
changed = (await childDoc._applyFormula(fieldname)) || changed;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
async _applyFormulaForFields(doc: Doc, fieldname?: string) {
|
||||
const formulaFields = Object.keys(this.formulas).map(
|
||||
(fn) => this.fieldMap[fn]
|
||||
);
|
||||
|
||||
changed ||= await this._applyFormulaForFields(
|
||||
formulaFields,
|
||||
doc,
|
||||
fieldname
|
||||
);
|
||||
return changed;
|
||||
}
|
||||
|
||||
async _applyFormulaForFields(
|
||||
formulaFields: Field[],
|
||||
doc: Doc,
|
||||
fieldname?: string
|
||||
) {
|
||||
let changed = false;
|
||||
for (const field of formulaFields) {
|
||||
const shouldApply = shouldApplyFormula(field, doc, fieldname);
|
||||
@ -662,7 +691,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
}
|
||||
|
||||
doc[field.fieldname] = newVal;
|
||||
changed = true;
|
||||
changed ||= true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
|
@ -7,6 +7,16 @@ import { SelectOption } from 'schemas/types';
|
||||
import { getCountryInfo } from 'utils/misc';
|
||||
|
||||
export default class SystemSettings extends Doc {
|
||||
dateFormat?: string;
|
||||
locale?: string;
|
||||
displayPrecision?: number;
|
||||
internalPrecision?: number;
|
||||
hideGetStarted?: boolean;
|
||||
countryCode?: string;
|
||||
currency?: string;
|
||||
version?: string;
|
||||
instanceId?: string;
|
||||
|
||||
validations: ValidationMap = {
|
||||
async displayPrecision(value: DocValue) {
|
||||
if ((value as number) >= 0 && (value as number) <= 9) {
|
||||
|
@ -70,7 +70,7 @@ function formatCurrency(
|
||||
doc: Doc | null,
|
||||
fyo: Fyo
|
||||
): string {
|
||||
const currency = getCurrency(field, doc, fyo);
|
||||
const currency = getCurrency(value as Money, field, doc, fyo);
|
||||
|
||||
let valueString;
|
||||
try {
|
||||
@ -128,7 +128,20 @@ function getNumberFormatter(fyo: Fyo) {
|
||||
}));
|
||||
}
|
||||
|
||||
function getCurrency(field: Field, doc: Doc | null, fyo: Fyo): string {
|
||||
function getCurrency(
|
||||
value: Money,
|
||||
field: Field,
|
||||
doc: Doc | null,
|
||||
fyo: Fyo
|
||||
): string {
|
||||
const currency = value?.getCurrency?.();
|
||||
const defaultCurrency =
|
||||
fyo.singles.SystemSettings?.currency ?? DEFAULT_CURRENCY;
|
||||
|
||||
if (currency && currency !== defaultCurrency) {
|
||||
return currency;
|
||||
}
|
||||
|
||||
let getCurrency = doc?.getCurrencies?.[field.fieldname];
|
||||
if (getCurrency !== undefined) {
|
||||
return getCurrency();
|
||||
@ -139,7 +152,7 @@ function getCurrency(field: Field, doc: Doc | null, fyo: Fyo): string {
|
||||
return getCurrency();
|
||||
}
|
||||
|
||||
return (fyo.singles.SystemSettings?.currency as string) ?? DEFAULT_CURRENCY;
|
||||
return defaultCurrency;
|
||||
}
|
||||
|
||||
function getField(df: string | Field): Field {
|
||||
|
@ -1,11 +1,20 @@
|
||||
import { DocValue } from 'fyo/core/types';
|
||||
import { Fyo } from 'fyo';
|
||||
import { DocValue, DocValueMap } from 'fyo/core/types';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { DefaultMap, FiltersMap, FormulaMap, HiddenMap } from 'fyo/model/types';
|
||||
import {
|
||||
CurrenciesMap,
|
||||
DefaultMap,
|
||||
FiltersMap,
|
||||
FormulaMap,
|
||||
HiddenMap
|
||||
} from 'fyo/model/types';
|
||||
import { DEFAULT_CURRENCY } from 'fyo/utils/consts';
|
||||
import { ValidationError } from 'fyo/utils/errors';
|
||||
import { getExchangeRate } from 'models/helpers';
|
||||
import { Transactional } from 'models/Transactional/Transactional';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { Money } from 'pesa';
|
||||
import { FieldTypeEnum, Schema } from 'schemas/types';
|
||||
import { getIsNullOrUndef } from 'utils';
|
||||
import { InvoiceItem } from '../InvoiceItem/InvoiceItem';
|
||||
import { Party } from '../Party/Party';
|
||||
@ -42,6 +51,23 @@ export abstract class Invoice extends Transactional {
|
||||
return !!this.fyo.singles?.AccountingSettings?.enableDiscounting;
|
||||
}
|
||||
|
||||
get isMultiCurrency() {
|
||||
if (!this.currency) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.fyo.singles.SystemSettings!.currency !== this.currency;
|
||||
}
|
||||
|
||||
get companyCurrency() {
|
||||
return this.fyo.singles.SystemSettings?.currency ?? DEFAULT_CURRENCY;
|
||||
}
|
||||
|
||||
constructor(schema: Schema, data: DocValueMap, fyo: Fyo) {
|
||||
super(schema, data, fyo);
|
||||
this._setGetCurrencies();
|
||||
}
|
||||
|
||||
async validate() {
|
||||
await super.validate();
|
||||
if (
|
||||
@ -126,10 +152,12 @@ export abstract class Invoice extends Transactional {
|
||||
if (this.currency === currency) {
|
||||
return 1.0;
|
||||
}
|
||||
return await getExchangeRate({
|
||||
const exchangeRate = await getExchangeRate({
|
||||
fromCurrency: this.currency!,
|
||||
toCurrency: currency as string,
|
||||
});
|
||||
|
||||
return parseFloat(exchangeRate.toFixed(2));
|
||||
}
|
||||
|
||||
async getTaxSummary() {
|
||||
@ -139,7 +167,6 @@ export abstract class Invoice extends Transactional {
|
||||
account: string;
|
||||
rate: number;
|
||||
amount: Money;
|
||||
baseAmount: Money;
|
||||
[key: string]: DocValue;
|
||||
}
|
||||
> = {};
|
||||
@ -157,7 +184,6 @@ export abstract class Invoice extends Transactional {
|
||||
account,
|
||||
rate,
|
||||
amount: this.fyo.pesa(0),
|
||||
baseAmount: this.fyo.pesa(0),
|
||||
};
|
||||
|
||||
let amount = item.amount!;
|
||||
@ -172,9 +198,7 @@ export abstract class Invoice extends Transactional {
|
||||
|
||||
return Object.keys(taxes)
|
||||
.map((account) => {
|
||||
const tax = taxes[account];
|
||||
tax.baseAmount = tax.amount.mul(this.exchangeRate!);
|
||||
return tax;
|
||||
return taxes[account];
|
||||
})
|
||||
.filter((tax) => !tax.amount.isZero());
|
||||
}
|
||||
@ -285,15 +309,28 @@ export abstract class Invoice extends Transactional {
|
||||
},
|
||||
dependsOn: ['party'],
|
||||
},
|
||||
exchangeRate: { formula: async () => await this.getExchangeRate() },
|
||||
netTotal: { formula: async () => this.getSum('items', 'amount', false) },
|
||||
baseNetTotal: {
|
||||
formula: async () => this.netTotal!.mul(this.exchangeRate!),
|
||||
exchangeRate: {
|
||||
formula: async () => {
|
||||
if (
|
||||
this.currency ===
|
||||
(this.fyo.singles.SystemSettings?.currency ?? DEFAULT_CURRENCY)
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (this.exchangeRate && this.exchangeRate !== 1) {
|
||||
return this.exchangeRate;
|
||||
}
|
||||
|
||||
return await this.getExchangeRate();
|
||||
},
|
||||
},
|
||||
netTotal: { formula: async () => this.getSum('items', 'amount', false) },
|
||||
taxes: { formula: async () => await this.getTaxSummary() },
|
||||
grandTotal: { formula: async () => await this.getGrandTotal() },
|
||||
baseGrandTotal: {
|
||||
formula: async () => (this.grandTotal as Money).mul(this.exchangeRate!),
|
||||
formula: async () =>
|
||||
(this.grandTotal as Money).mul(this.exchangeRate! ?? 1),
|
||||
},
|
||||
outstandingAmount: {
|
||||
formula: async () => {
|
||||
@ -345,4 +382,25 @@ export abstract class Invoice extends Transactional {
|
||||
role: doc.isSales ? 'Customer' : 'Supplier',
|
||||
}),
|
||||
};
|
||||
|
||||
getCurrencies: CurrenciesMap = {
|
||||
baseGrandTotal: () => this.companyCurrency,
|
||||
outstandingAmount: () => this.companyCurrency,
|
||||
};
|
||||
_getCurrency() {
|
||||
if (this.exchangeRate === 1) {
|
||||
return this.companyCurrency;
|
||||
}
|
||||
|
||||
return this.currency ?? DEFAULT_CURRENCY;
|
||||
}
|
||||
_setGetCurrencies() {
|
||||
const currencyFields = this.schema.fields.filter(
|
||||
({ fieldtype }) => fieldtype === FieldTypeEnum.Currency
|
||||
);
|
||||
|
||||
for (const { fieldname } of currencyFields) {
|
||||
this.getCurrencies[fieldname] ??= this._getCurrency.bind(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,23 @@
|
||||
import { DocValue } from 'fyo/core/types';
|
||||
import { Fyo } from 'fyo';
|
||||
import { DocValue, DocValueMap } from 'fyo/core/types';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import {
|
||||
CurrenciesMap,
|
||||
FiltersMap,
|
||||
FormulaMap,
|
||||
HiddenMap,
|
||||
ValidationMap
|
||||
} from 'fyo/model/types';
|
||||
import { DEFAULT_CURRENCY } from 'fyo/utils/consts';
|
||||
import { ValidationError } from 'fyo/utils/errors';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { Money } from 'pesa';
|
||||
import { FieldTypeEnum, Schema } from 'schemas/types';
|
||||
import { Invoice } from '../Invoice/Invoice';
|
||||
|
||||
export abstract class InvoiceItem extends Doc {
|
||||
account?: string;
|
||||
amount?: Money;
|
||||
baseAmount?: Money;
|
||||
exchangeRate?: number;
|
||||
parentdoc?: Invoice;
|
||||
rate?: Money;
|
||||
quantity?: number;
|
||||
@ -39,6 +41,23 @@ export abstract class InvoiceItem extends Doc {
|
||||
return !!this.fyo.singles?.AccountingSettings?.enableDiscounting;
|
||||
}
|
||||
|
||||
get currency() {
|
||||
return this.parentdoc?.currency ?? DEFAULT_CURRENCY;
|
||||
}
|
||||
|
||||
get exchangeRate() {
|
||||
return this.parentdoc?.exchangeRate ?? 1;
|
||||
}
|
||||
|
||||
get isMultiCurrency() {
|
||||
return this.parentdoc?.isMultiCurrency ?? false;
|
||||
}
|
||||
|
||||
constructor(schema: Schema, data: DocValueMap, fyo: Fyo) {
|
||||
super(schema, data, fyo);
|
||||
this._setGetCurrencies();
|
||||
}
|
||||
|
||||
async getTotalTaxRate(): Promise<number> {
|
||||
if (!this.tax) {
|
||||
return 0;
|
||||
@ -73,7 +92,7 @@ export abstract class InvoiceItem extends Doc {
|
||||
fieldname !== 'itemTaxedTotal' &&
|
||||
fieldname !== 'itemDiscountedTotal'
|
||||
) {
|
||||
return rate ?? this.fyo.pesa(0);
|
||||
return rate?.div(this.exchangeRate) ?? this.fyo.pesa(0);
|
||||
}
|
||||
|
||||
const quantity = this.quantity ?? 0;
|
||||
@ -102,17 +121,14 @@ export abstract class InvoiceItem extends Doc {
|
||||
return rateFromTotals ?? rate ?? this.fyo.pesa(0);
|
||||
},
|
||||
dependsOn: [
|
||||
'party',
|
||||
'exchangeRate',
|
||||
'item',
|
||||
'itemTaxedTotal',
|
||||
'itemDiscountedTotal',
|
||||
'setItemDiscountAmount',
|
||||
],
|
||||
},
|
||||
baseRate: {
|
||||
formula: () =>
|
||||
(this.rate as Money).mul(this.parentdoc!.exchangeRate as number),
|
||||
dependsOn: ['item', 'rate'],
|
||||
},
|
||||
quantity: {
|
||||
formula: async () => {
|
||||
if (!this.item) {
|
||||
@ -157,11 +173,6 @@ export abstract class InvoiceItem extends Doc {
|
||||
formula: () => (this.rate as Money).mul(this.quantity as number),
|
||||
dependsOn: ['item', 'rate', 'quantity'],
|
||||
},
|
||||
baseAmount: {
|
||||
formula: () =>
|
||||
(this.amount as Money).mul(this.parentdoc!.exchangeRate as number),
|
||||
dependsOn: ['item', 'amount', 'rate', 'quantity'],
|
||||
},
|
||||
hsnCode: {
|
||||
formula: async () =>
|
||||
await this.fyo.getValue('Item', this.item as string, 'hsnCode'),
|
||||
@ -338,6 +349,24 @@ export abstract class InvoiceItem extends Doc {
|
||||
return { for: doc.isSales ? 'Sales' : 'Purchases' };
|
||||
},
|
||||
};
|
||||
|
||||
getCurrencies: CurrenciesMap = {};
|
||||
_getCurrency() {
|
||||
if (this.exchangeRate === 1) {
|
||||
return this.fyo.singles.SystemSettings?.currency ?? DEFAULT_CURRENCY;
|
||||
}
|
||||
|
||||
return this.currency;
|
||||
}
|
||||
_setGetCurrencies() {
|
||||
const currencyFields = this.schema.fields.filter(
|
||||
({ fieldtype }) => fieldtype === FieldTypeEnum.Currency
|
||||
);
|
||||
|
||||
for (const { fieldname } of currencyFields) {
|
||||
this.getCurrencies[fieldname] ??= this._getCurrency.bind(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getDiscountedTotalBeforeTaxation(
|
||||
|
@ -87,7 +87,11 @@ export class Party extends Doc {
|
||||
dependsOn: ['role'],
|
||||
},
|
||||
currency: {
|
||||
formula: async () => this.fyo.singles.SystemSettings!.currency as string,
|
||||
formula: async () => {
|
||||
if (!this.currency) {
|
||||
return this.fyo.singles.SystemSettings!.currency as string;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -10,16 +10,17 @@ export class PurchaseInvoice extends Invoice {
|
||||
items?: PurchaseInvoiceItem[];
|
||||
|
||||
async getPosting() {
|
||||
const exchangeRate = this.exchangeRate ?? 1;
|
||||
const posting: LedgerPosting = new LedgerPosting(this, this.fyo);
|
||||
await posting.credit(this.account!, this.baseGrandTotal!);
|
||||
|
||||
for (const item of this.items!) {
|
||||
await posting.debit(item.account!, item.baseAmount!);
|
||||
await posting.debit(item.account!, item.amount!.mul(exchangeRate));
|
||||
}
|
||||
|
||||
if (this.taxes) {
|
||||
for (const tax of this.taxes) {
|
||||
await posting.debit(tax.account!, tax.baseAmount!);
|
||||
await posting.debit(tax.account!, tax.amount!.mul(exchangeRate));
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +28,7 @@ export class PurchaseInvoice extends Invoice {
|
||||
const discountAccount = this.fyo.singles.AccountingSettings
|
||||
?.discountAccount as string | undefined;
|
||||
if (discountAccount && discountAmount.isPositive()) {
|
||||
await posting.credit(discountAccount, discountAmount);
|
||||
await posting.credit(discountAccount, discountAmount.mul(exchangeRate));
|
||||
}
|
||||
|
||||
await posting.makeRoundOffEntry();
|
||||
@ -46,7 +47,7 @@ export class PurchaseInvoice extends Invoice {
|
||||
getTransactionStatusColumn(),
|
||||
'party',
|
||||
'date',
|
||||
'grandTotal',
|
||||
'baseGrandTotal',
|
||||
'outstandingAmount',
|
||||
],
|
||||
};
|
||||
|
@ -10,16 +10,17 @@ export class SalesInvoice extends Invoice {
|
||||
items?: SalesInvoiceItem[];
|
||||
|
||||
async getPosting() {
|
||||
const exchangeRate = this.exchangeRate ?? 1;
|
||||
const posting: LedgerPosting = new LedgerPosting(this, this.fyo);
|
||||
await posting.debit(this.account!, this.baseGrandTotal!);
|
||||
|
||||
for (const item of this.items!) {
|
||||
await posting.credit(item.account!, item.baseAmount!);
|
||||
await posting.credit(item.account!, item.amount!.mul(exchangeRate));
|
||||
}
|
||||
|
||||
if (this.taxes) {
|
||||
for (const tax of this.taxes!) {
|
||||
await posting.credit(tax.account!, tax.baseAmount!);
|
||||
await posting.credit(tax.account!, tax.amount!.mul(exchangeRate));
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +28,7 @@ export class SalesInvoice extends Invoice {
|
||||
const discountAccount = this.fyo.singles.AccountingSettings
|
||||
?.discountAccount as string | undefined;
|
||||
if (discountAccount && discountAmount.isPositive()) {
|
||||
await posting.debit(discountAccount, discountAmount);
|
||||
await posting.debit(discountAccount, discountAmount.mul(exchangeRate));
|
||||
}
|
||||
|
||||
await posting.makeRoundOffEntry();
|
||||
@ -46,7 +47,7 @@ export class SalesInvoice extends Invoice {
|
||||
getTransactionStatusColumn(),
|
||||
'party',
|
||||
'date',
|
||||
'grandTotal',
|
||||
'baseGrandTotal',
|
||||
'outstandingAmount',
|
||||
],
|
||||
};
|
||||
|
@ -1,21 +1,48 @@
|
||||
import { Fyo } from 'fyo';
|
||||
import { DocValueMap } from 'fyo/core/types';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { FormulaMap } from 'fyo/model/types';
|
||||
import { CurrenciesMap } from 'fyo/model/types';
|
||||
import { DEFAULT_CURRENCY } from 'fyo/utils/consts';
|
||||
import { Money } from 'pesa';
|
||||
import { FieldTypeEnum, Schema } from 'schemas/types';
|
||||
import { Invoice } from '../Invoice/Invoice';
|
||||
|
||||
export class TaxSummary extends Doc {
|
||||
account?: string;
|
||||
rate?: number;
|
||||
amount?: Money;
|
||||
baseAmount?: Money;
|
||||
parentdoc?: Invoice;
|
||||
|
||||
formulas: FormulaMap = {
|
||||
baseAmount: {
|
||||
formula: async () => {
|
||||
const amount = this.amount as Money;
|
||||
const exchangeRate = (this.parentdoc?.exchangeRate ?? 1) as number;
|
||||
return amount.mul(exchangeRate);
|
||||
},
|
||||
dependsOn: ['amount'],
|
||||
},
|
||||
};
|
||||
get exchangeRate() {
|
||||
return this.parentdoc?.exchangeRate ?? 1;
|
||||
}
|
||||
|
||||
get currency() {
|
||||
return this.parentdoc?.currency ?? DEFAULT_CURRENCY;
|
||||
}
|
||||
|
||||
constructor(schema: Schema, data: DocValueMap, fyo: Fyo) {
|
||||
super(schema, data, fyo);
|
||||
this._setGetCurrencies();
|
||||
}
|
||||
|
||||
getCurrencies: CurrenciesMap = {};
|
||||
_getCurrency() {
|
||||
if (this.exchangeRate === 1) {
|
||||
return this.fyo.singles.SystemSettings?.currency ?? DEFAULT_CURRENCY;
|
||||
}
|
||||
|
||||
return this.currency;
|
||||
}
|
||||
_setGetCurrencies() {
|
||||
const currencyFields = this.schema.fields.filter(
|
||||
({ fieldtype }) => fieldtype === FieldTypeEnum.Currency
|
||||
);
|
||||
|
||||
const getCurrency = this._getCurrency.bind(this);
|
||||
|
||||
for (const { fieldname } of currencyFields) {
|
||||
this.getCurrencies[fieldname] ??= getCurrency;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
import { Fyo, t } from 'fyo';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { Action, ColumnConfig, DocStatus, RenderData } from 'fyo/model/types';
|
||||
import { NotFoundError } from 'fyo/utils/errors';
|
||||
import { DateTime } from 'luxon';
|
||||
import { Money } from 'pesa';
|
||||
import { Router } from 'vue-router';
|
||||
import {
|
||||
AccountRootType,
|
||||
AccountRootTypeEnum,
|
||||
AccountRootTypeEnum
|
||||
} from './baseModels/Account/types';
|
||||
import { InvoiceStatus, ModelNameEnum } from './types';
|
||||
|
||||
@ -196,14 +195,12 @@ export async function getExchangeRate({
|
||||
toCurrency: string;
|
||||
date?: string;
|
||||
}) {
|
||||
if (!date) {
|
||||
date = DateTime.local().toISODate();
|
||||
if (!fetch) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!fromCurrency || !toCurrency) {
|
||||
throw new NotFoundError(
|
||||
'Please provide `fromCurrency` and `toCurrency` to get exchange rate.'
|
||||
);
|
||||
if (!date) {
|
||||
date = DateTime.local().toISODate();
|
||||
}
|
||||
|
||||
const cacheKey = `currencyExchangeRate:${date}:${fromCurrency}:${toCurrency}`;
|
||||
@ -215,26 +212,23 @@ export async function getExchangeRate({
|
||||
);
|
||||
}
|
||||
|
||||
if (!exchangeRate && fetch) {
|
||||
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];
|
||||
if (exchangeRate && exchangeRate !== 1) {
|
||||
return exchangeRate;
|
||||
}
|
||||
|
||||
if (localStorage) {
|
||||
localStorage.setItem(cacheKey, String(exchangeRate));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw new NotFoundError(
|
||||
`Could not fetch exchange rate for ${fromCurrency} -> ${toCurrency}`,
|
||||
false
|
||||
);
|
||||
}
|
||||
} else {
|
||||
exchangeRate = 1;
|
||||
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;
|
||||
@ -256,3 +250,6 @@ export function isCredit(rootType: AccountRootType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
window.gex = getExchangeRate;
|
||||
|
@ -218,6 +218,11 @@ async function generateB2bData(report: BaseGSTR): Promise<B2BCustomer[]> {
|
||||
? ModelNameEnum.SalesInvoiceItem
|
||||
: ModelNameEnum.PurchaseInvoiceItem;
|
||||
|
||||
const parentSchemaName =
|
||||
report.gstrType === 'GSTR-1'
|
||||
? ModelNameEnum.SalesInvoice
|
||||
: ModelNameEnum.PurchaseInvoice;
|
||||
|
||||
for (const row of report.gstrRows ?? []) {
|
||||
const invRecord: B2BInvRecord = {
|
||||
inum: row.invNo,
|
||||
@ -229,20 +234,29 @@ async function generateB2bData(report: BaseGSTR): Promise<B2BCustomer[]> {
|
||||
itms: [],
|
||||
};
|
||||
|
||||
const exchangeRate = (
|
||||
await fyo.db.getAllRaw(parentSchemaName, {
|
||||
fields: ['exchangeRate'],
|
||||
filters: { name: invRecord.inum },
|
||||
})
|
||||
)[0].exchangeRate as number;
|
||||
|
||||
const items = await fyo.db.getAllRaw(schemaName, {
|
||||
fields: ['baseAmount', 'tax', 'hsnCode'],
|
||||
filters: { parent: invRecord.inum as string },
|
||||
fields: ['amount', 'tax', 'hsnCode'],
|
||||
filters: { parent: invRecord.inum },
|
||||
});
|
||||
|
||||
items.forEach((item) => {
|
||||
const hsnCode = item.hsnCode as number;
|
||||
const tax = item.tax as string;
|
||||
const baseAmount = (item.baseAmount ?? 0) as string;
|
||||
const baseAmount = fyo
|
||||
.pesa((item.amount as string) ?? 0)
|
||||
.mul(exchangeRate);
|
||||
|
||||
const itemRecord: B2BItmRecord = {
|
||||
num: hsnCode,
|
||||
itm_det: {
|
||||
txval: fyo.pesa(baseAmount).float,
|
||||
txval: baseAmount.float,
|
||||
rt: GST[tax],
|
||||
csamt: 0,
|
||||
camt: fyo
|
||||
@ -292,6 +306,11 @@ async function generateB2clData(
|
||||
? ModelNameEnum.SalesInvoiceItem
|
||||
: ModelNameEnum.PurchaseInvoiceItem;
|
||||
|
||||
const parentSchemaName =
|
||||
report.gstrType === 'GSTR-1'
|
||||
? ModelNameEnum.SalesInvoice
|
||||
: ModelNameEnum.PurchaseInvoice;
|
||||
|
||||
for (const row of report.gstrRows ?? []) {
|
||||
const invRecord: B2CLInvRecord = {
|
||||
inum: row.invNo,
|
||||
@ -300,20 +319,29 @@ async function generateB2clData(
|
||||
itms: [],
|
||||
};
|
||||
|
||||
const exchangeRate = (
|
||||
await fyo.db.getAllRaw(parentSchemaName, {
|
||||
fields: ['exchangeRate'],
|
||||
filters: { name: invRecord.inum },
|
||||
})
|
||||
)[0].exchangeRate as number;
|
||||
|
||||
const items = await fyo.db.getAllRaw(schemaName, {
|
||||
fields: ['hsnCode', 'tax', 'baseAmount'],
|
||||
fields: ['amount', 'tax', 'hsnCode'],
|
||||
filters: { parent: invRecord.inum },
|
||||
});
|
||||
|
||||
items.forEach((item) => {
|
||||
const hsnCode = item.hsnCode as number;
|
||||
const tax = item.tax as string;
|
||||
const baseAmount = (item.baseAmount ?? 0) as string;
|
||||
const baseAmount = fyo
|
||||
.pesa((item.amount as string) ?? 0)
|
||||
.mul(exchangeRate);
|
||||
|
||||
const itemRecord: B2CLItmRecord = {
|
||||
num: hsnCode,
|
||||
itm_det: {
|
||||
txval: fyo.pesa(baseAmount).float,
|
||||
txval: baseAmount.float,
|
||||
rt: GST[tax] ?? 0,
|
||||
csamt: 0,
|
||||
iamt: fyo
|
||||
|
131
schemas/app/Invoice.json
Normal file
131
schemas/app/Invoice.json
Normal file
@ -0,0 +1,131 @@
|
||||
{
|
||||
"name": "Invoice",
|
||||
"label": "Invoice",
|
||||
"isAbstract": true,
|
||||
"isSingle": false,
|
||||
"isChild": false,
|
||||
"isSubmittable": true,
|
||||
"fields": [
|
||||
{
|
||||
"label": "Invoice No",
|
||||
"fieldname": "name",
|
||||
"fieldtype": "Data",
|
||||
"required": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "date",
|
||||
"label": "Date",
|
||||
"fieldtype": "Date",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "party",
|
||||
"label": "Party",
|
||||
"fieldtype": "Link",
|
||||
"target": "Party",
|
||||
"create": true,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
"label": "Account",
|
||||
"fieldtype": "Link",
|
||||
"target": "Account",
|
||||
"create": true,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "currency",
|
||||
"label": "Customer Currency",
|
||||
"fieldtype": "Link",
|
||||
"target": "Currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "exchangeRate",
|
||||
"label": "Exchange Rate",
|
||||
"fieldtype": "Float",
|
||||
"default": 1,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "netTotal",
|
||||
"label": "Net Total",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "taxes",
|
||||
"label": "Taxes",
|
||||
"fieldtype": "Table",
|
||||
"target": "TaxSummary",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "grandTotal",
|
||||
"label": "Grand Total",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseGrandTotal",
|
||||
"label": "Base Grand Total",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "outstandingAmount",
|
||||
"label": "Outstanding Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "setDiscountAmount",
|
||||
"label": "Set Discount Amount",
|
||||
"fieldtype": "Check",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"fieldname": "discountAmount",
|
||||
"label": "Discount Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "discountPercent",
|
||||
"label": "Discount Percent",
|
||||
"fieldtype": "Float",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "discountAfterTax",
|
||||
"label": "Discount After Tax",
|
||||
"fieldtype": "Check",
|
||||
"default": false,
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "entryCurrency",
|
||||
"label": "Entry Currency",
|
||||
"fieldtype": "Select",
|
||||
"options": [
|
||||
{
|
||||
"value": "Party",
|
||||
"label": "Party"
|
||||
},
|
||||
{
|
||||
"value": "Company",
|
||||
"label": "Company"
|
||||
}
|
||||
],
|
||||
"default": "Party"
|
||||
},
|
||||
{
|
||||
"fieldname": "terms",
|
||||
"label": "Notes",
|
||||
"placeholder": "Add invoice terms",
|
||||
"fieldtype": "Text"
|
||||
}
|
||||
],
|
||||
"keywordFields": ["name", "party"]
|
||||
}
|
109
schemas/app/InvoiceItem.json
Normal file
109
schemas/app/InvoiceItem.json
Normal file
@ -0,0 +1,109 @@
|
||||
{
|
||||
"name": "InvoiceItem",
|
||||
"label": "Invoice Item",
|
||||
"isAbstract": true,
|
||||
"isChild": true,
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "item",
|
||||
"label": "Item",
|
||||
"fieldtype": "Link",
|
||||
"target": "Item",
|
||||
"create": true,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"label": "Description",
|
||||
"fieldtype": "Text"
|
||||
},
|
||||
{
|
||||
"fieldname": "quantity",
|
||||
"label": "Quantity",
|
||||
"fieldtype": "Float",
|
||||
"required": true,
|
||||
"default": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"label": "Rate",
|
||||
"fieldtype": "Currency",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
"label": "Account",
|
||||
"fieldtype": "Link",
|
||||
"target": "Account",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "tax",
|
||||
"label": "Tax",
|
||||
"fieldtype": "Link",
|
||||
"create": true,
|
||||
"target": "Tax"
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
"label": "Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "setItemDiscountAmount",
|
||||
"label": "Set Discount Amount",
|
||||
"fieldtype": "Check",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"fieldname": "itemDiscountAmount",
|
||||
"label": "Discount Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "itemDiscountPercent",
|
||||
"label": "Discount Percent",
|
||||
"fieldtype": "Float",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "itemDiscountedTotal",
|
||||
"label": "Discounted Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": false,
|
||||
"computed": true
|
||||
},
|
||||
{
|
||||
"fieldname": "itemTaxedTotal",
|
||||
"label": "Taxed Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": false,
|
||||
"computed": true
|
||||
},
|
||||
{
|
||||
"fieldname": "hsnCode",
|
||||
"label": "HSN/SAC",
|
||||
"fieldtype": "Int",
|
||||
"placeholder": "HSN/SAC Code"
|
||||
}
|
||||
],
|
||||
"tableFields": ["item", "tax", "quantity", "rate", "amount"],
|
||||
"keywordFields": ["item", "tax"],
|
||||
"quickEditFields": [
|
||||
"item",
|
||||
"account",
|
||||
"description",
|
||||
"hsnCode",
|
||||
"tax",
|
||||
"quantity",
|
||||
"rate",
|
||||
"amount",
|
||||
"setItemDiscountAmount",
|
||||
"itemDiscountAmount",
|
||||
"itemDiscountPercent",
|
||||
"itemDiscountedTotal",
|
||||
"itemTaxedTotal"
|
||||
]
|
||||
}
|
@ -1,54 +1,10 @@
|
||||
{
|
||||
"name": "PurchaseInvoice",
|
||||
"label": "Purchase Invoice",
|
||||
"extends": "Invoice",
|
||||
"naming": "numberSeries",
|
||||
"isSingle": false,
|
||||
"isChild": false,
|
||||
"isSubmittable": true,
|
||||
"showTitle": true,
|
||||
"fields": [
|
||||
{
|
||||
"label": "Bill No",
|
||||
"fieldname": "name",
|
||||
"fieldtype": "Data",
|
||||
"required": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "date",
|
||||
"label": "Date",
|
||||
"fieldtype": "Date",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "party",
|
||||
"label": "Party",
|
||||
"fieldtype": "Link",
|
||||
"target": "Party",
|
||||
"create": true,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
"label": "Account",
|
||||
"fieldtype": "Link",
|
||||
"target": "Account",
|
||||
"create": true,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "currency",
|
||||
"label": "Supplier Currency",
|
||||
"fieldtype": "Link",
|
||||
"target": "Currency",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"fieldname": "exchangeRate",
|
||||
"label": "Exchange Rate",
|
||||
"fieldtype": "Float",
|
||||
"default": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "items",
|
||||
"label": "Items",
|
||||
@ -57,75 +13,6 @@
|
||||
"required": true,
|
||||
"edit": true
|
||||
},
|
||||
{
|
||||
"fieldname": "netTotal",
|
||||
"label": "Net Total",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseNetTotal",
|
||||
"label": "Net Total (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "taxes",
|
||||
"label": "Taxes",
|
||||
"fieldtype": "Table",
|
||||
"target": "TaxSummary",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "grandTotal",
|
||||
"label": "Grand Total",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseGrandTotal",
|
||||
"label": "Grand Total (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "outstandingAmount",
|
||||
"label": "Outstanding Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true,
|
||||
"filter": true
|
||||
},
|
||||
{
|
||||
"fieldname": "setDiscountAmount",
|
||||
"label": "Set Discount Amount",
|
||||
"fieldtype": "Check",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"fieldname": "discountAmount",
|
||||
"label": "Discount Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "discountPercent",
|
||||
"label": "Discount Percent",
|
||||
"fieldtype": "Float",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "discountAfterTax",
|
||||
"label": "Discount After Tax",
|
||||
"fieldtype": "Check",
|
||||
"default": false,
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "terms",
|
||||
"label": "Terms",
|
||||
"placeholder": "Add invoice terms",
|
||||
"fieldtype": "Text"
|
||||
},
|
||||
{
|
||||
"fieldname": "numberSeries",
|
||||
"label": "Number Series",
|
||||
|
@ -1,124 +1,5 @@
|
||||
{
|
||||
"name": "PurchaseInvoiceItem",
|
||||
"label": "Purchase Invoice Item",
|
||||
"isChild": true,
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "item",
|
||||
"label": "Item",
|
||||
"fieldtype": "Link",
|
||||
"target": "Item",
|
||||
"create": true,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"label": "Description",
|
||||
"fieldtype": "Text",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"fieldname": "quantity",
|
||||
"label": "Quantity",
|
||||
"fieldtype": "Float",
|
||||
"required": true,
|
||||
"default": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"label": "Rate",
|
||||
"fieldtype": "Currency",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseRate",
|
||||
"label": "Rate (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
"label": "Account",
|
||||
"fieldtype": "Link",
|
||||
"target": "Account",
|
||||
"create": true,
|
||||
"required": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "tax",
|
||||
"label": "Tax",
|
||||
"fieldtype": "Link",
|
||||
"create": true,
|
||||
"target": "Tax"
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
"label": "Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseAmount",
|
||||
"label": "Amount (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "setItemDiscountAmount",
|
||||
"label": "Set Discount Amount",
|
||||
"fieldtype": "Check",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"fieldname": "itemDiscountAmount",
|
||||
"label": "Discount Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "itemDiscountPercent",
|
||||
"label": "Discount Percent",
|
||||
"fieldtype": "Float",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "itemDiscountedTotal",
|
||||
"label": "Discounted Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": false,
|
||||
"computed": true
|
||||
},
|
||||
{
|
||||
"fieldname": "itemTaxedTotal",
|
||||
"label": "Taxed Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": false,
|
||||
"computed": true
|
||||
},
|
||||
{
|
||||
"fieldname": "hsnCode",
|
||||
"label": "HSN/SAC",
|
||||
"fieldtype": "Int",
|
||||
"placeholder": "HSN/SAC Code",
|
||||
"hidden": true
|
||||
}
|
||||
],
|
||||
"tableFields": ["item", "tax", "quantity", "rate", "amount"],
|
||||
"keywordFields": ["item", "tax"],
|
||||
"quickEditFields": [
|
||||
"item",
|
||||
"account",
|
||||
"description",
|
||||
"hsnCode",
|
||||
"tax",
|
||||
"quantity",
|
||||
"rate",
|
||||
"amount",
|
||||
"setItemDiscountAmount",
|
||||
"itemDiscountAmount",
|
||||
"itemDiscountPercent",
|
||||
"itemDiscountedTotal",
|
||||
"itemTaxedTotal"
|
||||
]
|
||||
"extends": "InvoiceItem"
|
||||
}
|
||||
|
@ -1,53 +1,10 @@
|
||||
{
|
||||
"name": "SalesInvoice",
|
||||
"label": "Sales Invoice",
|
||||
"extends": "Invoice",
|
||||
"naming": "numberSeries",
|
||||
"isSingle": false,
|
||||
"isChild": false,
|
||||
"isSubmittable": true,
|
||||
"showTitle": true,
|
||||
"fields": [
|
||||
{
|
||||
"label": "Invoice No",
|
||||
"fieldname": "name",
|
||||
"fieldtype": "Data",
|
||||
"required": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "date",
|
||||
"label": "Date",
|
||||
"fieldtype": "Date",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "party",
|
||||
"label": "Party",
|
||||
"fieldtype": "Link",
|
||||
"target": "Party",
|
||||
"create": true,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
"label": "Account",
|
||||
"fieldtype": "Link",
|
||||
"target": "Account",
|
||||
"create": true,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "currency",
|
||||
"label": "Customer Currency",
|
||||
"fieldtype": "Link",
|
||||
"target": "Currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "exchangeRate",
|
||||
"label": "Exchange Rate",
|
||||
"fieldtype": "Float",
|
||||
"default": 1,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "items",
|
||||
"label": "Items",
|
||||
@ -56,75 +13,6 @@
|
||||
"required": true,
|
||||
"edit": true
|
||||
},
|
||||
{
|
||||
"fieldname": "netTotal",
|
||||
"label": "Net Total",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseNetTotal",
|
||||
"label": "Net Total (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "taxes",
|
||||
"label": "Taxes",
|
||||
"fieldtype": "Table",
|
||||
"target": "TaxSummary",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "grandTotal",
|
||||
"label": "Grand Total",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseGrandTotal",
|
||||
"label": "Grand Total (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "outstandingAmount",
|
||||
"label": "Outstanding Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true,
|
||||
"filter": true
|
||||
},
|
||||
{
|
||||
"fieldname": "setDiscountAmount",
|
||||
"label": "Set Discount Amount",
|
||||
"fieldtype": "Check",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"fieldname": "discountAmount",
|
||||
"label": "Discount Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "discountPercent",
|
||||
"label": "Discount Percent",
|
||||
"fieldtype": "Float",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "discountAfterTax",
|
||||
"label": "Discount After Tax",
|
||||
"fieldtype": "Check",
|
||||
"default": false,
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "terms",
|
||||
"label": "Notes",
|
||||
"placeholder": "Add invoice terms",
|
||||
"fieldtype": "Text"
|
||||
},
|
||||
{
|
||||
"fieldname": "numberSeries",
|
||||
"label": "Number Series",
|
||||
|
@ -1,120 +1,5 @@
|
||||
{
|
||||
"name": "SalesInvoiceItem",
|
||||
"label": "Sales Invoice Item",
|
||||
"isChild": true,
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "item",
|
||||
"label": "Item",
|
||||
"fieldtype": "Link",
|
||||
"target": "Item",
|
||||
"create": true,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"label": "Description",
|
||||
"fieldtype": "Text"
|
||||
},
|
||||
{
|
||||
"fieldname": "quantity",
|
||||
"label": "Quantity",
|
||||
"fieldtype": "Float",
|
||||
"required": true,
|
||||
"default": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"label": "Rate",
|
||||
"fieldtype": "Currency",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseRate",
|
||||
"label": "Rate (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
"label": "Account",
|
||||
"fieldtype": "Link",
|
||||
"target": "Account",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "tax",
|
||||
"label": "Tax",
|
||||
"fieldtype": "Link",
|
||||
"create": true,
|
||||
"target": "Tax"
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
"label": "Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseAmount",
|
||||
"label": "Amount (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "setItemDiscountAmount",
|
||||
"label": "Set Discount Amount",
|
||||
"fieldtype": "Check",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"fieldname": "itemDiscountAmount",
|
||||
"label": "Discount Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "itemDiscountPercent",
|
||||
"label": "Discount Percent",
|
||||
"fieldtype": "Float",
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"fieldname": "itemDiscountedTotal",
|
||||
"label": "Discounted Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": false,
|
||||
"computed": true
|
||||
},
|
||||
{
|
||||
"fieldname": "itemTaxedTotal",
|
||||
"label": "Taxed Amount",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": false,
|
||||
"computed": true
|
||||
},
|
||||
{
|
||||
"fieldname": "hsnCode",
|
||||
"label": "HSN/SAC",
|
||||
"fieldtype": "Int",
|
||||
"placeholder": "HSN/SAC Code"
|
||||
}
|
||||
],
|
||||
"tableFields": ["item", "tax", "quantity", "rate", "amount"],
|
||||
"keywordFields": ["item", "tax"],
|
||||
"quickEditFields": [
|
||||
"item",
|
||||
"account",
|
||||
"description",
|
||||
"hsnCode",
|
||||
"tax",
|
||||
"quantity",
|
||||
"rate",
|
||||
"amount",
|
||||
"setItemDiscountAmount",
|
||||
"itemDiscountAmount",
|
||||
"itemDiscountPercent",
|
||||
"itemDiscountedTotal",
|
||||
"itemTaxedTotal"
|
||||
]
|
||||
"extends": "InvoiceItem"
|
||||
}
|
||||
|
@ -21,12 +21,6 @@
|
||||
"label": "Amount",
|
||||
"fieldtype": "Currency",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseAmount",
|
||||
"label": "Amount (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"readOnly": true
|
||||
}
|
||||
]
|
||||
}
|
@ -88,6 +88,7 @@
|
||||
"label": "Currency",
|
||||
"fieldtype": "AutoComplete",
|
||||
"default": "INR",
|
||||
"readOnly": true,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
|
@ -7,6 +7,8 @@ import Color from './app/Color.json';
|
||||
import CompanySettings from './app/CompanySettings.json';
|
||||
import Currency from './app/Currency.json';
|
||||
import GetStarted from './app/GetStarted.json';
|
||||
import Invoice from './app/Invoice.json';
|
||||
import InvoiceItem from './app/InvoiceItem.json';
|
||||
import Item from './app/Item.json';
|
||||
import JournalEntry from './app/JournalEntry.json';
|
||||
import JournalEntryAccount from './app/JournalEntryAccount.json';
|
||||
@ -73,11 +75,13 @@ export const appSchemas: Schema[] | SchemaStub[] = [
|
||||
JournalEntry as Schema,
|
||||
JournalEntryAccount as Schema,
|
||||
|
||||
PurchaseInvoice as Schema,
|
||||
PurchaseInvoiceItem as Schema,
|
||||
|
||||
Invoice as Schema,
|
||||
SalesInvoice as Schema,
|
||||
SalesInvoiceItem as Schema,
|
||||
PurchaseInvoice as Schema,
|
||||
|
||||
InvoiceItem as Schema,
|
||||
SalesInvoiceItem as SchemaStub,
|
||||
PurchaseInvoiceItem as SchemaStub,
|
||||
|
||||
Tax as Schema,
|
||||
TaxDetail as Schema,
|
||||
|
@ -151,13 +151,16 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getInputClassesFromProp(classes) {
|
||||
if (this.inputClass) {
|
||||
if (typeof this.inputClass === 'function') {
|
||||
classes = this.inputClass(classes);
|
||||
} else {
|
||||
classes.push(this.inputClass);
|
||||
}
|
||||
if (!this.inputClass) {
|
||||
return classes;
|
||||
}
|
||||
|
||||
if (typeof this.inputClass === 'function') {
|
||||
classes = this.inputClass(classes);
|
||||
} else {
|
||||
classes.push(this.inputClass);
|
||||
}
|
||||
|
||||
return classes;
|
||||
},
|
||||
focus() {
|
||||
|
102
src/components/Controls/ExchangeRate.vue
Normal file
102
src/components/Controls/ExchangeRate.vue
Normal file
@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div class="flex items-center bg-gray-100 rounded-md textsm px-1">
|
||||
<div
|
||||
class="rate-container"
|
||||
:class="disabled ? 'bg-gray-100' : 'bg-gray-25'"
|
||||
>
|
||||
<input type="number" v-model="fromValue" :disabled="disabled" min="0" />
|
||||
<p>{{ left }}</p>
|
||||
</div>
|
||||
|
||||
<p class="mx-1 text-gray-600">=</p>
|
||||
|
||||
<div
|
||||
class="rate-container"
|
||||
:class="disabled ? 'bg-gray-100' : 'bg-gray-25'"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
ref="toValue"
|
||||
:value="isSwapped ? fromValue / exchangeRate : exchangeRate * fromValue"
|
||||
:disabled="disabled"
|
||||
min="0"
|
||||
@change="rightChange"
|
||||
/>
|
||||
<p>{{ right }}</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="bg-green100 px-2 ml-1 -mr-0.5 h-full border-l"
|
||||
@click="swap"
|
||||
v-if="!disabled"
|
||||
>
|
||||
<feather-icon name="refresh-cw" class="w-3 h-3 text-gray-600" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
emits: ['change'],
|
||||
props: {
|
||||
disabled: { type: Boolean, default: false },
|
||||
fromCurrency: { type: String, default: 'USD' },
|
||||
toCurrency: { type: String, default: 'INR' },
|
||||
exchangeRate: { type: Number, default: 75 },
|
||||
},
|
||||
data() {
|
||||
return { fromValue: 1, isSwapped: false };
|
||||
},
|
||||
methods: {
|
||||
swap() {
|
||||
this.isSwapped = !this.isSwapped;
|
||||
},
|
||||
rightChange(e) {
|
||||
let value = this.$refs.toValue.value;
|
||||
if (e) {
|
||||
value = e.target.value;
|
||||
}
|
||||
|
||||
value = parseFloat(value);
|
||||
|
||||
let exchangeRate = value / this.fromValue;
|
||||
if (this.isSwapped) {
|
||||
exchangeRate = this.fromValue / value;
|
||||
}
|
||||
|
||||
this.$emit('change', exchangeRate);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
left() {
|
||||
if (this.isSwapped) {
|
||||
return this.toCurrency;
|
||||
}
|
||||
|
||||
return this.fromCurrency;
|
||||
},
|
||||
right() {
|
||||
if (this.isSwapped) {
|
||||
return this.fromCurrency;
|
||||
}
|
||||
|
||||
return this.toCurrency;
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
input[type='number'] {
|
||||
@apply w-12 outline-none bg-transparent p-0.5;
|
||||
}
|
||||
|
||||
.rate-container {
|
||||
@apply flex items-center rounded-md border border-gray-100 text-gray-900
|
||||
text-sm outline-none focus-within:bg-gray-50 px-1 focus-within:border-gray-200;
|
||||
}
|
||||
|
||||
.rate-container > p {
|
||||
@apply text-xs text-gray-600;
|
||||
}
|
||||
</style>
|
@ -1,15 +1,8 @@
|
||||
<script>
|
||||
import { fyo } from 'src/initFyo';
|
||||
export default {
|
||||
name: 'Base',
|
||||
props: { doc: Object, printSettings: Object },
|
||||
data: () => ({ party: null, companyAddress: null, partyAddress: null }),
|
||||
methods: {
|
||||
format(row, fieldname) {
|
||||
const value = row.get(fieldname);
|
||||
return fyo.format(value, fyo.getField(row.schemaName, fieldname));
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
await this.printSettings.loadLink('address');
|
||||
this.companyAddress = this.printSettings.getLink('address');
|
||||
@ -17,8 +10,49 @@ export default {
|
||||
await this.doc.loadLink('party');
|
||||
this.party = this.doc.getLink('party');
|
||||
this.partyAddress = this.party.getLink('address')?.addressDisplay ?? null;
|
||||
|
||||
if (this.fyo.store.isDevelopment) {
|
||||
window.bt = this;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getFormattedField(fieldname, doc) {
|
||||
doc ??= this.doc;
|
||||
const field = doc.fieldMap[fieldname];
|
||||
const value = doc.get(fieldname);
|
||||
if (Array.isArray(value)) {
|
||||
return this.getFormattedChildDocList(fieldname);
|
||||
}
|
||||
return this.fyo.format(value, field, doc);
|
||||
},
|
||||
getFormattedChildDocList(fieldname) {
|
||||
const formattedDocs = [];
|
||||
for (const childDoc of this.doc?.[fieldname] ?? {}) {
|
||||
formattedDocs.push(this.getFormattedChildDoc(childDoc));
|
||||
}
|
||||
return formattedDocs;
|
||||
},
|
||||
getFormattedChildDoc(childDoc) {
|
||||
const formattedChildDoc = {};
|
||||
for (const field of childDoc?.schema?.fields) {
|
||||
if (field.meta) {
|
||||
continue;
|
||||
}
|
||||
|
||||
formattedChildDoc[field.fieldname] = this.getFormattedField(
|
||||
field.fieldname,
|
||||
childDoc
|
||||
);
|
||||
}
|
||||
return formattedChildDoc;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
currency() {
|
||||
return this.doc.isMultiCurrency
|
||||
? this.doc.currency
|
||||
: this.fyo.singles.SystemSettings.currency;
|
||||
},
|
||||
isSalesInvoice() {
|
||||
return this.doc.schemaName === 'SalesInvoice';
|
||||
},
|
||||
@ -28,6 +62,46 @@ export default {
|
||||
totalDiscount() {
|
||||
return this.doc.getTotalDiscount();
|
||||
},
|
||||
formattedTotalDiscount() {
|
||||
if (!this.totalDiscount?.float) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const totalDiscount = this.fyo.format(this.totalDiscount, {
|
||||
fieldname: 'Total Discount',
|
||||
fieldtype: 'Currency',
|
||||
currency: this.currency,
|
||||
});
|
||||
|
||||
return `- ${totalDiscount}`;
|
||||
},
|
||||
printObject() {
|
||||
return {
|
||||
isSalesInvoice: this.isSalesInvoice,
|
||||
font: this.printSettings.font,
|
||||
color: this.printSettings.color,
|
||||
showHSN: this.showHSN,
|
||||
displayLogo: this.printSettings.displayLogo,
|
||||
discountAfterTax: this.doc.discountAfterTax,
|
||||
logo: this.printSettings.logo,
|
||||
companyName: this.fyo.singles.AccountingSettings.companyName,
|
||||
email: this.printSettings.email,
|
||||
phone: this.printSettings.phone,
|
||||
address: this.companyAddress?.addressDisplay,
|
||||
gstin: this.fyo.singles?.AccountingSettings?.gstin,
|
||||
invoiceName: this.doc.name,
|
||||
date: this.getFormattedField('date'),
|
||||
partyName: this.party?.name,
|
||||
partyAddress: this.partyAddress,
|
||||
partyGSTIN: this.party?.gstin,
|
||||
terms: this.doc.terms,
|
||||
netTotal: this.getFormattedField('netTotal'),
|
||||
items: this.getFormattedField('items'),
|
||||
taxes: this.getFormattedField('taxes'),
|
||||
grandTotal: this.getFormattedField('grandTotal'),
|
||||
totalDiscount: this.formattedTotalDiscount,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -1,36 +1,31 @@
|
||||
<template>
|
||||
<div
|
||||
class="bg-white border h-full"
|
||||
:style="{ 'font-family': printSettings.font }"
|
||||
:style="{ 'font-family': printObject.font }"
|
||||
>
|
||||
<div>
|
||||
<div class="px-6 pt-6" v-if="printSettings">
|
||||
<div class="px-6 pt-6">
|
||||
<div class="flex text-sm text-gray-900 border-b pb-4">
|
||||
<div class="w-1/3">
|
||||
<div v-if="printSettings.displayLogo">
|
||||
<div v-if="printObject.displayLogo">
|
||||
<img
|
||||
class="h-12 max-w-32 object-contain"
|
||||
:src="printSettings.logo"
|
||||
:src="printObject.logo"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-xl text-gray-700 font-semibold" v-else>
|
||||
{{ fyo.singles.AccountingSettings.companyName }}
|
||||
{{ printObject.companyName }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/3">
|
||||
<div>{{ printSettings.email }}</div>
|
||||
<div class="mt-1">{{ printSettings.phone }}</div>
|
||||
<div>{{ printObject.email }}</div>
|
||||
<div class="mt-1">{{ printObject.phone }}</div>
|
||||
</div>
|
||||
<div class="w-1/3">
|
||||
<div v-if="companyAddress">{{ companyAddress.addressDisplay }}</div>
|
||||
<div
|
||||
v-if="
|
||||
fyo.singles.AccountingSettings &&
|
||||
fyo.singles.AccountingSettings.gstin
|
||||
"
|
||||
>
|
||||
GSTIN: {{ fyo.singles.AccountingSettings.gstin }}
|
||||
<div v-if="printObject.address">
|
||||
{{ printObject.address }}
|
||||
</div>
|
||||
<div v-if="printObject.gstin">GSTIN: {{ printObject.gstin }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -38,27 +33,27 @@
|
||||
<div class="flex justify-between">
|
||||
<div class="w-1/3">
|
||||
<h1 class="text-2xl font-semibold">
|
||||
{{ doc.name }}
|
||||
{{ printObject.invoiceName }}
|
||||
</h1>
|
||||
<div class="py-2 text-base">
|
||||
{{ fyo.format(doc.date, 'Date') }}
|
||||
{{ printObject.date }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/3" v-if="party">
|
||||
<div class="w-1/3" v-if="printObject.partyName">
|
||||
<div class="py-1 text-right text-lg font-semibold">
|
||||
{{ party.name }}
|
||||
{{ printObject.partyName }}
|
||||
</div>
|
||||
<div
|
||||
v-if="partyAddress"
|
||||
v-if="printObject.partyAddress"
|
||||
class="mt-1 text-xs text-gray-600 text-right"
|
||||
>
|
||||
{{ partyAddress }}
|
||||
{{ printObject.partyAddress }}
|
||||
</div>
|
||||
<div
|
||||
v-if="party && party.gstin"
|
||||
v-if="printObject.partyGSTIN"
|
||||
class="mt-1 text-xs text-gray-600 text-right"
|
||||
>
|
||||
GSTIN: {{ party.gstin }}
|
||||
GSTIN: {{ printObject.partyGSTIN }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -67,64 +62,67 @@
|
||||
<div>
|
||||
<div class="text-gray-600 w-full flex border-b">
|
||||
<div class="py-4 w-5/12">Item</div>
|
||||
<div class="py-4 text-right w-2/12" v-if="showHSN">HSN/SAC</div>
|
||||
<div class="py-4 text-right w-2/12" v-if="printObject.showHSN">
|
||||
HSN/SAC
|
||||
</div>
|
||||
<div class="py-4 text-right w-1/12">Quantity</div>
|
||||
<div class="py-4 text-right w-3/12">Rate</div>
|
||||
<div class="py-4 text-right w-3/12">Amount</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex py-1 text-gray-900 w-full border-b"
|
||||
v-for="row in doc.items"
|
||||
v-for="row in printObject.items"
|
||||
:key="row.name"
|
||||
>
|
||||
<div class="w-5/12 py-4">{{ row.item }}</div>
|
||||
<div class="w-2/12 text-right py-4" v-if="showHSN">
|
||||
<div class="w-2/12 text-right py-4" v-if="printObject.showHSN">
|
||||
{{ row.hsnCode }}
|
||||
</div>
|
||||
<div class="w-1/12 text-right py-4">
|
||||
{{ format(row, 'quantity') }}
|
||||
</div>
|
||||
<div class="w-3/12 text-right py-4">{{ format(row, 'rate') }}</div>
|
||||
<div class="w-3/12 text-right py-4">
|
||||
{{ format(row, 'amount') }}
|
||||
</div>
|
||||
<div class="w-1/12 text-right py-4">{{ row.quantity }}</div>
|
||||
<div class="w-3/12 text-right py-4">{{ row.rate }}</div>
|
||||
<div class="w-3/12 text-right py-4">{{ row.amount }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-6 mt-2 flex justify-end text-base">
|
||||
<div class="w-1/2 bg-pink">
|
||||
<div class="text-sm tracking-widest text-gray-600 mt-2">Notes</div>
|
||||
<div class="w-1/2">
|
||||
<div
|
||||
class="text-sm tracking-widest text-gray-600 mt-2"
|
||||
v-if="printObject.terms"
|
||||
>
|
||||
Notes
|
||||
</div>
|
||||
<div class="my-4 text-lg whitespace-pre-line">
|
||||
{{ doc.terms }}
|
||||
{{ printObject.terms }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<div class="flex pl-2 justify-between py-3 border-b">
|
||||
<div>{{ t`Subtotal` }}</div>
|
||||
<div>{{ fyo.format(doc.netTotal, 'Currency') }}</div>
|
||||
<div>{{ printObject.netTotal }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex pl-2 justify-between py-3 border-b"
|
||||
v-if="totalDiscount?.float > 0 && !doc.discountAfterTax"
|
||||
v-if="printObject.totalDiscount && !printObject.discountAfterTax"
|
||||
>
|
||||
<div>{{ t`Discount` }}</div>
|
||||
<div>{{ `- ${fyo.format(totalDiscount, 'Currency')}` }}</div>
|
||||
<div>{{ printObject.totalDiscount }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex pl-2 justify-between py-3"
|
||||
v-for="tax in doc.taxes"
|
||||
v-for="tax in printObject.taxes"
|
||||
:key="tax.name"
|
||||
>
|
||||
<div>{{ tax.account }}</div>
|
||||
<div>{{ fyo.format(tax.amount, 'Currency') }}</div>
|
||||
<div>{{ tax.amount }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex pl-2 justify-between py-3 border-t"
|
||||
v-if="totalDiscount?.float > 0 && doc.discountAfterTax"
|
||||
v-if="printObject.totalDiscount && printObject.discountAfterTax"
|
||||
>
|
||||
<div>{{ t`Discount` }}</div>
|
||||
<div>{{ `- ${fyo.format(totalDiscount, 'Currency')}` }}</div>
|
||||
<div>{{ printObject.totalDiscount }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="
|
||||
@ -139,7 +137,7 @@
|
||||
"
|
||||
>
|
||||
<div>{{ t`Grand Total` }}</div>
|
||||
<div>{{ fyo.format(doc.grandTotal, 'Currency') }}</div>
|
||||
<div>{{ printObject.grandTotal }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -147,11 +145,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Base from './BaseTemplate.vue';
|
||||
import BaseTemplate from './BaseTemplate.vue';
|
||||
|
||||
export default {
|
||||
name: 'Default',
|
||||
extends: Base,
|
||||
props: ['doc', 'printSettings'],
|
||||
extends: BaseTemplate,
|
||||
};
|
||||
</script>
|
||||
|
@ -1,65 +1,58 @@
|
||||
<template>
|
||||
<div
|
||||
class="bg-white border h-full"
|
||||
:style="{ 'font-family': printSettings.font }"
|
||||
:style="{ 'font-family': printObject.font }"
|
||||
>
|
||||
<div class="bg-gray-100 px-12 py-10">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center rounded h-16">
|
||||
<div class="mr-4" v-if="printSettings.displayLogo">
|
||||
<img
|
||||
class="h-12 max-w-32 object-contain"
|
||||
:src="printSettings.logo"
|
||||
/>
|
||||
<div class="mr-4" v-if="printObject.displayLogo">
|
||||
<img class="h-12 max-w-32 object-contain" :src="printObject.logo" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="font-semibold text-xl"
|
||||
:style="{ color: printSettings.color }"
|
||||
:style="{ color: printObject.color }"
|
||||
>
|
||||
{{ fyo.singles.AccountingSettings.companyName }}
|
||||
{{ printObject.companyName }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-800" v-if="companyAddress">
|
||||
{{ companyAddress.addressDisplay }}
|
||||
<div class="text-sm text-gray-800" v-if="printObject.address">
|
||||
{{ printObject.address }}
|
||||
</div>
|
||||
<div
|
||||
class="text-sm text-gray-800"
|
||||
v-if="
|
||||
fyo.singles.AccountingSettings &&
|
||||
fyo.singles.AccountingSettings.gstin
|
||||
"
|
||||
>
|
||||
GSTIN: {{ fyo.singles.AccountingSettings.gstin }}
|
||||
<div class="text-sm text-gray-800" v-if="printObject.gstin">
|
||||
GSTIN: {{ printObject.gstin }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-8 text-lg">
|
||||
<div class="flex">
|
||||
<div class="w-1/3 font-semibold">
|
||||
{{ isSalesInvoice ? 'Invoice' : 'Bill' }}
|
||||
{{ printObject.isSalesInvoice ? 'Invoice' : 'Bill' }}
|
||||
</div>
|
||||
<div class="w-2/3 text-gray-800">
|
||||
<div class="font-semibold">
|
||||
{{ doc.name }}
|
||||
{{ printObject.invoiceName }}
|
||||
</div>
|
||||
<div>
|
||||
{{ fyo.format(doc.date, 'Date') }}
|
||||
{{ printObject.date }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<div class="w-1/3 font-semibold">
|
||||
{{ isSalesInvoice ? 'Customer' : 'Supplier' }}
|
||||
{{ printObject.isSalesInvoice ? 'Customer' : 'Supplier' }}
|
||||
</div>
|
||||
<div class="w-2/3 text-gray-800" v-if="party">
|
||||
<div class="w-2/3 text-gray-800" v-if="printObject.partyName">
|
||||
<div class="font-semibold">
|
||||
{{ party.name }}
|
||||
{{ printObject.partyName }}
|
||||
</div>
|
||||
<div v-if="partyAddress">
|
||||
{{ partyAddress }}
|
||||
<div v-if="printObject.partyAddress">
|
||||
{{ printObject.partyAddress }}
|
||||
</div>
|
||||
<div v-if="printObject.partyGSTIN">
|
||||
GSTIN: {{ printObject.partyGSTIN }}
|
||||
</div>
|
||||
<div v-if="party && party.gstin">GSTIN: {{ party.gstin }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -67,21 +60,23 @@
|
||||
<div class="px-12 py-12 text-lg">
|
||||
<div class="mb-4 flex font-semibold">
|
||||
<div class="w-4/12">Item</div>
|
||||
<div class="w-2/12 text-right" v-if="showHSN">HSN/SAC</div>
|
||||
<div class="w-2/12 text-right" v-if="printObject.showHSN">HSN/SAC</div>
|
||||
<div class="w-2/12 text-right">Quantity</div>
|
||||
<div class="w-3/12 text-right">Rate</div>
|
||||
<div class="w-3/12 text-right">Amount</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex py-1 text-gray-800"
|
||||
v-for="row in doc.items"
|
||||
v-for="row in printObject.items"
|
||||
:key="row.name"
|
||||
>
|
||||
<div class="w-4/12">{{ row.item }}</div>
|
||||
<div class="w-2/12 text-right" v-if="showHSN">{{ row.hsnCode }}</div>
|
||||
<div class="w-2/12 text-right">{{ format(row, 'quantity') }}</div>
|
||||
<div class="w-3/12 text-right">{{ format(row, 'rate') }}</div>
|
||||
<div class="w-3/12 text-right">{{ format(row, 'amount') }}</div>
|
||||
<div class="w-2/12 text-right" v-if="printObject.showHSN">
|
||||
{{ row.hsnCode }}
|
||||
</div>
|
||||
<div class="w-2/12 text-right">{{ row.quantity }}</div>
|
||||
<div class="w-3/12 text-right">{{ row.rate }}</div>
|
||||
<div class="w-3/12 text-right">{{ row.amount }}</div>
|
||||
</div>
|
||||
<div class="mt-12">
|
||||
<div class="flex -mx-3">
|
||||
@ -89,67 +84,70 @@
|
||||
<div class="text-right">
|
||||
<div class="text-gray-800">{{ t`Subtotal` }}</div>
|
||||
<div class="text-xl mt-2">
|
||||
{{ fyo.format(doc.netTotal, 'Currency') }}
|
||||
{{ printObject.netTotal }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="text-right"
|
||||
v-if="totalDiscount?.float > 0 && !doc.discountAfterTax"
|
||||
v-if="printObject.totalDiscount && !printObject.discountAfterTax"
|
||||
>
|
||||
<div class="text-gray-800">{{ t`Discount` }}</div>
|
||||
<div class="text-xl mt-2">
|
||||
{{ `- ${fyo.format(totalDiscount, 'Currency')}` }}
|
||||
{{ printObject.totalDiscount }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-right" v-for="tax in doc.taxes" :key="tax.name">
|
||||
<div
|
||||
class="text-right"
|
||||
v-for="tax in printObject.taxes"
|
||||
:key="tax.name"
|
||||
>
|
||||
<div class="text-gray-800">
|
||||
{{ tax.account }}
|
||||
</div>
|
||||
<div class="text-xl mt-2">
|
||||
{{ fyo.format(tax.amount, 'Currency') }}
|
||||
{{ tax.amount }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="text-right"
|
||||
v-if="totalDiscount?.float > 0 && !doc.discountAfterTax"
|
||||
v-if="printObject.totalDiscount && printObject.discountAfterTax"
|
||||
>
|
||||
<div class="text-gray-800">{{ t`Discount` }}</div>
|
||||
<div class="text-xl mt-2">
|
||||
{{ `- ${fyo.format(totalDiscount, 'Currency')}` }}
|
||||
{{ printObject.totalDiscount }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="py-3 px-4 text-right text-white"
|
||||
:style="{ backgroundColor: printSettings.color }"
|
||||
:style="{ backgroundColor: printObject.color }"
|
||||
>
|
||||
<div>
|
||||
<div>{{ t`Grand Total` }}</div>
|
||||
<div class="text-2xl mt-2 font-semibold">
|
||||
{{ fyo.format(doc.grandTotal, 'Currency') }}
|
||||
{{ printObject.grandTotal }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-12" v-if="doc.terms">
|
||||
<div class="mt-12" v-if="printObject.terms">
|
||||
<div class="text-lg font-semibold">Notes</div>
|
||||
<div class="mt-4 text-lg whitespace-pre-line">
|
||||
{{ doc.terms }}
|
||||
{{ printObject.terms }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Base from './BaseTemplate.vue';
|
||||
import BaseTemplate from './BaseTemplate.vue';
|
||||
|
||||
export default {
|
||||
name: 'Business',
|
||||
extends: Base,
|
||||
extends: BaseTemplate,
|
||||
};
|
||||
</script>
|
||||
|
@ -1,65 +1,60 @@
|
||||
<template>
|
||||
<div
|
||||
class="bg-white border h-full"
|
||||
:style="{ 'font-family': printSettings.font }"
|
||||
:style="{ 'font-family': printObject.font }"
|
||||
>
|
||||
<div class="flex items-center justify-between px-12 py-10 border-b">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center rounded h-16">
|
||||
<div class="mr-4" v-if="printSettings.displayLogo">
|
||||
<img
|
||||
class="h-12 max-w-32 object-contain"
|
||||
:src="printSettings.logo"
|
||||
/>
|
||||
<div class="mr-4" v-if="printObject.displayLogo">
|
||||
<img class="h-12 max-w-32 object-contain" :src="printObject.logo" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="font-semibold text-xl"
|
||||
:style="{ color: printSettings.color }"
|
||||
:style="{ color: printObject.color }"
|
||||
>
|
||||
{{ fyo.singles.AccountingSettings.companyName }}
|
||||
{{ printObject.companyName }}
|
||||
</div>
|
||||
<div>
|
||||
{{ fyo.format(doc.date, 'Date') }}
|
||||
{{ printObject.date }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div
|
||||
class="font-semibold text-xl"
|
||||
:style="{ color: printSettings.color }"
|
||||
:style="{ color: printObject.color }"
|
||||
>
|
||||
{{
|
||||
doc.schemaName === 'SalesInvoice'
|
||||
? t`Sales Invoice`
|
||||
: t`Purchase Invoice`
|
||||
printObject.isSalesInvoice ? t`Sales Invoice` : t`Purchase Invoice`
|
||||
}}
|
||||
</div>
|
||||
<div>
|
||||
{{ doc.name }}
|
||||
{{ printObject.invoiceName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex px-12 py-10 border-b">
|
||||
<div class="w-1/2" v-if="party">
|
||||
<div class="w-1/2" v-if="printObject.partyName">
|
||||
<div
|
||||
class="uppercase text-sm font-semibold tracking-widest text-gray-800"
|
||||
>
|
||||
{{ isSalesInvoice ? 'To' : 'From' }}
|
||||
{{ printObject.isSalesInvoice ? 'To' : 'From' }}
|
||||
</div>
|
||||
<div class="mt-4 text-black leading-relaxed text-lg">
|
||||
{{ party.name }} <br />
|
||||
{{ partyAddress ? partyAddress : '' }}
|
||||
{{ printObject.partyName }} <br />
|
||||
{{ printObject.partyAddress ?? '' }}
|
||||
</div>
|
||||
<div
|
||||
class="mt-4 text-black leading-relaxed text-lg"
|
||||
v-if="party && party.gstin"
|
||||
v-if="printObject.partyGSTIN"
|
||||
>
|
||||
GSTIN: {{ party.gstin }}
|
||||
GSTIN: {{ printObject.partyGSTIN }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/2" v-if="companyAddress">
|
||||
<div class="w-1/2" v-if="printObject.address">
|
||||
<div
|
||||
class="
|
||||
uppercase
|
||||
@ -70,19 +65,16 @@
|
||||
ml-8
|
||||
"
|
||||
>
|
||||
{{ isSalesInvoice ? 'From' : 'To' }}
|
||||
{{ printObject.isSalesInvoice ? 'From' : 'To' }}
|
||||
</div>
|
||||
<div class="mt-4 ml-8 text-black leading-relaxed text-lg">
|
||||
{{ companyAddress.addressDisplay }}
|
||||
{{ printObject.address }}
|
||||
</div>
|
||||
<div
|
||||
class="mt-4 ml-8 text-black leading-relaxed text-lg"
|
||||
v-if="
|
||||
fyo.singles.AccountingSettings &&
|
||||
fyo.singles.AccountingSettings.gstin
|
||||
"
|
||||
v-if="printObject.gstin"
|
||||
>
|
||||
GSTIN: {{ fyo.singles.AccountingSettings.gstin }}
|
||||
GSTIN: {{ printObject.gstin }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -99,74 +91,79 @@
|
||||
"
|
||||
>
|
||||
<div class="w-4/12">Item</div>
|
||||
<div class="w-2/12 text-right" v-if="showHSN">HSN/SAC</div>
|
||||
<div class="w-2/12 text-right" v-if="printObject.showHSN">HSN/SAC</div>
|
||||
<div class="w-2/12 text-right">Quantity</div>
|
||||
<div class="w-3/12 text-right">Rate</div>
|
||||
<div class="w-3/12 text-right">Amount</div>
|
||||
</div>
|
||||
<div class="flex py-1 text-lg" v-for="row in doc.items" :key="row.name">
|
||||
<div
|
||||
class="flex py-1 text-lg"
|
||||
v-for="row in printObject.items"
|
||||
:key="row.name"
|
||||
>
|
||||
<div class="w-4/12">{{ row.item }}</div>
|
||||
<div class="w-2/12 text-right" v-if="showHSN">{{ row.hsnCode }}</div>
|
||||
<div class="w-2/12 text-right">{{ format(row, 'quantity') }}</div>
|
||||
<div class="w-3/12 text-right">{{ format(row, 'rate') }}</div>
|
||||
<div class="w-3/12 text-right">{{ format(row, 'amount') }}</div>
|
||||
<div class="w-2/12 text-right" v-if="printObject.showHSN">
|
||||
{{ row.hsnCode }}
|
||||
</div>
|
||||
<div class="w-2/12 text-right">{{ row.quantity }}</div>
|
||||
<div class="w-3/12 text-right">{{ row.rate }}</div>
|
||||
<div class="w-3/12 text-right">{{ row.amount }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex px-12 py-10">
|
||||
<div class="w-1/2" v-if="doc.terms">
|
||||
<div class="w-1/2" v-if="printObject.terms">
|
||||
<div
|
||||
class="uppercase text-sm tracking-widest font-semibold text-gray-800"
|
||||
>
|
||||
Notes
|
||||
</div>
|
||||
<div class="mt-4 text-lg whitespace-pre-line">
|
||||
{{ doc.terms }}
|
||||
{{ printObject.terms }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/2 text-lg">
|
||||
<div class="flex pl-2 justify-between py-1">
|
||||
<div>{{ t`Subtotal` }}</div>
|
||||
<div>{{ fyo.format(doc.netTotal, 'Currency') }}</div>
|
||||
<div>{{ printObject.netTotal }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex pl-2 justify-between py-1"
|
||||
v-if="totalDiscount?.float > 0 && !doc.discountAfterTax"
|
||||
v-if="printObject.totalDiscount && !printObject.discountAfterTax"
|
||||
>
|
||||
<div>{{ t`Discount` }}</div>
|
||||
<div>{{ `- ${fyo.format(totalDiscount, 'Currency')}` }}</div>
|
||||
<div>{{ printObject.totalDiscount }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex pl-2 justify-between py-1"
|
||||
v-for="tax in doc.taxes"
|
||||
v-for="tax in printObject.taxes"
|
||||
:key="tax.name"
|
||||
>
|
||||
<div>{{ tax.account }}</div>
|
||||
<div>{{ fyo.format(tax.amount, 'Currency') }}</div>
|
||||
<div>{{ tax.amount }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex pl-2 justify-between py-1"
|
||||
v-if="totalDiscount?.float > 0 && doc.discountAfterTax"
|
||||
v-if="printObject.totalDiscount && printObject.discountAfterTax"
|
||||
>
|
||||
<div>{{ t`Discount` }}</div>
|
||||
<div>{{ `- ${fyo.format(totalDiscount, 'Currency')}` }}</div>
|
||||
<div>{{ printObject.totalDiscount }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex pl-2 justify-between py-1 font-semibold"
|
||||
:style="{ color: printSettings.color }"
|
||||
:style="{ color: printObject.color }"
|
||||
>
|
||||
<div>{{ t`Grand Total` }}</div>
|
||||
<div>{{ fyo.format(doc.grandTotal, 'Currency') }}</div>
|
||||
<div>{{ printObject.grandTotal }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Base from './BaseTemplate.vue';
|
||||
import BaseTemplate from './BaseTemplate.vue';
|
||||
|
||||
export default {
|
||||
name: 'Minimal',
|
||||
extends: Base,
|
||||
extends: BaseTemplate,
|
||||
};
|
||||
</script>
|
||||
|
@ -3,6 +3,16 @@
|
||||
<!-- Page Header (Title, Buttons, etc) -->
|
||||
<template #header v-if="doc">
|
||||
<StatusBadge :status="status" />
|
||||
<ExchangeRate
|
||||
v-if="doc.isMultiCurrency"
|
||||
:disabled="doc?.isSubmitted || doc?.isCancelled"
|
||||
:from-currency="fromCurrency"
|
||||
:to-currency="toCurrency"
|
||||
:exchange-rate="doc.exchangeRate"
|
||||
@change="
|
||||
async (exchangeRate) => await doc.set('exchangeRate', exchangeRate)
|
||||
"
|
||||
/>
|
||||
<Button
|
||||
v-if="!doc.isCancelled && !doc.dirty"
|
||||
:icon="true"
|
||||
@ -67,8 +77,8 @@
|
||||
:border="true"
|
||||
:df="getField('party')"
|
||||
:value="doc.party"
|
||||
@change="(value) => doc.set('party', value)"
|
||||
@new-doc="(party) => doc.set('party', party.name)"
|
||||
@change="(value) => doc.set('party', value, true)"
|
||||
@new-doc="(party) => doc.set('party', party.name, true)"
|
||||
:read-only="doc?.submitted"
|
||||
/>
|
||||
<FormControl
|
||||
@ -175,10 +185,14 @@
|
||||
<div>{{ tax.account }}</div>
|
||||
<div>
|
||||
{{
|
||||
fyo.format(tax.amount, {
|
||||
fieldtype: 'Currency',
|
||||
currency: doc.currency,
|
||||
})
|
||||
fyo.format(
|
||||
tax.amount,
|
||||
{
|
||||
fieldtype: 'Currency',
|
||||
fieldname: 'amount',
|
||||
},
|
||||
tax
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
@ -220,6 +234,21 @@
|
||||
<div>{{ formattedValue('grandTotal') }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Base Grand Total -->
|
||||
<div
|
||||
v-if="doc.isMultiCurrency"
|
||||
class="
|
||||
flex
|
||||
justify-between
|
||||
text-green-600
|
||||
font-semibold
|
||||
text-base
|
||||
"
|
||||
>
|
||||
<div>{{ t`Base Grand Total` }}</div>
|
||||
<div>{{ formattedValue('baseGrandTotal') }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Outstanding Amount -->
|
||||
<hr v-if="doc.outstandingAmount?.float > 0" />
|
||||
<div
|
||||
@ -256,6 +285,7 @@ import { computed } from '@vue/reactivity';
|
||||
import { getDocStatus } from 'models/helpers';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import Button from 'src/components/Button.vue';
|
||||
import ExchangeRate from 'src/components/Controls/ExchangeRate.vue';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import Table from 'src/components/Controls/Table.vue';
|
||||
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
||||
@ -284,6 +314,7 @@ export default {
|
||||
Table,
|
||||
FormContainer,
|
||||
QuickEditForm,
|
||||
ExchangeRate,
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
@ -342,6 +373,12 @@ export default {
|
||||
itemDiscountAmount() {
|
||||
return this.doc.getItemDiscountAmount();
|
||||
},
|
||||
fromCurrency() {
|
||||
return this.doc?.currency ?? this.toCurrency;
|
||||
},
|
||||
toCurrency() {
|
||||
return fyo.singles.SystemSettings.currency;
|
||||
},
|
||||
},
|
||||
activated() {
|
||||
docsPath.value = docsPathMap[this.schemaName];
|
||||
|
@ -56,6 +56,12 @@
|
||||
</p>
|
||||
|
||||
<TwoColumnForm :doc="doc" :read-only="loading" />
|
||||
<Button
|
||||
v-if="fyo.store.isDevelopment"
|
||||
class="m-4 text-sm min-w-28"
|
||||
@click="fill"
|
||||
>Fill</Button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
<template #secondaryButton>{{ t`Cancel` }}</template>
|
||||
@ -67,6 +73,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Button from 'src/components/Button.vue';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import TwoColumnForm from 'src/components/TwoColumnForm.vue';
|
||||
import { getErrorMessage } from 'src/utils';
|
||||
@ -95,6 +102,7 @@ export default {
|
||||
TwoColumnForm,
|
||||
FormControl,
|
||||
Slide,
|
||||
Button,
|
||||
},
|
||||
async mounted() {
|
||||
this.doc = await getSetupWizardDoc();
|
||||
@ -103,6 +111,13 @@ export default {
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
async fill() {
|
||||
await this.doc.set('companyName', "Lin's Things");
|
||||
await this.doc.set('email', 'lin@lthings.com');
|
||||
await this.doc.set('fullname', 'Lin Slovenly');
|
||||
await this.doc.set('bankName', 'Max Finance');
|
||||
await this.doc.set('country', 'India');
|
||||
},
|
||||
getField(fieldname) {
|
||||
return this.doc.schema?.fields.find((f) => f.fieldname === fieldname);
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user