Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -118,25 +118,6 @@ describe('Collection Object business rules', () => {
expect(collectionObject.get('collectingEvent')).toBeDefined();
});

test('Save blocked when CollectionObjectType of a CollectionObject does not have same tree definition as its associated Determination -> taxon', async () => {
const collectionObject = getBaseCollectionObject();

const determination =
collectionObject.getDependentResource('determinations')?.models[0];

const { result } = renderHook(() =>
useSaveBlockers(determination, tables.Determination.getField('taxon'))
);

await act(async () => {
await determination?.businessRuleManager?.checkField('taxon');
});

expect(result.current[0]).toStrictEqual([
'Taxon does not belong to the same tree as this Object Type',
]);
});

const otherCollectionObjectTypeUrl = getResourceApiUrl(
'CollectionObjectType',
2
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { formsText } from '../../localization/forms';
import { resourcesText } from '../../localization/resources';
import { f } from '../../utils/functools';
import type { BusinessRuleResult } from './businessRules';
Expand Down Expand Up @@ -50,7 +49,6 @@ type MappedBusinessRuleDefs = {
};

const CURRENT_DETERMINATION_KEY = 'determination-isCurrent';
const DETERMINATION_TAXON_KEY = 'determination-taxon';

const hasNoCurrentDetermination = (collection: Collection<Determination>) =>
collection.models.length > 0 &&
Expand Down Expand Up @@ -185,14 +183,8 @@ export const businessRuleDefs: MappedBusinessRuleDefs = {
const taxonTreeDefinition = fetchedTaxon.definition;
const COTypeTreeDefinition = fetchedCOType.taxonTreeDef;

return taxonTreeDefinition === COTypeTreeDefinition
? void setSaveBlockers(
currentDetermination as SpecifyResource<AnySchema>,
currentDetermination.specifyTable.field.taxon,
[],
DETERMINATION_TAXON_KEY
)
: resource.set('determinations', []);
if (taxonTreeDefinition !== COTypeTreeDefinition)
resource.set('determinations', []);
})
.catch((error) => {
console.error('Error fetching resources:', error);
Expand All @@ -219,57 +211,6 @@ export const businessRuleDefs: MappedBusinessRuleDefs = {
accepted === null ? taxon : getLastAccepted(accepted)
);

const related = determination.collection?.related;
if (
related !== undefined &&
related.specifyTable.name === 'CollectionObject'
) {
const collectionObject =
related as SpecifyResource<CollectionObject>;
void f
.all({
defaultType:
collectionObject.get('collectionObjectType') === undefined
? collectionObject
.rgetPromise('collection')
.then(async (collection) =>
collection.rgetPromise('collectionObjectType')
)
.then((coType) => coType ?? undefined)
: undefined,
coType: collectionObject.rgetPromise(
'collectionObjectType',
true
),
})
.then(({ defaultType, coType }) => {
const resolvedCoType = coType ?? defaultType;
/*
* Have to set save blockers directly here to get this working.
* Since following code has to wait for above rgetPromise to resolve, returning a Promise<BusinessRuleResult> for validation here is too slow and
* does not get captured by business rules.
*/
if (
resolvedCoType?.get('taxonTreeDef') ===
(taxon?.get('definition') ?? '')
) {
setSaveBlockers(
determination as SpecifyResource<AnySchema>,
determination.specifyTable.field.taxon,
[],
DETERMINATION_TAXON_KEY
);
} else {
setSaveBlockers(
determination as SpecifyResource<AnySchema>,
determination.specifyTable.field.taxon,
[formsText.invalidTree()],
DETERMINATION_TAXON_KEY
);
}
});
}

return taxon === null
? {
isValid: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { RA, WritableArray } from '../../utils/types';
import { toTable, toTreeTable } from '../DataModel/helpers';
import type { AnySchema } from '../DataModel/helperTypes';
import type { SpecifyResource } from '../DataModel/legacyTypes';
import { idFromUrl } from '../DataModel/resource';
import { idFromUrl, strictIdFromUrl } from '../DataModel/resource';
import type { Relationship } from '../DataModel/specifyField';
import type { SpecifyTable } from '../DataModel/specifyTable';
import { tables } from '../DataModel/tables';
Expand Down Expand Up @@ -73,13 +73,15 @@ export function getQueryComboBoxConditions({
treeData,
subViewRelationship,
relatedTable,
treeDefinition,
}: {
readonly resource: SpecifyResource<AnySchema>;
readonly fieldName: string;
readonly treeData: QueryComboBoxTreeData | undefined;
readonly collectionRelationships: CollectionRelationships | undefined;
readonly relatedTable: SpecifyTable;
readonly subViewRelationship: Relationship | undefined;
readonly treeDefinition: string | undefined;
}): RA<SpecifyResource<SpQueryField>> {
const fields: WritableArray<SpecifyResource<SpQueryField>> = [];
const treeResource = toTreeTable(resource);
Expand Down Expand Up @@ -141,6 +143,20 @@ export function getQueryComboBoxConditions({
}
}

if (
resource.specifyTable === tables.Determination &&
fieldName === 'fullName' &&
treeDefinition !== undefined
) {
fields.push(
QueryFieldSpec.fromPath(tables.Taxon.name, ['definition', 'id'])
.toSpQueryField()
.set('isDisplay', false)
.set('startValue', strictIdFromUrl(treeDefinition).toString())
.set('operStart', queryFieldFilters.equal.id)
);
}

if (
typeof collectionRelationships === 'object' &&
fieldName === 'collectionRelType'
Expand Down
28 changes: 28 additions & 0 deletions specifyweb/frontend/js_src/lib/components/QueryComboBox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ import {
import { serializeResource } from '../DataModel/serializers';
import type { Relationship } from '../DataModel/specifyField';
import type { SpecifyTable } from '../DataModel/specifyTable';
import { tables } from '../DataModel/tables';
import type {
CollectionObject,
CollectionObjectType,
} from '../DataModel/types';
import { format, naiveFormatter } from '../Formatters/formatters';
import type { FormType } from '../FormParse';
import { ResourceView, RESTRICT_ADDING } from '../Forms/ResourceView';
Expand Down Expand Up @@ -247,6 +252,26 @@ export function QueryComboBox({
(typeof typeSearch === 'object' ? typeSearch?.table : undefined) ??
field.relatedTable;

const [treeDefinition] = useAsyncState(
React.useCallback(
async () =>
resource?.specifyTable === tables.Determination &&
resource.collection?.related?.specifyTable === tables.CollectionObject
? (resource.collection?.related as SpecifyResource<CollectionObject>)
.rgetPromise('collectionObjectType')
.then(
(
collectionObjectType:
| SpecifyResource<CollectionObjectType>
| undefined
) => collectionObjectType?.get('taxonTreeDef')
)
: undefined,
[resource, resource?.collection?.related?.get('collectionObjectType')]
),
false
);

// FEATURE: use main table field if type search is not defined
const fetchSource = React.useCallback(
async (value: string): Promise<RA<AutoCompleteItem<string>>> =>
Comment thread
sharadsw marked this conversation as resolved.
Expand Down Expand Up @@ -274,6 +299,7 @@ export function QueryComboBox({
typeof treeData === 'object' ? treeData : undefined,
relatedTable,
subViewRelationship,
treeDefinition,
}),
})
)
Expand Down Expand Up @@ -322,6 +348,7 @@ export function QueryComboBox({
relatedCollectionId,
resource,
treeData,
treeDefinition,
]
);

Expand Down Expand Up @@ -473,6 +500,7 @@ export function QueryComboBox({
: undefined,
relatedTable,
subViewRelationship,
treeDefinition,
})
.map(serializeResource)
.map(({ fieldName, startValue }) =>
Expand Down
3 changes: 0 additions & 3 deletions specifyweb/frontend/js_src/lib/localization/forms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1159,7 +1159,4 @@ export const formsText = createDictionary({
'ru-ru': 'Номер по каталогу Числовой',
'uk-ua': 'Каталожний номер Числовий',
},
invalidTree: {
'en-us': 'Taxon does not belong to the same tree as this Object Type',
},
} as const);