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

fix(ux): common purpose entries displayed in specific lists

This commit is contained in:
18alantom 2023-01-04 17:53:42 +05:30
parent 013bf3af4f
commit 808abd224f
14 changed files with 157 additions and 63 deletions

View File

@ -153,7 +153,10 @@ export class Party extends Doc {
condition: (doc: Doc) =>
!doc.notInserted && (doc.role as PartyRole) !== 'Customer',
action: async (partyDoc, router) => {
router.push(`/list/PurchaseInvoice/party/${partyDoc.name}`);
await router.push({
path: '/list/PurchaseInvoice',
query: { filters: JSON.stringify({ party: partyDoc.name }) },
});
},
},
{
@ -161,11 +164,12 @@ export class Party extends Doc {
condition: (doc: Doc) =>
!doc.notInserted && (doc.role as PartyRole) !== 'Supplier',
action: async (partyDoc, router) => {
const doc = await fyo.doc.getNewDoc('SalesInvoice', {
const doc = fyo.doc.getNewDoc('SalesInvoice', {
party: partyDoc.name,
account: partyDoc.defaultAccount as string,
});
router.push({
await router.push({
path: `/edit/SalesInvoice/${doc.name}`,
query: {
schemaName: 'SalesInvoice',
@ -182,7 +186,10 @@ export class Party extends Doc {
condition: (doc: Doc) =>
!doc.notInserted && (doc.role as PartyRole) !== 'Supplier',
action: async (partyDoc, router) => {
router.push(`/list/SalesInvoice/party/${partyDoc.name}`);
router.push({
path: '/list/SalesInvoice',
query: { filters: JSON.stringify({ party: partyDoc.name }) },
});
},
},
];

View File

@ -147,7 +147,7 @@ export function getStatusText(status: DocStatus | InvoiceStatus): string {
case 'Saved':
return t`Saved`;
case 'NotSaved':
return t`NotSaved`;
return t`Not Saved`;
case 'Submitted':
return t`Submitted`;
case 'Cancelled':

View File

@ -3,6 +3,7 @@ import { t } from 'fyo';
import Badge from 'src/components/Badge.vue';
import { fyo } from 'src/initFyo';
import { fuzzyMatch } from 'src/utils';
import { getCreateFiltersFromListViewFilters } from 'src/utils/misc';
import { markRaw } from 'vue';
import AutoComplete from './AutoComplete.vue';
@ -145,23 +146,8 @@ export default {
return createFilters;
}
createFilters = {};
const filters = await this.getFilters();
for (const key of Object.keys(filters)) {
const value = filters[key];
if (value === undefined) {
continue;
}
if (Array.isArray(value)) {
continue;
}
createFilters[key] = value;
}
return createFilters;
return getCreateFiltersFromListViewFilters(filters);
},
async getFilters() {
const { schemaName, fieldname } = this.df;

View File

@ -230,17 +230,26 @@ export default {
}
if (group.route) {
routeTo(group.route);
routeTo(this.getPath(group));
}
},
onItemClick(item) {
if (item.action) {
item.action();
}
if (item.route) {
routeTo(item.route);
routeTo(this.getPath(item));
}
},
getPath(item) {
const { route: path, filters } = item;
if (!filters) {
return path;
}
return { path, query: { filters: JSON.stringify(filters) } };
},
},
};
</script>

View File

@ -89,13 +89,15 @@
</div>
</template>
<script>
import { clone } from 'lodash';
import Button from 'src/components/Button';
import Paginator from 'src/components/Paginator.vue';
import Row from 'src/components/Row';
import { fyo } from 'src/initFyo';
import { isNumeric } from 'src/utils';
import { openQuickEdit, routeTo } from 'src/utils/ui';
import { defineComponent } from 'vue';
import { objectForEach } from 'utils/index';
import { defineComponent, toRaw } from 'vue';
import ListCell from './ListCell';
export default defineComponent({
@ -194,6 +196,8 @@ export default defineComponent({
filters = { ...this.filters };
}
filters = objectForEach(clone(filters), toRaw);
const orderBy = !!fyo.getField(this.schemaName, 'date')
? 'date'
: 'created';

View File

@ -44,7 +44,7 @@ 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 } from 'src/utils/misc';
import { docsPathMap, getCreateFiltersFromListViewFilters } from 'src/utils/misc';
import { docsPath, routeTo } from 'src/utils/ui';
import List from './List.vue';
@ -77,6 +77,10 @@ export default {
this.listConfig = getListConfig(this.schemaName);
docsPath.value = docsPathMap[this.schemaName] ?? docsPathMap.Entries;
if (this.fyo.store.isDevelopment) {
window.lv = this;
}
},
deactivated() {
docsPath.value = '';
@ -86,10 +90,11 @@ export default {
this.listFilters = listFilters;
},
async makeNewDoc() {
const doc = await fyo.doc.getNewDoc(this.schemaName, this.filters ?? {});
const filters = getCreateFiltersFromListViewFilters(this.filters ?? {});
const doc = fyo.doc.getNewDoc(this.schemaName, filters);
const path = this.getFormPath(doc.name);
routeTo(path);
await routeTo(path);
doc.on('afterSync', () => {
const path = this.getFormPath(doc.name);
this.$router.replace(path);

View File

@ -80,7 +80,7 @@ const routes: RouteRecordRaw[] = [
},
},
{
path: '/list/:schemaName/:fieldname?/:value?/:pageTitle?',
path: '/list/:schemaName/:pageTitle?',
name: 'ListView',
components: {
default: ListView,
@ -88,18 +88,19 @@ const routes: RouteRecordRaw[] = [
},
props: {
default: (route) => {
const { schemaName, fieldname, value, pageTitle } = route.params;
let { filters } = route.params;
const { schemaName } = route.params;
const pageTitle = route.params.pageTitle ?? '';
if (filters === undefined && fieldname && value) {
// @ts-ignore
filters = { [fieldname as string]: value };
const filters = {};
const filterString = route.query.filters;
if (typeof filterString === 'string') {
Object.assign(filters, JSON.parse(filterString));
}
return {
schemaName,
filters,
pageTitle: pageTitle ?? '',
pageTitle,
};
},
edit: (route) => {

View File

@ -85,7 +85,13 @@ export function getGetStartedConfig() {
label: t`Add Items`,
icon: 'item',
description: t`Add products or services that you sell to your customers`,
action: () => routeTo(`/list/Item/for/Sales/${t`Sales Items`}`),
action: () =>
routeTo({
path: `/list/Item/${t`Sales Items`}`,
query: {
filters: JSON.stringify({ for: 'Sales' }),
},
}),
fieldname: 'salesItemCreated',
documentation:
'https://docs.frappebooks.com/setting-up/initial-entries.html#add-sales-items',
@ -95,7 +101,13 @@ export function getGetStartedConfig() {
label: t`Add Customers`,
icon: 'customer',
description: t`Add a few customers to create your first sales invoice`,
action: () => routeTo(`/list/Party/role/Customer/${t`Customers`}`),
action: () =>
routeTo({
path: `/list/Party/${t`Customers`}`,
query: {
filters: JSON.stringify({ role: 'Customer' }),
},
}),
fieldname: 'customerCreated',
documentation:
'https://docs.frappebooks.com/setting-up/initial-entries.html#add-customers',
@ -122,7 +134,12 @@ export function getGetStartedConfig() {
icon: 'item',
description: t`Add products or services that you buy from your suppliers`,
action: () =>
routeTo(`/list/Item/for/Purchases/${t`Purchase Items`}`),
routeTo({
path: `/list/Item/${t`Purchase Items`}`,
query: {
filters: JSON.stringify({ for: 'Purchases' }),
},
}),
fieldname: 'purchaseItemCreated',
},
{
@ -130,7 +147,11 @@ export function getGetStartedConfig() {
label: t`Add Suppliers`,
icon: 'supplier',
description: t`Add a few suppliers to create your first purchase invoice`,
action: () => routeTo(`/list/Party/role/Supplier/${t`Suppliers`}`),
action: () =>
routeTo({
path: `/list/Party/${t`Suppliers`}`,
query: { filters: JSON.stringify({ role: 'Supplier' }) },
}),
fieldname: 'supplierCreated',
},
{

View File

@ -6,6 +6,7 @@ import { ModelNameEnum } from 'models/types';
import SetupWizardSchema from 'schemas/app/SetupWizard.json';
import { Schema } from 'schemas/types';
import { fyo } from 'src/initFyo';
import { QueryFilter } from 'utils/db/types';
export function getDatesAndPeriodList(
period: 'This Year' | 'This Quarter' | 'This Month'
@ -96,7 +97,7 @@ export const docsPathMap: Record<string, string | undefined> = {
[ModelNameEnum.PurchaseInvoice]: 'transactions/purchase-invoices',
[ModelNameEnum.Payment]: 'transactions/payments',
[ModelNameEnum.JournalEntry]: 'transactions/journal-entries',
// Inventory
[ModelNameEnum.StockMovement]: 'inventory/stock-movement',
[ModelNameEnum.Shipment]: 'inventory/shipment',
@ -136,3 +137,23 @@ export async function convertFileToDataURL(file: File, type: string) {
const array = new Uint8Array(buffer);
return await getDataURL(type, array);
}
export function getCreateFiltersFromListViewFilters(filters: QueryFilter) {
const createFilters: Record<string, string | number | boolean | null> = {};
for (const key in filters) {
let value: typeof filters[string] | undefined | number = filters[key];
if (Array.isArray(value) && value[0] === 'in' && Array.isArray(value[1])) {
value = value[1].filter((v) => v !== 'Both')[0];
}
if (value === undefined || Array.isArray(value)) {
continue;
}
createFilters[key] = value;
}
return createFilters;
}

View File

@ -153,9 +153,11 @@ function getCreateList(fyo: Fyo): SearchItem[] {
},
].map(({ label, filter, schemaName }) => {
const fk = Object.keys(filter)[0] as 'for' | 'role';
const ep = `${fk}/${filter[fk]}`;
const route = {
path: `/list/${schemaName}/${label}`,
query: { filters: JSON.stringify({ [fk]: filter[fk] }) },
};
const route = `/list/${schemaName}/${ep}/${label}`;
return {
label,
group: 'Create',
@ -235,29 +237,47 @@ function getListViewList(fyo: Fyo): SearchItem[] {
);
const filteredLists = [
{ label: t`Customers`, route: `/list/Party/role/Customer/${t`Customers`}` },
{ label: t`Suppliers`, route: `/list/Party/role/Supplier/${t`Suppliers`}` },
{
label: t`Customers`,
route: `/list/Party/${t`Customers`}`,
filters: { role: ['Customer', 'Both'] },
},
{
label: t`Suppliers`,
route: `/list/Party/${t`Suppliers`}`,
filters: { role: ['Supplier', 'Both'] },
},
{
label: t`Sales Items`,
route: `/list/Item/for/Sales/${t`Sales Items`}`,
route: `/list/Item/${t`Sales Items`}`,
filters: { for: ['in', ['Sales', 'Both']] },
},
{
label: t`Sales Payments`,
route: `/list/Payment/paymentType/Receive/${t`Sales Payments`}`,
route: `/list/Payment/${t`Sales Payments`}`,
filters: { paymentType: 'Receive' },
},
{
label: t`Purchase Items`,
route: `/list/Item/for/Purchases/${t`Purchase Items`}`,
route: `/list/Item/${t`Purchase Items`}`,
filters: { for: ['in', ['Purchases', 'Both']] },
},
{
label: t`Common Items`,
route: `/list/Item/for/Both/${t`Common Items`}`,
route: `/list/Item/${t`Common Items`}`,
filters: { for: 'Both' },
},
{
label: t`Purchase Payments`,
route: `/list/Payment/paymentType/Pay/${t`Purchase Payments`}`,
route: `/list/Payment/${t`Purchase Payments`}`,
filters: { paymentType: 'Pay' },
},
].map((i) => ({ ...i, group: 'List' } as SearchItem));
].map((i) => {
const label = i.label;
const route = encodeURI(`${i.route}?filters=${JSON.stringify(i.filters)}`);
return { label, route, group: 'List' } as SearchItem;
});
return [standardLists, filteredLists].flat();
}

View File

@ -163,20 +163,23 @@ async function getCompleteSidebar(): Promise<SidebarConfig> {
{
label: t`Sales Payments`,
name: 'payments',
route: `/list/Payment/paymentType/Receive/${t`Sales Payments`}`,
route: `/list/Payment/${t`Sales Payments`}`,
schemaName: 'Payment',
filters: { paymentType: 'Receive' },
},
{
label: t`Customers`,
name: 'customers',
route: `/list/Party/role/Customer/${t`Customers`}`,
route: `/list/Party/${t`Customers`}`,
schemaName: 'Party',
filters: { role: ['in', ['Customer', 'Both']] },
},
{
label: t`Sales Items`,
name: 'sales-items',
route: `/list/Item/for/Sales/${t`Sales Items`}`,
route: `/list/Item/${t`Sales Items`}`,
schemaName: 'Item',
filters: { for: ['in', ['Sales', 'Both']] },
},
],
},
@ -195,20 +198,23 @@ async function getCompleteSidebar(): Promise<SidebarConfig> {
{
label: t`Purchase Payments`,
name: 'payments',
route: `/list/Payment/paymentType/Pay/${t`Purchase Payments`}`,
route: `/list/Payment/${t`Purchase Payments`}`,
schemaName: 'Payment',
filters: { paymentType: 'Pay' },
},
{
label: t`Suppliers`,
name: 'suppliers',
route: `/list/Party/role/Supplier/${t`Suppliers`}`,
route: `/list/Party/${t`Suppliers`}`,
schemaName: 'Party',
filters: { role: ['in', ['Supplier', 'Both']] },
},
{
label: t`Purchase Items`,
name: 'purchase-items',
route: `/list/Item/for/Purchases/${t`Purchase Items`}`,
route: `/list/Item/${t`Purchase Items`}`,
schemaName: 'Item',
filters: { for: ['in', ['Purchases', 'Both']] },
},
],
},
@ -227,14 +233,16 @@ async function getCompleteSidebar(): Promise<SidebarConfig> {
{
label: t`Party`,
name: 'party',
route: '/list/Party/role/Both',
route: '/list/Party',
schemaName: 'Party',
filters: { role: 'Both' },
},
{
label: t`Common Items`,
name: 'common-items',
route: `/list/Item/for/Both/${t`Common Items`}`,
route: `/list/Item/${t`Common Items`}`,
schemaName: 'Item',
filters: { for: 'Both' },
},
],
},

View File

@ -1,5 +1,6 @@
import { Doc } from "fyo/model/doc";
import { FieldTypeEnum } from "schemas/types";
import { QueryFilter } from "utils/db/types";
export interface MessageDialogButton {
label: string;
@ -42,6 +43,7 @@ export interface SidebarRoot {
iconHeight?: string;
hidden?: () => boolean;
items?: SidebarItem[];
filters?: QueryFilter
}
export interface SidebarItem {

View File

@ -142,7 +142,6 @@ export function openSettings(tab: SettingsTab) {
}
export async function routeTo(route: string | RouteLocationRaw) {
const routeOptions = route;
if (
typeof route === 'string' &&
route === router.currentRoute.value.fullPath
@ -150,11 +149,7 @@ export async function routeTo(route: string | RouteLocationRaw) {
return;
}
if (typeof route === 'string') {
return await router.push(route);
}
return await router.push(routeOptions);
return await router.push(route);
}
export async function deleteDocWithPrompt(doc: Doc) {

View File

@ -225,3 +225,18 @@ export function removeAtIndex<T>(array: T[], index: number): T[] {
return [...array.slice(0, index), ...array.slice(index + 1)];
}
export function objectForEach<T extends object | unknown>(
obj: T,
func: Function
) {
if (typeof obj !== 'object' || obj === null) {
return func(obj);
}
for (const key in obj) {
obj[key] = objectForEach(obj[key], func);
}
return func(obj);
}