mirror of
https://github.com/frappe/books.git
synced 2025-01-24 15:48:25 +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,
|
OptionField,
|
||||||
RawValue,
|
RawValue,
|
||||||
Schema,
|
Schema,
|
||||||
TargetField,
|
TargetField
|
||||||
} from 'schemas/types';
|
} from 'schemas/types';
|
||||||
import { getIsNullOrUndef, getMapFromList, getRandomString } from 'utils';
|
import { getIsNullOrUndef, getMapFromList, getRandomString } from 'utils';
|
||||||
import { markRaw } from 'vue';
|
import { markRaw } from 'vue';
|
||||||
@ -23,7 +23,7 @@ import {
|
|||||||
getMissingMandatoryMessage,
|
getMissingMandatoryMessage,
|
||||||
getPreDefaultValues,
|
getPreDefaultValues,
|
||||||
setChildDocIdx,
|
setChildDocIdx,
|
||||||
shouldApplyFormula,
|
shouldApplyFormula
|
||||||
} from './helpers';
|
} from './helpers';
|
||||||
import { setName } from './naming';
|
import { setName } from './naming';
|
||||||
import {
|
import {
|
||||||
@ -41,7 +41,7 @@ import {
|
|||||||
ReadOnlyMap,
|
ReadOnlyMap,
|
||||||
RequiredMap,
|
RequiredMap,
|
||||||
TreeViewSettings,
|
TreeViewSettings,
|
||||||
ValidationMap,
|
ValidationMap
|
||||||
} from './types';
|
} from './types';
|
||||||
import { validateOptions, validateRequired } from './validationFunction';
|
import { validateOptions, validateRequired } from './validationFunction';
|
||||||
|
|
||||||
@ -186,7 +186,8 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
|||||||
// set value and trigger change
|
// set value and trigger change
|
||||||
async set(
|
async set(
|
||||||
fieldname: string | DocValueMap,
|
fieldname: string | DocValueMap,
|
||||||
value?: DocValue | Doc[] | DocValueMap[]
|
value?: DocValue | Doc[] | DocValueMap[],
|
||||||
|
retriggerChildDocApplyChange: boolean = false
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if (typeof fieldname === 'object') {
|
if (typeof fieldname === 'object') {
|
||||||
return await this.setMultiple(fieldname as DocValueMap);
|
return await this.setMultiple(fieldname as DocValueMap);
|
||||||
@ -216,7 +217,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
|||||||
await this._applyChange(fieldname);
|
await this._applyChange(fieldname);
|
||||||
await this.parentdoc._applyChange(this.parentFieldname as string);
|
await this.parentdoc._applyChange(this.parentFieldname as string);
|
||||||
} else {
|
} else {
|
||||||
await this._applyChange(fieldname);
|
await this._applyChange(fieldname, retriggerChildDocApplyChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -259,8 +260,11 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
|||||||
return !areDocValuesEqual(currentValue as DocValue, value as DocValue);
|
return !areDocValuesEqual(currentValue as DocValue, value as DocValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _applyChange(fieldname: string): Promise<boolean> {
|
async _applyChange(
|
||||||
await this._applyFormula(fieldname);
|
fieldname: string,
|
||||||
|
retriggerChildDocApplyChange?: boolean
|
||||||
|
): Promise<boolean> {
|
||||||
|
await this._applyFormula(fieldname, retriggerChildDocApplyChange);
|
||||||
await this.trigger('change', {
|
await this.trigger('change', {
|
||||||
doc: this,
|
doc: this,
|
||||||
changed: fieldname,
|
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;
|
const doc = this;
|
||||||
let changed = false;
|
let changed = await this._callAllTableFieldsApplyFormula(fieldname);
|
||||||
|
changed = (await this._applyFormulaForFields(doc, fieldname)) || changed;
|
||||||
|
|
||||||
const childDocs = this.tableFields
|
if (changed && retriggerChildDocApplyChange) {
|
||||||
.map((f) => (this.get(f.fieldname) as Doc[]) ?? [])
|
await this._callAllTableFieldsApplyFormula(fieldname);
|
||||||
.flat();
|
await this._applyFormulaForFields(doc, fieldname);
|
||||||
|
|
||||||
// children
|
|
||||||
for (const row of childDocs) {
|
|
||||||
changed ||= (await row?._applyFormula()) ?? false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(
|
const formulaFields = Object.keys(this.formulas).map(
|
||||||
(fn) => this.fieldMap[fn]
|
(fn) => this.fieldMap[fn]
|
||||||
);
|
);
|
||||||
|
|
||||||
changed ||= await this._applyFormulaForFields(
|
|
||||||
formulaFields,
|
|
||||||
doc,
|
|
||||||
fieldname
|
|
||||||
);
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _applyFormulaForFields(
|
|
||||||
formulaFields: Field[],
|
|
||||||
doc: Doc,
|
|
||||||
fieldname?: string
|
|
||||||
) {
|
|
||||||
let changed = false;
|
let changed = false;
|
||||||
for (const field of formulaFields) {
|
for (const field of formulaFields) {
|
||||||
const shouldApply = shouldApplyFormula(field, doc, fieldname);
|
const shouldApply = shouldApplyFormula(field, doc, fieldname);
|
||||||
@ -662,7 +691,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
doc[field.fieldname] = newVal;
|
doc[field.fieldname] = newVal;
|
||||||
changed = true;
|
changed ||= true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
|
@ -7,6 +7,16 @@ import { SelectOption } from 'schemas/types';
|
|||||||
import { getCountryInfo } from 'utils/misc';
|
import { getCountryInfo } from 'utils/misc';
|
||||||
|
|
||||||
export default class SystemSettings extends Doc {
|
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 = {
|
validations: ValidationMap = {
|
||||||
async displayPrecision(value: DocValue) {
|
async displayPrecision(value: DocValue) {
|
||||||
if ((value as number) >= 0 && (value as number) <= 9) {
|
if ((value as number) >= 0 && (value as number) <= 9) {
|
||||||
|
@ -70,7 +70,7 @@ function formatCurrency(
|
|||||||
doc: Doc | null,
|
doc: Doc | null,
|
||||||
fyo: Fyo
|
fyo: Fyo
|
||||||
): string {
|
): string {
|
||||||
const currency = getCurrency(field, doc, fyo);
|
const currency = getCurrency(value as Money, field, doc, fyo);
|
||||||
|
|
||||||
let valueString;
|
let valueString;
|
||||||
try {
|
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];
|
let getCurrency = doc?.getCurrencies?.[field.fieldname];
|
||||||
if (getCurrency !== undefined) {
|
if (getCurrency !== undefined) {
|
||||||
return getCurrency();
|
return getCurrency();
|
||||||
@ -139,7 +152,7 @@ function getCurrency(field: Field, doc: Doc | null, fyo: Fyo): string {
|
|||||||
return getCurrency();
|
return getCurrency();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (fyo.singles.SystemSettings?.currency as string) ?? DEFAULT_CURRENCY;
|
return defaultCurrency;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getField(df: string | Field): Field {
|
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 { 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 { ValidationError } from 'fyo/utils/errors';
|
||||||
import { getExchangeRate } from 'models/helpers';
|
import { getExchangeRate } from 'models/helpers';
|
||||||
import { Transactional } from 'models/Transactional/Transactional';
|
import { Transactional } from 'models/Transactional/Transactional';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { Money } from 'pesa';
|
import { Money } from 'pesa';
|
||||||
|
import { FieldTypeEnum, Schema } from 'schemas/types';
|
||||||
import { getIsNullOrUndef } from 'utils';
|
import { getIsNullOrUndef } from 'utils';
|
||||||
import { InvoiceItem } from '../InvoiceItem/InvoiceItem';
|
import { InvoiceItem } from '../InvoiceItem/InvoiceItem';
|
||||||
import { Party } from '../Party/Party';
|
import { Party } from '../Party/Party';
|
||||||
@ -42,6 +51,23 @@ export abstract class Invoice extends Transactional {
|
|||||||
return !!this.fyo.singles?.AccountingSettings?.enableDiscounting;
|
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() {
|
async validate() {
|
||||||
await super.validate();
|
await super.validate();
|
||||||
if (
|
if (
|
||||||
@ -126,10 +152,12 @@ export abstract class Invoice extends Transactional {
|
|||||||
if (this.currency === currency) {
|
if (this.currency === currency) {
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
return await getExchangeRate({
|
const exchangeRate = await getExchangeRate({
|
||||||
fromCurrency: this.currency!,
|
fromCurrency: this.currency!,
|
||||||
toCurrency: currency as string,
|
toCurrency: currency as string,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return parseFloat(exchangeRate.toFixed(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTaxSummary() {
|
async getTaxSummary() {
|
||||||
@ -139,7 +167,6 @@ export abstract class Invoice extends Transactional {
|
|||||||
account: string;
|
account: string;
|
||||||
rate: number;
|
rate: number;
|
||||||
amount: Money;
|
amount: Money;
|
||||||
baseAmount: Money;
|
|
||||||
[key: string]: DocValue;
|
[key: string]: DocValue;
|
||||||
}
|
}
|
||||||
> = {};
|
> = {};
|
||||||
@ -157,7 +184,6 @@ export abstract class Invoice extends Transactional {
|
|||||||
account,
|
account,
|
||||||
rate,
|
rate,
|
||||||
amount: this.fyo.pesa(0),
|
amount: this.fyo.pesa(0),
|
||||||
baseAmount: this.fyo.pesa(0),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let amount = item.amount!;
|
let amount = item.amount!;
|
||||||
@ -172,9 +198,7 @@ export abstract class Invoice extends Transactional {
|
|||||||
|
|
||||||
return Object.keys(taxes)
|
return Object.keys(taxes)
|
||||||
.map((account) => {
|
.map((account) => {
|
||||||
const tax = taxes[account];
|
return taxes[account];
|
||||||
tax.baseAmount = tax.amount.mul(this.exchangeRate!);
|
|
||||||
return tax;
|
|
||||||
})
|
})
|
||||||
.filter((tax) => !tax.amount.isZero());
|
.filter((tax) => !tax.amount.isZero());
|
||||||
}
|
}
|
||||||
@ -285,15 +309,28 @@ export abstract class Invoice extends Transactional {
|
|||||||
},
|
},
|
||||||
dependsOn: ['party'],
|
dependsOn: ['party'],
|
||||||
},
|
},
|
||||||
exchangeRate: { formula: async () => await this.getExchangeRate() },
|
exchangeRate: {
|
||||||
netTotal: { formula: async () => this.getSum('items', 'amount', false) },
|
formula: async () => {
|
||||||
baseNetTotal: {
|
if (
|
||||||
formula: async () => this.netTotal!.mul(this.exchangeRate!),
|
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() },
|
taxes: { formula: async () => await this.getTaxSummary() },
|
||||||
grandTotal: { formula: async () => await this.getGrandTotal() },
|
grandTotal: { formula: async () => await this.getGrandTotal() },
|
||||||
baseGrandTotal: {
|
baseGrandTotal: {
|
||||||
formula: async () => (this.grandTotal as Money).mul(this.exchangeRate!),
|
formula: async () =>
|
||||||
|
(this.grandTotal as Money).mul(this.exchangeRate! ?? 1),
|
||||||
},
|
},
|
||||||
outstandingAmount: {
|
outstandingAmount: {
|
||||||
formula: async () => {
|
formula: async () => {
|
||||||
@ -345,4 +382,25 @@ export abstract class Invoice extends Transactional {
|
|||||||
role: doc.isSales ? 'Customer' : 'Supplier',
|
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 { Doc } from 'fyo/model/doc';
|
||||||
import {
|
import {
|
||||||
|
CurrenciesMap,
|
||||||
FiltersMap,
|
FiltersMap,
|
||||||
FormulaMap,
|
FormulaMap,
|
||||||
HiddenMap,
|
HiddenMap,
|
||||||
ValidationMap
|
ValidationMap
|
||||||
} from 'fyo/model/types';
|
} from 'fyo/model/types';
|
||||||
|
import { DEFAULT_CURRENCY } from 'fyo/utils/consts';
|
||||||
import { ValidationError } from 'fyo/utils/errors';
|
import { ValidationError } from 'fyo/utils/errors';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { Money } from 'pesa';
|
import { Money } from 'pesa';
|
||||||
|
import { FieldTypeEnum, Schema } from 'schemas/types';
|
||||||
import { Invoice } from '../Invoice/Invoice';
|
import { Invoice } from '../Invoice/Invoice';
|
||||||
|
|
||||||
export abstract class InvoiceItem extends Doc {
|
export abstract class InvoiceItem extends Doc {
|
||||||
account?: string;
|
account?: string;
|
||||||
amount?: Money;
|
amount?: Money;
|
||||||
baseAmount?: Money;
|
|
||||||
exchangeRate?: number;
|
|
||||||
parentdoc?: Invoice;
|
parentdoc?: Invoice;
|
||||||
rate?: Money;
|
rate?: Money;
|
||||||
quantity?: number;
|
quantity?: number;
|
||||||
@ -39,6 +41,23 @@ export abstract class InvoiceItem extends Doc {
|
|||||||
return !!this.fyo.singles?.AccountingSettings?.enableDiscounting;
|
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> {
|
async getTotalTaxRate(): Promise<number> {
|
||||||
if (!this.tax) {
|
if (!this.tax) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -73,7 +92,7 @@ export abstract class InvoiceItem extends Doc {
|
|||||||
fieldname !== 'itemTaxedTotal' &&
|
fieldname !== 'itemTaxedTotal' &&
|
||||||
fieldname !== 'itemDiscountedTotal'
|
fieldname !== 'itemDiscountedTotal'
|
||||||
) {
|
) {
|
||||||
return rate ?? this.fyo.pesa(0);
|
return rate?.div(this.exchangeRate) ?? this.fyo.pesa(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const quantity = this.quantity ?? 0;
|
const quantity = this.quantity ?? 0;
|
||||||
@ -102,17 +121,14 @@ export abstract class InvoiceItem extends Doc {
|
|||||||
return rateFromTotals ?? rate ?? this.fyo.pesa(0);
|
return rateFromTotals ?? rate ?? this.fyo.pesa(0);
|
||||||
},
|
},
|
||||||
dependsOn: [
|
dependsOn: [
|
||||||
|
'party',
|
||||||
|
'exchangeRate',
|
||||||
'item',
|
'item',
|
||||||
'itemTaxedTotal',
|
'itemTaxedTotal',
|
||||||
'itemDiscountedTotal',
|
'itemDiscountedTotal',
|
||||||
'setItemDiscountAmount',
|
'setItemDiscountAmount',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
baseRate: {
|
|
||||||
formula: () =>
|
|
||||||
(this.rate as Money).mul(this.parentdoc!.exchangeRate as number),
|
|
||||||
dependsOn: ['item', 'rate'],
|
|
||||||
},
|
|
||||||
quantity: {
|
quantity: {
|
||||||
formula: async () => {
|
formula: async () => {
|
||||||
if (!this.item) {
|
if (!this.item) {
|
||||||
@ -157,11 +173,6 @@ export abstract class InvoiceItem extends Doc {
|
|||||||
formula: () => (this.rate as Money).mul(this.quantity as number),
|
formula: () => (this.rate as Money).mul(this.quantity as number),
|
||||||
dependsOn: ['item', 'rate', 'quantity'],
|
dependsOn: ['item', 'rate', 'quantity'],
|
||||||
},
|
},
|
||||||
baseAmount: {
|
|
||||||
formula: () =>
|
|
||||||
(this.amount as Money).mul(this.parentdoc!.exchangeRate as number),
|
|
||||||
dependsOn: ['item', 'amount', 'rate', 'quantity'],
|
|
||||||
},
|
|
||||||
hsnCode: {
|
hsnCode: {
|
||||||
formula: async () =>
|
formula: async () =>
|
||||||
await this.fyo.getValue('Item', this.item as string, 'hsnCode'),
|
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' };
|
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(
|
function getDiscountedTotalBeforeTaxation(
|
||||||
|
@ -87,7 +87,11 @@ export class Party extends Doc {
|
|||||||
dependsOn: ['role'],
|
dependsOn: ['role'],
|
||||||
},
|
},
|
||||||
currency: {
|
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[];
|
items?: PurchaseInvoiceItem[];
|
||||||
|
|
||||||
async getPosting() {
|
async getPosting() {
|
||||||
|
const exchangeRate = this.exchangeRate ?? 1;
|
||||||
const posting: LedgerPosting = new LedgerPosting(this, this.fyo);
|
const posting: LedgerPosting = new LedgerPosting(this, this.fyo);
|
||||||
await posting.credit(this.account!, this.baseGrandTotal!);
|
await posting.credit(this.account!, this.baseGrandTotal!);
|
||||||
|
|
||||||
for (const item of this.items!) {
|
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) {
|
if (this.taxes) {
|
||||||
for (const tax of 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
|
const discountAccount = this.fyo.singles.AccountingSettings
|
||||||
?.discountAccount as string | undefined;
|
?.discountAccount as string | undefined;
|
||||||
if (discountAccount && discountAmount.isPositive()) {
|
if (discountAccount && discountAmount.isPositive()) {
|
||||||
await posting.credit(discountAccount, discountAmount);
|
await posting.credit(discountAccount, discountAmount.mul(exchangeRate));
|
||||||
}
|
}
|
||||||
|
|
||||||
await posting.makeRoundOffEntry();
|
await posting.makeRoundOffEntry();
|
||||||
@ -46,7 +47,7 @@ export class PurchaseInvoice extends Invoice {
|
|||||||
getTransactionStatusColumn(),
|
getTransactionStatusColumn(),
|
||||||
'party',
|
'party',
|
||||||
'date',
|
'date',
|
||||||
'grandTotal',
|
'baseGrandTotal',
|
||||||
'outstandingAmount',
|
'outstandingAmount',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -10,16 +10,17 @@ export class SalesInvoice extends Invoice {
|
|||||||
items?: SalesInvoiceItem[];
|
items?: SalesInvoiceItem[];
|
||||||
|
|
||||||
async getPosting() {
|
async getPosting() {
|
||||||
|
const exchangeRate = this.exchangeRate ?? 1;
|
||||||
const posting: LedgerPosting = new LedgerPosting(this, this.fyo);
|
const posting: LedgerPosting = new LedgerPosting(this, this.fyo);
|
||||||
await posting.debit(this.account!, this.baseGrandTotal!);
|
await posting.debit(this.account!, this.baseGrandTotal!);
|
||||||
|
|
||||||
for (const item of this.items!) {
|
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) {
|
if (this.taxes) {
|
||||||
for (const tax of 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
|
const discountAccount = this.fyo.singles.AccountingSettings
|
||||||
?.discountAccount as string | undefined;
|
?.discountAccount as string | undefined;
|
||||||
if (discountAccount && discountAmount.isPositive()) {
|
if (discountAccount && discountAmount.isPositive()) {
|
||||||
await posting.debit(discountAccount, discountAmount);
|
await posting.debit(discountAccount, discountAmount.mul(exchangeRate));
|
||||||
}
|
}
|
||||||
|
|
||||||
await posting.makeRoundOffEntry();
|
await posting.makeRoundOffEntry();
|
||||||
@ -46,7 +47,7 @@ export class SalesInvoice extends Invoice {
|
|||||||
getTransactionStatusColumn(),
|
getTransactionStatusColumn(),
|
||||||
'party',
|
'party',
|
||||||
'date',
|
'date',
|
||||||
'grandTotal',
|
'baseGrandTotal',
|
||||||
'outstandingAmount',
|
'outstandingAmount',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -1,21 +1,48 @@
|
|||||||
|
import { Fyo } from 'fyo';
|
||||||
|
import { DocValueMap } from 'fyo/core/types';
|
||||||
import { Doc } from 'fyo/model/doc';
|
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 { Money } from 'pesa';
|
||||||
|
import { FieldTypeEnum, Schema } from 'schemas/types';
|
||||||
|
import { Invoice } from '../Invoice/Invoice';
|
||||||
|
|
||||||
export class TaxSummary extends Doc {
|
export class TaxSummary extends Doc {
|
||||||
account?: string;
|
account?: string;
|
||||||
rate?: number;
|
rate?: number;
|
||||||
amount?: Money;
|
amount?: Money;
|
||||||
baseAmount?: Money;
|
parentdoc?: Invoice;
|
||||||
|
|
||||||
formulas: FormulaMap = {
|
get exchangeRate() {
|
||||||
baseAmount: {
|
return this.parentdoc?.exchangeRate ?? 1;
|
||||||
formula: async () => {
|
}
|
||||||
const amount = this.amount as Money;
|
|
||||||
const exchangeRate = (this.parentdoc?.exchangeRate ?? 1) as number;
|
get currency() {
|
||||||
return amount.mul(exchangeRate);
|
return this.parentdoc?.currency ?? DEFAULT_CURRENCY;
|
||||||
},
|
}
|
||||||
dependsOn: ['amount'],
|
|
||||||
},
|
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 { Fyo, t } from 'fyo';
|
||||||
import { Doc } from 'fyo/model/doc';
|
import { Doc } from 'fyo/model/doc';
|
||||||
import { Action, ColumnConfig, DocStatus, RenderData } from 'fyo/model/types';
|
import { Action, ColumnConfig, DocStatus, RenderData } from 'fyo/model/types';
|
||||||
import { NotFoundError } from 'fyo/utils/errors';
|
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { Money } from 'pesa';
|
import { Money } from 'pesa';
|
||||||
import { Router } from 'vue-router';
|
import { Router } from 'vue-router';
|
||||||
import {
|
import {
|
||||||
AccountRootType,
|
AccountRootType,
|
||||||
AccountRootTypeEnum,
|
AccountRootTypeEnum
|
||||||
} from './baseModels/Account/types';
|
} from './baseModels/Account/types';
|
||||||
import { InvoiceStatus, ModelNameEnum } from './types';
|
import { InvoiceStatus, ModelNameEnum } from './types';
|
||||||
|
|
||||||
@ -196,14 +195,12 @@ export async function getExchangeRate({
|
|||||||
toCurrency: string;
|
toCurrency: string;
|
||||||
date?: string;
|
date?: string;
|
||||||
}) {
|
}) {
|
||||||
if (!date) {
|
if (!fetch) {
|
||||||
date = DateTime.local().toISODate();
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fromCurrency || !toCurrency) {
|
if (!date) {
|
||||||
throw new NotFoundError(
|
date = DateTime.local().toISODate();
|
||||||
'Please provide `fromCurrency` and `toCurrency` to get exchange rate.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheKey = `currencyExchangeRate:${date}:${fromCurrency}:${toCurrency}`;
|
const cacheKey = `currencyExchangeRate:${date}:${fromCurrency}:${toCurrency}`;
|
||||||
@ -215,27 +212,24 @@ export async function getExchangeRate({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!exchangeRate && fetch) {
|
if (exchangeRate && exchangeRate !== 1) {
|
||||||
|
return exchangeRate;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`https://api.vatcomply.com/rates?date=${date}&base=${fromCurrency}&symbols=${toCurrency}`
|
`https://api.vatcomply.com/rates?date=${date}&base=${fromCurrency}&symbols=${toCurrency}`
|
||||||
);
|
);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
exchangeRate = data.rates[toCurrency];
|
exchangeRate = data.rates[toCurrency];
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
exchangeRate ??= 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (localStorage) {
|
if (localStorage) {
|
||||||
localStorage.setItem(cacheKey, String(exchangeRate));
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return exchangeRate;
|
return exchangeRate;
|
||||||
}
|
}
|
||||||
@ -256,3 +250,6 @@ export function isCredit(rootType: AccountRootType) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
window.gex = getExchangeRate;
|
||||||
|
@ -218,6 +218,11 @@ async function generateB2bData(report: BaseGSTR): Promise<B2BCustomer[]> {
|
|||||||
? ModelNameEnum.SalesInvoiceItem
|
? ModelNameEnum.SalesInvoiceItem
|
||||||
: ModelNameEnum.PurchaseInvoiceItem;
|
: ModelNameEnum.PurchaseInvoiceItem;
|
||||||
|
|
||||||
|
const parentSchemaName =
|
||||||
|
report.gstrType === 'GSTR-1'
|
||||||
|
? ModelNameEnum.SalesInvoice
|
||||||
|
: ModelNameEnum.PurchaseInvoice;
|
||||||
|
|
||||||
for (const row of report.gstrRows ?? []) {
|
for (const row of report.gstrRows ?? []) {
|
||||||
const invRecord: B2BInvRecord = {
|
const invRecord: B2BInvRecord = {
|
||||||
inum: row.invNo,
|
inum: row.invNo,
|
||||||
@ -229,20 +234,29 @@ async function generateB2bData(report: BaseGSTR): Promise<B2BCustomer[]> {
|
|||||||
itms: [],
|
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, {
|
const items = await fyo.db.getAllRaw(schemaName, {
|
||||||
fields: ['baseAmount', 'tax', 'hsnCode'],
|
fields: ['amount', 'tax', 'hsnCode'],
|
||||||
filters: { parent: invRecord.inum as string },
|
filters: { parent: invRecord.inum },
|
||||||
});
|
});
|
||||||
|
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
const hsnCode = item.hsnCode as number;
|
const hsnCode = item.hsnCode as number;
|
||||||
const tax = item.tax as string;
|
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 = {
|
const itemRecord: B2BItmRecord = {
|
||||||
num: hsnCode,
|
num: hsnCode,
|
||||||
itm_det: {
|
itm_det: {
|
||||||
txval: fyo.pesa(baseAmount).float,
|
txval: baseAmount.float,
|
||||||
rt: GST[tax],
|
rt: GST[tax],
|
||||||
csamt: 0,
|
csamt: 0,
|
||||||
camt: fyo
|
camt: fyo
|
||||||
@ -292,6 +306,11 @@ async function generateB2clData(
|
|||||||
? ModelNameEnum.SalesInvoiceItem
|
? ModelNameEnum.SalesInvoiceItem
|
||||||
: ModelNameEnum.PurchaseInvoiceItem;
|
: ModelNameEnum.PurchaseInvoiceItem;
|
||||||
|
|
||||||
|
const parentSchemaName =
|
||||||
|
report.gstrType === 'GSTR-1'
|
||||||
|
? ModelNameEnum.SalesInvoice
|
||||||
|
: ModelNameEnum.PurchaseInvoice;
|
||||||
|
|
||||||
for (const row of report.gstrRows ?? []) {
|
for (const row of report.gstrRows ?? []) {
|
||||||
const invRecord: B2CLInvRecord = {
|
const invRecord: B2CLInvRecord = {
|
||||||
inum: row.invNo,
|
inum: row.invNo,
|
||||||
@ -300,20 +319,29 @@ async function generateB2clData(
|
|||||||
itms: [],
|
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, {
|
const items = await fyo.db.getAllRaw(schemaName, {
|
||||||
fields: ['hsnCode', 'tax', 'baseAmount'],
|
fields: ['amount', 'tax', 'hsnCode'],
|
||||||
filters: { parent: invRecord.inum },
|
filters: { parent: invRecord.inum },
|
||||||
});
|
});
|
||||||
|
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
const hsnCode = item.hsnCode as number;
|
const hsnCode = item.hsnCode as number;
|
||||||
const tax = item.tax as string;
|
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 = {
|
const itemRecord: B2CLItmRecord = {
|
||||||
num: hsnCode,
|
num: hsnCode,
|
||||||
itm_det: {
|
itm_det: {
|
||||||
txval: fyo.pesa(baseAmount).float,
|
txval: baseAmount.float,
|
||||||
rt: GST[tax] ?? 0,
|
rt: GST[tax] ?? 0,
|
||||||
csamt: 0,
|
csamt: 0,
|
||||||
iamt: fyo
|
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",
|
"name": "PurchaseInvoice",
|
||||||
"label": "Purchase Invoice",
|
"label": "Purchase Invoice",
|
||||||
|
"extends": "Invoice",
|
||||||
"naming": "numberSeries",
|
"naming": "numberSeries",
|
||||||
"isSingle": false,
|
|
||||||
"isChild": false,
|
|
||||||
"isSubmittable": true,
|
|
||||||
"showTitle": true,
|
"showTitle": true,
|
||||||
"fields": [
|
"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",
|
"fieldname": "items",
|
||||||
"label": "Items",
|
"label": "Items",
|
||||||
@ -57,75 +13,6 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"edit": 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",
|
"fieldname": "numberSeries",
|
||||||
"label": "Number Series",
|
"label": "Number Series",
|
||||||
|
@ -1,124 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "PurchaseInvoiceItem",
|
"name": "PurchaseInvoiceItem",
|
||||||
"label": "Purchase Invoice Item",
|
"label": "Purchase Invoice Item",
|
||||||
"isChild": true,
|
"extends": "InvoiceItem"
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -1,53 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "SalesInvoice",
|
"name": "SalesInvoice",
|
||||||
"label": "Sales Invoice",
|
"label": "Sales Invoice",
|
||||||
|
"extends": "Invoice",
|
||||||
"naming": "numberSeries",
|
"naming": "numberSeries",
|
||||||
"isSingle": false,
|
"showTitle": true,
|
||||||
"isChild": false,
|
|
||||||
"isSubmittable": true,
|
|
||||||
"fields": [
|
"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",
|
"fieldname": "items",
|
||||||
"label": "Items",
|
"label": "Items",
|
||||||
@ -56,75 +13,6 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"edit": 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",
|
"fieldname": "numberSeries",
|
||||||
"label": "Number Series",
|
"label": "Number Series",
|
||||||
|
@ -1,120 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "SalesInvoiceItem",
|
"name": "SalesInvoiceItem",
|
||||||
"label": "Sales Invoice Item",
|
"label": "Sales Invoice Item",
|
||||||
"isChild": true,
|
"extends": "InvoiceItem"
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,6 @@
|
|||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "baseAmount",
|
|
||||||
"label": "Amount (Company Currency)",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"readOnly": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -88,6 +88,7 @@
|
|||||||
"label": "Currency",
|
"label": "Currency",
|
||||||
"fieldtype": "AutoComplete",
|
"fieldtype": "AutoComplete",
|
||||||
"default": "INR",
|
"default": "INR",
|
||||||
|
"readOnly": true,
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,8 @@ import Color from './app/Color.json';
|
|||||||
import CompanySettings from './app/CompanySettings.json';
|
import CompanySettings from './app/CompanySettings.json';
|
||||||
import Currency from './app/Currency.json';
|
import Currency from './app/Currency.json';
|
||||||
import GetStarted from './app/GetStarted.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 Item from './app/Item.json';
|
||||||
import JournalEntry from './app/JournalEntry.json';
|
import JournalEntry from './app/JournalEntry.json';
|
||||||
import JournalEntryAccount from './app/JournalEntryAccount.json';
|
import JournalEntryAccount from './app/JournalEntryAccount.json';
|
||||||
@ -73,11 +75,13 @@ export const appSchemas: Schema[] | SchemaStub[] = [
|
|||||||
JournalEntry as Schema,
|
JournalEntry as Schema,
|
||||||
JournalEntryAccount as Schema,
|
JournalEntryAccount as Schema,
|
||||||
|
|
||||||
PurchaseInvoice as Schema,
|
Invoice as Schema,
|
||||||
PurchaseInvoiceItem as Schema,
|
|
||||||
|
|
||||||
SalesInvoice as Schema,
|
SalesInvoice as Schema,
|
||||||
SalesInvoiceItem as Schema,
|
PurchaseInvoice as Schema,
|
||||||
|
|
||||||
|
InvoiceItem as Schema,
|
||||||
|
SalesInvoiceItem as SchemaStub,
|
||||||
|
PurchaseInvoiceItem as SchemaStub,
|
||||||
|
|
||||||
Tax as Schema,
|
Tax as Schema,
|
||||||
TaxDetail as Schema,
|
TaxDetail as Schema,
|
||||||
|
@ -151,13 +151,16 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getInputClassesFromProp(classes) {
|
getInputClassesFromProp(classes) {
|
||||||
if (this.inputClass) {
|
if (!this.inputClass) {
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof this.inputClass === 'function') {
|
if (typeof this.inputClass === 'function') {
|
||||||
classes = this.inputClass(classes);
|
classes = this.inputClass(classes);
|
||||||
} else {
|
} else {
|
||||||
classes.push(this.inputClass);
|
classes.push(this.inputClass);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return classes;
|
return classes;
|
||||||
},
|
},
|
||||||
focus() {
|
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>
|
<script>
|
||||||
import { fyo } from 'src/initFyo';
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Base',
|
name: 'Base',
|
||||||
props: { doc: Object, printSettings: Object },
|
props: { doc: Object, printSettings: Object },
|
||||||
data: () => ({ party: null, companyAddress: null, partyAddress: null }),
|
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() {
|
async mounted() {
|
||||||
await this.printSettings.loadLink('address');
|
await this.printSettings.loadLink('address');
|
||||||
this.companyAddress = this.printSettings.getLink('address');
|
this.companyAddress = this.printSettings.getLink('address');
|
||||||
@ -17,8 +10,49 @@ export default {
|
|||||||
await this.doc.loadLink('party');
|
await this.doc.loadLink('party');
|
||||||
this.party = this.doc.getLink('party');
|
this.party = this.doc.getLink('party');
|
||||||
this.partyAddress = this.party.getLink('address')?.addressDisplay ?? null;
|
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: {
|
computed: {
|
||||||
|
currency() {
|
||||||
|
return this.doc.isMultiCurrency
|
||||||
|
? this.doc.currency
|
||||||
|
: this.fyo.singles.SystemSettings.currency;
|
||||||
|
},
|
||||||
isSalesInvoice() {
|
isSalesInvoice() {
|
||||||
return this.doc.schemaName === 'SalesInvoice';
|
return this.doc.schemaName === 'SalesInvoice';
|
||||||
},
|
},
|
||||||
@ -28,6 +62,46 @@ export default {
|
|||||||
totalDiscount() {
|
totalDiscount() {
|
||||||
return this.doc.getTotalDiscount();
|
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>
|
</script>
|
||||||
|
@ -1,36 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="bg-white border h-full"
|
class="bg-white border h-full"
|
||||||
:style="{ 'font-family': printSettings.font }"
|
:style="{ 'font-family': printObject.font }"
|
||||||
>
|
>
|
||||||
<div>
|
<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="flex text-sm text-gray-900 border-b pb-4">
|
||||||
<div class="w-1/3">
|
<div class="w-1/3">
|
||||||
<div v-if="printSettings.displayLogo">
|
<div v-if="printObject.displayLogo">
|
||||||
<img
|
<img
|
||||||
class="h-12 max-w-32 object-contain"
|
class="h-12 max-w-32 object-contain"
|
||||||
:src="printSettings.logo"
|
:src="printObject.logo"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xl text-gray-700 font-semibold" v-else>
|
<div class="text-xl text-gray-700 font-semibold" v-else>
|
||||||
{{ fyo.singles.AccountingSettings.companyName }}
|
{{ printObject.companyName }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-1/3">
|
<div class="w-1/3">
|
||||||
<div>{{ printSettings.email }}</div>
|
<div>{{ printObject.email }}</div>
|
||||||
<div class="mt-1">{{ printSettings.phone }}</div>
|
<div class="mt-1">{{ printObject.phone }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-1/3">
|
<div class="w-1/3">
|
||||||
<div v-if="companyAddress">{{ companyAddress.addressDisplay }}</div>
|
<div v-if="printObject.address">
|
||||||
<div
|
{{ printObject.address }}
|
||||||
v-if="
|
|
||||||
fyo.singles.AccountingSettings &&
|
|
||||||
fyo.singles.AccountingSettings.gstin
|
|
||||||
"
|
|
||||||
>
|
|
||||||
GSTIN: {{ fyo.singles.AccountingSettings.gstin }}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="printObject.gstin">GSTIN: {{ printObject.gstin }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -38,27 +33,27 @@
|
|||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<div class="w-1/3">
|
<div class="w-1/3">
|
||||||
<h1 class="text-2xl font-semibold">
|
<h1 class="text-2xl font-semibold">
|
||||||
{{ doc.name }}
|
{{ printObject.invoiceName }}
|
||||||
</h1>
|
</h1>
|
||||||
<div class="py-2 text-base">
|
<div class="py-2 text-base">
|
||||||
{{ fyo.format(doc.date, 'Date') }}
|
{{ printObject.date }}
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<div class="py-1 text-right text-lg font-semibold">
|
||||||
{{ party.name }}
|
{{ printObject.partyName }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="partyAddress"
|
v-if="printObject.partyAddress"
|
||||||
class="mt-1 text-xs text-gray-600 text-right"
|
class="mt-1 text-xs text-gray-600 text-right"
|
||||||
>
|
>
|
||||||
{{ partyAddress }}
|
{{ printObject.partyAddress }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="party && party.gstin"
|
v-if="printObject.partyGSTIN"
|
||||||
class="mt-1 text-xs text-gray-600 text-right"
|
class="mt-1 text-xs text-gray-600 text-right"
|
||||||
>
|
>
|
||||||
GSTIN: {{ party.gstin }}
|
GSTIN: {{ printObject.partyGSTIN }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -67,64 +62,67 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="text-gray-600 w-full flex border-b">
|
<div class="text-gray-600 w-full flex border-b">
|
||||||
<div class="py-4 w-5/12">Item</div>
|
<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-1/12">Quantity</div>
|
||||||
<div class="py-4 text-right w-3/12">Rate</div>
|
<div class="py-4 text-right w-3/12">Rate</div>
|
||||||
<div class="py-4 text-right w-3/12">Amount</div>
|
<div class="py-4 text-right w-3/12">Amount</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex py-1 text-gray-900 w-full border-b"
|
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"
|
:key="row.name"
|
||||||
>
|
>
|
||||||
<div class="w-5/12 py-4">{{ row.item }}</div>
|
<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 }}
|
{{ row.hsnCode }}
|
||||||
</div>
|
</div>
|
||||||
<div class="w-1/12 text-right py-4">
|
<div class="w-1/12 text-right py-4">{{ row.quantity }}</div>
|
||||||
{{ format(row, 'quantity') }}
|
<div class="w-3/12 text-right py-4">{{ row.rate }}</div>
|
||||||
</div>
|
<div class="w-3/12 text-right py-4">{{ row.amount }}</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-6 mt-2 flex justify-end text-base">
|
<div class="px-6 mt-2 flex justify-end text-base">
|
||||||
<div class="w-1/2 bg-pink">
|
<div class="w-1/2">
|
||||||
<div class="text-sm tracking-widest text-gray-600 mt-2">Notes</div>
|
<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">
|
<div class="my-4 text-lg whitespace-pre-line">
|
||||||
{{ doc.terms }}
|
{{ printObject.terms }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-1/2">
|
<div class="w-1/2">
|
||||||
<div class="flex pl-2 justify-between py-3 border-b">
|
<div class="flex pl-2 justify-between py-3 border-b">
|
||||||
<div>{{ t`Subtotal` }}</div>
|
<div>{{ t`Subtotal` }}</div>
|
||||||
<div>{{ fyo.format(doc.netTotal, 'Currency') }}</div>
|
<div>{{ printObject.netTotal }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex pl-2 justify-between py-3 border-b"
|
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>{{ t`Discount` }}</div>
|
||||||
<div>{{ `- ${fyo.format(totalDiscount, 'Currency')}` }}</div>
|
<div>{{ printObject.totalDiscount }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex pl-2 justify-between py-3"
|
class="flex pl-2 justify-between py-3"
|
||||||
v-for="tax in doc.taxes"
|
v-for="tax in printObject.taxes"
|
||||||
:key="tax.name"
|
:key="tax.name"
|
||||||
>
|
>
|
||||||
<div>{{ tax.account }}</div>
|
<div>{{ tax.account }}</div>
|
||||||
<div>{{ fyo.format(tax.amount, 'Currency') }}</div>
|
<div>{{ tax.amount }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex pl-2 justify-between py-3 border-t"
|
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>{{ t`Discount` }}</div>
|
||||||
<div>{{ `- ${fyo.format(totalDiscount, 'Currency')}` }}</div>
|
<div>{{ printObject.totalDiscount }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
@ -139,7 +137,7 @@
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div>{{ t`Grand Total` }}</div>
|
<div>{{ t`Grand Total` }}</div>
|
||||||
<div>{{ fyo.format(doc.grandTotal, 'Currency') }}</div>
|
<div>{{ printObject.grandTotal }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -147,11 +145,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Base from './BaseTemplate.vue';
|
import BaseTemplate from './BaseTemplate.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Default',
|
name: 'Default',
|
||||||
extends: Base,
|
extends: BaseTemplate,
|
||||||
props: ['doc', 'printSettings'],
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,65 +1,58 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="bg-white border h-full"
|
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="bg-gray-100 px-12 py-10">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex items-center rounded h-16">
|
<div class="flex items-center rounded h-16">
|
||||||
<div class="mr-4" v-if="printSettings.displayLogo">
|
<div class="mr-4" v-if="printObject.displayLogo">
|
||||||
<img
|
<img class="h-12 max-w-32 object-contain" :src="printObject.logo" />
|
||||||
class="h-12 max-w-32 object-contain"
|
|
||||||
:src="printSettings.logo"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="font-semibold text-xl"
|
class="font-semibold text-xl"
|
||||||
:style="{ color: printSettings.color }"
|
:style="{ color: printObject.color }"
|
||||||
>
|
>
|
||||||
{{ fyo.singles.AccountingSettings.companyName }}
|
{{ printObject.companyName }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm text-gray-800" v-if="companyAddress">
|
<div class="text-sm text-gray-800" v-if="printObject.address">
|
||||||
{{ companyAddress.addressDisplay }}
|
{{ printObject.address }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="text-sm text-gray-800" v-if="printObject.gstin">
|
||||||
class="text-sm text-gray-800"
|
GSTIN: {{ printObject.gstin }}
|
||||||
v-if="
|
|
||||||
fyo.singles.AccountingSettings &&
|
|
||||||
fyo.singles.AccountingSettings.gstin
|
|
||||||
"
|
|
||||||
>
|
|
||||||
GSTIN: {{ fyo.singles.AccountingSettings.gstin }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-8 text-lg">
|
<div class="mt-8 text-lg">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="w-1/3 font-semibold">
|
<div class="w-1/3 font-semibold">
|
||||||
{{ isSalesInvoice ? 'Invoice' : 'Bill' }}
|
{{ printObject.isSalesInvoice ? 'Invoice' : 'Bill' }}
|
||||||
</div>
|
</div>
|
||||||
<div class="w-2/3 text-gray-800">
|
<div class="w-2/3 text-gray-800">
|
||||||
<div class="font-semibold">
|
<div class="font-semibold">
|
||||||
{{ doc.name }}
|
{{ printObject.invoiceName }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{ fyo.format(doc.date, 'Date') }}
|
{{ printObject.date }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 flex">
|
<div class="mt-4 flex">
|
||||||
<div class="w-1/3 font-semibold">
|
<div class="w-1/3 font-semibold">
|
||||||
{{ isSalesInvoice ? 'Customer' : 'Supplier' }}
|
{{ printObject.isSalesInvoice ? 'Customer' : 'Supplier' }}
|
||||||
</div>
|
</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">
|
<div class="font-semibold">
|
||||||
{{ party.name }}
|
{{ printObject.partyName }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="partyAddress">
|
<div v-if="printObject.partyAddress">
|
||||||
{{ partyAddress }}
|
{{ printObject.partyAddress }}
|
||||||
|
</div>
|
||||||
|
<div v-if="printObject.partyGSTIN">
|
||||||
|
GSTIN: {{ printObject.partyGSTIN }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="party && party.gstin">GSTIN: {{ party.gstin }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -67,21 +60,23 @@
|
|||||||
<div class="px-12 py-12 text-lg">
|
<div class="px-12 py-12 text-lg">
|
||||||
<div class="mb-4 flex font-semibold">
|
<div class="mb-4 flex font-semibold">
|
||||||
<div class="w-4/12">Item</div>
|
<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-2/12 text-right">Quantity</div>
|
||||||
<div class="w-3/12 text-right">Rate</div>
|
<div class="w-3/12 text-right">Rate</div>
|
||||||
<div class="w-3/12 text-right">Amount</div>
|
<div class="w-3/12 text-right">Amount</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex py-1 text-gray-800"
|
class="flex py-1 text-gray-800"
|
||||||
v-for="row in doc.items"
|
v-for="row in printObject.items"
|
||||||
:key="row.name"
|
:key="row.name"
|
||||||
>
|
>
|
||||||
<div class="w-4/12">{{ row.item }}</div>
|
<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" v-if="printObject.showHSN">
|
||||||
<div class="w-2/12 text-right">{{ format(row, 'quantity') }}</div>
|
{{ row.hsnCode }}
|
||||||
<div class="w-3/12 text-right">{{ format(row, 'rate') }}</div>
|
</div>
|
||||||
<div class="w-3/12 text-right">{{ format(row, 'amount') }}</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="mt-12">
|
<div class="mt-12">
|
||||||
<div class="flex -mx-3">
|
<div class="flex -mx-3">
|
||||||
@ -89,67 +84,70 @@
|
|||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<div class="text-gray-800">{{ t`Subtotal` }}</div>
|
<div class="text-gray-800">{{ t`Subtotal` }}</div>
|
||||||
<div class="text-xl mt-2">
|
<div class="text-xl mt-2">
|
||||||
{{ fyo.format(doc.netTotal, 'Currency') }}
|
{{ printObject.netTotal }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="text-right"
|
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-gray-800">{{ t`Discount` }}</div>
|
||||||
<div class="text-xl mt-2">
|
<div class="text-xl mt-2">
|
||||||
{{ `- ${fyo.format(totalDiscount, 'Currency')}` }}
|
{{ printObject.totalDiscount }}
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<div class="text-gray-800">
|
||||||
{{ tax.account }}
|
{{ tax.account }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xl mt-2">
|
<div class="text-xl mt-2">
|
||||||
{{ fyo.format(tax.amount, 'Currency') }}
|
{{ tax.amount }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="text-right"
|
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-gray-800">{{ t`Discount` }}</div>
|
||||||
<div class="text-xl mt-2">
|
<div class="text-xl mt-2">
|
||||||
{{ `- ${fyo.format(totalDiscount, 'Currency')}` }}
|
{{ printObject.totalDiscount }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="py-3 px-4 text-right text-white"
|
class="py-3 px-4 text-right text-white"
|
||||||
:style="{ backgroundColor: printSettings.color }"
|
:style="{ backgroundColor: printObject.color }"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div>{{ t`Grand Total` }}</div>
|
<div>{{ t`Grand Total` }}</div>
|
||||||
<div class="text-2xl mt-2 font-semibold">
|
<div class="text-2xl mt-2 font-semibold">
|
||||||
{{ fyo.format(doc.grandTotal, 'Currency') }}
|
{{ printObject.grandTotal }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</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="text-lg font-semibold">Notes</div>
|
||||||
<div class="mt-4 text-lg whitespace-pre-line">
|
<div class="mt-4 text-lg whitespace-pre-line">
|
||||||
{{ doc.terms }}
|
{{ printObject.terms }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Base from './BaseTemplate.vue';
|
import BaseTemplate from './BaseTemplate.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Business',
|
name: 'Business',
|
||||||
extends: Base,
|
extends: BaseTemplate,
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,65 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="bg-white border h-full"
|
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 justify-between px-12 py-10 border-b">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex items-center rounded h-16">
|
<div class="flex items-center rounded h-16">
|
||||||
<div class="mr-4" v-if="printSettings.displayLogo">
|
<div class="mr-4" v-if="printObject.displayLogo">
|
||||||
<img
|
<img class="h-12 max-w-32 object-contain" :src="printObject.logo" />
|
||||||
class="h-12 max-w-32 object-contain"
|
|
||||||
:src="printSettings.logo"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="font-semibold text-xl"
|
class="font-semibold text-xl"
|
||||||
:style="{ color: printSettings.color }"
|
:style="{ color: printObject.color }"
|
||||||
>
|
>
|
||||||
{{ fyo.singles.AccountingSettings.companyName }}
|
{{ printObject.companyName }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{ fyo.format(doc.date, 'Date') }}
|
{{ printObject.date }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<div
|
<div
|
||||||
class="font-semibold text-xl"
|
class="font-semibold text-xl"
|
||||||
:style="{ color: printSettings.color }"
|
:style="{ color: printObject.color }"
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
doc.schemaName === 'SalesInvoice'
|
printObject.isSalesInvoice ? t`Sales Invoice` : t`Purchase Invoice`
|
||||||
? t`Sales Invoice`
|
|
||||||
: t`Purchase Invoice`
|
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{ doc.name }}
|
{{ printObject.invoiceName }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex px-12 py-10 border-b">
|
<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
|
<div
|
||||||
class="uppercase text-sm font-semibold tracking-widest text-gray-800"
|
class="uppercase text-sm font-semibold tracking-widest text-gray-800"
|
||||||
>
|
>
|
||||||
{{ isSalesInvoice ? 'To' : 'From' }}
|
{{ printObject.isSalesInvoice ? 'To' : 'From' }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 text-black leading-relaxed text-lg">
|
<div class="mt-4 text-black leading-relaxed text-lg">
|
||||||
{{ party.name }} <br />
|
{{ printObject.partyName }} <br />
|
||||||
{{ partyAddress ? partyAddress : '' }}
|
{{ printObject.partyAddress ?? '' }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mt-4 text-black leading-relaxed text-lg"
|
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>
|
</div>
|
||||||
<div class="w-1/2" v-if="companyAddress">
|
<div class="w-1/2" v-if="printObject.address">
|
||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
uppercase
|
uppercase
|
||||||
@ -70,19 +65,16 @@
|
|||||||
ml-8
|
ml-8
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{{ isSalesInvoice ? 'From' : 'To' }}
|
{{ printObject.isSalesInvoice ? 'From' : 'To' }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 ml-8 text-black leading-relaxed text-lg">
|
<div class="mt-4 ml-8 text-black leading-relaxed text-lg">
|
||||||
{{ companyAddress.addressDisplay }}
|
{{ printObject.address }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mt-4 ml-8 text-black leading-relaxed text-lg"
|
class="mt-4 ml-8 text-black leading-relaxed text-lg"
|
||||||
v-if="
|
v-if="printObject.gstin"
|
||||||
fyo.singles.AccountingSettings &&
|
|
||||||
fyo.singles.AccountingSettings.gstin
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
GSTIN: {{ fyo.singles.AccountingSettings.gstin }}
|
GSTIN: {{ printObject.gstin }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -99,74 +91,79 @@
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div class="w-4/12">Item</div>
|
<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-2/12 text-right">Quantity</div>
|
||||||
<div class="w-3/12 text-right">Rate</div>
|
<div class="w-3/12 text-right">Rate</div>
|
||||||
<div class="w-3/12 text-right">Amount</div>
|
<div class="w-3/12 text-right">Amount</div>
|
||||||
</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-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" v-if="printObject.showHSN">
|
||||||
<div class="w-2/12 text-right">{{ format(row, 'quantity') }}</div>
|
{{ row.hsnCode }}
|
||||||
<div class="w-3/12 text-right">{{ format(row, 'rate') }}</div>
|
</div>
|
||||||
<div class="w-3/12 text-right">{{ format(row, 'amount') }}</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>
|
</div>
|
||||||
<div class="flex px-12 py-10">
|
<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
|
<div
|
||||||
class="uppercase text-sm tracking-widest font-semibold text-gray-800"
|
class="uppercase text-sm tracking-widest font-semibold text-gray-800"
|
||||||
>
|
>
|
||||||
Notes
|
Notes
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 text-lg whitespace-pre-line">
|
<div class="mt-4 text-lg whitespace-pre-line">
|
||||||
{{ doc.terms }}
|
{{ printObject.terms }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-1/2 text-lg">
|
<div class="w-1/2 text-lg">
|
||||||
<div class="flex pl-2 justify-between py-1">
|
<div class="flex pl-2 justify-between py-1">
|
||||||
<div>{{ t`Subtotal` }}</div>
|
<div>{{ t`Subtotal` }}</div>
|
||||||
<div>{{ fyo.format(doc.netTotal, 'Currency') }}</div>
|
<div>{{ printObject.netTotal }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex pl-2 justify-between py-1"
|
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>{{ t`Discount` }}</div>
|
||||||
<div>{{ `- ${fyo.format(totalDiscount, 'Currency')}` }}</div>
|
<div>{{ printObject.totalDiscount }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex pl-2 justify-between py-1"
|
class="flex pl-2 justify-between py-1"
|
||||||
v-for="tax in doc.taxes"
|
v-for="tax in printObject.taxes"
|
||||||
:key="tax.name"
|
:key="tax.name"
|
||||||
>
|
>
|
||||||
<div>{{ tax.account }}</div>
|
<div>{{ tax.account }}</div>
|
||||||
<div>{{ fyo.format(tax.amount, 'Currency') }}</div>
|
<div>{{ tax.amount }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex pl-2 justify-between py-1"
|
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>{{ t`Discount` }}</div>
|
||||||
<div>{{ `- ${fyo.format(totalDiscount, 'Currency')}` }}</div>
|
<div>{{ printObject.totalDiscount }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex pl-2 justify-between py-1 font-semibold"
|
class="flex pl-2 justify-between py-1 font-semibold"
|
||||||
:style="{ color: printSettings.color }"
|
:style="{ color: printObject.color }"
|
||||||
>
|
>
|
||||||
<div>{{ t`Grand Total` }}</div>
|
<div>{{ t`Grand Total` }}</div>
|
||||||
<div>{{ fyo.format(doc.grandTotal, 'Currency') }}</div>
|
<div>{{ printObject.grandTotal }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Base from './BaseTemplate.vue';
|
import BaseTemplate from './BaseTemplate.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Minimal',
|
name: 'Minimal',
|
||||||
extends: Base,
|
extends: BaseTemplate,
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -3,6 +3,16 @@
|
|||||||
<!-- Page Header (Title, Buttons, etc) -->
|
<!-- Page Header (Title, Buttons, etc) -->
|
||||||
<template #header v-if="doc">
|
<template #header v-if="doc">
|
||||||
<StatusBadge :status="status" />
|
<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
|
<Button
|
||||||
v-if="!doc.isCancelled && !doc.dirty"
|
v-if="!doc.isCancelled && !doc.dirty"
|
||||||
:icon="true"
|
:icon="true"
|
||||||
@ -67,8 +77,8 @@
|
|||||||
:border="true"
|
:border="true"
|
||||||
:df="getField('party')"
|
:df="getField('party')"
|
||||||
:value="doc.party"
|
:value="doc.party"
|
||||||
@change="(value) => doc.set('party', value)"
|
@change="(value) => doc.set('party', value, true)"
|
||||||
@new-doc="(party) => doc.set('party', party.name)"
|
@new-doc="(party) => doc.set('party', party.name, true)"
|
||||||
:read-only="doc?.submitted"
|
:read-only="doc?.submitted"
|
||||||
/>
|
/>
|
||||||
<FormControl
|
<FormControl
|
||||||
@ -175,10 +185,14 @@
|
|||||||
<div>{{ tax.account }}</div>
|
<div>{{ tax.account }}</div>
|
||||||
<div>
|
<div>
|
||||||
{{
|
{{
|
||||||
fyo.format(tax.amount, {
|
fyo.format(
|
||||||
|
tax.amount,
|
||||||
|
{
|
||||||
fieldtype: 'Currency',
|
fieldtype: 'Currency',
|
||||||
currency: doc.currency,
|
fieldname: 'amount',
|
||||||
})
|
},
|
||||||
|
tax
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -220,6 +234,21 @@
|
|||||||
<div>{{ formattedValue('grandTotal') }}</div>
|
<div>{{ formattedValue('grandTotal') }}</div>
|
||||||
</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 -->
|
<!-- Outstanding Amount -->
|
||||||
<hr v-if="doc.outstandingAmount?.float > 0" />
|
<hr v-if="doc.outstandingAmount?.float > 0" />
|
||||||
<div
|
<div
|
||||||
@ -256,6 +285,7 @@ import { computed } from '@vue/reactivity';
|
|||||||
import { getDocStatus } from 'models/helpers';
|
import { getDocStatus } from 'models/helpers';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import Button from 'src/components/Button.vue';
|
import Button from 'src/components/Button.vue';
|
||||||
|
import ExchangeRate from 'src/components/Controls/ExchangeRate.vue';
|
||||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||||
import Table from 'src/components/Controls/Table.vue';
|
import Table from 'src/components/Controls/Table.vue';
|
||||||
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
||||||
@ -284,6 +314,7 @@ export default {
|
|||||||
Table,
|
Table,
|
||||||
FormContainer,
|
FormContainer,
|
||||||
QuickEditForm,
|
QuickEditForm,
|
||||||
|
ExchangeRate,
|
||||||
},
|
},
|
||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
@ -342,6 +373,12 @@ export default {
|
|||||||
itemDiscountAmount() {
|
itemDiscountAmount() {
|
||||||
return this.doc.getItemDiscountAmount();
|
return this.doc.getItemDiscountAmount();
|
||||||
},
|
},
|
||||||
|
fromCurrency() {
|
||||||
|
return this.doc?.currency ?? this.toCurrency;
|
||||||
|
},
|
||||||
|
toCurrency() {
|
||||||
|
return fyo.singles.SystemSettings.currency;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
activated() {
|
activated() {
|
||||||
docsPath.value = docsPathMap[this.schemaName];
|
docsPath.value = docsPathMap[this.schemaName];
|
||||||
|
@ -56,6 +56,12 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<TwoColumnForm :doc="doc" :read-only="loading" />
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #secondaryButton>{{ t`Cancel` }}</template>
|
<template #secondaryButton>{{ t`Cancel` }}</template>
|
||||||
@ -67,6 +73,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Button from 'src/components/Button.vue';
|
||||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||||
import TwoColumnForm from 'src/components/TwoColumnForm.vue';
|
import TwoColumnForm from 'src/components/TwoColumnForm.vue';
|
||||||
import { getErrorMessage } from 'src/utils';
|
import { getErrorMessage } from 'src/utils';
|
||||||
@ -95,6 +102,7 @@ export default {
|
|||||||
TwoColumnForm,
|
TwoColumnForm,
|
||||||
FormControl,
|
FormControl,
|
||||||
Slide,
|
Slide,
|
||||||
|
Button,
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.doc = await getSetupWizardDoc();
|
this.doc = await getSetupWizardDoc();
|
||||||
@ -103,6 +111,13 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
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) {
|
getField(fieldname) {
|
||||||
return this.doc.schema?.fields.find((f) => f.fieldname === fieldname);
|
return this.doc.schema?.fields.find((f) => f.fieldname === fieldname);
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user