mirror of
https://github.com/frappe/books.git
synced 2024-11-09 23:30:56 +00:00
commit
341148e326
@ -14,6 +14,7 @@ import { getCountryInfo } from 'utils/misc';
|
||||
export class AccountingSettings extends Doc {
|
||||
enableDiscounting?: boolean;
|
||||
enableInventory?: boolean;
|
||||
enablePriceList?: boolean;
|
||||
|
||||
static filters: FiltersMap = {
|
||||
writeOffAccount: () => ({
|
||||
|
@ -35,6 +35,7 @@ export abstract class Invoice extends Transactional {
|
||||
party?: string;
|
||||
account?: string;
|
||||
currency?: string;
|
||||
priceList?: string;
|
||||
netTotal?: Money;
|
||||
grandTotal?: Money;
|
||||
baseGrandTotal?: Money;
|
||||
@ -514,6 +515,7 @@ export abstract class Invoice extends Transactional {
|
||||
attachment: () =>
|
||||
!(this.attachment || !(this.isSubmitted || this.isCancelled)),
|
||||
backReference: () => !this.backReference,
|
||||
priceList: () => !this.fyo.singles.AccountingSettings?.enablePriceList,
|
||||
};
|
||||
|
||||
static defaults: DefaultMap = {
|
||||
@ -544,6 +546,10 @@ export abstract class Invoice extends Transactional {
|
||||
accountType: doc.isSales ? 'Receivable' : 'Payable',
|
||||
}),
|
||||
numberSeries: (doc: Doc) => ({ referenceType: doc.schemaName }),
|
||||
priceList: (doc: Doc) => ({
|
||||
enabled: true,
|
||||
...(doc.isSales ? { selling: true } : { buying: true }),
|
||||
}),
|
||||
};
|
||||
|
||||
static createFilters: FiltersMap = {
|
||||
|
@ -17,6 +17,7 @@ import { safeParseFloat } from 'utils/index';
|
||||
import { Invoice } from '../Invoice/Invoice';
|
||||
import { Item } from '../Item/Item';
|
||||
import { StockTransfer } from 'models/inventory/StockTransfer';
|
||||
import { getPriceListRate } from 'models/helpers';
|
||||
|
||||
export abstract class InvoiceItem extends Doc {
|
||||
item?: string;
|
||||
@ -48,6 +49,18 @@ export abstract class InvoiceItem extends Doc {
|
||||
return this.schemaName === 'SalesInvoiceItem';
|
||||
}
|
||||
|
||||
get date() {
|
||||
return this.parentdoc?.date ?? undefined;
|
||||
}
|
||||
|
||||
get party() {
|
||||
return this.parentdoc?.party ?? undefined;
|
||||
}
|
||||
|
||||
get priceList() {
|
||||
return this.parentdoc?.priceList ?? undefined;
|
||||
}
|
||||
|
||||
get discountAfterTax() {
|
||||
return !!this?.parentdoc?.discountAfterTax;
|
||||
}
|
||||
@ -101,12 +114,15 @@ export abstract class InvoiceItem extends Doc {
|
||||
},
|
||||
rate: {
|
||||
formula: async (fieldname) => {
|
||||
const rate = (await this.fyo.getValue(
|
||||
const priceListRate = await getPriceListRate(this);
|
||||
const itemRate = (await this.fyo.getValue(
|
||||
'Item',
|
||||
this.item as string,
|
||||
'rate'
|
||||
)) as undefined | Money;
|
||||
|
||||
const rate = priceListRate instanceof Money ? priceListRate : itemRate;
|
||||
|
||||
if (!rate?.float && this.rate?.float) {
|
||||
return this.rate;
|
||||
}
|
||||
@ -144,6 +160,9 @@ export abstract class InvoiceItem extends Doc {
|
||||
return rateFromTotals ?? rate ?? this.fyo.pesa(0);
|
||||
},
|
||||
dependsOn: [
|
||||
'date',
|
||||
'priceList',
|
||||
'batch',
|
||||
'party',
|
||||
'exchangeRate',
|
||||
'item',
|
||||
|
60
models/baseModels/ItemPrice/ItemPrice.ts
Normal file
60
models/baseModels/ItemPrice/ItemPrice.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { t } from 'fyo';
|
||||
import { DocValue } from 'fyo/core/types';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { ValidationMap } from 'fyo/model/types';
|
||||
import { ValidationError } from 'fyo/utils/errors';
|
||||
import { getItemPrice } from 'models/helpers';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { Money } from 'pesa';
|
||||
|
||||
export class ItemPrice extends Doc {
|
||||
item?: string;
|
||||
rate?: Money;
|
||||
validFrom?: Date;
|
||||
validUpto?: Date;
|
||||
|
||||
get isBuying() {
|
||||
return !!this.parentdoc?.buying;
|
||||
}
|
||||
|
||||
get isSelling() {
|
||||
return !!this.parentdoc?.selling;
|
||||
}
|
||||
|
||||
get priceList() {
|
||||
return this.parentdoc?.name;
|
||||
}
|
||||
|
||||
validations: ValidationMap = {
|
||||
validUpto: async (value: DocValue) => {
|
||||
if (!value || !this.validFrom) {
|
||||
return;
|
||||
}
|
||||
if (value < this.validFrom) {
|
||||
throw new ValidationError(
|
||||
t`Valid From date can not be greater than Valid To date.`
|
||||
);
|
||||
}
|
||||
|
||||
const itemPrice = await getItemPrice(
|
||||
this,
|
||||
this.validFrom,
|
||||
this.validUpto
|
||||
);
|
||||
|
||||
if (!itemPrice) {
|
||||
return;
|
||||
}
|
||||
|
||||
const priceList = (await this.fyo.getValue(
|
||||
ModelNameEnum.ItemPrice,
|
||||
itemPrice,
|
||||
'parent'
|
||||
)) as string;
|
||||
|
||||
throw new ValidationError(
|
||||
t`an Item Price already exists for the given date in Price List ${priceList}`
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
18
models/baseModels/PriceList/PriceList.ts
Normal file
18
models/baseModels/PriceList/PriceList.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { ListViewSettings } from 'fyo/model/types';
|
||||
import { ItemPrice } from '../ItemPrice/ItemPrice';
|
||||
import { getPriceListStatusColumn } from 'models/helpers';
|
||||
|
||||
export class PriceList extends Doc {
|
||||
enabled?: boolean;
|
||||
buying?: boolean;
|
||||
selling?: boolean;
|
||||
isUomDependent?: boolean;
|
||||
priceListItem?: ItemPrice[];
|
||||
|
||||
static getListViewSettings(): ListViewSettings {
|
||||
return {
|
||||
columns: ['name', getPriceListStatusColumn()],
|
||||
};
|
||||
}
|
||||
}
|
220
models/baseModels/tests/testPriceList.spec.ts
Normal file
220
models/baseModels/tests/testPriceList.spec.ts
Normal file
@ -0,0 +1,220 @@
|
||||
import test from 'tape';
|
||||
import { getDefaultMetaFieldValueMap } from 'backend/helpers';
|
||||
import { closeTestFyo, getTestFyo, setupTestFyo } from 'tests/helpers';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { getItem } from 'models/inventory/tests/helpers';
|
||||
import { getItemPrice } from 'models/helpers';
|
||||
import { SalesInvoiceItem } from '../SalesInvoiceItem/SalesInvoiceItem';
|
||||
import { SalesInvoice } from '../SalesInvoice/SalesInvoice';
|
||||
import { PurchaseInvoiceItem } from '../PurchaseInvoiceItem/PurchaseInvoiceItem';
|
||||
|
||||
const fyo = getTestFyo();
|
||||
setupTestFyo(fyo, __filename);
|
||||
|
||||
const itemMap = {
|
||||
Pen: {
|
||||
name: 'Pen',
|
||||
rate: 100,
|
||||
hasBatch: true,
|
||||
},
|
||||
Ink: {
|
||||
name: 'Ink',
|
||||
rate: 50,
|
||||
},
|
||||
};
|
||||
|
||||
const partyMap = {
|
||||
partyOne: {
|
||||
name: 'John Whoe',
|
||||
email: 'john@whoe.com',
|
||||
},
|
||||
};
|
||||
|
||||
const batchMap = {
|
||||
batchOne: {
|
||||
name: 'PL-AB001',
|
||||
manufactureDate: '2022-11-03T09:57:04.528',
|
||||
},
|
||||
};
|
||||
|
||||
const priceListMap = {
|
||||
PL_SELL: {
|
||||
name: 'PL_SELL',
|
||||
enabled: true,
|
||||
party: 'Shaju',
|
||||
buying: false,
|
||||
selling: true,
|
||||
isUomDependent: false,
|
||||
itemPrice: [
|
||||
{
|
||||
enabled: true,
|
||||
item: itemMap.Pen.name,
|
||||
rate: 101,
|
||||
buying: false,
|
||||
selling: true,
|
||||
party: partyMap.partyOne.name,
|
||||
validFrom: '2023-02-28T18:30:00.678Z',
|
||||
validUpto: '2023-03-30T18:30:00.678Z',
|
||||
...getDefaultMetaFieldValueMap(),
|
||||
},
|
||||
],
|
||||
},
|
||||
PL_BUY: {
|
||||
name: 'PL_BUY',
|
||||
enabled: true,
|
||||
buying: true,
|
||||
selling: false,
|
||||
isUomDependent: false,
|
||||
itemPrice: [
|
||||
{
|
||||
enabled: true,
|
||||
item: itemMap.Pen.name,
|
||||
rate: 102,
|
||||
buying: true,
|
||||
selling: false,
|
||||
party: partyMap.partyOne.name,
|
||||
validFrom: '2023-02-28T18:30:00.678Z',
|
||||
validUpto: '2023-03-30T18:30:00.678Z',
|
||||
...getDefaultMetaFieldValueMap(),
|
||||
},
|
||||
],
|
||||
},
|
||||
PL_SB: {
|
||||
name: 'PL_SB',
|
||||
enabled: true,
|
||||
selling: true,
|
||||
buying: true,
|
||||
isUomDependent: false,
|
||||
itemPrice: [
|
||||
{
|
||||
enabled: true,
|
||||
item: itemMap.Pen.name,
|
||||
rate: 104,
|
||||
batch: batchMap.batchOne.name,
|
||||
buying: true,
|
||||
selling: true,
|
||||
party: partyMap.partyOne.name,
|
||||
validFrom: '2023-05-05T18:30:00.000Z',
|
||||
validUpto: '2023-06-05T18:30:00.000Z',
|
||||
...getDefaultMetaFieldValueMap(),
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
test('Price List: create dummy items, parties and batches', async (t) => {
|
||||
// Create Items
|
||||
for (const { name, rate } of Object.values(itemMap)) {
|
||||
const item = getItem(name, rate, false);
|
||||
await fyo.doc.getNewDoc(ModelNameEnum.Item, item).sync();
|
||||
t.ok(await fyo.db.exists(ModelNameEnum.Item, name), `Item: ${name} exists`);
|
||||
}
|
||||
|
||||
// Create Parties
|
||||
for (const { name, email } of Object.values(partyMap)) {
|
||||
await fyo.doc.getNewDoc(ModelNameEnum.Party, { name, email }).sync();
|
||||
t.ok(
|
||||
await fyo.db.exists(ModelNameEnum.Party, name),
|
||||
`Party: ${name} exists`
|
||||
);
|
||||
}
|
||||
|
||||
// Create Batches
|
||||
for (const batch of Object.values(batchMap)) {
|
||||
await fyo.doc.getNewDoc(ModelNameEnum.Batch, batch).sync();
|
||||
t.ok(
|
||||
await fyo.db.exists(ModelNameEnum.Batch, batch.name),
|
||||
`Batch: ${batch.name} exists`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('create Price Lists', async (t) => {
|
||||
for (const priceListItem of Object.values(priceListMap)) {
|
||||
await fyo.doc.getNewDoc(ModelNameEnum.PriceList, priceListItem).sync();
|
||||
t.ok(
|
||||
await fyo.db.exists(ModelNameEnum.PriceList, priceListItem.name),
|
||||
`Price List ${priceListItem.name} exists`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('check item price', async (t) => {
|
||||
// check selling enabled item price
|
||||
const sinv = fyo.doc.getNewDoc(ModelNameEnum.SalesInvoice, {
|
||||
items: [{ item: itemMap.Pen.name, quantity: 1 }],
|
||||
date: priceListMap.PL_SELL.itemPrice[0].validFrom,
|
||||
priceList: priceListMap.PL_SELL.name,
|
||||
party: partyMap.partyOne.name,
|
||||
}) as SalesInvoice;
|
||||
|
||||
const sinvItem = Object.values(sinv.items ?? {})[0];
|
||||
const sellEnabled = await getItemPrice(sinvItem as SalesInvoiceItem);
|
||||
|
||||
const sellEnabledPLName = await fyo.getValue(
|
||||
ModelNameEnum.ItemPrice,
|
||||
sellEnabled as string,
|
||||
'parent'
|
||||
);
|
||||
|
||||
t.equal(sellEnabledPLName, priceListMap.PL_SELL.name);
|
||||
|
||||
// check buying enabled item price
|
||||
const pinv = fyo.doc.getNewDoc(ModelNameEnum.PurchaseInvoice, {
|
||||
items: [{ item: itemMap.Pen.name, quantity: 1 }],
|
||||
date: priceListMap.PL_BUY.itemPrice[0].validFrom,
|
||||
priceList: priceListMap.PL_BUY.name,
|
||||
party: partyMap.partyOne.name,
|
||||
}) as SalesInvoice;
|
||||
|
||||
const pinvItem = Object.values(pinv.items ?? {})[0];
|
||||
const buyEnabled = await getItemPrice(pinvItem as PurchaseInvoiceItem);
|
||||
|
||||
const buyEnabledPLName = await fyo.getValue(
|
||||
ModelNameEnum.ItemPrice,
|
||||
buyEnabled as string,
|
||||
'parent'
|
||||
);
|
||||
|
||||
t.equal(buyEnabledPLName, priceListMap.PL_BUY.name);
|
||||
|
||||
// check sell batch enabled
|
||||
const sinv1 = fyo.doc.getNewDoc(ModelNameEnum.SalesInvoice, {
|
||||
items: [
|
||||
{ item: itemMap.Pen.name, quantity: 1, batch: batchMap.batchOne.name },
|
||||
],
|
||||
date: priceListMap.PL_SB.itemPrice[0].validFrom,
|
||||
priceList: priceListMap.PL_SB.name,
|
||||
party: partyMap.partyOne.name,
|
||||
}) as SalesInvoice;
|
||||
|
||||
const sinv1Item = Object.values(sinv1.items ?? {})[0];
|
||||
const sellBatchEnabled = await getItemPrice(sinv1Item as SalesInvoiceItem);
|
||||
|
||||
const sellBatchEnabledPLName = await fyo.getValue(
|
||||
ModelNameEnum.ItemPrice,
|
||||
sellBatchEnabled as string,
|
||||
'parent'
|
||||
);
|
||||
|
||||
t.equal(sellBatchEnabledPLName, priceListMap.PL_SB.name);
|
||||
|
||||
// undefined returns
|
||||
const sinv2 = fyo.doc.getNewDoc(ModelNameEnum.SalesInvoice, {
|
||||
items: [{ item: itemMap.Ink.name, quantity: 1 }],
|
||||
date: priceListMap.PL_SELL.itemPrice[0].validFrom,
|
||||
priceList: priceListMap.PL_SELL.name,
|
||||
party: partyMap.partyOne.name,
|
||||
}) as SalesInvoice;
|
||||
|
||||
const sinv2Item = Object.values(sinv2.items ?? {})[0];
|
||||
const nonExistItem = await getItemPrice(sinv2Item as SalesInvoiceItem);
|
||||
|
||||
t.equal(
|
||||
nonExistItem,
|
||||
undefined,
|
||||
'itemPrice of non-existing item in price list returns false'
|
||||
);
|
||||
});
|
||||
|
||||
closeTestFyo(fyo, __filename);
|
@ -17,6 +17,8 @@ import { Invoice } from './baseModels/Invoice/Invoice';
|
||||
import { StockMovement } from './inventory/StockMovement';
|
||||
import { StockTransfer } from './inventory/StockTransfer';
|
||||
import { InvoiceStatus, ModelNameEnum } from './types';
|
||||
import { InvoiceItem } from './baseModels/InvoiceItem/InvoiceItem';
|
||||
import { ItemPrice } from './baseModels/ItemPrice/ItemPrice';
|
||||
|
||||
export function getInvoiceActions(
|
||||
fyo: Fyo,
|
||||
@ -325,6 +327,122 @@ export function getSerialNumberStatusText(status: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
export function getPriceListStatusColumn(): ColumnConfig {
|
||||
return {
|
||||
label: t`Enabled For`,
|
||||
fieldname: 'enabledFor',
|
||||
fieldtype: 'Select',
|
||||
render(doc) {
|
||||
let status = 'None';
|
||||
|
||||
if (doc.buying && !doc.selling) {
|
||||
status = 'Buying';
|
||||
}
|
||||
|
||||
if (doc.selling && !doc.buying) {
|
||||
status = 'Selling';
|
||||
}
|
||||
|
||||
if (doc.buying && doc.selling) {
|
||||
status = 'Buying & Selling';
|
||||
}
|
||||
|
||||
return {
|
||||
template: `<Badge class="text-xs" color="gray">${status}</Badge>`,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function getItemPrice(
|
||||
doc: InvoiceItem | ItemPrice,
|
||||
validFrom?: Date,
|
||||
validUpto?: Date
|
||||
): Promise<string | undefined> {
|
||||
if (!doc.item || !doc.priceList) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isUomDependent = await doc.fyo.getValue(
|
||||
ModelNameEnum.PriceList,
|
||||
doc.priceList,
|
||||
'isUomDependent'
|
||||
);
|
||||
|
||||
const itemPriceQuery = Object.values(
|
||||
await doc.fyo.db.getAll(ModelNameEnum.ItemPrice, {
|
||||
filters: {
|
||||
enabled: true,
|
||||
item: doc.item,
|
||||
...(doc.isSales ? { selling: true } : { buying: true }),
|
||||
...(doc.batch ? { batch: doc.batch as string } : { batch: null }),
|
||||
},
|
||||
fields: ['name', 'unit', 'party', 'batch', 'validFrom', 'validUpto'],
|
||||
})
|
||||
)[0];
|
||||
|
||||
if (!itemPriceQuery) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { name, unit, party } = itemPriceQuery;
|
||||
const validFromDate = validFrom ?? itemPriceQuery.validFrom;
|
||||
const validUptoDate = validFrom ?? itemPriceQuery.validUpto;
|
||||
let date;
|
||||
|
||||
if (doc.date) {
|
||||
date = new Date((doc.date as Date).setHours(0, 0, 0));
|
||||
}
|
||||
|
||||
if (isUomDependent && unit !== doc.unit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (party && doc.party !== party) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (date instanceof Date) {
|
||||
if (validFromDate && date < validFromDate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (validUptoDate && date > validUptoDate) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (validFrom && validUpto) {
|
||||
if (validFromDate && validFrom < validFromDate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (validUptoDate && validFrom > validUptoDate) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return name as string;
|
||||
}
|
||||
|
||||
export async function getPriceListRate(
|
||||
doc: InvoiceItem
|
||||
): Promise<Money | undefined> {
|
||||
const itemPrice = await getItemPrice(doc);
|
||||
|
||||
if (!itemPrice) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemPriceRate = (await doc.fyo.getValue(
|
||||
ModelNameEnum.ItemPrice,
|
||||
itemPrice,
|
||||
'rate'
|
||||
)) as Money;
|
||||
|
||||
return itemPriceRate;
|
||||
}
|
||||
|
||||
export async function getExchangeRate({
|
||||
fromCurrency,
|
||||
toCurrency,
|
||||
|
@ -10,6 +10,8 @@ import { JournalEntryAccount } from './baseModels/JournalEntryAccount/JournalEnt
|
||||
import { Party } from './baseModels/Party/Party';
|
||||
import { Payment } from './baseModels/Payment/Payment';
|
||||
import { PaymentFor } from './baseModels/PaymentFor/PaymentFor';
|
||||
import { PriceList } from './baseModels/PriceList/PriceList';
|
||||
import { ItemPrice } from './baseModels/ItemPrice/ItemPrice';
|
||||
import { PrintSettings } from './baseModels/PrintSettings/PrintSettings';
|
||||
import { PurchaseInvoice } from './baseModels/PurchaseInvoice/PurchaseInvoice';
|
||||
import { PurchaseInvoiceItem } from './baseModels/PurchaseInvoiceItem/PurchaseInvoiceItem';
|
||||
@ -45,6 +47,8 @@ export const models = {
|
||||
Payment,
|
||||
PaymentFor,
|
||||
PrintSettings,
|
||||
PriceList,
|
||||
ItemPrice,
|
||||
PurchaseInvoice,
|
||||
PurchaseInvoiceItem,
|
||||
SalesInvoice,
|
||||
|
@ -10,6 +10,7 @@ export enum ModelNameEnum {
|
||||
GetStarted = 'GetStarted',
|
||||
Defaults = 'Defaults',
|
||||
Item = 'Item',
|
||||
ItemPrice = 'ItemPrice',
|
||||
UOM = 'UOM',
|
||||
UOMConversionItem = 'UOMConversionItem',
|
||||
JournalEntry = 'JournalEntry',
|
||||
@ -19,6 +20,7 @@ export enum ModelNameEnum {
|
||||
Party = 'Party',
|
||||
Payment = 'Payment',
|
||||
PaymentFor = 'PaymentFor',
|
||||
PriceList = 'PriceList',
|
||||
PrintSettings = 'PrintSettings',
|
||||
PrintTemplate = 'PrintTemplate',
|
||||
PurchaseInvoice = 'PurchaseInvoice',
|
||||
|
@ -79,6 +79,13 @@
|
||||
"default": false,
|
||||
"section": "Features"
|
||||
},
|
||||
{
|
||||
"fieldname": "enablePriceList",
|
||||
"label": "Enable Price List",
|
||||
"fieldtype": "Check",
|
||||
"default": false,
|
||||
"section": "Features"
|
||||
},
|
||||
{
|
||||
"fieldname": "fiscalYearStart",
|
||||
"label": "Fiscal Year Start Date",
|
||||
|
@ -43,6 +43,13 @@
|
||||
"required": true,
|
||||
"section": "Default"
|
||||
},
|
||||
{
|
||||
"fieldname": "priceList",
|
||||
"label": "Price List",
|
||||
"fieldtype": "Link",
|
||||
"target": "PriceList",
|
||||
"section": "Default"
|
||||
},
|
||||
{
|
||||
"abstract": true,
|
||||
"fieldname": "items",
|
||||
|
98
schemas/app/ItemPrice.json
Normal file
98
schemas/app/ItemPrice.json
Normal file
@ -0,0 +1,98 @@
|
||||
{
|
||||
"name": "ItemPrice",
|
||||
"label": "Item Price",
|
||||
"isChild": true,
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "enabled",
|
||||
"label": "Enabled",
|
||||
"fieldtype": "Check",
|
||||
"default": true,
|
||||
"section": "Price List"
|
||||
},
|
||||
{
|
||||
"fieldname": "buying",
|
||||
"label": "Buying",
|
||||
"fieldtype": "Check",
|
||||
"placeholder": "Buying",
|
||||
"default": false,
|
||||
"section": "Price List"
|
||||
},
|
||||
{
|
||||
"fieldname": "selling",
|
||||
"label": "Selling",
|
||||
"fieldtype": "Check",
|
||||
"placeholder": "Selling",
|
||||
"default": false,
|
||||
"section": "Price List"
|
||||
},
|
||||
{
|
||||
"fieldname": "party",
|
||||
"label": "Party",
|
||||
"placeholder": "Party",
|
||||
"fieldtype": "Link",
|
||||
"target": "Party",
|
||||
"create": true,
|
||||
"section": "Price List"
|
||||
},
|
||||
{
|
||||
"fieldname": "item",
|
||||
"label": "Item",
|
||||
"fieldtype": "Link",
|
||||
"target": "Item",
|
||||
"required": true,
|
||||
"create": true,
|
||||
"section": "Item"
|
||||
},
|
||||
{
|
||||
"fieldname": "unit",
|
||||
"label": "Unit Type",
|
||||
"fieldtype": "Link",
|
||||
"target": "UOM",
|
||||
"default": "Unit",
|
||||
"section": "Item"
|
||||
},
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"label": "Rate",
|
||||
"fieldtype": "Currency",
|
||||
"required": true,
|
||||
"section": "Item"
|
||||
},
|
||||
{
|
||||
"fieldname": "batch",
|
||||
"label": "Batch",
|
||||
"fieldtype": "Link",
|
||||
"target": "Batch",
|
||||
"create": true,
|
||||
"section": "Item"
|
||||
},
|
||||
{
|
||||
"fieldname": "validFrom",
|
||||
"label": "Valid From",
|
||||
"fieldtype": "Date",
|
||||
"placeholder": "Valid From",
|
||||
"section": "Validity"
|
||||
},
|
||||
{
|
||||
"fieldname": "validUpto",
|
||||
"label": "Valid Upto",
|
||||
"fieldtype": "Date",
|
||||
"placeholder": "Valid Upto",
|
||||
"section": "Validity"
|
||||
}
|
||||
],
|
||||
"tableFields": ["item", "rate", "enabled"],
|
||||
"quickEditFields": [
|
||||
"enabled",
|
||||
"buying",
|
||||
"selling",
|
||||
"party",
|
||||
"item",
|
||||
"unit",
|
||||
"rate",
|
||||
"batch",
|
||||
"validFrom",
|
||||
"validUpto"
|
||||
]
|
||||
}
|
48
schemas/app/PriceList.json
Normal file
48
schemas/app/PriceList.json
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "PriceList",
|
||||
"label": "Price List",
|
||||
"naming": "manual",
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "name",
|
||||
"label": "Name",
|
||||
"fieldtype": "Data",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "enabled",
|
||||
"label": "Enabled",
|
||||
"fieldtype": "Check",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"fieldname": "buying",
|
||||
"label": "Buying",
|
||||
"fieldtype": "Check",
|
||||
"default": false,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "selling",
|
||||
"label": "Selling",
|
||||
"fieldtype": "Check",
|
||||
"default": false,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "isUomDependent",
|
||||
"label": "Is Price UOM Dependent",
|
||||
"fieldtype": "Check",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"fieldname": "itemPrice",
|
||||
"label": "Item Prices",
|
||||
"fieldtype": "Table",
|
||||
"target": "ItemPrice",
|
||||
"edit": true,
|
||||
"required": true,
|
||||
"section": "Item Prices"
|
||||
}
|
||||
]
|
||||
}
|
@ -9,6 +9,8 @@ import Defaults from './app/Defaults.json';
|
||||
import GetStarted from './app/GetStarted.json';
|
||||
import InventorySettings from './app/inventory/InventorySettings.json';
|
||||
import Location from './app/inventory/Location.json';
|
||||
import PriceList from './app/PriceList.json';
|
||||
import ItemPrice from './app/ItemPrice.json';
|
||||
import PurchaseReceipt from './app/inventory/PurchaseReceipt.json';
|
||||
import PurchaseReceiptItem from './app/inventory/PurchaseReceiptItem.json';
|
||||
import SerialNumber from './app/inventory/SerialNumber.json';
|
||||
@ -100,6 +102,9 @@ export const appSchemas: Schema[] | SchemaStub[] = [
|
||||
SalesInvoiceItem as SchemaStub,
|
||||
PurchaseInvoiceItem as SchemaStub,
|
||||
|
||||
PriceList as Schema,
|
||||
ItemPrice as SchemaStub,
|
||||
|
||||
Tax as Schema,
|
||||
TaxDetail as Schema,
|
||||
TaxSummary as Schema,
|
||||
|
@ -245,6 +245,13 @@ async function getCompleteSidebar(): Promise<SidebarConfig> {
|
||||
schemaName: 'Item',
|
||||
filters: { for: 'Both' },
|
||||
},
|
||||
{
|
||||
label: t`Price List`,
|
||||
name: 'price-list',
|
||||
route: '/list/PriceList',
|
||||
schemaName: 'PriceList',
|
||||
hidden: () => !fyo.singles.AccountingSettings?.enablePriceList,
|
||||
},
|
||||
] as SidebarItem[],
|
||||
},
|
||||
await getReportSidebar(),
|
||||
|
Loading…
Reference in New Issue
Block a user