diff --git a/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py b/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py index e44e3e20..f6aaae7e 100644 --- a/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py +++ b/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py @@ -47,7 +47,10 @@ def get_fields_layout(doctype: str, type: str, parent_doctype: str | None = None for tab in tabs: for section in tab.get("sections"): + if section.get("columns"): + section["columns"] = [column for column in section.get("columns") if column] for column in section.get("columns") if section.get("columns") else []: + column["fields"] = [field for field in column.get("fields") if field] for field in column.get("fields") if column.get("fields") else []: field = next((f for f in fields if f.fieldname == field), None) if field: diff --git a/frontend/src/components/Activities/DataFields.vue b/frontend/src/components/Activities/DataFields.vue index e73fb891..f572bbc0 100644 --- a/frontend/src/components/Activities/DataFields.vue +++ b/frontend/src/components/Activities/DataFields.vue @@ -5,7 +5,7 @@
{{ __('Data') }}
@@ -38,7 +38,7 @@
@@ -49,7 +49,7 @@ @reload=" () => { tabs.reload() - data.reload() + document.reload() } " /> @@ -59,10 +59,10 @@ import EditIcon from '@/components/Icons/EditIcon.vue' import DataFieldsModal from '@/components/Modals/DataFieldsModal.vue' import FieldLayout from '@/components/FieldLayout/FieldLayout.vue' -import { Badge, createResource, createDocumentResource } from 'frappe-ui' +import { Badge, createResource } from 'frappe-ui' import LoadingIndicator from '@/components/Icons/LoadingIndicator.vue' -import { createToast } from '@/utils' import { usersStore } from '@/stores/users' +import { useDocument } from '@/data/document' import { isMobileView } from '@/composables/settings' import { ref, watch } from 'vue' @@ -76,33 +76,11 @@ const props = defineProps({ required: true, }, }) - const { isManager } = usersStore() const showDataFieldsModal = ref(false) -const data = createDocumentResource({ - doctype: props.doctype, - name: props.docname, - setValue: { - onSuccess: () => { - data.reload() - createToast({ - title: 'Data Updated', - icon: 'check', - iconClasses: 'text-ink-green-3', - }) - }, - onError: (err) => { - createToast({ - title: 'Error', - text: err.messages[0], - icon: 'x', - iconClasses: 'text-red-600', - }) - }, - }, -}) +const { document } = useDocument(props.doctype, props.docname) const tabs = createResource({ url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout', @@ -112,19 +90,19 @@ const tabs = createResource({ }) function saveChanges() { - data.save.submit() + document.save.submit() } watch( - () => data.doc, + () => document.doc, (newValue, oldValue) => { if (!oldValue) return if (newValue && oldValue) { const isDirty = - JSON.stringify(newValue) !== JSON.stringify(data.originalDoc) - data.isDirty = isDirty + JSON.stringify(newValue) !== JSON.stringify(document.originalDoc) + document.isDirty = isDirty if (isDirty) { - data.save.loading = false + document.save.loading = false } } }, diff --git a/frontend/src/components/Controls/Grid.vue b/frontend/src/components/Controls/Grid.vue index 7a14049a..16da5ee5 100644 --- a/frontend/src/components/Controls/Grid.vue +++ b/frontend/src/components/Controls/Grid.vue @@ -93,7 +93,10 @@ :key="field.fieldname" > @@ -148,23 +152,26 @@ class="cursor-pointer duration-300" v-model="row[field.fieldname]" :disabled="!gridSettings.editable_grid" + @change="(e) => fieldChange(e.target.checked, field, row)" /> - + + + + @@ -265,6 +300,7 @@ import EditIcon from '@/components/Icons/EditIcon.vue' import Link from '@/components/Controls/Link.vue' import UserAvatar from '@/components/UserAvatar.vue' import { getRandom, getFormat, isTouchScreenDevice } from '@/utils' +import { flt } from '@/utils/numberFormat.js' import { usersStore } from '@/stores/users' import { getMeta } from '@/stores/meta' import { @@ -274,9 +310,10 @@ import { DateTimePicker, DatePicker, Tooltip, + dayjs, } from 'frappe-ui' import Draggable from 'vuedraggable' -import { ref, reactive, computed } from 'vue' +import { ref, reactive, computed, inject } from 'vue' const props = defineProps({ label: { @@ -291,11 +328,24 @@ const props = defineProps({ type: String, required: true, }, + parentFieldname: { + type: String, + required: true, + }, }) -const { getGridViewSettings, getFields, getGridSettings } = getMeta( - props.doctype, -) +const triggerOnChange = inject('triggerOnChange') +const triggerOnRowAdd = inject('triggerOnRowAdd') +const triggerOnRowRemove = inject('triggerOnRowRemove') + +const { + getGridViewSettings, + getFields, + getFormattedPercent, + getFormattedFloat, + getFormattedCurrency, + getGridSettings, +} = getMeta(props.doctype) getMeta(props.parentDoctype) const { getUser } = usersStore() @@ -322,6 +372,10 @@ const fields = computed(() => { ) }) +const allFields = computed(() => { + return getFields()?.map((f) => getFieldObj(f)) || [] +}) + function getFieldObj(field) { return { ...field, @@ -367,21 +421,71 @@ const toggleSelectRow = (row) => { const addRow = () => { const newRow = {} - fields.value?.forEach((field) => { - if (field.fieldtype === 'Check') newRow[field.fieldname] = false - else newRow[field.fieldname] = '' + allFields.value?.forEach((field) => { + if (field.fieldtype === 'Check') { + newRow[field.fieldname] = false + } else { + newRow[field.fieldname] = '' + } + + if (field.default) { + newRow[field.fieldname] = getDefaultValue(field.default, field.fieldtype) + } }) newRow.name = getRandom(10) showRowList.value.push(false) newRow['__islocal'] = true + newRow['idx'] = rows.value.length + 1 + newRow['doctype'] = props.doctype + newRow['parentfield'] = props.parentFieldname + newRow['parenttype'] = props.parentDoctype rows.value.push(newRow) + triggerOnRowAdd(newRow) } const deleteRows = () => { rows.value = rows.value.filter((row) => !selectedRows.has(row.name)) + triggerOnRowRemove(selectedRows, rows.value) + showRowList.value.pop() selectedRows.clear() } + +function fieldChange(value, field, row) { + row[field.fieldname] = value + triggerOnChange(field.fieldname, row) +} + +function getDefaultValue(defaultValue, fieldtype) { + if (['Float', 'Currency', 'Percent'].includes(fieldtype)) { + return flt(defaultValue) + } else if (fieldtype === 'Check') { + if (['1', 'true', 'True'].includes(defaultValue)) { + return true + } else if (['0', 'false', 'False'].includes(defaultValue)) { + return false + } + } else if (fieldtype === 'Int') { + return parseInt(defaultValue) + } else if (defaultValue === 'Today' && fieldtype === 'Date') { + return dayjs().format('YYYY-MM-DD') + } else if ( + ['Now', 'now'].includes(defaultValue) && + fieldtype === 'Datetime' + ) { + return dayjs().format('YYYY-MM-DD HH:mm:ss') + } else if (['Now', 'now'].includes(defaultValue) && fieldtype === 'Time') { + return dayjs().format('HH:mm:ss') + } else if (fieldtype === 'Date') { + return dayjs(defaultValue).format('YYYY-MM-DD') + } else if (fieldtype === 'Datetime') { + return dayjs(defaultValue).format('YYYY-MM-DD HH:mm:ss') + } else if (fieldtype === 'Time') { + return dayjs(defaultValue).format('HH:mm:ss') + } + + return defaultValue +}