mirror of
https://github.com/frappe/books.git
synced 2024-11-08 14:50:56 +00:00
incr: type Party, JournalEntry
- add removeFields - fix Party schema
This commit is contained in:
parent
71800dffd6
commit
b08a3b9d52
@ -33,6 +33,7 @@ import {
|
||||
EmptyMessageMap,
|
||||
FiltersMap,
|
||||
FormulaMap,
|
||||
HiddenMap,
|
||||
ListsMap,
|
||||
ListViewSettings,
|
||||
RequiredMap,
|
||||
@ -702,6 +703,7 @@ export default class Doc extends Observable<DocValue | Doc[]> {
|
||||
defaults: DefaultMap = {};
|
||||
validations: ValidationMap = {};
|
||||
required: RequiredMap = {};
|
||||
hidden: HiddenMap = {};
|
||||
dependsOn: DependsOnMap = {};
|
||||
|
||||
static lists: ListsMap = {};
|
||||
|
@ -20,12 +20,13 @@ export type Formula = () => Promise<DocValue | undefined>;
|
||||
export type Default = () => DocValue;
|
||||
export type Validation = (value: DocValue) => Promise<void>;
|
||||
export type Required = () => boolean;
|
||||
export type Hidden = () => boolean;
|
||||
|
||||
export type FormulaMap = Record<string, Formula | undefined>;
|
||||
export type DefaultMap = Record<string, Default | undefined>;
|
||||
export type ValidationMap = Record<string, Validation | undefined>;
|
||||
export type RequiredMap = Record<string, Required | undefined>;
|
||||
|
||||
export type HiddenMap = Record<string, Hidden>;
|
||||
export type DependsOnMap = Record<string, string[]>;
|
||||
|
||||
/**
|
||||
@ -55,6 +56,7 @@ export interface Action {
|
||||
export interface ColumnConfig {
|
||||
label: string;
|
||||
fieldtype: FieldType;
|
||||
fieldname?: string;
|
||||
size?: string;
|
||||
render?: (doc: Doc) => { template: string };
|
||||
getValue?: (doc: Doc) => string;
|
||||
|
@ -63,7 +63,7 @@ export class Item extends Doc {
|
||||
},
|
||||
};
|
||||
|
||||
actions: Action[] = [
|
||||
static actions: Action[] = [
|
||||
{
|
||||
label: frappe.t`New Invoice`,
|
||||
condition: (doc) => !doc.isNew,
|
||||
|
@ -1,92 +0,0 @@
|
||||
import { t } from 'frappe';
|
||||
import { DateTime } from 'luxon';
|
||||
import { ledgerLink } from '../../../accounting/utils';
|
||||
import { DEFAULT_NUMBER_SERIES } from '../../../frappe/utils/consts';
|
||||
|
||||
export const journalEntryTypeMap = {
|
||||
'Journal Entry': t`Journal Entry`,
|
||||
'Bank Entry': t`Bank Entry`,
|
||||
'Cash Entry': t`Cash Entry`,
|
||||
'Credit Card Entry': t`Credit Card Entry`,
|
||||
'Debit Note': t`Debit Note`,
|
||||
'Credit Note': t`Credit Note`,
|
||||
'Contra Entry': t`Contra Entry`,
|
||||
'Excise Entry': t`Excise Entry`,
|
||||
'Write Off Entry': t`Write Off Entry`,
|
||||
'Opening Entry': t`Opening Entry`,
|
||||
'Depreciation Entry': t`Depreciation Entry`,
|
||||
};
|
||||
|
||||
export default {
|
||||
label: t`Journal Entry`,
|
||||
name: 'JournalEntry',
|
||||
doctype: 'DocType',
|
||||
isSubmittable: 1,
|
||||
settings: 'JournalEntrySettings',
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'entryType',
|
||||
label: t`Entry Type`,
|
||||
fieldtype: 'Select',
|
||||
placeholder: t`Entry Type`,
|
||||
options: Object.keys(journalEntryTypeMap),
|
||||
map: journalEntryTypeMap,
|
||||
required: 1,
|
||||
},
|
||||
{
|
||||
label: t`Entry No`,
|
||||
fieldname: 'name',
|
||||
fieldtype: 'Data',
|
||||
required: 1,
|
||||
readOnly: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'date',
|
||||
label: t`Date`,
|
||||
fieldtype: 'Date',
|
||||
default: () => DateTime.local().toISODate(),
|
||||
},
|
||||
{
|
||||
fieldname: 'accounts',
|
||||
label: t`Account Entries`,
|
||||
fieldtype: 'Table',
|
||||
childtype: 'JournalEntryAccount',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
fieldname: 'referenceNumber',
|
||||
label: t`Reference Number`,
|
||||
fieldtype: 'Data',
|
||||
},
|
||||
{
|
||||
fieldname: 'referenceDate',
|
||||
label: t`Reference Date`,
|
||||
fieldtype: 'Date',
|
||||
},
|
||||
{
|
||||
fieldname: 'userRemark',
|
||||
label: t`User Remark`,
|
||||
fieldtype: 'Text',
|
||||
placeholder: t`User Remark`,
|
||||
},
|
||||
{
|
||||
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: 'JournalEntry' };
|
||||
},
|
||||
default: DEFAULT_NUMBER_SERIES['JournalEntry'],
|
||||
},
|
||||
],
|
||||
actions: [ledgerLink],
|
||||
};
|
99
models/baseModels/JournalEntry/JournalEntry.ts
Normal file
99
models/baseModels/JournalEntry/JournalEntry.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import frappe from 'frappe';
|
||||
import Doc from 'frappe/model/doc';
|
||||
import {
|
||||
Action,
|
||||
DefaultMap,
|
||||
FiltersMap,
|
||||
ListViewSettings,
|
||||
} from 'frappe/model/types';
|
||||
import { DateTime } from 'luxon';
|
||||
import { getLedgerLinkAction } from 'models/helpers';
|
||||
import Money from 'pesa/dist/types/src/money';
|
||||
import { LedgerPosting } from '../../../accounting/ledgerPosting';
|
||||
|
||||
export class JournalEntry extends Doc {
|
||||
accounts: Doc[] = [];
|
||||
|
||||
getPosting() {
|
||||
const entries = new LedgerPosting({ reference: this });
|
||||
|
||||
for (const row of this.accounts) {
|
||||
const debit = row.debit as Money;
|
||||
const credit = row.credit as Money;
|
||||
const account = row.account as string;
|
||||
|
||||
if (!debit.isZero()) {
|
||||
entries.debit(account, debit);
|
||||
} else if (!credit.isZero()) {
|
||||
entries.credit(account, credit);
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
beforeUpdate() {
|
||||
this.getPosting().validateEntries();
|
||||
}
|
||||
|
||||
beforeInsert() {
|
||||
this.getPosting().validateEntries();
|
||||
}
|
||||
|
||||
async beforeSubmit() {
|
||||
await this.getPosting().post();
|
||||
}
|
||||
|
||||
async afterRevert() {
|
||||
await this.getPosting().postReverse();
|
||||
}
|
||||
|
||||
defaults: DefaultMap = {
|
||||
date: () => DateTime.local().toISODate(),
|
||||
};
|
||||
|
||||
static filters: FiltersMap = {
|
||||
numberSeries: () => ({ referenceType: 'JournalEntry' }),
|
||||
};
|
||||
|
||||
static actions: Action[] = [getLedgerLinkAction()];
|
||||
|
||||
static listSettings: ListViewSettings = {
|
||||
formRoute: (name) => `/edit/JournalEntry/${name}`,
|
||||
columns: [
|
||||
'date',
|
||||
{
|
||||
label: frappe.t`Status`,
|
||||
fieldtype: 'Select',
|
||||
size: 'small',
|
||||
render(doc) {
|
||||
let status = 'Draft';
|
||||
let color = 'gray';
|
||||
if (doc.submitted) {
|
||||
color = 'green';
|
||||
status = 'Submitted';
|
||||
}
|
||||
|
||||
if (doc.cancelled) {
|
||||
color = 'red';
|
||||
status = 'Cancelled';
|
||||
}
|
||||
|
||||
return {
|
||||
template: `<Badge class="text-xs" color="${color}">${status}</Badge>`,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
label: frappe.t`Entry ID`,
|
||||
fieldtype: 'Data',
|
||||
fieldname: 'name',
|
||||
getValue(doc) {
|
||||
return doc.name as string;
|
||||
},
|
||||
},
|
||||
'entryType',
|
||||
'referenceNumber',
|
||||
],
|
||||
};
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
import Badge from '@/components/Badge';
|
||||
import { t } from 'frappe';
|
||||
|
||||
export default {
|
||||
doctype: 'JournalEntry',
|
||||
title: t`Journal Entry`,
|
||||
formRoute: (name) => `/edit/JournalEntry/${name}`,
|
||||
columns: [
|
||||
'date',
|
||||
{
|
||||
label: t`Status`,
|
||||
fieldtype: 'Select',
|
||||
size: 'small',
|
||||
render(doc) {
|
||||
let status = 'Draft';
|
||||
let color = 'gray';
|
||||
if (doc.submitted === 1) {
|
||||
color = 'green';
|
||||
status = 'Submitted';
|
||||
}
|
||||
|
||||
if (doc.cancelled === 1) {
|
||||
color = 'red';
|
||||
status = 'Cancelled';
|
||||
}
|
||||
|
||||
return {
|
||||
template: `<Badge class="text-xs" color="${color}">${status}</Badge>`,
|
||||
components: { Badge },
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t`Entry ID`,
|
||||
fieldname: 'name',
|
||||
fieldtype: 'Data',
|
||||
getValue(doc) {
|
||||
return doc.name;
|
||||
},
|
||||
},
|
||||
'entryType',
|
||||
'referenceNumber',
|
||||
],
|
||||
};
|
@ -1,34 +0,0 @@
|
||||
import Document from 'frappe/model/document';
|
||||
import LedgerPosting from '../../../accounting/ledgerPosting';
|
||||
|
||||
export default class JournalEntryServer extends Document {
|
||||
getPosting() {
|
||||
let entries = new LedgerPosting({ reference: this });
|
||||
|
||||
for (let row of this.accounts) {
|
||||
if (!row.debit.isZero()) {
|
||||
entries.debit(row.account, row.debit);
|
||||
} else if (!row.credit.isZero()) {
|
||||
entries.credit(row.account, row.credit);
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
beforeUpdate() {
|
||||
this.getPosting().validateEntries();
|
||||
}
|
||||
|
||||
beforeInsert() {
|
||||
this.getPosting().validateEntries();
|
||||
}
|
||||
|
||||
async beforeSubmit() {
|
||||
await this.getPosting().post();
|
||||
}
|
||||
|
||||
async afterRevert() {
|
||||
await this.getPosting().postReverse();
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
import router from '@/router';
|
||||
import frappe, { t } from 'frappe';
|
||||
import { h } from 'vue';
|
||||
import PartyWidget from './PartyWidget.vue';
|
||||
|
||||
export default {
|
||||
name: 'Customer',
|
||||
label: t`Customer`,
|
||||
basedOn: 'Party',
|
||||
filters: {
|
||||
customer: 1,
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
label: t`Create Invoice`,
|
||||
condition: (doc) => !doc.isNew(),
|
||||
action: async (customer) => {
|
||||
let doc = await frappe.getEmptyDoc('SalesInvoice');
|
||||
router.push({
|
||||
path: `/edit/SalesInvoice/${doc.name}`,
|
||||
query: {
|
||||
doctype: 'SalesInvoice',
|
||||
values: {
|
||||
customer: customer.name,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t`View Invoices`,
|
||||
condition: (doc) => !doc.isNew(),
|
||||
action: (customer) => {
|
||||
router.push({
|
||||
name: 'ListView',
|
||||
params: {
|
||||
doctype: 'SalesInvoice',
|
||||
filters: {
|
||||
customer: customer.name,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
quickEditWidget: (doc) => ({
|
||||
render() {
|
||||
return h(PartyWidget, {
|
||||
doc,
|
||||
});
|
||||
},
|
||||
}),
|
||||
};
|
@ -1,7 +0,0 @@
|
||||
import { t } from 'frappe';
|
||||
|
||||
export default {
|
||||
doctype: 'Customer',
|
||||
title: t`Customers`,
|
||||
columns: ['name', 'phone', 'outstandingAmount'],
|
||||
};
|
@ -1,105 +0,0 @@
|
||||
import frappe, { t } from 'frappe';
|
||||
|
||||
export default {
|
||||
name: 'Party',
|
||||
label: t`Party`,
|
||||
regional: 1,
|
||||
keywordFields: ['name'],
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'name',
|
||||
label: t`Name`,
|
||||
fieldtype: 'Data',
|
||||
required: 1,
|
||||
placeholder: t`Full Name`,
|
||||
},
|
||||
{
|
||||
fieldname: 'image',
|
||||
label: t`Image`,
|
||||
fieldtype: 'AttachImage',
|
||||
},
|
||||
{
|
||||
fieldname: 'customer',
|
||||
label: t`Is Customer`,
|
||||
fieldtype: 'Check',
|
||||
},
|
||||
{
|
||||
fieldname: 'supplier',
|
||||
label: t`Is Supplier`,
|
||||
fieldtype: 'Check',
|
||||
},
|
||||
{
|
||||
fieldname: 'defaultAccount',
|
||||
label: t`Default Account`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
getFilters: (query, doc) => {
|
||||
return {
|
||||
isGroup: 0,
|
||||
accountType: doc.customer ? 'Receivable' : 'Payable',
|
||||
};
|
||||
},
|
||||
async formula(doc) {
|
||||
let accountName = 'Debtors'; // if Party is a Customer
|
||||
if (doc.supplier) {
|
||||
accountName = 'Creditors';
|
||||
}
|
||||
|
||||
const accountExists = await frappe.db.exists('Account', accountName);
|
||||
return accountExists ? accountName : '';
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: 'outstandingAmount',
|
||||
label: t`Outstanding Amount`,
|
||||
fieldtype: 'Currency',
|
||||
},
|
||||
{
|
||||
fieldname: 'currency',
|
||||
label: t`Currency`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Currency',
|
||||
placeholder: t`INR`,
|
||||
formula: () => frappe.AccountingSettings.currency,
|
||||
},
|
||||
{
|
||||
fieldname: 'email',
|
||||
label: t`Email`,
|
||||
fieldtype: 'Data',
|
||||
placeholder: t`john@doe.com`,
|
||||
validate: {
|
||||
type: 'email',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: 'phone',
|
||||
label: t`Phone`,
|
||||
fieldtype: 'Data',
|
||||
placeholder: t`Phone`,
|
||||
validate: {
|
||||
type: 'phone',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: 'address',
|
||||
label: t`Address`,
|
||||
fieldtype: 'Link',
|
||||
target: 'Address',
|
||||
placeholder: t`Click to create`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
fieldname: 'addressDisplay',
|
||||
label: t`Address Display`,
|
||||
fieldtype: 'Text',
|
||||
readOnly: true,
|
||||
formula: (doc) => {
|
||||
if (doc.address) {
|
||||
return doc.getFrom('Address', doc.address, 'addressDisplay');
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
quickEditFields: ['email', 'phone', 'address', 'defaultAccount', 'currency'],
|
||||
};
|
161
models/baseModels/Party/Party.ts
Normal file
161
models/baseModels/Party/Party.ts
Normal file
@ -0,0 +1,161 @@
|
||||
import frappe from 'frappe';
|
||||
import Doc from 'frappe/model/doc';
|
||||
import {
|
||||
Action,
|
||||
FiltersMap,
|
||||
FormulaMap,
|
||||
ListViewSettings,
|
||||
} from 'frappe/model/types';
|
||||
import { PartyRole } from './types';
|
||||
|
||||
export class Party extends Doc {
|
||||
async updateOutstandingAmounts() {
|
||||
const role = this.role as PartyRole;
|
||||
switch (role) {
|
||||
case 'Customer':
|
||||
await this._updateOutstandingAmount('SalesInvoice');
|
||||
break;
|
||||
case 'Supplier':
|
||||
await this._updateOutstandingAmount('PurchaseInvoice');
|
||||
break;
|
||||
case 'Both':
|
||||
await this._updateOutstandingAmount('SalesInvoice');
|
||||
await this._updateOutstandingAmount('PurchaseInvoice');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async _updateOutstandingAmount(
|
||||
schemaName: 'SalesInvoice' | 'PurchaseInvoice'
|
||||
) {
|
||||
const outstandingAmounts = (
|
||||
await frappe.db.getAllRaw(schemaName, {
|
||||
fields: ['outstandingAmount', 'party'],
|
||||
filters: { submitted: true },
|
||||
})
|
||||
).filter(({ party }) => party === this.name);
|
||||
|
||||
const totalOutstanding = outstandingAmounts
|
||||
.map(({ outstandingAmount }) => frappe.pesa(outstandingAmount as number))
|
||||
.reduce((a, b) => a.add(b), frappe.pesa(0));
|
||||
|
||||
await this.set('outstandingAmount', totalOutstanding);
|
||||
await this.update();
|
||||
}
|
||||
|
||||
formulas: FormulaMap = {
|
||||
defaultAccount: async () => {
|
||||
const role = this.role as PartyRole;
|
||||
if (role === 'Both') {
|
||||
return '';
|
||||
}
|
||||
|
||||
let accountName = 'Debtors';
|
||||
if (role === 'Supplier') {
|
||||
accountName = 'Creditors';
|
||||
}
|
||||
|
||||
const accountExists = await frappe.db.exists('Account', accountName);
|
||||
return accountExists ? accountName : '';
|
||||
},
|
||||
currency: async () => frappe.singles.AccountingSettings!.currency as string,
|
||||
addressDisplay: async () => {
|
||||
const address = this.address as string | undefined;
|
||||
if (address) {
|
||||
return this.getFrom('Address', address, 'addressDisplay') as string;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
};
|
||||
|
||||
static filters: FiltersMap = {
|
||||
defaultAccount: (doc: Doc) => {
|
||||
const role = doc.role as PartyRole;
|
||||
if (role === 'Both') {
|
||||
return { isGroup: false, accountType: ['Payable', 'Receivable'] };
|
||||
}
|
||||
|
||||
return {
|
||||
isGroup: false,
|
||||
accountType: role === 'Customer' ? 'Receivable' : 'Payable',
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
static listSettings: ListViewSettings = {
|
||||
columns: ['name', 'phone', 'outstandingAmount'],
|
||||
};
|
||||
|
||||
static actions: Action[] = [
|
||||
{
|
||||
label: frappe.t`Create Bill`,
|
||||
condition: (doc: Doc) =>
|
||||
!doc.isNew && (doc.role as PartyRole) !== 'Customer',
|
||||
action: async (partyDoc, router) => {
|
||||
const doc = await frappe.doc.getEmptyDoc('PurchaseInvoice');
|
||||
router.push({
|
||||
path: `/edit/PurchaseInvoice/${doc.name}`,
|
||||
query: {
|
||||
doctype: 'PurchaseInvoice',
|
||||
values: {
|
||||
// @ts-ignore
|
||||
party: partyDoc.name!,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: frappe.t`View Bills`,
|
||||
condition: (doc: Doc) =>
|
||||
!doc.isNew && (doc.role as PartyRole) !== 'Customer',
|
||||
action: async (partyDoc, router) => {
|
||||
router.push({
|
||||
name: 'ListView',
|
||||
params: {
|
||||
doctype: 'PurchaseInvoice',
|
||||
filters: {
|
||||
// @ts-ignore
|
||||
party: partyDoc.name!,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: frappe.t`Create Invoice`,
|
||||
condition: (doc: Doc) =>
|
||||
!doc.isNew && (doc.role as PartyRole) !== 'Supplier',
|
||||
action: async (partyDoc, router) => {
|
||||
const doc = await frappe.doc.getEmptyDoc('SalesInvoice');
|
||||
router.push({
|
||||
path: `/edit/SalesInvoice/${doc.name}`,
|
||||
query: {
|
||||
doctype: 'SalesInvoice',
|
||||
values: {
|
||||
// @ts-ignore
|
||||
party: partyDoc.name!,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: frappe.t`View Invoices`,
|
||||
condition: (doc: Doc) =>
|
||||
!doc.isNew && (doc.role as PartyRole) !== 'Supplier',
|
||||
action: async (partyDoc, router) => {
|
||||
router.push({
|
||||
name: 'ListView',
|
||||
params: {
|
||||
doctype: 'SalesInvoice',
|
||||
filters: {
|
||||
// @ts-ignore
|
||||
party: partyDoc.name!,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
export default {
|
||||
doctype: 'Party',
|
||||
columns: ['name', 'phone', 'outstandingAmount'],
|
||||
};
|
@ -1,37 +0,0 @@
|
||||
import frappe from 'frappe';
|
||||
import Document from 'frappe/model/document';
|
||||
|
||||
export default class PartyServer extends Document {
|
||||
beforeInsert() {
|
||||
if (this.customer && this.supplier) {
|
||||
throw new Error('Select a single party type.');
|
||||
}
|
||||
|
||||
if (!this.customer && !this.supplier) {
|
||||
this.supplier = 1;
|
||||
}
|
||||
|
||||
if (this.gstin && ['Unregistered', 'Consumer'].includes(this.gstType)) {
|
||||
this.gstin = '';
|
||||
}
|
||||
}
|
||||
|
||||
async updateOutstandingAmount() {
|
||||
let isCustomer = this.customer;
|
||||
let doctype = isCustomer ? 'SalesInvoice' : 'PurchaseInvoice';
|
||||
|
||||
const outstandingAmounts = (
|
||||
await frappe.db.getAllRaw(doctype, {
|
||||
fields: ['outstandingAmount', 'party'],
|
||||
filters: { submitted: true },
|
||||
})
|
||||
).filter(({ party }) => party === this.name);
|
||||
|
||||
const totalOutstanding = outstandingAmounts
|
||||
.map(({ outstandingAmount }) => frappe.pesa(outstandingAmount))
|
||||
.reduce((a, b) => a.add(b), frappe.pesa(0));
|
||||
|
||||
await this.set('outstandingAmount', totalOutstanding);
|
||||
await this.update();
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
import { t } from 'frappe';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import PartyOriginal from './Party';
|
||||
|
||||
const gstTypes = ['Unregistered', 'Registered Regular', 'Consumer'];
|
||||
|
||||
export default function getAugmentedParty({ country }) {
|
||||
const Party = cloneDeep(PartyOriginal);
|
||||
if (!country) {
|
||||
return Party;
|
||||
}
|
||||
|
||||
if (country === 'India') {
|
||||
Party.fields.splice(
|
||||
3,
|
||||
0,
|
||||
{
|
||||
fieldname: 'gstin',
|
||||
label: t`GSTIN No.`,
|
||||
fieldtype: 'Data',
|
||||
hidden: (doc) => (doc.gstType === 'Registered Regular' ? 0 : 1),
|
||||
},
|
||||
{
|
||||
fieldname: 'gstType',
|
||||
label: t`GST Registration`,
|
||||
placeholder: t`GST Registration`,
|
||||
fieldtype: 'Select',
|
||||
default: gstTypes[0],
|
||||
options: gstTypes,
|
||||
}
|
||||
);
|
||||
Party.quickEditFields.push('gstType');
|
||||
Party.quickEditFields.push('gstin');
|
||||
} else {
|
||||
Party.fields.splice(3, 0, {
|
||||
fieldname: 'taxId',
|
||||
label: t`Tax ID`,
|
||||
fieldtype: 'Data',
|
||||
});
|
||||
Party.quickEditFields.push('taxId');
|
||||
}
|
||||
return Party;
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
import router from '@/router';
|
||||
import frappe, { t } from 'frappe';
|
||||
import { h } from 'vue';
|
||||
import PartyWidget from './PartyWidget.vue';
|
||||
|
||||
export default {
|
||||
name: 'Supplier',
|
||||
label: t`Supplier`,
|
||||
basedOn: 'Party',
|
||||
filters: {
|
||||
supplier: 1,
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
label: t`Create Bill`,
|
||||
condition: (doc) => !doc.isNew(),
|
||||
action: async (supplier) => {
|
||||
let doc = await frappe.getEmptyDoc('PurchaseInvoice');
|
||||
router.push({
|
||||
path: `/edit/PurchaseInvoice/${doc.name}`,
|
||||
query: {
|
||||
doctype: 'PurchaseInvoice',
|
||||
values: {
|
||||
supplier: supplier.name,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t`View Bills`,
|
||||
condition: (doc) => !doc.isNew(),
|
||||
action: (supplier) => {
|
||||
router.push({
|
||||
name: 'ListView',
|
||||
params: {
|
||||
doctype: 'PurchaseInvoice',
|
||||
filters: {
|
||||
supplier: supplier.name,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
quickEditWidget: (doc) => ({
|
||||
render() {
|
||||
return h(PartyWidget, {
|
||||
doc,
|
||||
});
|
||||
},
|
||||
}),
|
||||
};
|
@ -1,7 +0,0 @@
|
||||
import { t } from 'frappe';
|
||||
|
||||
export default {
|
||||
doctype: 'Supplier',
|
||||
title: t`Supplier`,
|
||||
columns: ['name', 'phone', 'outstandingAmount'],
|
||||
};
|
1
models/baseModels/Party/types.ts
Normal file
1
models/baseModels/Party/types.ts
Normal file
@ -0,0 +1 @@
|
||||
export type PartyRole = 'Both' | 'Supplier' | 'Customer';
|
24
models/helpers.ts
Normal file
24
models/helpers.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import frappe from 'frappe';
|
||||
import Doc from 'frappe/model/doc';
|
||||
import { Action } from 'frappe/model/types';
|
||||
import { Router } from 'vue-router';
|
||||
|
||||
export function getLedgerLinkAction(): Action {
|
||||
return {
|
||||
label: frappe.t`Ledger Entries`,
|
||||
condition: (doc: Doc) => !!doc.submitted,
|
||||
action: async (doc: Doc, router: Router) => {
|
||||
router.push({
|
||||
name: 'Report',
|
||||
params: {
|
||||
reportName: 'general-ledger',
|
||||
defaultFilters: {
|
||||
// @ts-ignore
|
||||
referenceType: doc.schemaName,
|
||||
referenceName: doc.name,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
18
models/regionalModels/in/Party.ts
Normal file
18
models/regionalModels/in/Party.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { HiddenMap } from 'frappe/model/types';
|
||||
import { Party as BaseParty } from 'models/baseModels/Party/Party';
|
||||
import { GSTType } from './types';
|
||||
|
||||
export class Party extends BaseParty {
|
||||
beforeInsert() {
|
||||
const gstin = this.get('gstin') as string | undefined;
|
||||
const gstType = this.get('gstType') as GSTType;
|
||||
|
||||
if (gstin && gstType !== 'Registered Regular') {
|
||||
this.gstin = '';
|
||||
}
|
||||
}
|
||||
|
||||
hidden: HiddenMap = {
|
||||
gstin: () => (this.gstType as GSTType) !== 'Registered Regular',
|
||||
};
|
||||
}
|
1
models/regionalModels/in/types.ts
Normal file
1
models/regionalModels/in/types.ts
Normal file
@ -0,0 +1 @@
|
||||
export type GSTType = 'Unregistered' | 'Registered Regular' | 'Consumer';
|
@ -72,15 +72,13 @@
|
||||
"target": "Address",
|
||||
"placeholder": "Click to create",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"fieldname": "taxId",
|
||||
"label": "Tax ID",
|
||||
"fieldtype": "Data"
|
||||
}
|
||||
],
|
||||
"quickEditFields": [
|
||||
"email",
|
||||
"role",
|
||||
"phone",
|
||||
"address",
|
||||
"defaultAccount",
|
||||
"currency"
|
||||
],
|
||||
"quickEditFields": ["email", "role", "phone", "address", "defaultAccount", "currency", "taxId"],
|
||||
"keywordFields": ["name"]
|
||||
}
|
||||
|
@ -18,10 +18,39 @@ export function getSchemas(countryCode: string = '-'): Readonly<SchemaMap> {
|
||||
|
||||
let schemaMap = Object.assign({}, builtAppSchemas, builtCoreSchemas);
|
||||
schemaMap = addMetaFields(schemaMap);
|
||||
schemaMap = removeFields(schemaMap);
|
||||
|
||||
deepFreeze(schemaMap);
|
||||
return schemaMap;
|
||||
}
|
||||
|
||||
function removeFields(schemaMap: SchemaMap): SchemaMap {
|
||||
for (const schemaName in schemaMap) {
|
||||
const schema = schemaMap[schemaName]!;
|
||||
if (schema.removeFields === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const fieldname of schema.removeFields) {
|
||||
schema.fields = schema.fields.filter((f) => f.fieldname !== fieldname);
|
||||
schema.tableFields = schema.tableFields?.filter((fn) => fn !== fieldname);
|
||||
schema.quickEditFields = schema.quickEditFields?.filter(
|
||||
(fn) => fn !== fieldname
|
||||
);
|
||||
schema.keywordFields = schema.keywordFields?.filter(
|
||||
(fn) => fn !== fieldname
|
||||
);
|
||||
if (schema.inlineEditDisplayField === fieldname) {
|
||||
delete schema.inlineEditDisplayField;
|
||||
}
|
||||
}
|
||||
|
||||
delete schema.removeFields;
|
||||
}
|
||||
|
||||
return schemaMap;
|
||||
}
|
||||
|
||||
function deepFreeze(schemaMap: SchemaMap) {
|
||||
Object.freeze(schemaMap);
|
||||
for (const schemaName in schemaMap) {
|
||||
|
@ -36,5 +36,6 @@
|
||||
"currency",
|
||||
"gstType",
|
||||
"gstin"
|
||||
]
|
||||
],
|
||||
"removeFields": ["taxId"]
|
||||
}
|
||||
|
@ -124,8 +124,9 @@ export interface Schema {
|
||||
keywordFields?: string[]; // Used to get fields that are to be used for search.
|
||||
quickEditFields?: string[]; // Used to get fields for the quickEditForm
|
||||
treeSettings?: TreeSettings; // Used to determine root nodes
|
||||
inlineEditDisplayField?:string,// Display field if inline editable
|
||||
inlineEditDisplayField?:string;// Display field if inline editable
|
||||
naming?: Naming; // Used for assigning name, default is 'random' else 'numberSeries' if present
|
||||
removeFields?: string[]; // Used by the builder to remove fields.
|
||||
}
|
||||
|
||||
export interface SchemaStub extends Partial<Schema> {
|
||||
|
Loading…
Reference in New Issue
Block a user