mirror of
https://github.com/frappe/books.git
synced 2024-09-20 03:29:00 +00:00
incr: complete typing models
- fix model exports - add a README for models base
This commit is contained in:
parent
91bf6e03fa
commit
591e7b3163
@ -33,6 +33,7 @@ import {
|
||||
EmptyMessageMap,
|
||||
FiltersMap,
|
||||
FormulaMap,
|
||||
FormulaReturn,
|
||||
HiddenMap,
|
||||
ListsMap,
|
||||
ListViewSettings,
|
||||
@ -507,7 +508,7 @@ export default class Doc extends Observable<DocValue | Doc[]> {
|
||||
}
|
||||
|
||||
async getValueFromFormula(field: Field, doc: Doc) {
|
||||
let value: Doc[] | DocValue | undefined;
|
||||
let value: FormulaReturn;
|
||||
|
||||
const formula = doc.formulas[field.fieldtype];
|
||||
if (formula === undefined) {
|
||||
@ -515,7 +516,6 @@ export default class Doc extends Observable<DocValue | Doc[]> {
|
||||
}
|
||||
|
||||
value = await formula();
|
||||
|
||||
if (Array.isArray(value) && field.fieldtype === FieldTypeEnum.Table) {
|
||||
value = value.map((row) => this._initChild(row, field.fieldname));
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { DocValue } from 'frappe/core/types';
|
||||
import { DocValue, DocValueMap } from 'frappe/core/types';
|
||||
import { FieldType } from 'schemas/types';
|
||||
import { QueryFilter } from 'utils/db/types';
|
||||
import { Router } from 'vue-router';
|
||||
@ -16,7 +16,8 @@ import Doc from './doc';
|
||||
* - `Validation`: Async function that throw an error if the value is invalid.
|
||||
* - `Required`: Regular function used to decide if a value is mandatory (there are !notnul in the db).
|
||||
*/
|
||||
export type Formula = () => Promise<DocValue | undefined>;
|
||||
export type FormulaReturn = DocValue | DocValueMap[] | undefined | Doc[];
|
||||
export type Formula = () => Promise<FormulaReturn> | FormulaReturn;
|
||||
export type Default = () => DocValue;
|
||||
export type Validation = (value: DocValue) => Promise<void>;
|
||||
export type Required = () => boolean;
|
||||
|
32
models/README.md
Normal file
32
models/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Models
|
||||
|
||||
The `models` root folder contains all the model files, i.e. files containing all the models. **Models** here, refers to the classes that handle the data, its validation and updation and a bunch of other stuff.
|
||||
|
||||
Each model directly or indirectly extends the `Doc` class from `frappe/model/doc.ts` so for more info check that file and the associated types in `frappe/model/types.ts`.
|
||||
|
||||
A model class can used even if the class body has no content, for example `PurchaseInvoiceItem`. Else the model used will default to using `Doc`. The class can also be used to provide type information for the field types else they default to the catch all `DocValue` example:
|
||||
|
||||
```typescript
|
||||
class Todo extends Doc {
|
||||
title?: string;
|
||||
date?: Date;
|
||||
completed?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
While this has obvious advantages, the drawback is if the underlying fieldtype changes this too will have to be changed.
|
||||
|
||||
## Adding Stuff
|
||||
|
||||
When adding stuff to `models/**` make sure that it isn't importing any Vue code or other frontend specific code globally. This is cause model file tests will directly use the the `Frappe` class and will be run using `mocha` on `node`.
|
||||
|
||||
Importing frontend code will break all the tests. This also implies that one should be wary about transitive dependencies.
|
||||
|
||||
_Note: Frontend specific code can be imported but they should be done so, only using dynamic imports i.e. `await import('...')`._
|
||||
|
||||
## Regional Models
|
||||
|
||||
Regional models should as far as possible extend the base model and override what's required.
|
||||
|
||||
They should then be imported dynamicall and returned from `getRegionalModels` in `models/index.ts` on the basis of `countryCode`.
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
} from 'frappe/model/types';
|
||||
import { QueryFilter } from 'utils/db/types';
|
||||
|
||||
export default class Account extends Doc {
|
||||
export class Account extends Doc {
|
||||
async beforeInsert() {
|
||||
if (this.accountType || !this.parentAccount) {
|
||||
return;
|
||||
|
@ -1,21 +1,36 @@
|
||||
import { LedgerPosting } from 'accounting/ledgerPosting';
|
||||
import frappe from 'frappe';
|
||||
import { DocValue } from 'frappe/core/types';
|
||||
import Doc from 'frappe/model/doc';
|
||||
import { DefaultMap, FiltersMap, FormulaMap } from 'frappe/model/types';
|
||||
import Money from 'pesa/dist/types/src/money';
|
||||
import { getExchangeRate } from '../../../accounting/exchangeRate';
|
||||
import { Party } from '../Party/Party';
|
||||
import { Payment } from '../Payment/Payment';
|
||||
import { Tax } from '../Tax/Tax';
|
||||
import { TaxSummary } from '../TaxSummary/TaxSummary';
|
||||
|
||||
export abstract class Transaction extends Doc {
|
||||
export abstract class Invoice extends Doc {
|
||||
_taxes: Record<string, Tax> = {};
|
||||
taxes?: TaxSummary[];
|
||||
|
||||
abstract getPosting(): LedgerPosting;
|
||||
party?: string;
|
||||
account?: string;
|
||||
currency?: string;
|
||||
netTotal?: Money;
|
||||
baseGrandTotal?: Money;
|
||||
exchangeRate?: number;
|
||||
|
||||
abstract getPosting(): Promise<LedgerPosting>;
|
||||
|
||||
get isSales() {
|
||||
return this.schemaName === 'SalesInvoice';
|
||||
}
|
||||
|
||||
async getPayments() {
|
||||
const payments = await frappe.db.getAll('PaymentFor', {
|
||||
fields: ['parent'],
|
||||
filters: { referenceName: this.name as string },
|
||||
filters: { referenceName: this.name! },
|
||||
orderBy: 'name',
|
||||
});
|
||||
|
||||
@ -43,13 +58,10 @@ export abstract class Transaction extends Doc {
|
||||
// update outstanding amounts
|
||||
await frappe.db.update(this.schemaName, {
|
||||
name: this.name as string,
|
||||
outstandingAmount: this.baseGrandTotal as Money,
|
||||
outstandingAmount: this.baseGrandTotal!,
|
||||
});
|
||||
|
||||
const party = (await frappe.doc.getDoc(
|
||||
'Party',
|
||||
this.party as string
|
||||
)) as Party;
|
||||
const party = (await frappe.doc.getDoc('Party', this.party!)) as Party;
|
||||
await party.updateOutstandingAmount();
|
||||
}
|
||||
|
||||
@ -87,7 +99,7 @@ export abstract class Transaction extends Doc {
|
||||
return 1.0;
|
||||
}
|
||||
return await getExchangeRate({
|
||||
fromCurrency: this.currency as string,
|
||||
fromCurrency: this.currency!,
|
||||
toCurrency: companyCurrency as string,
|
||||
});
|
||||
}
|
||||
@ -95,7 +107,13 @@ export abstract class Transaction extends Doc {
|
||||
async getTaxSummary() {
|
||||
const taxes: Record<
|
||||
string,
|
||||
{ account: string; rate: number; amount: Money; baseAmount?: Money }
|
||||
{
|
||||
account: string;
|
||||
rate: number;
|
||||
amount: Money;
|
||||
baseAmount: Money;
|
||||
[key: string]: DocValue;
|
||||
}
|
||||
> = {};
|
||||
|
||||
for (const row of this.items as Doc[]) {
|
||||
@ -112,6 +130,7 @@ export abstract class Transaction extends Doc {
|
||||
account,
|
||||
rate,
|
||||
amount: frappe.pesa(0),
|
||||
baseAmount: frappe.pesa(0),
|
||||
};
|
||||
|
||||
const amount = (row.amount as Money).mul(rate).div(100);
|
||||
@ -122,7 +141,7 @@ export abstract class Transaction extends Doc {
|
||||
return Object.keys(taxes)
|
||||
.map((account) => {
|
||||
const tax = taxes[account];
|
||||
tax.baseAmount = tax.amount.mul(this.exchangeRate as number);
|
||||
tax.baseAmount = tax.amount.mul(this.exchangeRate!);
|
||||
return tax;
|
||||
})
|
||||
.filter((tax) => !tax.amount.isZero());
|
||||
@ -139,6 +158,40 @@ export abstract class Transaction extends Doc {
|
||||
async getGrandTotal() {
|
||||
return ((this.taxes ?? []) as Doc[])
|
||||
.map((doc) => doc.amount as Money)
|
||||
.reduce((a, b) => a.add(b), this.netTotal as Money);
|
||||
.reduce((a, b) => a.add(b), this.netTotal!);
|
||||
}
|
||||
|
||||
formulas: FormulaMap = {
|
||||
account: async () =>
|
||||
this.getFrom('Party', this.party!, 'defaultAccount') as string,
|
||||
currency: async () =>
|
||||
(this.getFrom('Party', this.party!, 'currency') as string) ||
|
||||
(frappe.singles.AccountingSettings!.currency as string),
|
||||
exchangeRate: async () => await this.getExchangeRate(),
|
||||
netTotal: async () => this.getSum('items', 'amount', false),
|
||||
baseNetTotal: async () => this.netTotal!.mul(this.exchangeRate!),
|
||||
taxes: async () => await this.getTaxSummary(),
|
||||
grandTotal: async () => await this.getGrandTotal(),
|
||||
baseGrandTotal: async () =>
|
||||
(this.grandTotal as Money).mul(this.exchangeRate!),
|
||||
outstandingAmount: async () => {
|
||||
if (this.submitted) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.baseGrandTotal!;
|
||||
},
|
||||
};
|
||||
|
||||
defaults: DefaultMap = {
|
||||
date: () => new Date().toISOString().slice(0, 10),
|
||||
};
|
||||
|
||||
static filters: FiltersMap = {
|
||||
account: (doc: Doc) => ({
|
||||
isGroup: false,
|
||||
accountType: doc.isSales ? 'Receivable' : 'Payable',
|
||||
}),
|
||||
numberSeries: (doc: Doc) => ({ referenceType: doc.schemaName }),
|
||||
};
|
||||
}
|
106
models/baseModels/InvoiceItem/InvoiceItem.ts
Normal file
106
models/baseModels/InvoiceItem/InvoiceItem.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import frappe from 'frappe';
|
||||
import { DocValue } from 'frappe/core/types';
|
||||
import Doc from 'frappe/model/doc';
|
||||
import {
|
||||
DependsOnMap,
|
||||
FiltersMap,
|
||||
FormulaMap,
|
||||
ValidationMap,
|
||||
} from 'frappe/model/types';
|
||||
import Money from 'pesa/dist/types/src/money';
|
||||
import { Invoice } from '../Invoice/Invoice';
|
||||
|
||||
export abstract class InvoiceItem extends Doc {
|
||||
account?: string;
|
||||
baseAmount?: Money;
|
||||
exchangeRate?: number;
|
||||
parentdoc?: Invoice;
|
||||
|
||||
get isSales() {
|
||||
return this.schemaName === 'SalesInvoiceItem';
|
||||
}
|
||||
|
||||
formulas: FormulaMap = {
|
||||
description: () =>
|
||||
this.parentdoc!.getFrom(
|
||||
'Item',
|
||||
this.item as string,
|
||||
'description'
|
||||
) as string,
|
||||
rate: async () => {
|
||||
const baseRate = ((await this.parentdoc!.getFrom(
|
||||
'Item',
|
||||
this.item as string,
|
||||
'rate'
|
||||
)) || frappe.pesa(0)) as Money;
|
||||
|
||||
return baseRate.div(this.exchangeRate!);
|
||||
},
|
||||
baseRate: () =>
|
||||
(this.rate as Money).mul(this.parentdoc!.exchangeRate as number),
|
||||
account: () => {
|
||||
let accountType = 'expenseAccount';
|
||||
if (this.isSales) {
|
||||
accountType = 'incomeAccount';
|
||||
}
|
||||
return this.parentdoc!.getFrom('Item', this.item as string, accountType);
|
||||
},
|
||||
tax: () => {
|
||||
if (this.tax) {
|
||||
return this.tax as string;
|
||||
}
|
||||
|
||||
return this.parentdoc!.getFrom(
|
||||
'Item',
|
||||
this.item as string,
|
||||
'tax'
|
||||
) as string;
|
||||
},
|
||||
amount: () => (this.rate as Money).mul(this.quantity as number),
|
||||
baseAmount: () =>
|
||||
(this.amount as Money).mul(this.parentdoc!.exchangeRate as number),
|
||||
hsnCode: () =>
|
||||
this.parentdoc!.getFrom('Item', this.item as string, 'hsnCode'),
|
||||
};
|
||||
|
||||
dependsOn: DependsOnMap = {
|
||||
hsnCode: ['item'],
|
||||
};
|
||||
|
||||
validations: ValidationMap = {
|
||||
rate: async (value: DocValue) => {
|
||||
if ((value as Money).gte(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new frappe.errors.ValidationError(
|
||||
frappe.t`Rate (${frappe.format(
|
||||
value,
|
||||
'Currency'
|
||||
)}) cannot be less zero.`
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
static filters: FiltersMap = {
|
||||
item: (doc: Doc) => {
|
||||
const itemList = doc.parentdoc!.items as Doc[];
|
||||
const items = itemList.map((d) => d.item as string).filter(Boolean);
|
||||
|
||||
let itemNotFor = 'sales';
|
||||
if (doc.isSales) {
|
||||
itemNotFor = 'purchases';
|
||||
}
|
||||
|
||||
const baseFilter = { for: ['not in', [itemNotFor]] };
|
||||
if (items.length <= 0) {
|
||||
return baseFilter;
|
||||
}
|
||||
|
||||
return {
|
||||
name: ['not in', items],
|
||||
...baseFilter,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
@ -20,6 +20,8 @@ import { Party } from '../Party/Party';
|
||||
import { PaymentMethod, PaymentType } from './types';
|
||||
|
||||
export class Payment extends Doc {
|
||||
party?: string;
|
||||
|
||||
async change({ changed }: { changed: string }) {
|
||||
switch (changed) {
|
||||
case 'for': {
|
||||
@ -59,7 +61,7 @@ export class Payment extends Doc {
|
||||
paymentType = 'Pay';
|
||||
}
|
||||
|
||||
this.party = party;
|
||||
this.party = party as string;
|
||||
this.paymentType = paymentType;
|
||||
}
|
||||
|
||||
@ -152,7 +154,7 @@ export class Payment extends Doc {
|
||||
const writeoff = this.writeoff as Money;
|
||||
const entries = new LedgerPosting({
|
||||
reference: this,
|
||||
party: this.party as string,
|
||||
party: this.party!,
|
||||
});
|
||||
|
||||
await entries.debit(paymentAccount as string, amount.sub(writeoff));
|
||||
@ -164,7 +166,7 @@ export class Payment extends Doc {
|
||||
|
||||
const writeoffEntry = new LedgerPosting({
|
||||
reference: this,
|
||||
party: this.party as string,
|
||||
party: this.party!,
|
||||
});
|
||||
const writeOffAccount = frappe.singles.AccountingSettings!
|
||||
.writeOffAccount as string;
|
||||
@ -227,10 +229,7 @@ export class Payment extends Doc {
|
||||
const newOutstanding = outstandingAmount.sub(amount);
|
||||
await referenceDoc.set('outstandingAmount', newOutstanding);
|
||||
await referenceDoc.update();
|
||||
const party = (await frappe.doc.getDoc(
|
||||
'Party',
|
||||
this.party as string
|
||||
)) as Party;
|
||||
const party = (await frappe.doc.getDoc('Party', this.party!)) as Party;
|
||||
|
||||
await party.updateOutstandingAmount();
|
||||
}
|
||||
|
@ -1,152 +0,0 @@
|
||||
import { t } from 'frappe';
|
||||
import { DEFAULT_NUMBER_SERIES } from '../../../frappe/utils/consts';
|
||||
import InvoiceTemplate from '../SalesInvoice/InvoiceTemplate.vue';
|
||||
import { getActions } from '../Transaction/Transaction';
|
||||
import PurchaseInvoice from './PurchaseInvoiceDocument';
|
||||
|
||||
export default {
|
||||
name: 'PurchaseInvoice',
|
||||
doctype: 'DocType',
|
||||
label: t`Bill`,
|
||||
documentClass: PurchaseInvoice,
|
||||
printTemplate: InvoiceTemplate,
|
||||
isSingle: 0,
|
||||
isChild: 0,
|
||||
isSubmittable: 1,
|
||||
keywordFields: ['name', 'supplier'],
|
||||
settings: 'PurchaseInvoiceSettings',
|
||||
showTitle: true,
|
||||
fields: [
|
||||
{
|
||||
label: t`Bill No`,
|
||||
fieldname: 'name',
|
||||
fieldtype: 'Data',
|
||||
required: 1,
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'date',
|
||||
label: t`Date`,
|
||||
fieldtype: 'Date',
|
||||
default: () => new Date().toISOString().slice(0, 10),
|
||||
},
|
||||
{
|
||||
fieldname: 'supplier',
|
||||
label: t`Supplier`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Supplier',
|
||||
required: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'account',
|
||||
label: t`Account`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
formula: (doc) => doc.getFrom('Party', doc.supplier, 'defaultAccount'),
|
||||
getFilters: () => {
|
||||
return {
|
||||
isGroup: 0,
|
||||
accountType: 'Payable',
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: 'currency',
|
||||
label: t`Supplier Currency`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Currency',
|
||||
hidden: 1,
|
||||
formula: (doc) =>
|
||||
doc.getFrom('Party', doc.supplier, 'currency') ||
|
||||
frappe.AccountingSettings.currency,
|
||||
formulaDependsOn: ['supplier'],
|
||||
},
|
||||
{
|
||||
fieldname: 'exchangeRate',
|
||||
label: t`Exchange Rate`,
|
||||
fieldtype: 'Float',
|
||||
default: 1,
|
||||
formula: async (doc) => await doc.getExchangeRate(),
|
||||
},
|
||||
{
|
||||
fieldname: 'items',
|
||||
label: t`Items`,
|
||||
fieldtype: 'Table',
|
||||
childtype: 'PurchaseInvoiceItem',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
fieldname: 'netTotal',
|
||||
label: t`Net Total`,
|
||||
fieldtype: 'Currency',
|
||||
formula: (doc) => doc.getSum('items', 'amount', false),
|
||||
readOnly: 1,
|
||||
getCurrency: (doc) => doc.currency,
|
||||
},
|
||||
{
|
||||
fieldname: 'baseNetTotal',
|
||||
label: t`Net Total (Company Currency)`,
|
||||
fieldtype: 'Currency',
|
||||
formula: (doc) => doc.netTotal.mul(doc.exchangeRate),
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'taxes',
|
||||
label: t`Taxes`,
|
||||
fieldtype: 'Table',
|
||||
childtype: 'TaxSummary',
|
||||
formula: (doc) => doc.getTaxSummary(),
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'grandTotal',
|
||||
label: t`Grand Total`,
|
||||
fieldtype: 'Currency',
|
||||
formula: (doc) => doc.getGrandTotal(),
|
||||
readOnly: 1,
|
||||
getCurrency: (doc) => doc.currency,
|
||||
},
|
||||
{
|
||||
fieldname: 'baseGrandTotal',
|
||||
label: t`Grand Total (Company Currency)`,
|
||||
fieldtype: 'Currency',
|
||||
formula: (doc) => doc.grandTotal.mul(doc.exchangeRate),
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'outstandingAmount',
|
||||
label: t`Outstanding Amount`,
|
||||
fieldtype: 'Currency',
|
||||
formula: (doc) => {
|
||||
if (doc.submitted) return;
|
||||
return doc.baseGrandTotal;
|
||||
},
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'terms',
|
||||
label: t`Terms`,
|
||||
fieldtype: 'Text',
|
||||
},
|
||||
{
|
||||
fieldname: 'cancelled',
|
||||
label: t`Cancelled`,
|
||||
fieldtype: 'Check',
|
||||
default: 0,
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'numberSeries',
|
||||
label: t`Number Series`,
|
||||
fieldtype: 'Link',
|
||||
target: 'NumberSeries',
|
||||
required: 1,
|
||||
getFilters: () => {
|
||||
return { referenceType: 'PurchaseInvoice' };
|
||||
},
|
||||
default: DEFAULT_NUMBER_SERIES['PurchaseInvoice'],
|
||||
},
|
||||
],
|
||||
|
||||
actions: getActions('PurchaseInvoice'),
|
||||
};
|
@ -0,0 +1,48 @@
|
||||
import { LedgerPosting } from 'accounting/ledgerPosting';
|
||||
import { Action, ListViewSettings } from 'frappe/model/types';
|
||||
import {
|
||||
getTransactionActions,
|
||||
getTransactionStatusColumn,
|
||||
} from '../../helpers';
|
||||
import { Invoice } from '../Invoice/Invoice';
|
||||
import { PurchaseInvoiceItem } from '../PurchaseInvoiceItem/PurchaseInvoiceItem';
|
||||
|
||||
export class PurchaseInvoice extends Invoice {
|
||||
items?: PurchaseInvoiceItem[];
|
||||
|
||||
async getPosting() {
|
||||
const entries: LedgerPosting = new LedgerPosting({
|
||||
reference: this,
|
||||
party: this.party,
|
||||
});
|
||||
|
||||
await entries.credit(this.account!, this.baseGrandTotal!);
|
||||
|
||||
for (const item of this.items!) {
|
||||
await entries.debit(item.account!, item.baseAmount!);
|
||||
}
|
||||
|
||||
if (this.taxes) {
|
||||
for (const tax of this.taxes) {
|
||||
await entries.debit(tax.account!, tax.baseAmount!);
|
||||
}
|
||||
}
|
||||
|
||||
entries.makeRoundOffEntry();
|
||||
return entries;
|
||||
}
|
||||
|
||||
static actions: Action[] = getTransactionActions('PurchaseInvoice');
|
||||
|
||||
static listSettings: ListViewSettings = {
|
||||
formRoute: (name) => `/edit/PurchaseInvoice/${name}`,
|
||||
columns: [
|
||||
'party',
|
||||
'name',
|
||||
getTransactionStatusColumn(),
|
||||
'date',
|
||||
'grandTotal',
|
||||
'outstandingAmount',
|
||||
],
|
||||
};
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
import TransactionDocument from '../Transaction/TransactionDocument';
|
||||
|
||||
export default class PurchaseInvoice extends TransactionDocument {};
|
@ -1,16 +0,0 @@
|
||||
import { t } from 'frappe';
|
||||
import { getStatusColumn } from '../Transaction/Transaction';
|
||||
|
||||
export default {
|
||||
doctype: 'PurchaseInvoice',
|
||||
title: t`Bills`,
|
||||
formRoute: (name) => `/edit/PurchaseInvoice/${name}`,
|
||||
columns: [
|
||||
'supplier',
|
||||
'name',
|
||||
getStatusColumn('PurchaseInvoice'),
|
||||
'date',
|
||||
'grandTotal',
|
||||
'outstandingAmount',
|
||||
],
|
||||
};
|
@ -1,27 +0,0 @@
|
||||
import TransactionServer from '../Transaction/TransactionServer';
|
||||
import PurchaseInvoice from './PurchaseInvoiceDocument';
|
||||
import LedgerPosting from '../../../accounting/ledgerPosting';
|
||||
|
||||
class PurchaseInvoiceServer extends PurchaseInvoice {
|
||||
async getPosting() {
|
||||
let entries = new LedgerPosting({ reference: this, party: this.supplier });
|
||||
await entries.credit(this.account, this.baseGrandTotal);
|
||||
|
||||
for (let item of this.items) {
|
||||
await entries.debit(item.account, item.baseAmount);
|
||||
}
|
||||
|
||||
if (this.taxes) {
|
||||
for (let tax of this.taxes) {
|
||||
await entries.debit(tax.account, tax.baseAmount);
|
||||
}
|
||||
}
|
||||
entries.makeRoundOffEntry();
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
|
||||
// apply common methods from TransactionServer
|
||||
Object.assign(PurchaseInvoiceServer.prototype, TransactionServer);
|
||||
|
||||
export default PurchaseInvoiceServer;
|
@ -1,109 +0,0 @@
|
||||
import { t } from 'frappe';
|
||||
export default {
|
||||
name: 'PurchaseInvoiceItem',
|
||||
doctype: 'DocType',
|
||||
isChild: 1,
|
||||
keywordFields: [],
|
||||
tableFields: ['item', 'tax', 'quantity', 'rate', 'amount'],
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'item',
|
||||
label: t`Item`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Item',
|
||||
required: 1,
|
||||
getFilters(_, doc) {
|
||||
let items = doc.parentdoc.items.map((d) => d.item).filter(Boolean);
|
||||
|
||||
const baseFilter = { for: ['not in', ['sales']] };
|
||||
if (items.length <= 0) {
|
||||
return baseFilter;
|
||||
}
|
||||
|
||||
return {
|
||||
name: ['not in', items],
|
||||
...baseFilter,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: 'description',
|
||||
label: t`Description`,
|
||||
fieldtype: 'Text',
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'description'),
|
||||
hidden: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'quantity',
|
||||
label: t`Quantity`,
|
||||
fieldtype: 'Float',
|
||||
required: 1,
|
||||
default: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'rate',
|
||||
label: t`Rate`,
|
||||
fieldtype: 'Currency',
|
||||
required: 1,
|
||||
formula: async (row, doc) => {
|
||||
const baseRate =
|
||||
(await doc.getFrom('Item', row.item, 'rate')) || frappe.pesa(0);
|
||||
return baseRate.div(doc.exchangeRate);
|
||||
},
|
||||
getCurrency: (row, doc) => doc.currency,
|
||||
validate(value) {
|
||||
if (value.gte(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new frappe.errors.ValidationError(
|
||||
frappe.t`Rate (${frappe.format(
|
||||
value,
|
||||
'Currency'
|
||||
)}) cannot be less zero.`
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: 'baseRate',
|
||||
label: t`Rate (Company Currency)`,
|
||||
fieldtype: 'Currency',
|
||||
formula: (row, doc) => row.rate.mul(doc.exchangeRate),
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'account',
|
||||
label: t`Account`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
required: 1,
|
||||
readOnly: 1,
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'expenseAccount'),
|
||||
},
|
||||
{
|
||||
fieldname: 'tax',
|
||||
label: t`Tax`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Tax',
|
||||
formula: (row, doc) => {
|
||||
if (row.tax) return row.tax;
|
||||
return doc.getFrom('Item', row.item, 'tax');
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: 'amount',
|
||||
label: t`Amount`,
|
||||
fieldtype: 'Currency',
|
||||
readOnly: 1,
|
||||
formula: (row) => row.rate.mul(row.quantity),
|
||||
getCurrency: (row, doc) => doc.currency,
|
||||
},
|
||||
{
|
||||
fieldname: 'baseAmount',
|
||||
label: t`Amount (Company Currency)`,
|
||||
fieldtype: 'Currency',
|
||||
readOnly: 1,
|
||||
formula: (row, doc) => row.amount.mul(doc.exchangeRate),
|
||||
},
|
||||
],
|
||||
};
|
@ -0,0 +1,3 @@
|
||||
import { InvoiceItem } from '../InvoiceItem/InvoiceItem';
|
||||
|
||||
export class PurchaseInvoiceItem extends InvoiceItem {}
|
@ -1,25 +0,0 @@
|
||||
import { t } from 'frappe';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import PurchaseInvoiceItemOriginal from './PurchaseInvoiceItem';
|
||||
|
||||
export default function getAugmentedPurchaseInvoiceItem({ country }) {
|
||||
const PurchaseInvoiceItem = cloneDeep(PurchaseInvoiceItemOriginal);
|
||||
if (!country) {
|
||||
return PurchaseInvoiceItem;
|
||||
}
|
||||
|
||||
if (country === 'India') {
|
||||
PurchaseInvoiceItem.fields = [
|
||||
...PurchaseInvoiceItem.fields,
|
||||
{
|
||||
fieldname: 'hsnCode',
|
||||
label: t`HSN/SAC`,
|
||||
fieldtype: 'Int',
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'hsnCode'),
|
||||
formulaDependsOn: ['item'],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return PurchaseInvoiceItem;
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
import { t } from 'frappe';
|
||||
import { DEFAULT_NUMBER_SERIES } from '../../../frappe/utils/consts';
|
||||
import { getActions } from '../Transaction/Transaction';
|
||||
import InvoiceTemplate from './InvoiceTemplate.vue';
|
||||
import SalesInvoice from './SalesInvoiceDocument';
|
||||
|
||||
export default {
|
||||
name: 'SalesInvoice',
|
||||
label: t`Invoice`,
|
||||
doctype: 'DocType',
|
||||
documentClass: SalesInvoice,
|
||||
printTemplate: InvoiceTemplate,
|
||||
isSingle: 0,
|
||||
isChild: 0,
|
||||
isSubmittable: 1,
|
||||
keywordFields: ['name', 'customer'],
|
||||
settings: 'SalesInvoiceSettings',
|
||||
fields: [
|
||||
{
|
||||
label: t`Invoice No`,
|
||||
fieldname: 'name',
|
||||
fieldtype: 'Data',
|
||||
required: 1,
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'date',
|
||||
label: t`Date`,
|
||||
fieldtype: 'Date',
|
||||
default: () => new Date().toISOString().slice(0, 10),
|
||||
},
|
||||
{
|
||||
fieldname: 'customer',
|
||||
label: t`Customer`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Customer',
|
||||
required: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'account',
|
||||
label: t`Account`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
disableCreation: true,
|
||||
formula: (doc) => doc.getFrom('Party', doc.customer, 'defaultAccount'),
|
||||
getFilters: () => {
|
||||
return {
|
||||
isGroup: 0,
|
||||
accountType: 'Receivable',
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: 'currency',
|
||||
label: t`Customer Currency`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Currency',
|
||||
formula: (doc) =>
|
||||
doc.getFrom('Party', doc.customer, 'currency') ||
|
||||
frappe.AccountingSettings.currency,
|
||||
formulaDependsOn: ['customer'],
|
||||
},
|
||||
{
|
||||
fieldname: 'exchangeRate',
|
||||
label: t`Exchange Rate`,
|
||||
fieldtype: 'Float',
|
||||
default: 1,
|
||||
formula: (doc) => doc.getExchangeRate(),
|
||||
readOnly: true,
|
||||
},
|
||||
{
|
||||
fieldname: 'items',
|
||||
label: t`Items`,
|
||||
fieldtype: 'Table',
|
||||
childtype: 'SalesInvoiceItem',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
fieldname: 'netTotal',
|
||||
label: t`Net Total`,
|
||||
fieldtype: 'Currency',
|
||||
formula: (doc) => doc.getSum('items', 'amount', false),
|
||||
readOnly: 1,
|
||||
getCurrency: (doc) => doc.currency,
|
||||
},
|
||||
{
|
||||
fieldname: 'baseNetTotal',
|
||||
label: t`Net Total (Company Currency)`,
|
||||
fieldtype: 'Currency',
|
||||
formula: (doc) => doc.netTotal.mul(doc.exchangeRate),
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'taxes',
|
||||
label: t`Taxes`,
|
||||
fieldtype: 'Table',
|
||||
childtype: 'TaxSummary',
|
||||
formula: (doc) => doc.getTaxSummary(),
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'grandTotal',
|
||||
label: t`Grand Total`,
|
||||
fieldtype: 'Currency',
|
||||
formula: (doc) => doc.getGrandTotal(),
|
||||
readOnly: 1,
|
||||
getCurrency: (doc) => doc.currency,
|
||||
},
|
||||
{
|
||||
fieldname: 'baseGrandTotal',
|
||||
label: t`Grand Total (Company Currency)`,
|
||||
fieldtype: 'Currency',
|
||||
formula: (doc) => doc.grandTotal.mul(doc.exchangeRate),
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'outstandingAmount',
|
||||
label: t`Outstanding Amount`,
|
||||
fieldtype: 'Currency',
|
||||
formula: (doc) => {
|
||||
if (doc.submitted) return;
|
||||
return doc.baseGrandTotal;
|
||||
},
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'terms',
|
||||
label: t`Notes`,
|
||||
fieldtype: 'Text',
|
||||
},
|
||||
{
|
||||
fieldname: 'cancelled',
|
||||
label: t`Cancelled`,
|
||||
fieldtype: 'Check',
|
||||
default: 0,
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'numberSeries',
|
||||
label: t`Number Series`,
|
||||
fieldtype: 'Link',
|
||||
target: 'NumberSeries',
|
||||
required: 1,
|
||||
getFilters: () => {
|
||||
return { referenceType: 'SalesInvoice' };
|
||||
},
|
||||
default: DEFAULT_NUMBER_SERIES['SalesInvoice'],
|
||||
},
|
||||
],
|
||||
|
||||
actions: getActions('SalesInvoice'),
|
||||
};
|
46
models/baseModels/SalesInvoice/SalesInvoice.ts
Normal file
46
models/baseModels/SalesInvoice/SalesInvoice.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { LedgerPosting } from 'accounting/ledgerPosting';
|
||||
import { Action, ListViewSettings } from 'frappe/model/types';
|
||||
import {
|
||||
getTransactionActions,
|
||||
getTransactionStatusColumn,
|
||||
} from '../../helpers';
|
||||
import { Invoice } from '../Invoice/Invoice';
|
||||
import { SalesInvoiceItem } from '../SalesInvoiceItem/SalesInvoiceItem';
|
||||
|
||||
export class SalesInvoice extends Invoice {
|
||||
items?: SalesInvoiceItem[];
|
||||
|
||||
async getPosting() {
|
||||
const entries: LedgerPosting = new LedgerPosting({
|
||||
reference: this,
|
||||
party: this.party,
|
||||
});
|
||||
await entries.debit(this.account!, this.baseGrandTotal!);
|
||||
|
||||
for (const item of this.items!) {
|
||||
await entries.credit(item.account!, item.baseAmount!);
|
||||
}
|
||||
|
||||
if (this.taxes) {
|
||||
for (const tax of this.taxes!) {
|
||||
await entries.credit(tax.account!, tax.baseAmount!);
|
||||
}
|
||||
}
|
||||
entries.makeRoundOffEntry();
|
||||
return entries;
|
||||
}
|
||||
|
||||
static actions: Action[] = getTransactionActions('SalesInvoice');
|
||||
|
||||
static listSettings: ListViewSettings = {
|
||||
formRoute: (name) => `/edit/SalesInvoice/${name}`,
|
||||
columns: [
|
||||
'party',
|
||||
'name',
|
||||
getTransactionStatusColumn(),
|
||||
'date',
|
||||
'grandTotal',
|
||||
'outstandingAmount',
|
||||
],
|
||||
};
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
import TransactionDocument from '../Transaction/TransactionDocument';
|
||||
|
||||
export default class SalesInvoice extends TransactionDocument {};
|
@ -1,16 +0,0 @@
|
||||
import { t } from 'frappe';
|
||||
import { getStatusColumn } from '../Transaction/Transaction';
|
||||
|
||||
export default {
|
||||
doctype: 'SalesInvoice',
|
||||
title: t`Invoices`,
|
||||
formRoute: (name) => `/edit/SalesInvoice/${name}`,
|
||||
columns: [
|
||||
'customer',
|
||||
'name',
|
||||
getStatusColumn('SalesInvoice'),
|
||||
'date',
|
||||
'grandTotal',
|
||||
'outstandingAmount',
|
||||
],
|
||||
};
|
@ -1,27 +0,0 @@
|
||||
import TransactionServer from '../Transaction/TransactionServer';
|
||||
import SalesInvoice from './SalesInvoiceDocument';
|
||||
import LedgerPosting from '../../../accounting/ledgerPosting';
|
||||
|
||||
class SalesInvoiceServer extends SalesInvoice {
|
||||
async getPosting() {
|
||||
let entries = new LedgerPosting({ reference: this, party: this.customer });
|
||||
await entries.debit(this.account, this.baseGrandTotal);
|
||||
|
||||
for (let item of this.items) {
|
||||
await entries.credit(item.account, item.baseAmount);
|
||||
}
|
||||
|
||||
if (this.taxes) {
|
||||
for (let tax of this.taxes) {
|
||||
await entries.credit(tax.account, tax.baseAmount);
|
||||
}
|
||||
}
|
||||
entries.makeRoundOffEntry();
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
|
||||
// apply common methods from TransactionServer
|
||||
Object.assign(SalesInvoiceServer.prototype, TransactionServer);
|
||||
|
||||
export default SalesInvoiceServer;
|
@ -1,25 +0,0 @@
|
||||
import { t } from 'frappe';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import SalesInvoiceItemOriginal from './SalesInvoiceItem';
|
||||
|
||||
export default function getAugmentedSalesInvoiceItem({ country }) {
|
||||
const SalesInvoiceItem = cloneDeep(SalesInvoiceItemOriginal);
|
||||
if (!country) {
|
||||
return SalesInvoiceItem;
|
||||
}
|
||||
|
||||
if (country === 'India') {
|
||||
SalesInvoiceItem.fields = [
|
||||
...SalesInvoiceItem.fields,
|
||||
{
|
||||
fieldname: 'hsnCode',
|
||||
label: t`HSN/SAC`,
|
||||
fieldtype: 'Int',
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'hsnCode'),
|
||||
formulaDependsOn: ['item'],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return SalesInvoiceItem;
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
import { t } from 'frappe';
|
||||
export default {
|
||||
name: 'SalesInvoiceItem',
|
||||
doctype: 'DocType',
|
||||
isChild: 1,
|
||||
regional: 1,
|
||||
keywordFields: [],
|
||||
tableFields: ['item', 'tax', 'quantity', 'rate', 'amount'],
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'item',
|
||||
label: t`Item`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Item',
|
||||
required: 1,
|
||||
getFilters(_, doc) {
|
||||
let items = doc.parentdoc.items.map((d) => d.item).filter(Boolean);
|
||||
|
||||
const baseFilter = { for: ['not in', ['purchases']] };
|
||||
if (items.length <= 0) {
|
||||
return baseFilter;
|
||||
}
|
||||
|
||||
return {
|
||||
name: ['not in', items],
|
||||
...baseFilter,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: 'description',
|
||||
label: t`Description`,
|
||||
fieldtype: 'Text',
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'description'),
|
||||
hidden: 1,
|
||||
formulaDependsOn: ['item'],
|
||||
},
|
||||
{
|
||||
fieldname: 'quantity',
|
||||
label: t`Quantity`,
|
||||
fieldtype: 'Float',
|
||||
required: 1,
|
||||
default: 1,
|
||||
validate(value, doc) {
|
||||
if (value >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new frappe.errors.ValidationError(
|
||||
frappe.t`Quantity (${value}) cannot be less than zero.`
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: 'rate',
|
||||
label: t`Rate`,
|
||||
fieldtype: 'Currency',
|
||||
required: 1,
|
||||
formula: async (row, doc) => {
|
||||
const baseRate =
|
||||
(await doc.getFrom('Item', row.item, 'rate')) || frappe.pesa(0);
|
||||
return baseRate.div(doc.exchangeRate);
|
||||
},
|
||||
getCurrency: (row, doc) => doc.currency,
|
||||
formulaDependsOn: ['item'],
|
||||
validate(value, doc) {
|
||||
if (value.gte(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new frappe.errors.ValidationError(
|
||||
frappe.t`Rate (${frappe.format(
|
||||
value,
|
||||
'Currency'
|
||||
)}) cannot be less zero.`
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: 'baseRate',
|
||||
label: t`Rate (Company Currency)`,
|
||||
fieldtype: 'Currency',
|
||||
formula: (row, doc) => row.rate.mul(doc.exchangeRate),
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'account',
|
||||
label: t`Account`,
|
||||
hidden: 1,
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
required: 1,
|
||||
readOnly: 1,
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'incomeAccount'),
|
||||
},
|
||||
{
|
||||
fieldname: 'tax',
|
||||
label: t`Tax`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Tax',
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'tax'),
|
||||
formulaDependsOn: ['item'],
|
||||
},
|
||||
{
|
||||
fieldname: 'amount',
|
||||
label: t`Amount`,
|
||||
fieldtype: 'Currency',
|
||||
readOnly: 1,
|
||||
formula: (row) => row.rate.mul(row.quantity),
|
||||
getCurrency: (row, doc) => doc.currency,
|
||||
},
|
||||
{
|
||||
fieldname: 'baseAmount',
|
||||
label: t`Amount (Company Currency)`,
|
||||
fieldtype: 'Currency',
|
||||
readOnly: 1,
|
||||
formula: (row, doc) => row.amount.mul(doc.exchangeRate),
|
||||
},
|
||||
],
|
||||
};
|
3
models/baseModels/SalesInvoiceItem/SalesInvoiceItem.ts
Normal file
3
models/baseModels/SalesInvoiceItem/SalesInvoiceItem.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { InvoiceItem } from '../InvoiceItem/InvoiceItem';
|
||||
|
||||
export class SalesInvoiceItem extends InvoiceItem {}
|
@ -3,6 +3,11 @@ import { FormulaMap } from 'frappe/model/types';
|
||||
import Money from 'pesa/dist/types/src/money';
|
||||
|
||||
export class TaxSummary extends Doc {
|
||||
account?: string;
|
||||
rate?: number;
|
||||
amount?: Money;
|
||||
baseAmount?: Money;
|
||||
|
||||
formulas: FormulaMap = {
|
||||
baseAmount: async () => {
|
||||
const amount = this.amount as Money;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { openQuickEdit } from '@/utils';
|
||||
import frappe from 'frappe';
|
||||
import Doc from 'frappe/model/doc';
|
||||
import { Action } from 'frappe/model/types';
|
||||
import { Action, ColumnConfig } from 'frappe/model/types';
|
||||
import Money from 'pesa/dist/types/src/money';
|
||||
import { Router } from 'vue-router';
|
||||
import { InvoiceStatus } from './types';
|
||||
@ -26,12 +26,12 @@ export function getLedgerLinkAction(): Action {
|
||||
};
|
||||
}
|
||||
|
||||
export function getTransactionActions(schemaName: string) {
|
||||
export function getTransactionActions(schemaName: string): Action[] {
|
||||
return [
|
||||
{
|
||||
label: frappe.t`Make Payment`,
|
||||
condition: (doc: Doc) =>
|
||||
doc.submitted && (doc.outstandingAmount as Money).gt(0),
|
||||
(doc.submitted as boolean) && (doc.outstandingAmount as Money).gt(0),
|
||||
action: async function makePayment(doc: Doc) {
|
||||
const payment = await frappe.doc.getEmptyDoc('Payment');
|
||||
payment.once('afterInsert', async () => {
|
||||
@ -64,8 +64,8 @@ export function getTransactionActions(schemaName: string) {
|
||||
},
|
||||
{
|
||||
label: frappe.t`Print`,
|
||||
condition: (doc: Doc) => doc.submitted,
|
||||
action(doc: Doc, router: Router) {
|
||||
condition: (doc: Doc) => doc.submitted as boolean,
|
||||
action: async (doc: Doc, router: Router) => {
|
||||
router.push({ path: `/print/${doc.doctype}/${doc.name}` });
|
||||
},
|
||||
},
|
||||
@ -73,7 +73,7 @@ export function getTransactionActions(schemaName: string) {
|
||||
];
|
||||
}
|
||||
|
||||
export function getTransactionStatusColumn() {
|
||||
export function getTransactionStatusColumn(): ColumnConfig {
|
||||
const statusMap = {
|
||||
Unpaid: frappe.t`Unpaid`,
|
||||
Paid: frappe.t`Paid`,
|
||||
|
@ -1,63 +0,0 @@
|
||||
import Account from './doctype/Account/Account.js';
|
||||
import AccountingLedgerEntry from './doctype/AccountingLedgerEntry/AccountingLedgerEntry.js';
|
||||
import AccountingSettings from './doctype/AccountingSettings/AccountingSettings.js';
|
||||
import Address from './doctype/Address/Address.js';
|
||||
import Color from './doctype/Color/Color.js';
|
||||
import CompanySettings from './doctype/CompanySettings/CompanySettings.js';
|
||||
import Contact from './doctype/Contact/Contact.js';
|
||||
import Currency from './doctype/Currency/Currency.js';
|
||||
import GetStarted from './doctype/GetStarted/GetStarted.js';
|
||||
import Item from './doctype/Item/Item.js';
|
||||
import JournalEntry from './doctype/JournalEntry/JournalEntry.js';
|
||||
import JournalEntryAccount from './doctype/JournalEntryAccount/JournalEntryAccount.js';
|
||||
import JournalEntrySettings from './doctype/JournalEntrySettings/JournalEntrySettings.js';
|
||||
import Customer from './doctype/Party/Customer.js';
|
||||
import Party from './doctype/Party/Party.js';
|
||||
import Supplier from './doctype/Party/Supplier.js';
|
||||
import Payment from './doctype/Payment/Payment.js';
|
||||
import PaymentFor from './doctype/PaymentFor/PaymentFor.js';
|
||||
import PaymentSettings from './doctype/PaymentSettings/PaymentSettings.js';
|
||||
import PrintSettings from './doctype/PrintSettings/PrintSettings.js';
|
||||
import PurchaseInvoice from './doctype/PurchaseInvoice/PurchaseInvoice.js';
|
||||
import PurchaseInvoiceItem from './doctype/PurchaseInvoiceItem/PurchaseInvoiceItem.js';
|
||||
import PurchaseInvoiceSettings from './doctype/PurchaseInvoiceSettings/PurchaseInvoiceSettings.js';
|
||||
import SalesInvoice from './doctype/SalesInvoice/SalesInvoice.js';
|
||||
import SalesInvoiceItem from './doctype/SalesInvoiceItem/SalesInvoiceItem.js';
|
||||
import SalesInvoiceSettings from './doctype/SalesInvoiceSettings/SalesInvoiceSettings.js';
|
||||
import SetupWizard from './doctype/SetupWizard/SetupWizard.js';
|
||||
import Tax from './doctype/Tax/Tax.js';
|
||||
import TaxDetail from './doctype/TaxDetail/TaxDetail.js';
|
||||
import TaxSummary from './doctype/TaxSummary/TaxSummary.js';
|
||||
|
||||
export default {
|
||||
SetupWizard,
|
||||
Currency,
|
||||
Color,
|
||||
Account,
|
||||
AccountingSettings,
|
||||
CompanySettings,
|
||||
AccountingLedgerEntry,
|
||||
Party,
|
||||
Customer,
|
||||
Supplier,
|
||||
Payment,
|
||||
PaymentFor,
|
||||
PaymentSettings,
|
||||
Item,
|
||||
SalesInvoice,
|
||||
SalesInvoiceItem,
|
||||
SalesInvoiceSettings,
|
||||
PurchaseInvoice,
|
||||
PurchaseInvoiceItem,
|
||||
PurchaseInvoiceSettings,
|
||||
Tax,
|
||||
TaxDetail,
|
||||
TaxSummary,
|
||||
Address,
|
||||
Contact,
|
||||
JournalEntry,
|
||||
JournalEntryAccount,
|
||||
JournalEntrySettings,
|
||||
PrintSettings,
|
||||
GetStarted,
|
||||
};
|
47
models/index.ts
Normal file
47
models/index.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { Account } from './baseModels/Account/Account';
|
||||
import { AccountingLedgerEntry } from './baseModels/AccountingLedgerEntry/AccountingLedgerEntry';
|
||||
import { AccountingSettings } from './baseModels/AccountingSettings/AccountingSettings';
|
||||
import { Address } from './baseModels/Address/Address';
|
||||
import { Item } from './baseModels/Item/Item';
|
||||
import { JournalEntry } from './baseModels/JournalEntry/JournalEntry';
|
||||
import { JournalEntryAccount } from './baseModels/JournalEntryAccount/JournalEntryAccount';
|
||||
import { Party } from './baseModels/Party/Party';
|
||||
import { Payment } from './baseModels/Payment/Payment';
|
||||
import { PaymentFor } from './baseModels/PaymentFor/PaymentFor';
|
||||
import { PurchaseInvoice } from './baseModels/PurchaseInvoice/PurchaseInvoice';
|
||||
import { PurchaseInvoiceItem } from './baseModels/PurchaseInvoiceItem/PurchaseInvoiceItem';
|
||||
import { SalesInvoice } from './baseModels/SalesInvoice/SalesInvoice';
|
||||
import { SalesInvoiceItem } from './baseModels/SalesInvoiceItem/SalesInvoiceItem';
|
||||
import { SetupWizard } from './baseModels/SetupWizard/SetupWizard';
|
||||
import { Tax } from './baseModels/Tax/Tax';
|
||||
import { TaxSummary } from './baseModels/TaxSummary/TaxSummary';
|
||||
|
||||
export default {
|
||||
Account,
|
||||
AccountingLedgerEntry,
|
||||
AccountingSettings,
|
||||
Address,
|
||||
Item,
|
||||
JournalEntry,
|
||||
JournalEntryAccount,
|
||||
Party,
|
||||
Payment,
|
||||
PaymentFor,
|
||||
PurchaseInvoice,
|
||||
PurchaseInvoiceItem,
|
||||
SalesInvoice,
|
||||
SalesInvoiceItem,
|
||||
SetupWizard,
|
||||
Tax,
|
||||
TaxSummary,
|
||||
};
|
||||
|
||||
export async function getRegionalModels(countryCode: string) {
|
||||
if (countryCode !== 'in') {
|
||||
return {};
|
||||
}
|
||||
|
||||
const { Address } = await import('./regionalModels/in/Address');
|
||||
const { Party } = await import('./regionalModels/in/Party');
|
||||
return { Address, Party };
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import frappe from 'frappe';
|
||||
|
||||
async function setAugmentedModel(model, regionalInfo) {
|
||||
const getAugmentedModel = (
|
||||
await import('./doctype/' + model + '/RegionalChanges')
|
||||
).default;
|
||||
const augmentedModel = getAugmentedModel(regionalInfo);
|
||||
frappe.models[model] = augmentedModel;
|
||||
frappe.models[model].augmented = 1;
|
||||
}
|
||||
|
||||
export default async function regionalModelUpdates(regionalInfo) {
|
||||
for (let model in frappe.models) {
|
||||
const { regional, basedOn, augmented } = frappe.models[model];
|
||||
if (!regional || basedOn || augmented) {
|
||||
continue;
|
||||
}
|
||||
await setAugmentedModel(model, regionalInfo);
|
||||
}
|
||||
}
|
@ -1,54 +1 @@
|
||||
export enum DoctypeName {
|
||||
SetupWizard = 'SetupWizard',
|
||||
Currency = 'Currency',
|
||||
Color = 'Color',
|
||||
Account = 'Account',
|
||||
AccountingSettings = 'AccountingSettings',
|
||||
CompanySettings = 'CompanySettings',
|
||||
AccountingLedgerEntry = 'AccountingLedgerEntry',
|
||||
Party = 'Party',
|
||||
Customer = 'Customer',
|
||||
Supplier = 'Supplier',
|
||||
Payment = 'Payment',
|
||||
PaymentFor = 'PaymentFor',
|
||||
PaymentSettings = 'PaymentSettings',
|
||||
Item = 'Item',
|
||||
SalesInvoice = 'SalesInvoice',
|
||||
SalesInvoiceItem = 'SalesInvoiceItem',
|
||||
SalesInvoiceSettings = 'SalesInvoiceSettings',
|
||||
PurchaseInvoice = 'PurchaseInvoice',
|
||||
PurchaseInvoiceItem = 'PurchaseInvoiceItem',
|
||||
PurchaseInvoiceSettings = 'PurchaseInvoiceSettings',
|
||||
Tax = 'Tax',
|
||||
TaxDetail = 'TaxDetail',
|
||||
TaxSummary = 'TaxSummary',
|
||||
Address = 'Address',
|
||||
Contact = 'Contact',
|
||||
JournalEntry = 'JournalEntry',
|
||||
JournalEntryAccount = 'JournalEntryAccount',
|
||||
JournalEntrySettings = 'JournalEntrySettings',
|
||||
Quotation = 'Quotation',
|
||||
QuotationItem = 'QuotationItem',
|
||||
QuotationSettings = 'QuotationSettings',
|
||||
SalesOrder = 'SalesOrder',
|
||||
SalesOrderItem = 'SalesOrderItem',
|
||||
SalesOrderSettings = 'SalesOrderSettings',
|
||||
Fulfillment = 'Fulfillment',
|
||||
FulfillmentItem = 'FulfillmentItem',
|
||||
FulfillmentSettings = 'FulfillmentSettings',
|
||||
PurchaseOrder = 'PurchaseOrder',
|
||||
PurchaseOrderItem = 'PurchaseOrderItem',
|
||||
PurchaseOrderSettings = 'PurchaseOrderSettings',
|
||||
PurchaseReceipt = 'PurchaseReceipt',
|
||||
PurchaseReceiptItem = 'PurchaseReceiptItem',
|
||||
PurchaseReceiptSettings = 'PurchaseReceiptSettings',
|
||||
Event = 'Event',
|
||||
EventSchedule = 'EventSchedule',
|
||||
EventSettings = 'EventSettings',
|
||||
Email = 'Email',
|
||||
EmailAccount = 'EmailAccount',
|
||||
PrintSettings = 'PrintSettings',
|
||||
GetStarted = 'GetStarted',
|
||||
}
|
||||
|
||||
export type InvoiceStatus = 'Draft' | 'Unpaid' | 'Cancelled' | 'Paid';
|
||||
|
@ -2,7 +2,6 @@
|
||||
"name": "PurchaseInvoiceItem",
|
||||
"label": "Purchase Invoice Item",
|
||||
"isChild": true,
|
||||
"tableFields": ["item", "tax", "quantity", "rate", "amount"],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "item",
|
||||
@ -64,6 +63,13 @@
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "hsnCode",
|
||||
"label": "HSN/SAC",
|
||||
"fieldtype": "Int",
|
||||
"placeholder": "HSN/SAC Code"
|
||||
}
|
||||
]
|
||||
],
|
||||
"tableFields": ["item", "tax", "quantity", "rate", "amount"]
|
||||
}
|
@ -64,6 +64,12 @@
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "hsnCode",
|
||||
"label": "HSN/SAC",
|
||||
"fieldtype": "Int",
|
||||
"placeholder": "HSN/SAC Code"
|
||||
}
|
||||
],
|
||||
"tableFields": ["item", "tax", "quantity", "rate", "amount"]
|
||||
|
Loading…
Reference in New Issue
Block a user