mirror of
https://github.com/frappe/books.git
synced 2024-12-23 03:19:01 +00:00
Merge pull request #988 from AbleKSaju/feat-multi-label-link
feat: MultiLabelLink functionality in POS
This commit is contained in:
commit
5f99a0df5f
199
src/components/Controls/MultiLabelLink.vue
Normal file
199
src/components/Controls/MultiLabelLink.vue
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
<script>
|
||||||
|
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';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MultiLabelLink',
|
||||||
|
extends: AutoComplete,
|
||||||
|
data() {
|
||||||
|
return { results: [] };
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value: {
|
||||||
|
immediate: true,
|
||||||
|
handler(newValue) {
|
||||||
|
this.setLinkValue(newValue);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
secondaryLink: String,
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.value) {
|
||||||
|
this.setLinkValue();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async setLinkValue(newValue, isInput) {
|
||||||
|
if (isInput) {
|
||||||
|
return (this.linkValue = newValue || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = newValue ?? this.value;
|
||||||
|
const { fieldname, target } = this.df ?? {};
|
||||||
|
const linkDisplayField = fyo.schemaMap[target ?? '']?.linkDisplayField;
|
||||||
|
|
||||||
|
if (!linkDisplayField) {
|
||||||
|
return (this.linkValue = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const linkDoc = await this.doc?.loadAndGetLink(fieldname);
|
||||||
|
this.linkValue = linkDoc?.get(linkDisplayField) ?? '';
|
||||||
|
},
|
||||||
|
getTargetSchemaName() {
|
||||||
|
return this.df.target;
|
||||||
|
},
|
||||||
|
async getOptions() {
|
||||||
|
const schemaName = this.getTargetSchemaName();
|
||||||
|
|
||||||
|
if (!schemaName) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.results?.length) {
|
||||||
|
return this.results;
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = fyo.schemaMap[schemaName];
|
||||||
|
const filters = await this.getFilters();
|
||||||
|
|
||||||
|
const fields = [
|
||||||
|
...new Set([
|
||||||
|
'name',
|
||||||
|
this.secondaryLink,
|
||||||
|
schema.titleField,
|
||||||
|
this.df.groupBy,
|
||||||
|
]),
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
const results = await fyo.db.getAll(schemaName, {
|
||||||
|
filters,
|
||||||
|
fields,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (this.results = results
|
||||||
|
.map((r) => {
|
||||||
|
const option = {
|
||||||
|
label: r[this.secondaryLink]
|
||||||
|
? `${r[schema.titleField]} ` + ` ${r[this.secondaryLink]}`
|
||||||
|
: r[schema.titleField],
|
||||||
|
value: r.name,
|
||||||
|
value2: r[this.secondaryLink],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.df.groupBy) {
|
||||||
|
option.group = r[this.df.groupBy];
|
||||||
|
}
|
||||||
|
return option;
|
||||||
|
})
|
||||||
|
.filter(Boolean));
|
||||||
|
},
|
||||||
|
async getSuggestions(keyword = '') {
|
||||||
|
let options = await this.getOptions();
|
||||||
|
|
||||||
|
if (keyword) {
|
||||||
|
options = options
|
||||||
|
.map((item) => ({ ...fuzzyMatch(keyword, item.label), item }))
|
||||||
|
.filter(({ isMatch }) => isMatch)
|
||||||
|
.sort((a, b) => a.distance - b.distance)
|
||||||
|
.map(({ item }) => item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.doc && this.df.create) {
|
||||||
|
options = options.concat(this.getCreateNewOption());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.length === 0 && !this.df.emptyMessage) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
component: markRaw({
|
||||||
|
template:
|
||||||
|
'<span class="text-gray-600 dark:text-gray-400">{{ t`No results found` }}</span>',
|
||||||
|
}),
|
||||||
|
action: () => {},
|
||||||
|
actionOnly: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
},
|
||||||
|
getCreateNewOption() {
|
||||||
|
return {
|
||||||
|
label: t`Create`,
|
||||||
|
action: () => this.openNewDoc(),
|
||||||
|
actionOnly: true,
|
||||||
|
component: markRaw({
|
||||||
|
template:
|
||||||
|
'<div class="flex items-center font-semibold">{{ t`Create` }}' +
|
||||||
|
'<Badge color="blue" class="ms-2" v-if="isNewValue">{{ linkValue }}</Badge>' +
|
||||||
|
'</div>',
|
||||||
|
computed: {
|
||||||
|
value: () => this.value,
|
||||||
|
linkValue: () => this.linkValue,
|
||||||
|
isNewValue: () => {
|
||||||
|
const values = this.suggestions.map((d) => d.label);
|
||||||
|
return this.linkValue && !values.includes(this.linkValue);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: { Badge },
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async openNewDoc() {
|
||||||
|
const schemaName = this.df.target;
|
||||||
|
const name =
|
||||||
|
this.linkValue || fyo.doc.getTemporaryName(fyo.schemaMap[schemaName]);
|
||||||
|
const filters = await this.getCreateFilters();
|
||||||
|
const { openQuickEdit } = await import('src/utils/ui');
|
||||||
|
|
||||||
|
const doc = fyo.doc.getNewDoc(schemaName, { name, ...filters });
|
||||||
|
openQuickEdit({ doc });
|
||||||
|
|
||||||
|
doc.once('afterSync', () => {
|
||||||
|
this.$router.back();
|
||||||
|
this.results = [];
|
||||||
|
this.triggerChange(doc.name);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getCreateFilters() {
|
||||||
|
const { schemaName, fieldname } = this.df;
|
||||||
|
|
||||||
|
const getCreateFilters =
|
||||||
|
fyo.models[schemaName]?.createFilters?.[fieldname];
|
||||||
|
let createFilters = await getCreateFilters?.(this.doc);
|
||||||
|
|
||||||
|
if (createFilters !== undefined) {
|
||||||
|
return createFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filters = await this.getFilters();
|
||||||
|
return getCreateFiltersFromListViewFilters(filters);
|
||||||
|
},
|
||||||
|
async getFilters() {
|
||||||
|
const { schemaName, fieldname } = this.df;
|
||||||
|
const getFilters = fyo.models[schemaName]?.filters?.[fieldname];
|
||||||
|
|
||||||
|
if (getFilters === undefined) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.doc) {
|
||||||
|
return (await getFilters(this.doc)) ?? {};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (await getFilters()) ?? {};
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
@ -382,9 +382,10 @@
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<!-- Customer Search -->
|
<!-- Customer Search -->
|
||||||
<Link
|
<MultiLabelLink
|
||||||
v-if="sinvDoc.fieldMap"
|
v-if="sinvDoc.fieldMap"
|
||||||
class="flex-shrink-0"
|
class="flex-shrink-0"
|
||||||
|
secondary-link="phone"
|
||||||
:border="true"
|
:border="true"
|
||||||
:value="sinvDoc.party"
|
:value="sinvDoc.party"
|
||||||
:df="sinvDoc.fieldMap.party"
|
:df="sinvDoc.fieldMap.party"
|
||||||
@ -567,6 +568,7 @@ import AlertModal from './AlertModal.vue';
|
|||||||
import SavedInvoiceModal from './SavedInvoiceModal.vue';
|
import SavedInvoiceModal from './SavedInvoiceModal.vue';
|
||||||
import CouponCodeModal from './CouponCodeModal.vue';
|
import CouponCodeModal from './CouponCodeModal.vue';
|
||||||
import { AppliedCouponCodes } from 'models/baseModels/AppliedCouponCodes/AppliedCouponCodes';
|
import { AppliedCouponCodes } from 'models/baseModels/AppliedCouponCodes/AppliedCouponCodes';
|
||||||
|
import MultiLabelLink from 'src/components/Controls/MultiLabelLink.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'POS',
|
name: 'POS',
|
||||||
@ -578,6 +580,7 @@ export default defineComponent({
|
|||||||
ItemsTable,
|
ItemsTable,
|
||||||
ItemsGrid,
|
ItemsGrid,
|
||||||
Link,
|
Link,
|
||||||
|
MultiLabelLink,
|
||||||
AlertModal,
|
AlertModal,
|
||||||
OpenPOSShiftModal,
|
OpenPOSShiftModal,
|
||||||
PageHeader,
|
PageHeader,
|
||||||
|
Loading…
Reference in New Issue
Block a user