2
0
mirror of https://github.com/frappe/books.git synced 2024-11-08 14:50:56 +00:00

fix(ui): a few route issues with common docs

This commit is contained in:
18alantom 2023-01-09 12:33:26 +05:30
parent 694268f843
commit 4aa92457c7
16 changed files with 253 additions and 73 deletions

View File

@ -2,7 +2,7 @@ import { DocValue, DocValueMap } from 'fyo/core/types';
import SystemSettings from 'fyo/models/SystemSettings';
import { FieldType, Schema, SelectOption } from 'schemas/types';
import { QueryFilter } from 'utils/db/types';
import { Router } from 'vue-router';
import { RouteLocationRaw, Router } from 'vue-router';
import { Doc } from './doc';
/**
@ -87,7 +87,7 @@ export interface ColumnConfig {
export type ListViewColumn = string | ColumnConfig;
export interface ListViewSettings {
formRoute?: (name: string) => string;
formRoute?: (doc: Doc) => RouteLocationRaw;
columns?: ListViewColumn[];
}

View File

@ -17,6 +17,7 @@ import { AccountRootTypeEnum, AccountTypeEnum } from '../Account/types';
export class Item extends Doc {
trackItem?: boolean;
itemType?: 'Product' | 'Service';
for?: 'Purchases' | 'Sales' | 'Both';
formulas: FormulaMap = {
incomeAccount: {

View File

@ -4,14 +4,15 @@ import {
Action,
DefaultMap,
FiltersMap,
ListViewSettings
ListViewSettings,
} from 'fyo/model/types';
import { DateTime } from 'luxon';
import {
getDocStatus,
getLedgerLinkAction,
getNumberSeries, getStatusText,
statusColor
getNumberSeries,
getStatusText,
statusColor,
} from 'models/helpers';
import { Transactional } from 'models/Transactional/Transactional';
import { Money } from 'pesa';
@ -53,7 +54,7 @@ export class JournalEntry extends Transactional {
static getListViewSettings(): ListViewSettings {
return {
formRoute: (name) => `/edit/JournalEntry/${name}`,
formRoute: ({ name }) => `/edit/JournalEntry/${name}`,
columns: [
'name',
{

View File

@ -37,7 +37,7 @@ export class PurchaseInvoice extends Invoice {
static getListViewSettings(): ListViewSettings {
return {
formRoute: (name) => `/edit/PurchaseInvoice/${name}`,
formRoute: ({ name }) => `/edit/PurchaseInvoice/${name}`,
columns: [
'name',
getTransactionStatusColumn(),

View File

@ -37,7 +37,7 @@ export class SalesInvoice extends Invoice {
static getListViewSettings(): ListViewSettings {
return {
formRoute: (name) => `/edit/SalesInvoice/${name}`,
formRoute: ({ name }) => `/edit/SalesInvoice/${name}`,
columns: [
'name',
getTransactionStatusColumn(),

View File

@ -8,7 +8,7 @@ export class PurchaseReceipt extends StockTransfer {
static getListViewSettings(): ListViewSettings {
return {
formRoute: (name) => `/edit/PurchaseReceipt/${name}`,
formRoute: ({ name }) => `/edit/PurchaseReceipt/${name}`,
columns: [
'name',
getTransactionStatusColumn(),

View File

@ -8,7 +8,7 @@ export class Shipment extends StockTransfer {
static getListViewSettings(): ListViewSettings {
return {
formRoute: (name) => `/edit/Shipment/${name}`,
formRoute: ({ name }) => `/edit/Shipment/${name}`,
columns: [
'name',
getTransactionStatusColumn(),

View File

@ -55,7 +55,7 @@
<script lang="ts">
import { Money } from 'pesa';
import { getEntryRoute } from 'src/router';
import { getCreateRoute } from 'src/router';
import { getStatus, routeTo } from 'src/utils/ui';
import { defineComponent, PropType } from 'vue';
import Button from '../Button.vue';
@ -80,7 +80,7 @@ export default defineComponent({
methods: {
getStatus,
async openEntry(name: string) {
const route = getEntryRoute(this.linked.schemaName, name);
const route = getCreateRoute(this.linked.schemaName, name);
await routeTo(route);
},
},

View File

@ -46,7 +46,7 @@
<Row
gap="1rem"
class="cursor-pointer text-gray-900 flex-1 border-none h-row-mid"
@click="openForm(doc)"
@click="$emit('openDoc', doc.name)"
:columnCount="columns.length"
>
<ListCell
@ -103,7 +103,7 @@ import ListCell from './ListCell';
export default defineComponent({
name: 'List',
props: { listConfig: Object, filters: Object, schemaName: String },
emits: ['makeNewDoc', 'updatedData'],
emits: ['openDoc', 'makeNewDoc', 'updatedData'],
components: {
Row,
ListCell,
@ -180,17 +180,6 @@ export default defineComponent({
fyo.db.observer.on(`delete:${this.schemaName}`, listener);
fyo.doc.observer.on(`rename:${this.schemaName}`, listener);
},
openForm(doc) {
if (this.listConfig.formRoute) {
routeTo(this.listConfig.formRoute(doc.name));
return;
}
openQuickEdit({
schemaName: this.schemaName,
name: doc.name,
});
},
async updateData(filters) {
if (!filters) {
filters = { ...this.filters };

View File

@ -25,6 +25,7 @@
:listConfig="listConfig"
:filters="filters"
class="flex-1 flex h-full"
@openDoc="openDoc"
@updatedData="updatedData"
@makeNewDoc="makeNewDoc"
/>
@ -44,8 +45,12 @@ import FilterDropdown from 'src/components/FilterDropdown.vue';
import Modal from 'src/components/Modal.vue';
import PageHeader from 'src/components/PageHeader.vue';
import { fyo } from 'src/initFyo';
import { docsPathMap, getCreateFiltersFromListViewFilters } from 'src/utils/misc';
import { docsPath, routeTo } from 'src/utils/ui';
import { getRouteData } from 'src/utils/filters';
import {
docsPathMap,
getCreateFiltersFromListViewFilters,
} from 'src/utils/misc';
import { docsPath, openQuickEdit, routeTo } from 'src/utils/ui';
import List from './List.vue';
export default {
@ -89,32 +94,52 @@ export default {
updatedData(listFilters) {
this.listFilters = listFilters;
},
async openDoc(name) {
const doc = await this.fyo.doc.getDoc(this.schemaName, name);
if (this.listConfig.formRoute) {
return await routeTo(this.listConfig.formRoute(doc));
}
const { routeFilter } = getRouteData({ doc });
openQuickEdit({
doc,
listFilters: routeFilter,
});
},
async makeNewDoc() {
const filters = getCreateFiltersFromListViewFilters(this.filters ?? {});
const doc = fyo.doc.getNewDoc(this.schemaName, filters);
const path = this.getFormPath(doc.name);
const path = this.getFormPath(doc);
await routeTo(path);
doc.on('afterSync', () => {
const path = this.getFormPath(doc.name);
const path = this.getFormPath(doc);
this.$router.replace(path);
});
},
applyFilter(filters) {
this.$refs.list.updateData(filters);
},
getFormPath(name) {
getFormPath(doc) {
const { label, routeFilter } = getRouteData({ doc });
let path = {
path: `/list/${this.schemaName}`,
path: `/list/${this.schemaName}/${label}`,
query: {
edit: 1,
schemaName: this.schemaName,
name,
name: doc.name,
filters: JSON.stringify(routeFilter),
},
};
if (this.listConfig.formRoute) {
path = this.listConfig.formRoute(name);
path = this.listConfig.formRoute(doc);
}
if (typeof path === 'object') {
return path;
}
// Maintain filter if present

View File

@ -11,7 +11,12 @@ import PrintView from 'src/pages/PrintView/PrintView.vue';
import QuickEditForm from 'src/pages/QuickEditForm.vue';
import Report from 'src/pages/Report.vue';
import Settings from 'src/pages/Settings/Settings.vue';
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import {
createRouter,
createWebHistory,
RouteLocationRaw,
RouteRecordRaw,
} from 'vue-router';
function getGeneralFormItems(): RouteRecordRaw[] {
return [ModelNameEnum.Shipment, ModelNameEnum.PurchaseReceipt].map(
@ -151,7 +156,10 @@ const routes: RouteRecordRaw[] = [
},
];
export function getEntryRoute(schemaName: string, name: string) {
export function getCreateRoute(
schemaName: string,
name: string
): RouteLocationRaw {
if (
[
ModelNameEnum.SalesInvoice,

124
src/utils/filters.ts Normal file
View File

@ -0,0 +1,124 @@
import { t } from 'fyo';
import type { Doc } from 'fyo/model/doc';
import { ValueError } from 'fyo/utils/errors';
import type { Item } from 'models/baseModels/Item/Item';
import type { Party } from 'models/baseModels/Party/Party';
import type { Payment } from 'models/baseModels/Payment/Payment';
import { ModelNameEnum } from 'models/types';
import { fyo } from 'src/initFyo';
export const routeFilters = {
SalesItems: { for: ['in', ['Sales', 'Both']] },
PurchaseItems: { for: ['in', ['Purchases', 'Both']] },
Items: { for: 'Both' },
PurchasePayments: { paymentType: 'Pay' },
SalesPayments: { paymentType: 'Receive' },
Suppliers: { role: ['in', ['Supplier', 'Both']] },
Customers: { role: ['in', ['Customer', 'Both']] },
Party: { role: 'Both' },
};
export const createFilters = {
SalesItems: { for: 'Sales' },
PurchaseItems: { for: 'Purchases' },
Items: { for: 'Both' },
PurchasePayments: { paymentType: 'Pay' },
SalesPayments: { paymentType: 'Receive' },
Suppliers: { role: 'Supplier' },
Customers: { role: 'Customer' },
Party: { role: 'Both' },
};
export function getRouteData({ doc, uiname }: { doc?: Doc; uiname?: string }) {
if (doc) {
uiname = getUiName(doc);
}
console.log(uiname);
if (!uiname) {
throw new ValueError(`Doc and uiname not passed to getFilters`);
}
const routeFilter = routeFilters[uiname as keyof typeof routeFilters] ?? {};
const createFilter =
createFilters[uiname as keyof typeof createFilters] ?? {};
const label = getLabelName(uiname);
return { routeFilter, createFilter, label };
}
function getLabelName(uiname: string) {
const labels = {
SalesItems: t`Sales Items`,
PurchaseItems: t`Purchase Items`,
Items: t`Items`,
Suppliers: t`Suppliers`,
Customers: t`Customers`,
Party: t`Party`,
PurchasePayments: t`Purchase Payments`,
SalesPayments: t`Sales Payments`,
};
return labels[uiname as keyof typeof labels] ?? fyo.schemaMap[uiname]?.label;
}
function getUiName(doc: Doc) {
const isDual = [
ModelNameEnum.Party,
ModelNameEnum.Payment,
ModelNameEnum.Item,
].includes(doc.schemaName as ModelNameEnum);
if (!isDual) {
return doc.schemaName;
}
if (doc.schemaName === ModelNameEnum.Party) {
return getPartyUiName(doc as Party);
}
if (doc.schemaName === ModelNameEnum.Payment) {
return getPaymentUiName(doc as Payment);
}
if (doc.schemaName === ModelNameEnum.Item) {
return getItemUiName(doc as Item);
}
return doc.schemaName;
}
function getPartyUiName(doc: Party) {
switch (doc.role) {
case 'Customer':
return 'Customers';
case 'Supplier':
return 'Suppliers';
default:
return doc.schemaName;
}
}
function getPaymentUiName(doc: Payment) {
switch (doc.paymentType) {
case 'Pay':
return 'PurchasePayments';
case 'Receive':
return 'SalesPayments';
default:
return doc.schemaName;
}
}
function getItemUiName(doc: Item) {
switch (doc.for) {
case 'Purchases':
return 'PurchaseItems';
case 'Sales':
return 'SalesItems';
case 'Both':
return 'Items';
default:
return doc.schemaName;
}
}

View File

@ -4,9 +4,11 @@ import { groupBy } from 'lodash';
import { ModelNameEnum } from 'models/types';
import { reports } from 'reports';
import { OptionField } from 'schemas/types';
import { getEntryRoute } from 'src/router';
import { getCreateRoute } from 'src/router';
import { createFilters, routeFilters } from 'src/utils/filters';
import { GetAllOptions } from 'utils/db/types';
import { safeParseFloat } from 'utils/index';
import { RouteLocationRaw } from 'vue-router';
import { fuzzyMatch } from '.';
import { routeTo } from './ui';
@ -92,9 +94,6 @@ async function openFormEditDoc(schemaName: string, fyo: Fyo) {
function getCreateList(fyo: Fyo): SearchItem[] {
const hasInventory = fyo.doc.singles.AccountingSettings?.enableInventory;
const quickEditCreateList = [
ModelNameEnum.Item,
ModelNameEnum.Party,
ModelNameEnum.Payment,
...(hasInventory ? [ModelNameEnum.StockMovement] : []),
].map(
(schemaName) =>
@ -126,36 +125,60 @@ function getCreateList(fyo: Fyo): SearchItem[] {
);
const filteredCreateList = [
{
label: t`Sales Payments`,
schemaName: ModelNameEnum.Payment,
route: `/list/Payment/${t`Sales Payments`}`,
create: createFilters.SalesPayments,
filter: routeFilters.SalesPayments,
},
{
label: t`Purchase Payments`,
schemaName: ModelNameEnum.Payment,
route: `/list/Payment/${t`Purchase Payments`}`,
create: createFilters.PurchasePayments,
filter: routeFilters.PurchasePayments,
},
{
label: t`Customers`,
schemaName: ModelNameEnum.Party,
filter: { role: 'Customer' },
create: createFilters.Customers,
filter: routeFilters.Customers,
},
{
label: t`Suppliers`,
schemaName: ModelNameEnum.Party,
filter: { role: 'Supplier' },
create: createFilters.Suppliers,
filter: routeFilters.Suppliers,
},
{
label: t`Party`,
schemaName: ModelNameEnum.Party,
create: createFilters.Party,
filter: routeFilters.Party,
},
{
label: t`Sales Items`,
schemaName: ModelNameEnum.Item,
filter: { for: 'Sales' },
create: createFilters.SalesItems,
filter: routeFilters.SalesItems,
},
{
label: t`Purchase Items`,
schemaName: ModelNameEnum.Item,
filter: { for: 'Purchases' },
create: createFilters.PurchaseItems,
filter: routeFilters.PurchaseItems,
},
{
label: t`Common Items`,
label: t`Items`,
schemaName: ModelNameEnum.Item,
filter: { for: 'Both' },
create: createFilters.Items,
filter: routeFilters.Items,
},
].map(({ label, filter, schemaName }) => {
const fk = Object.keys(filter)[0] as 'for' | 'role';
].map(({ label, filter, create, schemaName }) => {
const route = {
path: `/list/${schemaName}/${label}`,
query: { filters: JSON.stringify({ [fk]: filter[fk] }) },
query: { filters: JSON.stringify(filter) },
};
return {
@ -163,11 +186,12 @@ function getCreateList(fyo: Fyo): SearchItem[] {
group: 'Create',
async action() {
await routeTo(route);
const doc = await fyo.doc.getNewDoc(schemaName, filter);
const doc = fyo.doc.getNewDoc(schemaName, create);
const { openQuickEdit } = await import('src/utils/ui');
await openQuickEdit({
schemaName,
name: doc.name as string,
listFilters: filter,
});
},
} as SearchItem;
@ -204,8 +228,6 @@ function getReportList(fyo: Fyo): SearchItem[] {
function getListViewList(fyo: Fyo): SearchItem[] {
let schemaNames = [
ModelNameEnum.Account,
ModelNameEnum.Party,
ModelNameEnum.Payment,
ModelNameEnum.JournalEntry,
ModelNameEnum.PurchaseInvoice,
ModelNameEnum.SalesInvoice,
@ -224,6 +246,7 @@ function getListViewList(fyo: Fyo): SearchItem[] {
if (fyo.store.isDevelopment) {
schemaNames = Object.keys(fyo.schemaMap) as ModelNameEnum[];
}
const standardLists = schemaNames
.map((s) => fyo.schemaMap[s])
.filter((s) => s && !s.isChild && !s.isSingle)
@ -240,37 +263,42 @@ function getListViewList(fyo: Fyo): SearchItem[] {
{
label: t`Customers`,
route: `/list/Party/${t`Customers`}`,
filters: { role: ['Customer', 'Both'] },
filters: routeFilters.Customers,
},
{
label: t`Suppliers`,
route: `/list/Party/${t`Suppliers`}`,
filters: { role: ['Supplier', 'Both'] },
filters: routeFilters.Suppliers,
},
{
label: t`Party`,
route: `/list/Party/${t`Party`}`,
filters: routeFilters.Party,
},
{
label: t`Sales Items`,
route: `/list/Item/${t`Sales Items`}`,
filters: { for: ['in', ['Sales', 'Both']] },
filters: routeFilters.SalesItems,
},
{
label: t`Sales Payments`,
route: `/list/Payment/${t`Sales Payments`}`,
filters: { paymentType: 'Receive' },
filters: routeFilters.SalesPayments,
},
{
label: t`Purchase Items`,
route: `/list/Item/${t`Purchase Items`}`,
filters: { for: ['in', ['Purchases', 'Both']] },
filters: routeFilters.PurchaseItems,
},
{
label: t`Common Items`,
route: `/list/Item/${t`Common Items`}`,
filters: { for: 'Both' },
label: t`Items`,
route: `/list/Item/${t`Items`}`,
filters: routeFilters.Items,
},
{
label: t`Purchase Payments`,
route: `/list/Payment/${t`Purchase Payments`}`,
filters: { paymentType: 'Pay' },
filters: routeFilters.PurchasePayments,
},
].map((i) => {
const label = i.label;
@ -705,13 +733,13 @@ export class Search {
};
}
_getRouteFromKeyword(keyword: Keyword): string {
_getRouteFromKeyword(keyword: Keyword): RouteLocationRaw {
const { parent, parentSchemaName, schemaName } = keyword.meta;
if (parent && parentSchemaName) {
return getEntryRoute(parentSchemaName as string, parent as string);
return getCreateRoute(parentSchemaName as string, parent as string);
}
return getEntryRoute(schemaName as string, keyword.values[0]);
return getCreateRoute(schemaName as string, keyword.values[0]);
}
_getGroupedKeywords() {

View File

@ -1,4 +1,5 @@
import { t } from 'fyo';
import { routeFilters } from 'src/utils/filters';
import { fyo } from '../initFyo';
import { SidebarConfig, SidebarRoot } from './types';
@ -165,21 +166,21 @@ async function getCompleteSidebar(): Promise<SidebarConfig> {
name: 'payments',
route: `/list/Payment/${t`Sales Payments`}`,
schemaName: 'Payment',
filters: { paymentType: 'Receive' },
filters: routeFilters.SalesPayments,
},
{
label: t`Customers`,
name: 'customers',
route: `/list/Party/${t`Customers`}`,
schemaName: 'Party',
filters: { role: ['in', ['Customer', 'Both']] },
filters: routeFilters.Customers,
},
{
label: t`Sales Items`,
name: 'sales-items',
route: `/list/Item/${t`Sales Items`}`,
schemaName: 'Item',
filters: { for: ['in', ['Sales', 'Both']] },
filters: routeFilters.SalesItems,
},
],
},
@ -200,21 +201,21 @@ async function getCompleteSidebar(): Promise<SidebarConfig> {
name: 'payments',
route: `/list/Payment/${t`Purchase Payments`}`,
schemaName: 'Payment',
filters: { paymentType: 'Pay' },
filters: routeFilters.PurchasePayments,
},
{
label: t`Suppliers`,
name: 'suppliers',
route: `/list/Party/${t`Suppliers`}`,
schemaName: 'Party',
filters: { role: ['in', ['Supplier', 'Both']] },
filters: routeFilters.Suppliers,
},
{
label: t`Purchase Items`,
name: 'purchase-items',
route: `/list/Item/${t`Purchase Items`}`,
schemaName: 'Item',
filters: { for: ['in', ['Purchases', 'Both']] },
filters: routeFilters.PurchaseItems,
},
],
},
@ -238,9 +239,9 @@ async function getCompleteSidebar(): Promise<SidebarConfig> {
filters: { role: 'Both' },
},
{
label: t`Common Items`,
label: t`Items`,
name: 'common-items',
route: `/list/Item/${t`Common Items`}`,
route: `/list/Item/${t`Items`}`,
schemaName: 'Item',
filters: { for: 'Both' },
},

View File

@ -31,6 +31,7 @@ export interface QuickEditOptions {
hideFields?: string[];
showFields?: string[];
defaults?: Record<string, unknown>;
listFilters?: QueryFilter
}
export type SidebarConfig = SidebarRoot[];

View File

@ -32,6 +32,7 @@ export async function openQuickEdit({
hideFields = [],
showFields = [],
defaults = {},
listFilters = {},
}: QuickEditOptions) {
if (doc) {
schemaName = doc.schemaName;
@ -74,12 +75,13 @@ export async function openQuickEdit({
router[method]({
query: {
edit: '1',
edit: 1,
schemaName,
name,
showFields,
hideFields,
defaults: stringifyCircular(defaults),
filters: JSON.stringify(listFilters),
},
});
}
@ -141,7 +143,7 @@ export function openSettings(tab: SettingsTab) {
routeTo({ path: '/settings', query: { tab } });
}
export async function routeTo(route: string | RouteLocationRaw) {
export async function routeTo(route: RouteLocationRaw) {
if (
typeof route === 'string' &&
route === router.currentRoute.value.fullPath