Skip to content
Open
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
14 changes: 11 additions & 3 deletions specifyweb/backend/businessrules/rules/collectionobject_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from specifyweb.backend.businessrules.exceptions import BusinessRuleException
from specifyweb.backend.businessrules.utils import get_unique_catnum_across_comp_co_coll_pref
from specifyweb.specify.api.utils import get_or_create_default_collection_object_type
from specifyweb.specify.models import Component


Expand All @@ -10,8 +11,15 @@ def collectionobject_pre_save(co):
if co.collectionmemberid is None:
co.collectionmemberid = co.collection_id

if co.collectionobjecttype is None:
co.collectionobjecttype = co.collection.collectionobjecttype
if co.collectionobjecttype is None:
if co.collection.collectionobjecttype is not None:
co.collectionobjecttype = co.collection.collectionobjecttype
elif co.pk is not None:
co.collectionobjecttype = (
get_or_create_default_collection_object_type(
co.collection, using=co._state.db or 'default'
)
)

agent = co.createdbyagent
if agent is not None and agent.specifyuser is not None:
Expand All @@ -25,4 +33,4 @@ def collectionobject_pre_save(co):

if contains_component_duplicates:
raise BusinessRuleException(
'Catalog Number is already in use for another Component in this collection.')
'Catalog Number is already in use for another Component in this collection.')
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,64 @@ describe('Collection Object business rules', () => {
expect(result.current[0]).toStrictEqual([]);
});

test('CollectionObject -> determinations: Save is not blocked when collection object type is missing', async () => {
const collectionObject = getBaseCollectionObject();
collectionObject.set('collectionObjectType', null as never, {
silent: true,
});

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

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

await act(async () => {
await collectionObject?.businessRuleManager?.checkField(
'collectionObjectType'
);
});

expect(result.current[0]).toStrictEqual([]);
});

test('CollectionObject -> determinations: Missing collection object type clears invalid determination blockers', async () => {
const collectionObject = getBaseCollectionObject();
collectionObject.set(
'collectionObjectType',
getResourceApiUrl('CollectionObjectType', 1)
);

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

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

await act(async () => {
await collectionObject?.businessRuleManager?.checkField(
'collectionObjectType'
);
});
expect(result.current[0]).toStrictEqual([
resourcesText.invalidDeterminationTaxon(),
]);

collectionObject.set('collectionObjectType', null as never, {
silent: true,
});

await act(async () => {
await collectionObject?.businessRuleManager?.checkField(
'collectionObjectType'
);
});

expect(result.current[0]).toStrictEqual([]);
});

test('Newly added determinations are current by default', async () => {
const collectionObject = getBaseCollectionObject();
const determinations =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,19 @@ export const businessRuleDefs: MappedBusinessRuleDefs = {
determinations.models.map(async (det) => det.rgetPromise('taxon'))
);
const coType = await resource.rgetPromise('collectionObjectType');

if (coType === null) {
determinations.models.forEach((determination) => {
setSaveBlockers(
determination,
determination.specifyTable.field.taxon,
[],
DETERMINATION_TAXON_KEY
);
});
return;
}

const coTypeTreeDef = coType.get('taxonTreeDef');

// Block save when a Determination -> Taxon does not belong to the COType's tree definition
Expand Down
28 changes: 27 additions & 1 deletion specifyweb/specify/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,32 @@ def log_sqlalchemy_query(query):
# Run in the storred_queries.execute file, in the execute function, right before the return statement, line 546
# from specifyweb.specify.utils import log_sqlalchemy_query; log_sqlalchemy_query(query)

def get_or_create_default_collection_object_type(collection: spmodels.Collection, using: str = "default"):
db = using or "default"

if collection.collectionobjecttype is not None:
return collection.collectionobjecttype

discipline_name = collection.discipline.name
taxon_tree_def_id = collection.discipline.taxontreedef_id

if discipline_name is None or taxon_tree_def_id is None:
return None

default_type, _ = spmodels.Collectionobjecttype.objects.using(db).get_or_create(
name=discipline_name,
collection=collection,
taxontreedef_id=taxon_tree_def_id,
)

type(collection).objects.using(db).filter(
pk=collection.pk,
collectionobjecttype__isnull=True,
).update(collectionobjecttype=default_type)
collection.collectionobjecttype = default_type

return default_type

def create_default_collection_types(apps, using="default"):
db = using or "default"

Expand Down Expand Up @@ -90,4 +116,4 @@ def get_picklists(collection: spmodels.Collection, tablename: str, fieldname: st
if len(collection_picklists) > 0:
picklists = collection_picklists

return picklists, schemaitem
return picklists, schemaitem
Loading