diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts b/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts index 2fe9e86ae5f..9504410a7b9 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/resourceApi.ts @@ -174,6 +174,7 @@ export const ResourceBase = Backbone.Model.extend({ { createdBy: 'clone' } ); + newResource.specifyTable = this.specifyTable; newResource.needsSaved = self.needsSaved; await Promise.all( @@ -215,6 +216,13 @@ export const ResourceBase = Backbone.Model.extend({ ); break; } + case 'one-to-one': { + newResource.set( + fieldName, + await related?.clone(cloneAll, isBulkCarry) + ); + break; + } default: { throw new Error('unhandled relationship type'); } @@ -551,7 +559,18 @@ export const ResourceBase = Backbone.Model.extend({ * Needed for taxonTreeDef on discipline because field.isVirtual equals false */ case 'one-to-one': { - return value; + if (!value) { + if (field.isDependent()) this.storeDependent(field, null); + else this.storeIndependent(field, null); + return value; + } + + const oneToOne = maybeMakeResource(value, relatedTable); + if (field.isDependent()) this.storeDependent(field, oneToOne); + else this.storeIndependent(field, oneToOne); + this.trigger(`change:${fieldName}`, this); + this.trigger('change', this); + return oneToOne.url(); } } if (!field.isVirtual) diff --git a/specifyweb/frontend/js_src/lib/components/Forms/Save.tsx b/specifyweb/frontend/js_src/lib/components/Forms/Save.tsx index c7f2f14ddad..08f9fbf9c1f 100644 --- a/specifyweb/frontend/js_src/lib/components/Forms/Save.tsx +++ b/specifyweb/frontend/js_src/lib/components/Forms/Save.tsx @@ -269,25 +269,26 @@ export function SaveButton({ carryForwardAmount > 1 ? async (): Promise>> => { const formatter = - tables.CollectionObject.strictGetLiteralField( - 'catalogNumber' - ).getUiFormatter()!; - const wildCard = formatter.valueOrWild(); - - const clonePromises = Array.from( - { length: carryForwardAmount }, - async () => { - const clonedResource = await resource.clone( - false, - true - ); + tables.CollectionObject.strictGetLiteralField( + 'catalogNumber' + ).getUiFormatter()!; + const wildCard =formatter === undefined ? null : formatter.valueOrWild(); + const clonePromises = Array.from( + { length: carryForwardAmount }, + async () => { + const clonedResource = await resource.clone( + false, + true + ); + if (wildCard !== null) { clonedResource.set( 'catalogNumber', wildCard as never ); - return clonedResource; } - ); + return clonedResource; + } + ); const clones = await Promise.all(clonePromises); diff --git a/specifyweb/specify/api.py b/specifyweb/specify/api.py index 2980b4945ff..965ddbc5b04 100644 --- a/specifyweb/specify/api.py +++ b/specifyweb/specify/api.py @@ -483,7 +483,8 @@ def create_obj(collection, agent, model, data: Dict[str, Any], parent_obj=None): model = get_model_or_404(model) data = cleanData(model, data, agent) obj = model() - handle_fk_fields(collection, agent, obj, data) + _handle_special_create_priors(obj, data, parent_obj) + handle_fk_fields(collection, agent, obj, data, parent_obj) set_fields_from_data(obj, data) set_field_if_exists(obj, 'createdbyagent', agent) set_field_if_exists(obj, 'collectionmemberid', collection.id) @@ -492,7 +493,8 @@ def create_obj(collection, agent, model, data: Dict[str, Any], parent_obj=None): except AutonumberOverflowException as e: logger.warn("autonumbering overflow: %s", e) - _handle_special_save_priors(obj) + # _handle_special_create_posteriors(obj, data, parent_obj, delay_fields) + _handle_special_save_priors(obj) # TODO: move before autonumbering_and_save if obj.id is not None: # was the object actually saved? check_table_permissions(collection, agent, obj, "create") @@ -574,7 +576,7 @@ def reorder_fields_for_embedding(cls, data: Dict[str, Any]) -> Iterable[Tuple[st yield (key, data[key]) -def handle_fk_fields(collection, agent, obj, data: Dict[str, Any]) -> Tuple[List, List[FieldChangeInfo]]: +def handle_fk_fields(collection, agent, obj, data: Dict[str, Any], parent_obj = None) -> Tuple[List, List[FieldChangeInfo]]: """Where 'obj' is a Django model instance and 'data' is a dict, set foreign key fields in the object from the provided data. """ @@ -614,8 +616,22 @@ def handle_fk_fields(collection, agent, obj, data: Dict[str, Any]) -> Tuple[List elif hasattr(val, 'items'): # i.e. it's a dict of some sort # The related object is represented by a nested dict of data. rel_model = field.related_model - - rel_obj = update_or_create_resource(collection, agent, rel_model, val, obj if dependent else None) + rel_obj = None + + if (field_name == 'childco' + and obj._meta.model_name == 'collectionobjectgroupjoin' + and parent_obj is not None + and parent_obj._meta.model_name == 'collectionobject'): + # parent_obj.save() # TODO: Decide to save here or in special save priors function + rel_obj = parent_obj + else: + rel_obj = update_or_create_resource( + collection, + agent, + rel_model, + val, + obj if dependent else None, + ) setattr(obj, field_name, rel_obj) if dependent and old_related and old_related.id != rel_obj.id: @@ -1084,4 +1100,24 @@ def _handle_special_save_priors(obj): def _handle_special_update_priors(obj, data): data = modify_update_of_interaction_sibling_preps(obj, data) - pass \ No newline at end of file + +def _save_parent_co_prior(obj, data, parent_obj): + """ + Save the parent collection object and update the 'childco' field in the data. + """ + if obj._meta.model_name == "collectionobjectgroupjoin" and parent_obj._meta.model_name == "collectionobject": + if data.get("childco") == "/api/specify/collectionobject/": + parent_obj.save() # temporary fix for saving co cojo.childco + data["childco"] = f"{data['childco']}{parent_obj.id}/" + elif "catalognumber" not in data: # and 'id' not in data: + parent_obj.save() + +def _handle_special_create_priors(obj, data, parent_obj): + _save_parent_co_prior(obj, data, parent_obj) + +def _save_cojo_child_co_posterior(obj, data, parent_obj=None): + if obj._meta.model_name == "collectionobjectgroupjoin" and data["childco"] == "/api/specify/collectionobject/": + obj.save() + +def _handle_special_create_posteriors(obj, data, parent_obj): + _save_cojo_child_co_posterior(obj, data, parent_obj)