2023-02-17 21:05:06 +05:30
|
|
|
<template>
|
|
|
|
<FormContainer>
|
2023-03-06 11:56:54 +05:30
|
|
|
<template #header-left v-if="hasDoc">
|
|
|
|
<StatusBadge :status="status" class="h-8" />
|
|
|
|
</template>
|
2023-02-20 10:22:19 +05:30
|
|
|
<template #header v-if="hasDoc">
|
2023-03-06 15:13:54 +05:30
|
|
|
<Button
|
|
|
|
v-if="!doc.isCancelled && !doc.dirty && isPrintable"
|
|
|
|
:icon="true"
|
|
|
|
@click="routeTo(`/print/${doc.schemaName}/${doc.name}`)"
|
|
|
|
>
|
|
|
|
{{ t`Print` }}
|
|
|
|
</Button>
|
2023-02-20 10:22:19 +05:30
|
|
|
<DropdownWithActions
|
|
|
|
v-for="group of groupedActions"
|
|
|
|
:key="group.label"
|
|
|
|
:type="group.type"
|
|
|
|
:actions="group.actions"
|
|
|
|
>
|
|
|
|
<p v-if="group.group">
|
|
|
|
{{ group.group }}
|
|
|
|
</p>
|
|
|
|
<feather-icon v-else name="more-horizontal" class="w-4 h-4" />
|
|
|
|
</DropdownWithActions>
|
2023-02-21 11:04:35 +05:30
|
|
|
<Button v-if="doc?.canSave" type="primary" @click="sync">
|
2023-02-20 10:22:19 +05:30
|
|
|
{{ t`Save` }}
|
|
|
|
</Button>
|
2023-02-21 11:04:35 +05:30
|
|
|
<Button v-else-if="doc?.canSubmit" type="primary" @click="submit">{{
|
|
|
|
t`Submit`
|
|
|
|
}}</Button>
|
2023-02-20 10:22:19 +05:30
|
|
|
</template>
|
2023-02-17 21:05:06 +05:30
|
|
|
<template #body>
|
|
|
|
<FormHeader
|
|
|
|
:form-title="title"
|
|
|
|
:form-sub-title="schema.label"
|
|
|
|
class="sticky top-0 bg-white border-b"
|
2023-02-20 10:22:19 +05:30
|
|
|
>
|
|
|
|
</FormHeader>
|
2023-02-17 21:05:06 +05:30
|
|
|
|
|
|
|
<!-- Section Container -->
|
|
|
|
<div v-if="hasDoc" class="overflow-auto custom-scroll">
|
|
|
|
<CommonFormSection
|
2023-02-17 21:56:41 +05:30
|
|
|
v-for="([name, fields], idx) in activeGroup.entries()"
|
2023-02-20 11:22:48 +05:30
|
|
|
@editrow="(doc: Doc) => toggleQuickEditDoc(doc)"
|
2023-02-17 21:56:41 +05:30
|
|
|
:key="name + idx"
|
2023-02-17 21:05:06 +05:30
|
|
|
ref="section"
|
|
|
|
class="p-4"
|
2023-02-17 21:56:41 +05:30
|
|
|
:class="idx !== 0 && activeGroup.size > 1 ? 'border-t' : ''"
|
|
|
|
:show-title="activeGroup.size > 1 && name !== t`Default`"
|
2023-02-17 21:05:06 +05:30
|
|
|
:title="name"
|
|
|
|
:fields="fields"
|
|
|
|
:doc="doc"
|
2023-02-21 11:04:35 +05:30
|
|
|
:errors="errors"
|
|
|
|
@value-change="onValueChange"
|
2023-02-17 21:05:06 +05:30
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Tab Bar -->
|
|
|
|
<div
|
|
|
|
class="
|
|
|
|
mt-auto
|
|
|
|
px-4
|
|
|
|
pb-4
|
|
|
|
flex
|
|
|
|
gap-8
|
|
|
|
border-t
|
|
|
|
flex-shrink-0
|
|
|
|
sticky
|
|
|
|
bottom-0
|
|
|
|
bg-white
|
|
|
|
"
|
2023-02-21 11:04:35 +05:30
|
|
|
v-if="groupedFields && groupedFields.size > 1"
|
2023-02-17 21:05:06 +05:30
|
|
|
>
|
|
|
|
<div
|
2023-02-20 11:22:48 +05:30
|
|
|
v-for="key of groupedFields.keys()"
|
2023-02-17 21:05:06 +05:30
|
|
|
:key="key"
|
|
|
|
@click="activeTab = key"
|
|
|
|
class="text-sm cursor-pointer"
|
|
|
|
:class="
|
|
|
|
key === activeTab
|
|
|
|
? 'text-blue-500 font-semibold border-t-2 border-blue-500'
|
|
|
|
: ''
|
|
|
|
"
|
|
|
|
:style="{
|
|
|
|
paddingTop: key === activeTab ? 'calc(1rem - 2px)' : '1rem',
|
|
|
|
}"
|
|
|
|
>
|
|
|
|
{{ key }}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
2023-02-20 11:22:48 +05:30
|
|
|
<template #quickedit>
|
|
|
|
<Transition name="quickedit">
|
|
|
|
<QuickEditForm
|
|
|
|
v-if="hasQeDoc"
|
|
|
|
:name="qeDoc.name"
|
|
|
|
:show-name="false"
|
|
|
|
:show-save="false"
|
|
|
|
:source-doc="qeDoc"
|
|
|
|
:schema-name="qeDoc.schemaName"
|
|
|
|
:white="true"
|
|
|
|
:route-back="false"
|
|
|
|
:load-on-close="false"
|
|
|
|
@close="() => toggleQuickEditDoc(null)"
|
|
|
|
/>
|
|
|
|
</Transition>
|
|
|
|
</template>
|
2023-02-17 21:05:06 +05:30
|
|
|
</FormContainer>
|
|
|
|
</template>
|
|
|
|
<script lang="ts">
|
2023-02-21 11:04:35 +05:30
|
|
|
import { DocValue } from 'fyo/core/types';
|
2023-02-17 21:05:06 +05:30
|
|
|
import { Doc } from 'fyo/model/doc';
|
|
|
|
import { ValidationError } from 'fyo/utils/errors';
|
2023-02-20 10:22:19 +05:30
|
|
|
import { getDocStatus } from 'models/helpers';
|
2023-02-17 21:05:06 +05:30
|
|
|
import { ModelNameEnum } from 'models/types';
|
|
|
|
import { Field, Schema } from 'schemas/types';
|
2023-02-20 10:22:19 +05:30
|
|
|
import Button from 'src/components/Button.vue';
|
|
|
|
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
2023-02-17 21:05:06 +05:30
|
|
|
import FormContainer from 'src/components/FormContainer.vue';
|
|
|
|
import FormHeader from 'src/components/FormHeader.vue';
|
2023-02-20 10:22:19 +05:30
|
|
|
import StatusBadge from 'src/components/StatusBadge.vue';
|
2023-02-21 11:04:35 +05:30
|
|
|
import { handleErrorWithDialog } from 'src/errorHandling';
|
|
|
|
import { getErrorMessage } from 'src/utils';
|
2023-03-01 14:05:59 +05:30
|
|
|
import { docsPathMap } from 'src/utils/misc';
|
|
|
|
import { docsPathRef, focusedDocsRef } from 'src/utils/refs';
|
2023-02-20 10:22:19 +05:30
|
|
|
import { ActionGroup, UIGroupedFields } from 'src/utils/types';
|
|
|
|
import {
|
2023-03-15 14:02:52 +05:30
|
|
|
commonDocSubmit,
|
|
|
|
commonDocSync,
|
2023-02-22 15:51:20 +05:30
|
|
|
getDocFromNameIfExistsElseNew,
|
2023-02-20 10:22:19 +05:30
|
|
|
getFieldsGroupedByTabAndSection,
|
|
|
|
getGroupedActionsForDoc,
|
2023-03-06 15:13:54 +05:30
|
|
|
isPrintable,
|
|
|
|
routeTo,
|
2023-02-20 10:22:19 +05:30
|
|
|
} from 'src/utils/ui';
|
2023-02-20 11:22:48 +05:30
|
|
|
import { computed, defineComponent, nextTick } from 'vue';
|
|
|
|
import QuickEditForm from '../QuickEditForm.vue';
|
2023-02-17 21:05:06 +05:30
|
|
|
import CommonFormSection from './CommonFormSection.vue';
|
|
|
|
|
|
|
|
export default defineComponent({
|
|
|
|
props: {
|
2023-02-20 11:22:48 +05:30
|
|
|
name: { type: String, default: '' },
|
2023-02-21 11:04:35 +05:30
|
|
|
schemaName: { type: String, default: ModelNameEnum.SalesInvoice },
|
2023-02-17 21:05:06 +05:30
|
|
|
},
|
2023-02-20 10:22:19 +05:30
|
|
|
provide() {
|
|
|
|
return {
|
|
|
|
schemaName: computed(() => this.docOrNull?.schemaName),
|
|
|
|
name: computed(() => this.docOrNull?.name),
|
|
|
|
doc: computed(() => this.docOrNull),
|
|
|
|
};
|
|
|
|
},
|
2023-02-17 21:05:06 +05:30
|
|
|
data() {
|
|
|
|
return {
|
2023-02-21 11:04:35 +05:30
|
|
|
errors: {},
|
2023-02-17 21:05:06 +05:30
|
|
|
docOrNull: null,
|
2023-03-15 12:48:10 +05:30
|
|
|
activeTab: this.t`Default`,
|
2023-02-21 11:04:35 +05:30
|
|
|
groupedFields: null,
|
2023-02-20 11:22:48 +05:30
|
|
|
quickEditDoc: null,
|
2023-03-06 15:13:54 +05:30
|
|
|
isPrintable: false,
|
2023-02-20 11:22:48 +05:30
|
|
|
} as {
|
2023-02-21 11:04:35 +05:30
|
|
|
errors: Record<string, string>;
|
2023-02-20 11:22:48 +05:30
|
|
|
docOrNull: null | Doc;
|
|
|
|
activeTab: string;
|
2023-02-21 11:04:35 +05:30
|
|
|
groupedFields: null | UIGroupedFields;
|
2023-02-20 11:22:48 +05:30
|
|
|
quickEditDoc: null | Doc;
|
2023-03-06 15:13:54 +05:30
|
|
|
isPrintable: boolean;
|
2023-02-20 11:22:48 +05:30
|
|
|
};
|
2023-02-17 21:05:06 +05:30
|
|
|
},
|
|
|
|
async mounted() {
|
|
|
|
if (this.fyo.store.isDevelopment) {
|
|
|
|
// @ts-ignore
|
|
|
|
window.cf = this;
|
|
|
|
}
|
2023-02-20 10:22:19 +05:30
|
|
|
|
|
|
|
await this.setDoc();
|
2023-03-01 14:05:59 +05:30
|
|
|
focusedDocsRef.add(this.docOrNull);
|
2023-02-21 11:51:32 +05:30
|
|
|
this.updateGroupedFields();
|
2023-03-15 12:48:10 +05:30
|
|
|
if (this.groupedFields) {
|
|
|
|
this.activeTab = [...this.groupedFields.keys()][0];
|
|
|
|
}
|
2023-03-06 15:13:54 +05:30
|
|
|
this.isPrintable = await isPrintable(this.schemaName);
|
2023-02-17 21:05:06 +05:30
|
|
|
},
|
2023-03-01 14:05:59 +05:30
|
|
|
activated(): void {
|
|
|
|
docsPathRef.value = docsPathMap[this.schemaName] ?? '';
|
|
|
|
focusedDocsRef.add(this.docOrNull);
|
|
|
|
},
|
|
|
|
deactivated(): void {
|
|
|
|
docsPathRef.value = '';
|
2023-03-09 21:46:31 +05:30
|
|
|
if (this.docOrNull) {
|
|
|
|
focusedDocsRef.delete(this.doc);
|
|
|
|
}
|
2023-03-01 14:05:59 +05:30
|
|
|
},
|
2023-02-17 21:05:06 +05:30
|
|
|
computed: {
|
|
|
|
hasDoc(): boolean {
|
|
|
|
return !!this.docOrNull;
|
|
|
|
},
|
2023-02-20 11:22:48 +05:30
|
|
|
hasQeDoc(): boolean {
|
|
|
|
return !!this.quickEditDoc;
|
|
|
|
},
|
2023-02-20 10:22:19 +05:30
|
|
|
status(): string {
|
|
|
|
if (!this.hasDoc) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
return getDocStatus(this.doc);
|
|
|
|
},
|
2023-02-17 21:05:06 +05:30
|
|
|
doc(): Doc {
|
|
|
|
const doc = this.docOrNull as Doc | null;
|
|
|
|
if (!doc) {
|
|
|
|
throw new ValidationError(
|
|
|
|
this.t`Doc ${this.schema.label} ${this.name} not set`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return doc;
|
|
|
|
},
|
2023-02-20 11:22:48 +05:30
|
|
|
qeDoc(): Doc {
|
|
|
|
const doc = this.quickEditDoc as Doc | null;
|
|
|
|
if (!doc) {
|
|
|
|
throw new ValidationError(
|
|
|
|
this.t`Doc ${this.schema.label} ${this.name} not set`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return doc;
|
|
|
|
},
|
2023-02-17 21:05:06 +05:30
|
|
|
title(): string {
|
2023-02-20 10:22:19 +05:30
|
|
|
if (this.schema.isSubmittable && this.docOrNull?.notInserted) {
|
2023-02-17 21:05:06 +05:30
|
|
|
return this.t`New Entry`;
|
|
|
|
}
|
2023-02-20 10:22:19 +05:30
|
|
|
|
2023-02-21 11:04:35 +05:30
|
|
|
return this.docOrNull?.name! ?? this.t`New Entry`;
|
2023-02-17 21:05:06 +05:30
|
|
|
},
|
|
|
|
schema(): Schema {
|
|
|
|
const schema = this.fyo.schemaMap[this.schemaName];
|
|
|
|
if (!schema) {
|
|
|
|
throw new ValidationError(`no schema found with ${this.schemaName}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return schema;
|
|
|
|
},
|
|
|
|
activeGroup(): Map<string, Field[]> {
|
2023-02-21 11:04:35 +05:30
|
|
|
if (!this.groupedFields) {
|
|
|
|
return new Map();
|
|
|
|
}
|
|
|
|
|
2023-02-20 11:22:48 +05:30
|
|
|
const group = this.groupedFields.get(this.activeTab);
|
2023-02-17 21:05:06 +05:30
|
|
|
if (!group) {
|
|
|
|
throw new ValidationError(
|
|
|
|
`Tab group ${this.activeTab} has no value set`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return group;
|
|
|
|
},
|
2023-02-20 10:22:19 +05:30
|
|
|
groupedActions(): ActionGroup[] {
|
|
|
|
if (!this.hasDoc) {
|
|
|
|
return [];
|
|
|
|
}
|
2023-02-17 21:05:06 +05:30
|
|
|
|
2023-02-20 10:22:19 +05:30
|
|
|
return getGroupedActionsForDoc(this.doc);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
methods: {
|
2023-03-06 15:13:54 +05:30
|
|
|
routeTo,
|
2023-02-21 11:51:32 +05:30
|
|
|
updateGroupedFields(): void {
|
2023-02-21 11:04:35 +05:30
|
|
|
if (!this.hasDoc) {
|
2023-02-21 11:51:32 +05:30
|
|
|
return;
|
2023-02-21 11:04:35 +05:30
|
|
|
}
|
|
|
|
|
2023-02-21 11:51:32 +05:30
|
|
|
this.groupedFields = getFieldsGroupedByTabAndSection(
|
|
|
|
this.schema,
|
|
|
|
this.doc
|
|
|
|
);
|
2023-02-21 11:04:35 +05:30
|
|
|
},
|
|
|
|
async sync() {
|
2023-03-15 14:02:52 +05:30
|
|
|
if (await commonDocSync(this.doc)) {
|
2023-02-21 11:51:32 +05:30
|
|
|
this.updateGroupedFields();
|
2023-02-21 11:04:35 +05:30
|
|
|
}
|
|
|
|
},
|
|
|
|
async submit() {
|
2023-03-15 14:02:52 +05:30
|
|
|
if (await commonDocSubmit(this.doc)) {
|
2023-02-21 11:51:32 +05:30
|
|
|
this.updateGroupedFields();
|
2023-02-21 11:04:35 +05:30
|
|
|
}
|
|
|
|
},
|
2023-02-20 10:22:19 +05:30
|
|
|
async setDoc() {
|
|
|
|
if (this.hasDoc) {
|
|
|
|
return;
|
|
|
|
}
|
2023-02-17 21:05:06 +05:30
|
|
|
|
2023-02-22 15:51:20 +05:30
|
|
|
this.docOrNull = await getDocFromNameIfExistsElseNew(
|
|
|
|
this.schemaName,
|
|
|
|
this.name
|
|
|
|
);
|
2023-02-21 11:04:35 +05:30
|
|
|
},
|
2023-02-20 11:22:48 +05:30
|
|
|
async toggleQuickEditDoc(doc: Doc | null) {
|
|
|
|
if (this.quickEditDoc && doc) {
|
|
|
|
this.quickEditDoc = null;
|
|
|
|
await nextTick();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.quickEditDoc = doc;
|
|
|
|
},
|
2023-02-21 11:04:35 +05:30
|
|
|
async onValueChange(field: Field, value: DocValue) {
|
|
|
|
const { fieldname } = field;
|
|
|
|
delete this.errors[fieldname];
|
|
|
|
|
|
|
|
try {
|
|
|
|
await this.doc.set(fieldname, value);
|
|
|
|
} catch (err) {
|
|
|
|
if (!(err instanceof Error)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.errors[fieldname] = getErrorMessage(err, this.doc);
|
|
|
|
}
|
|
|
|
|
2023-02-21 11:51:32 +05:30
|
|
|
this.updateGroupedFields();
|
2023-02-21 11:04:35 +05:30
|
|
|
},
|
2023-02-20 10:22:19 +05:30
|
|
|
},
|
|
|
|
components: {
|
|
|
|
FormContainer,
|
|
|
|
FormHeader,
|
|
|
|
CommonFormSection,
|
|
|
|
StatusBadge,
|
|
|
|
Button,
|
|
|
|
DropdownWithActions,
|
2023-02-20 11:22:48 +05:30
|
|
|
QuickEditForm,
|
2023-02-20 10:22:19 +05:30
|
|
|
},
|
|
|
|
});
|
2023-02-17 21:05:06 +05:30
|
|
|
</script>
|