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

Merge pull request #568 from frappe/navigable-link-fields

feat: navigable link fields
This commit is contained in:
Alan 2023-03-03 04:23:39 -08:00 committed by GitHub
commit 205ef4aa6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 108 additions and 69 deletions

View File

@ -87,7 +87,7 @@ export interface ColumnConfig {
export type ListViewColumn = string | ColumnConfig; export type ListViewColumn = string | ColumnConfig;
export interface ListViewSettings { export interface ListViewSettings {
formRoute?: (doc: Doc) => RouteLocationRaw; formRoute?: (name: string) => RouteLocationRaw;
columns?: ListViewColumn[]; columns?: ListViewColumn[];
} }

View File

@ -112,7 +112,7 @@ export class Item extends Doc {
static getListViewSettings(): ListViewSettings { static getListViewSettings(): ListViewSettings {
return { return {
formRoute: ({ name }) => `/edit/Item/${name}`, formRoute: (name) => `/edit/Item/${name}`,
columns: ['name', 'unit', 'tax', 'rate'], columns: ['name', 'unit', 'tax', 'rate'],
}; };
} }

View File

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

View File

@ -617,7 +617,7 @@ export class Payment extends Transactional {
static getListViewSettings(fyo: Fyo): ListViewSettings { static getListViewSettings(fyo: Fyo): ListViewSettings {
return { return {
formRoute: ({ name }) => `/edit/Payment/${name}`, formRoute: (name) => `/edit/Payment/${name}`,
columns: ['name', getDocStatusListColumn(), 'party', 'date', 'amount'], columns: ['name', getDocStatusListColumn(), 'party', 'date', 'amount'],
}; };
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -81,7 +81,7 @@ export class StockMovement extends Transfer {
static getListViewSettings(fyo: Fyo): ListViewSettings { static getListViewSettings(fyo: Fyo): ListViewSettings {
return { return {
formRoute: ({ name }) => `/edit/StockMovement/${name}`, formRoute: (name) => `/edit/StockMovement/${name}`,
columns: [ columns: [
'name', 'name',
getDocStatusListColumn(), getDocStatusListColumn(),

View File

@ -50,7 +50,7 @@
:class="[!isReadOnly ? 'group-hover:flex' : '']" :class="[!isReadOnly ? 'group-hover:flex' : '']"
style="background: rgba(0, 0, 0, 0.2); backdrop-filter: blur(2px)" style="background: rgba(0, 0, 0, 0.2); backdrop-filter: blur(2px)"
> >
<button class="bg-gray-300 p-0.5 rounded mb-1" @click="handleClick"> <button class="bg-gray-100 p-0.5 rounded mb-1" @click="handleClick">
<FeatherIcon <FeatherIcon
:name="shouldClear ? 'x' : 'upload'" :name="shouldClear ? 'x' : 'upload'"
class="w-4 h-4 text-gray-600" class="w-4 h-4 text-gray-600"

View File

@ -21,25 +21,21 @@
/> />
<!-- Buttons --> <!-- Buttons -->
<div class="me-2 flex gap-2"> <div class="me-2 flex gap-1">
<!-- Upload Button --> <!-- Upload Button -->
<button v-if="!value" class="bg-gray-300 p-0.5 rounded" @click="upload"> <button v-if="!value" class="p-0.5 rounded" @click="upload">
<FeatherIcon name="upload" class="h-4 w-4 text-gray-600" /> <FeatherIcon name="upload" class="h-4 w-4 text-gray-600" />
</button> </button>
<!-- Download Button --> <!-- Download Button -->
<button <button v-if="value" class="p-0.5 rounded" @click="download">
v-if="value"
class="bg-gray-300 p-0.5 rounded"
@click="download"
>
<FeatherIcon name="download" class="h-4 w-4 text-gray-600" /> <FeatherIcon name="download" class="h-4 w-4 text-gray-600" />
</button> </button>
<!-- Clear Button --> <!-- Clear Button -->
<button <button
v-if="value && !isReadOnly" v-if="value && !isReadOnly"
class="bg-gray-300 p-0.5 rounded" class="p-0.5 rounded"
@click="clear" @click="clear"
> >
<FeatherIcon name="x" class="h-4 w-4 text-gray-600" /> <FeatherIcon name="x" class="h-4 w-4 text-gray-600" />

View File

@ -35,7 +35,7 @@
@keydown.esc="toggleDropdown(false)" @keydown.esc="toggleDropdown(false)"
/> />
<svg <svg
v-if="!isReadOnly" v-if="!isReadOnly && !canLink"
class="w-3 h-3" class="w-3 h-3"
style="background: inherit; margin-right: -3px" style="background: inherit; margin-right: -3px"
viewBox="0 0 5 10" viewBox="0 0 5 10"
@ -45,21 +45,30 @@
<path <path
d="M1 2.636L2.636 1l1.637 1.636M1 7.364L2.636 9l1.637-1.636" d="M1 2.636L2.636 1l1.637 1.636M1 7.364L2.636 9l1.637-1.636"
class="stroke-current" class="stroke-current"
:class="showMandatory ? 'text-red-500' : 'text-gray-500'" :class="showMandatory ? 'text-red-400' : 'text-gray-400'"
fill="none" fill="none"
fill-rule="evenodd" fill-rule="evenodd"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
/> />
</svg> </svg>
<button
v-if="canLink"
class="p-0.5 rounded -me1 bg-transparent"
@click="routeToLinkedDoc"
>
<FeatherIcon name="chevron-right" class="w-4 h-4 text-gray-600" />
</button>
</div> </div>
</template> </template>
</Dropdown> </Dropdown>
</template> </template>
<script> <script>
import { getOptionList } from 'fyo/utils'; import { getOptionList } from 'fyo/utils';
import { FieldTypeEnum } from 'schemas/types';
import Dropdown from 'src/components/Dropdown.vue'; import Dropdown from 'src/components/Dropdown.vue';
import { fuzzyMatch } from 'src/utils'; import { fuzzyMatch } from 'src/utils';
import { getFormRoute, routeTo } from 'src/utils/ui';
import Base from './Base.vue'; import Base from './Base.vue';
export default { export default {
@ -100,8 +109,52 @@ export default {
return getOptionList(this.df, this.doc); return getOptionList(this.df, this.doc);
}, },
canLink() {
if (!this.value || !this.df) {
return false;
}
const fieldtype = this.df?.fieldtype;
const isLink = fieldtype === FieldTypeEnum.Link;
const isDynamicLink = fieldtype === FieldTypeEnum.DynamicLink;
if (!isLink && !isDynamicLink) {
return false;
}
if (isLink && this.df.target) {
return true;
}
const references = this.df.references;
if (!references) {
return false;
}
if (!this.doc?.[references]) {
return false;
}
return true;
},
}, },
methods: { methods: {
async routeToLinkedDoc() {
let schemaName = this.df?.target;
const name = this.value;
if (!schemaName) {
const references = this.df?.references ?? '';
schemaName = this.doc?.[references];
}
if (!schemaName || !name) {
return;
}
const route = getFormRoute(schemaName, name);
await routeTo(route);
},
setLinkValue(value) { setLinkValue(value) {
this.linkValue = value; this.linkValue = value;
}, },

View File

@ -21,12 +21,7 @@
{{ inputPlaceholder }} {{ inputPlaceholder }}
</p> </p>
<button <button v-if="!isReadOnly" class="p-0.5 rounded -me-1 ms-1">
v-if="!isReadOnly"
class="p-0.5 rounded -me-1 ms-1"
:class="showMandatory ? 'bg-red-300' : 'bg-gray-300'"
@click="togglePopover"
>
<FeatherIcon <FeatherIcon
name="calendar" name="calendar"
class="w-4 h-4" class="w-4 h-4"

View File

@ -50,7 +50,7 @@
<path <path
d="M1 2.636L2.636 1l1.637 1.636M1 7.364L2.636 9l1.637-1.636" d="M1 2.636L2.636 1l1.637 1.636M1 7.364L2.636 9l1.637-1.636"
class="stroke-current" class="stroke-current"
:class="showMandatory ? 'text-red-500' : 'text-gray-500'" :class="showMandatory ? 'text-red-400' : 'text-gray-400'"
fill="none" fill="none"
fill-rule="evenodd" fill-rule="evenodd"
stroke-linecap="round" stroke-linecap="round"

View File

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

View File

@ -100,7 +100,7 @@ export default {
const doc = await this.fyo.doc.getDoc(this.schemaName, name); const doc = await this.fyo.doc.getDoc(this.schemaName, name);
if (this.listConfig.formRoute) { if (this.listConfig.formRoute) {
return await routeTo(this.listConfig.formRoute(doc)); return await routeTo(this.listConfig.formRoute(name));
} }
const { routeFilter } = getRouteData({ doc }); const { routeFilter } = getRouteData({ doc });
@ -137,7 +137,7 @@ export default {
}; };
if (this.listConfig.formRoute) { if (this.listConfig.formRoute) {
path = this.listConfig.formRoute(doc); path = this.listConfig.formRoute(doc.name);
} }
if (typeof path === 'object') { if (typeof path === 'object') {

View File

@ -141,28 +141,6 @@ const routes: RouteRecordRaw[] = [
}, },
]; ];
export function getCreateRoute(
schemaName: string,
name: string
): RouteLocationRaw {
if (
[
ModelNameEnum.SalesInvoice,
ModelNameEnum.PurchaseInvoice,
ModelNameEnum.JournalEntry,
ModelNameEnum.Shipment,
ModelNameEnum.PurchaseReceipt,
ModelNameEnum.StockMovement,
ModelNameEnum.Payment,
ModelNameEnum.Item,
].includes(schemaName as ModelNameEnum)
) {
return `/edit/${schemaName}/${name}`;
}
return `/list/${schemaName}?edit=1&schemaName=${schemaName}&name=${name}`;
}
const router = createRouter({ routes, history: createWebHistory() }); const router = createRouter({ routes, history: createWebHistory() });
export default router; export default router;

View File

@ -4,13 +4,12 @@ import { groupBy } from 'lodash';
import { ModelNameEnum } from 'models/types'; import { ModelNameEnum } from 'models/types';
import { reports } from 'reports'; import { reports } from 'reports';
import { OptionField } from 'schemas/types'; import { OptionField } from 'schemas/types';
import { getCreateRoute } from 'src/router';
import { createFilters, routeFilters } from 'src/utils/filters'; import { createFilters, routeFilters } from 'src/utils/filters';
import { GetAllOptions } from 'utils/db/types'; import { GetAllOptions } from 'utils/db/types';
import { safeParseFloat } from 'utils/index'; import { safeParseFloat } from 'utils/index';
import { RouteLocationRaw } from 'vue-router'; import { RouteLocationRaw } from 'vue-router';
import { fuzzyMatch } from '.'; import { fuzzyMatch } from '.';
import { routeTo } from './ui'; import { getFormRoute, routeTo } from './ui';
export const searchGroups = ['Docs', 'List', 'Create', 'Report', 'Page']; export const searchGroups = ['Docs', 'List', 'Create', 'Report', 'Page'];
enum SearchGroupEnum { enum SearchGroupEnum {
@ -73,17 +72,6 @@ export function getGroupLabelMap() {
}; };
} }
async function openQuickEditDoc(schemaName: string, fyo: Fyo) {
await routeTo(`/list/${schemaName}`);
const doc = await fyo.doc.getNewDoc(schemaName);
const { openQuickEdit } = await import('src/utils/ui');
await openQuickEdit({
schemaName,
name: doc.name as string,
});
}
async function openFormEditDoc(schemaName: string, fyo: Fyo) { async function openFormEditDoc(schemaName: string, fyo: Fyo) {
const doc = fyo.doc.getNewDoc(schemaName); const doc = fyo.doc.getNewDoc(schemaName);
const name = doc.name; const name = doc.name;
@ -735,10 +723,10 @@ export class Search {
_getRouteFromKeyword(keyword: Keyword): RouteLocationRaw { _getRouteFromKeyword(keyword: Keyword): RouteLocationRaw {
const { parent, parentSchemaName, schemaName } = keyword.meta; const { parent, parentSchemaName, schemaName } = keyword.meta;
if (parent && parentSchemaName) { if (parent && parentSchemaName) {
return getCreateRoute(parentSchemaName as string, parent as string); return getFormRoute(parentSchemaName as string, parent as string);
} }
return getCreateRoute(schemaName as string, keyword.values[0]); return getFormRoute(schemaName as string, keyword.values[0]);
} }
_getGroupedKeywords() { _getGroupedKeywords() {

View File

@ -420,3 +420,33 @@ export function getFieldsGroupedByTabAndSection(
return grouped; return grouped;
} }
export function getFormRoute(
schemaName: string,
name: string
): RouteLocationRaw {
const route = fyo.models[schemaName]
?.getListViewSettings(fyo)
?.formRoute?.(name);
if (typeof route === 'string') {
return route;
}
if (
[
ModelNameEnum.SalesInvoice,
ModelNameEnum.PurchaseInvoice,
ModelNameEnum.JournalEntry,
ModelNameEnum.Shipment,
ModelNameEnum.PurchaseReceipt,
ModelNameEnum.StockMovement,
ModelNameEnum.Payment,
ModelNameEnum.Item,
].includes(schemaName as ModelNameEnum)
) {
return `/edit/${schemaName}/${name}`;
}
return `/list/${schemaName}?edit=1&schemaName=${schemaName}&name=${name}`;
}