mirror of
https://github.com/frappe/books.git
synced 2024-12-22 19:09: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 -->
|
||||
<Link
|
||||
<MultiLabelLink
|
||||
v-if="sinvDoc.fieldMap"
|
||||
class="flex-shrink-0"
|
||||
secondary-link="phone"
|
||||
:border="true"
|
||||
:value="sinvDoc.party"
|
||||
:df="sinvDoc.fieldMap.party"
|
||||
@ -567,6 +568,7 @@ import AlertModal from './AlertModal.vue';
|
||||
import SavedInvoiceModal from './SavedInvoiceModal.vue';
|
||||
import CouponCodeModal from './CouponCodeModal.vue';
|
||||
import { AppliedCouponCodes } from 'models/baseModels/AppliedCouponCodes/AppliedCouponCodes';
|
||||
import MultiLabelLink from 'src/components/Controls/MultiLabelLink.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'POS',
|
||||
@ -578,6 +580,7 @@ export default defineComponent({
|
||||
ItemsTable,
|
||||
ItemsGrid,
|
||||
Link,
|
||||
MultiLabelLink,
|
||||
AlertModal,
|
||||
OpenPOSShiftModal,
|
||||
PageHeader,
|
||||
|
Loading…
Reference in New Issue
Block a user