|
|
@ -2,21 +2,38 @@ |
|
|
|
import type { Ref } from 'vue' |
|
|
|
import type { Ref } from 'vue' |
|
|
|
import type { ListItem as AntListItem } from 'ant-design-vue' |
|
|
|
import type { ListItem as AntListItem } from 'ant-design-vue' |
|
|
|
import jsep from 'jsep' |
|
|
|
import jsep from 'jsep' |
|
|
|
import type { ColumnType, FormulaType } from 'nocodb-sdk' |
|
|
|
import type { ColumnType, FormulaType, LinkToAnotherRecordType, TableType } from 'nocodb-sdk' |
|
|
|
import { UITypes, jsepCurlyHook, substituteColumnIdWithAliasInFormula } from 'nocodb-sdk' |
|
|
|
import { |
|
|
|
|
|
|
|
UITypes, |
|
|
|
|
|
|
|
isLinksOrLTAR, |
|
|
|
|
|
|
|
isNumericCol, |
|
|
|
|
|
|
|
isSystemColumn, |
|
|
|
|
|
|
|
jsepCurlyHook, |
|
|
|
|
|
|
|
substituteColumnIdWithAliasInFormula, |
|
|
|
|
|
|
|
} from 'nocodb-sdk' |
|
|
|
import { |
|
|
|
import { |
|
|
|
MetaInj, |
|
|
|
MetaInj, |
|
|
|
NcAutocompleteTree, |
|
|
|
NcAutocompleteTree, |
|
|
|
|
|
|
|
computed, |
|
|
|
formulaList, |
|
|
|
formulaList, |
|
|
|
formulaTypes, |
|
|
|
formulaTypes, |
|
|
|
formulas, |
|
|
|
formulas, |
|
|
|
getUIDTIcon, |
|
|
|
getUIDTIcon, |
|
|
|
getWordUntilCaret, |
|
|
|
getWordUntilCaret, |
|
|
|
iconMap, |
|
|
|
iconMap, |
|
|
|
|
|
|
|
inject, |
|
|
|
insertAtCursor, |
|
|
|
insertAtCursor, |
|
|
|
|
|
|
|
isDate, |
|
|
|
|
|
|
|
nextTick, |
|
|
|
onMounted, |
|
|
|
onMounted, |
|
|
|
|
|
|
|
ref, |
|
|
|
|
|
|
|
storeToRefs, |
|
|
|
|
|
|
|
useBase, |
|
|
|
useColumnCreateStoreOrThrow, |
|
|
|
useColumnCreateStoreOrThrow, |
|
|
|
useDebounceFn, |
|
|
|
useDebounceFn, |
|
|
|
|
|
|
|
useI18n, |
|
|
|
|
|
|
|
useMetas, |
|
|
|
|
|
|
|
useNocoEe, |
|
|
|
useVModel, |
|
|
|
useVModel, |
|
|
|
validateDateWithUnknownFormat, |
|
|
|
validateDateWithUnknownFormat, |
|
|
|
} from '#imports' |
|
|
|
} from '#imports' |
|
|
@ -35,6 +52,10 @@ const { setAdditionalValidations, validateInfos, sqlUi, column } = useColumnCrea |
|
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n() |
|
|
|
const { t } = useI18n() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const baseStore = useBase() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const { tables } = storeToRefs(baseStore) |
|
|
|
|
|
|
|
|
|
|
|
const { predictFunction: _predictFunction } = useNocoEe() |
|
|
|
const { predictFunction: _predictFunction } = useNocoEe() |
|
|
|
|
|
|
|
|
|
|
|
enum JSEPNode { |
|
|
|
enum JSEPNode { |
|
|
@ -54,6 +75,23 @@ const meta = inject(MetaInj, ref()) |
|
|
|
const supportedColumns = computed( |
|
|
|
const supportedColumns = computed( |
|
|
|
() => meta?.value?.columns?.filter((col) => !uiTypesNotSupportedInFormulas.includes(col.uidt as UITypes)) || [], |
|
|
|
() => meta?.value?.columns?.filter((col) => !uiTypesNotSupportedInFormulas.includes(col.uidt as UITypes)) || [], |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
const { metas } = useMetas() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const refTables = computed(() => { |
|
|
|
|
|
|
|
if (!tables.value || !tables.value.length || !meta.value || !meta.value.columns) { |
|
|
|
|
|
|
|
return [] |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const _refTables = meta.value.columns |
|
|
|
|
|
|
|
.filter((column) => isLinksOrLTAR(column) && !column.system && column.source_id === meta.value?.source_id) |
|
|
|
|
|
|
|
.map((column) => ({ |
|
|
|
|
|
|
|
col: column.colOptions, |
|
|
|
|
|
|
|
column, |
|
|
|
|
|
|
|
...tables.value.find((table) => table.id === (column.colOptions as LinkToAnotherRecordType).fk_related_model_id), |
|
|
|
|
|
|
|
})) |
|
|
|
|
|
|
|
.filter((table) => (table.col as LinkToAnotherRecordType)?.fk_related_model_id === table.id && !table.mm) |
|
|
|
|
|
|
|
return _refTables as Required<TableType & { column: ColumnType; col: Required<LinkToAnotherRecordType> }>[] |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
const validators = { |
|
|
|
const validators = { |
|
|
|
formula_raw: [ |
|
|
|
formula_raw: [ |
|
|
@ -501,6 +539,53 @@ function validateAgainstType(parsedTree: any, expectedType: string, func: any, t |
|
|
|
} |
|
|
|
} |
|
|
|
break |
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case UITypes.Rollup: { |
|
|
|
|
|
|
|
const rollupFunction = col.colOptions.rollup_function |
|
|
|
|
|
|
|
if (['count', 'avg', 'sum', 'countDistinct', 'sumDistinct', 'avgDistinct'].includes(rollupFunction)) { |
|
|
|
|
|
|
|
// these functions produce a numeric value, which can be used in numeric functions |
|
|
|
|
|
|
|
if (expectedType !== formulaTypes.NUMERIC) { |
|
|
|
|
|
|
|
typeErrors.add( |
|
|
|
|
|
|
|
t('msg.formula.columnWithTypeFoundButExpected', { |
|
|
|
|
|
|
|
columnName: parsedTree.name, |
|
|
|
|
|
|
|
columnType: formulaTypes.NUMERIC, |
|
|
|
|
|
|
|
expectedType, |
|
|
|
|
|
|
|
}), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// the value is based on the foreign rollup column type |
|
|
|
|
|
|
|
const selectedTable = refTables.value.find((t) => t.column.id === col.colOptions.fk_relation_column_id) |
|
|
|
|
|
|
|
const refTableColumns = metas.value[selectedTable.id].columns.filter( |
|
|
|
|
|
|
|
(c: ColumnType) => |
|
|
|
|
|
|
|
vModel.value.fk_lookup_column_id === c.id || |
|
|
|
|
|
|
|
(!isSystemColumn(c) && c.id !== vModel.value.id && c.uidt !== UITypes.Links), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
const childFieldColumn = refTableColumns.find( |
|
|
|
|
|
|
|
(column: ColumnType) => column.id === col.colOptions.fk_rollup_column_id, |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
const abstractType = sqlUi.value.getAbstractType(childFieldColumn) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (expectedType === formulaTypes.DATE && !isDate(childFieldColumn, sqlUi.value.getAbstractType(childFieldColumn))) { |
|
|
|
|
|
|
|
typeErrors.add( |
|
|
|
|
|
|
|
t('msg.formula.columnWithTypeFoundButExpected', { |
|
|
|
|
|
|
|
columnName: parsedTree.name, |
|
|
|
|
|
|
|
columnType: abstractType, |
|
|
|
|
|
|
|
expectedType, |
|
|
|
|
|
|
|
}), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} else if (expectedType === formulaTypes.NUMERIC && !isNumericCol(childFieldColumn)) { |
|
|
|
|
|
|
|
typeErrors.add( |
|
|
|
|
|
|
|
t('msg.formula.columnWithTypeFoundButExpected', { |
|
|
|
|
|
|
|
columnName: parsedTree.name, |
|
|
|
|
|
|
|
columnType: abstractType, |
|
|
|
|
|
|
|
expectedType, |
|
|
|
|
|
|
|
}), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// not supported |
|
|
|
// not supported |
|
|
|
case UITypes.ForeignKey: |
|
|
|
case UITypes.ForeignKey: |
|
|
|
case UITypes.Attachment: |
|
|
|
case UITypes.Attachment: |
|
|
@ -508,7 +593,6 @@ function validateAgainstType(parsedTree: any, expectedType: string, func: any, t |
|
|
|
case UITypes.Time: |
|
|
|
case UITypes.Time: |
|
|
|
case UITypes.Percent: |
|
|
|
case UITypes.Percent: |
|
|
|
case UITypes.Duration: |
|
|
|
case UITypes.Duration: |
|
|
|
case UITypes.Rollup: |
|
|
|
|
|
|
|
case UITypes.Lookup: |
|
|
|
case UITypes.Lookup: |
|
|
|
case UITypes.Barcode: |
|
|
|
case UITypes.Barcode: |
|
|
|
case UITypes.Button: |
|
|
|
case UITypes.Button: |
|
|
|