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"
>
fieldChange(v, field, row)"
/>
(row[field.fieldname] = v)"
+ @change="(v) => fieldChange(v, field, row)"
:placeholder="field.placeholder"
:hideMe="true"
>
@@ -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)"
/>
fieldChange(v, field, row)"
/>
fieldChange(v, field, row)"
/>
-
fieldChange(e.target.value, 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
+}