From 309e6394fc798278781ff40d9fc7db2ed5343a24 Mon Sep 17 00:00:00 2001
From: Grant Fitzsimmons <37256050+grantfitzsimmons@users.noreply.github.com>
Date: Wed, 23 Jul 2025 21:57:55 -0500
Subject: [PATCH 1/4] feat(datamodel): Add agent fields to Loan and Gift
---
config/common/common.views.xml | 34 +++-
.../js_src/lib/components/DataModel/types.ts | 10 ++
specifyweb/specify/datamodel.py | 10 ++
.../migration_utils/sp7_schemaconfig.py | 22 +++
.../0038_agent_fields_for_loan_and_gift.py | 153 ++++++++++++++++++
specifyweb/specify/models.py | 10 ++
6 files changed, 237 insertions(+), 2 deletions(-)
create mode 100644 specifyweb/specify/migrations/0038_agent_fields_for_loan_and_gift.py
diff --git a/config/common/common.views.xml b/config/common/common.views.xml
index 573e1e68532..94c312500c6 100644
--- a/config/common/common.views.xml
+++ b/config/common/common.views.xml
@@ -3514,7 +3514,22 @@
|
|
|
- -->
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+ |
+ |
+ |
+ |
+
+ -->
@@ -4202,7 +4217,22 @@
|
|
|
- -->
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+ |
+ |
+ |
+ |
+
+ -->
diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts
index 3b38f5ce27b..ffe95745858 100644
--- a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts
+++ b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts
@@ -3369,6 +3369,11 @@ export type Gift = {
};
readonly toOneDependent: { readonly addressOfRecord: AddressOfRecord | null };
readonly toOneIndependent: {
+ readonly agent1: Agent | null;
+ readonly agent2: Agent | null;
+ readonly agent3: Agent | null;
+ readonly agent4: Agent | null;
+ readonly agent5: Agent | null;
readonly createdByAgent: Agent | null;
readonly deaccession: Deaccession | null;
readonly discipline: Discipline;
@@ -3762,6 +3767,11 @@ export type Loan = {
};
readonly toOneDependent: { readonly addressOfRecord: AddressOfRecord | null };
readonly toOneIndependent: {
+ readonly agent1: Agent | null;
+ readonly agent2: Agent | null;
+ readonly agent3: Agent | null;
+ readonly agent4: Agent | null;
+ readonly agent5: Agent | null;
readonly createdByAgent: Agent | null;
readonly discipline: Discipline;
readonly division: Division | null;
diff --git a/specifyweb/specify/datamodel.py b/specifyweb/specify/datamodel.py
index 10f1d2f331a..da4f561043f 100644
--- a/specifyweb/specify/datamodel.py
+++ b/specifyweb/specify/datamodel.py
@@ -4065,6 +4065,11 @@ def is_tree_table(table: Table):
],
relationships=[
Relationship(name='addressOfRecord', type='many-to-one',required=False, relatedModelName='AddressOfRecord', column='AddressOfRecordID', dependent=True),
+ Relationship(name='agent1', type='many-to-one',required=False, relatedModelName='Agent', column='Agent1ID'),
+ Relationship(name='agent2', type='many-to-one',required=False, relatedModelName='Agent', column='Agent2ID'),
+ Relationship(name='agent3', type='many-to-one',required=False, relatedModelName='Agent', column='Agent3ID'),
+ Relationship(name='agent4', type='many-to-one',required=False, relatedModelName='Agent', column='Agent4ID'),
+ Relationship(name='agent5', type='many-to-one',required=False, relatedModelName='Agent', column='Agent5ID'),
Relationship(name='createdByAgent', type='many-to-one',required=False, relatedModelName='Agent', column='CreatedByAgentID'),
Relationship(name='deaccession', type='many-to-one',required=False, relatedModelName='Deaccession', column='DeaccessionID', otherSideName='gifts'),
Relationship(name='discipline', type='many-to-one',required=True, relatedModelName='Discipline', column='DisciplineID'),
@@ -4591,6 +4596,11 @@ def is_tree_table(table: Table):
],
relationships=[
Relationship(name='addressOfRecord', type='many-to-one',required=False, relatedModelName='AddressOfRecord', column='AddressOfRecordID', otherSideName='loans', dependent=True),
+ Relationship(name='agent1', type='many-to-one',required=False, relatedModelName='Agent', column='Agent1ID'),
+ Relationship(name='agent2', type='many-to-one',required=False, relatedModelName='Agent', column='Agent2ID'),
+ Relationship(name='agent3', type='many-to-one',required=False, relatedModelName='Agent', column='Agent3ID'),
+ Relationship(name='agent4', type='many-to-one',required=False, relatedModelName='Agent', column='Agent4ID'),
+ Relationship(name='agent5', type='many-to-one',required=False, relatedModelName='Agent', column='Agent5ID'),
Relationship(name='createdByAgent', type='many-to-one',required=False, relatedModelName='Agent', column='CreatedByAgentID'),
Relationship(name='discipline', type='many-to-one',required=True, relatedModelName='Discipline', column='DisciplineID'),
Relationship(name='division', type='many-to-one',required=False, relatedModelName='Division', column='DivisionID'),
diff --git a/specifyweb/specify/migration_utils/sp7_schemaconfig.py b/specifyweb/specify/migration_utils/sp7_schemaconfig.py
index 943144b5cff..07caa8dc3cd 100644
--- a/specifyweb/specify/migration_utils/sp7_schemaconfig.py
+++ b/specifyweb/specify/migration_utils/sp7_schemaconfig.py
@@ -221,4 +221,26 @@
('date2', 'Date 2', 'Date 2'),
('date2Precision', 'Date 2 Precision', 'Date 2 Precision'),
]
+}
+
+MIGRATION_0038_FIELDS = {
+ 'Loan': ['agent1', 'agent2', 'agent3', 'agent4', 'agent5'],
+ 'Gift': ['agent1', 'agent2', 'agent3', 'agent4', 'agent5'],
+}
+
+MIGRATION_0038_UPDATE_FIELDS = {
+ 'Loan': [
+ ('agent1','Agent 1','Agent 1'),
+ ('agent2','Agent 2','Agent 2'),
+ ('agent3','Agent 3','Agent 3'),
+ ('agent4','Agent 4','Agent 4'),
+ ('agent5','Agent 5','Agent 5'),
+ ],
+ 'Gift': [
+ ('agent1','Agent 1','Agent 1'),
+ ('agent2','Agent 2','Agent 2'),
+ ('agent3','Agent 3','Agent 3'),
+ ('agent4','Agent 4','Agent 4'),
+ ('agent5','Agent 5','Agent 5'),
+ ]
}
\ No newline at end of file
diff --git a/specifyweb/specify/migrations/0038_agent_fields_for_loan_and_gift.py b/specifyweb/specify/migrations/0038_agent_fields_for_loan_and_gift.py
new file mode 100644
index 00000000000..e8ef0a5e0ce
--- /dev/null
+++ b/specifyweb/specify/migrations/0038_agent_fields_for_loan_and_gift.py
@@ -0,0 +1,153 @@
+# Generated by Django 4.2.22 on 2025-07-23 21:18
+
+from django.apps import apps as specify_apps
+from django.db import migrations, models
+from specifyweb.specify.models import protect_with_blockers
+
+from specifyweb.specify.migration_utils.update_schema_config import (
+ revert_table_field_schema_config,
+ update_table_field_schema_config_with_defaults,
+)
+from specifyweb.specify.migration_utils.sp7_schemaconfig import (
+ MIGRATION_0038_FIELDS as SCHEMA_CONFIG_RAW_FIELDS,
+ MIGRATION_0038_UPDATE_FIELDS as SCHEMA_CONFIG_FIELD_DESC,
+)
+
+def update_0038_fields(apps):
+ """
+ Update table-field schema entries for plain field names
+ (e.g., MIGRATION_0038_FIELDS).
+ """
+ Discipline = apps.get_model('specify', 'Discipline')
+ for discipline in Discipline.objects.all():
+ for table, fields in SCHEMA_CONFIG_RAW_FIELDS.items():
+ for field_name in fields:
+ update_table_field_schema_config_with_defaults(table, discipline.id, field_name, apps)
+
+def revert_0038_fields(apps):
+ """
+ Revert table-field entries for plain field names.
+ """
+ for table, fields in SCHEMA_CONFIG_RAW_FIELDS.items():
+ for field_name in fields:
+ revert_table_field_schema_config(table, field_name, apps)
+
+def update_0038_schema_config_field_desc(apps, schema_editor):
+ """
+ Update field descriptions and display names using MIGRATION_0038_UPDATE_FIELDS
+ (tuple: (fieldName, newLabel, newDesc)).
+ """
+ Splocalecontainer = apps.get_model('specify', 'Splocalecontainer')
+ Splocalecontaineritem = apps.get_model('specify', 'Splocalecontaineritem')
+ Splocaleitemstr = apps.get_model('specify', 'Splocaleitemstr')
+
+ for table, fields in SCHEMA_CONFIG_FIELD_DESC.items():
+ containers = Splocalecontainer.objects.filter(name=table.lower())
+ for container in containers:
+ for (field_name, new_name, new_desc) in fields:
+ items = Splocalecontaineritem.objects.filter(
+ container=container,
+ name=field_name.lower()
+ )
+ for item in items:
+ item.ishidden = True
+ item.save()
+ desc_str = Splocaleitemstr.objects.filter(itemdesc_id=item.id).first()
+ name_str = Splocaleitemstr.objects.filter(itemname_id=item.id).first()
+ if not desc_str or not name_str:
+ continue
+ desc_str.text = new_desc
+ desc_str.save()
+ name_str.text = new_name
+ name_str.save()
+
+def revert_0038_schema_config_field_desc(apps, schema_editor):
+ """
+ Revert the field name/description updates.
+ """
+ Splocalecontainer = apps.get_model('specify', 'Splocalecontainer')
+ Splocalecontaineritem = apps.get_model('specify', 'Splocalecontaineritem')
+
+ for table, fields in SCHEMA_CONFIG_FIELD_DESC.items():
+ containers = Splocalecontainer.objects.filter(name=table.lower())
+ for container in containers:
+ for (field_name, _, _) in fields:
+ items = Splocalecontaineritem.objects.filter(
+ container=container,
+ name=field_name.lower()
+ )
+ for item in items:
+ # If needed, reset ishidden or revert text
+ pass
+
+def consolidated_0038_forward(apps, schema_editor):
+ update_0038_fields(apps)
+ update_0038_schema_config_field_desc(apps, schema_editor)
+
+def consolidated_0038_backward(apps, schema_editor):
+ revert_0038_schema_config_field_desc(apps, schema_editor)
+ revert_0038_fields(apps)
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('specify', '0037_make_smushed_default_false'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='gift',
+ name='agent1',
+ field=models.ForeignKey(db_column='Agent1ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'),
+ ),
+ migrations.AddField(
+ model_name='gift',
+ name='agent2',
+ field=models.ForeignKey(db_column='Agent2ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'),
+ ),
+ migrations.AddField(
+ model_name='gift',
+ name='agent3',
+ field=models.ForeignKey(db_column='Agent3ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'),
+ ),
+ migrations.AddField(
+ model_name='gift',
+ name='agent4',
+ field=models.ForeignKey(db_column='Agent4ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'),
+ ),
+ migrations.AddField(
+ model_name='gift',
+ name='agent5',
+ field=models.ForeignKey(db_column='Agent5ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'),
+ ),
+ migrations.AddField(
+ model_name='loan',
+ name='agent1',
+ field=models.ForeignKey(db_column='Agent1ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'),
+ ),
+ migrations.AddField(
+ model_name='loan',
+ name='agent2',
+ field=models.ForeignKey(db_column='Agent2ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'),
+ ),
+ migrations.AddField(
+ model_name='loan',
+ name='agent3',
+ field=models.ForeignKey(db_column='Agent3ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'),
+ ),
+ migrations.AddField(
+ model_name='loan',
+ name='agent4',
+ field=models.ForeignKey(db_column='Agent4ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'),
+ ),
+ migrations.AddField(
+ model_name='loan',
+ name='agent5',
+ field=models.ForeignKey(db_column='Agent5ID', null=True, on_delete=protect_with_blockers, related_name='+', to='specify.agent'),
+ ),
+ migrations.RunPython(
+ consolidated_0038_forward,
+ consolidated_0038_backward,
+ atomic=True,
+ ),
+ ]
\ No newline at end of file
diff --git a/specifyweb/specify/models.py b/specifyweb/specify/models.py
index b41d28b476f..c64d0eaab0b 100644
--- a/specifyweb/specify/models.py
+++ b/specifyweb/specify/models.py
@@ -3825,6 +3825,11 @@ class Gift(models.Model):
# Relationships: Many-to-One
addressofrecord = models.ForeignKey('AddressOfRecord', db_column='AddressOfRecordID', related_name='+', null=True, on_delete=protect_with_blockers)
+ agent1 = models.ForeignKey('Agent', db_column='Agent1ID', related_name='+', null=True, on_delete=protect_with_blockers)
+ agent2 = models.ForeignKey('Agent', db_column='Agent2ID', related_name='+', null=True, on_delete=protect_with_blockers)
+ agent3 = models.ForeignKey('Agent', db_column='Agent3ID', related_name='+', null=True, on_delete=protect_with_blockers)
+ agent4 = models.ForeignKey('Agent', db_column='Agent4ID', related_name='+', null=True, on_delete=protect_with_blockers)
+ agent5 = models.ForeignKey('Agent', db_column='Agent5ID', related_name='+', null=True, on_delete=protect_with_blockers)
createdbyagent = models.ForeignKey('Agent', db_column='CreatedByAgentID', related_name='+', null=True, on_delete=protect_with_blockers)
deaccession = models.ForeignKey('Deaccession', db_column='DeaccessionID', related_name='gifts', null=True, on_delete=protect_with_blockers)
discipline = models.ForeignKey('Discipline', db_column='DisciplineID', related_name='+', null=False, on_delete=protect_with_blockers)
@@ -4322,6 +4327,11 @@ class Loan(models.Model):
# Relationships: Many-to-One
addressofrecord = models.ForeignKey('AddressOfRecord', db_column='AddressOfRecordID', related_name='loans', null=True, on_delete=protect_with_blockers)
+ agent1 = models.ForeignKey('Agent', db_column='Agent1ID', related_name='+', null=True, on_delete=protect_with_blockers)
+ agent2 = models.ForeignKey('Agent', db_column='Agent2ID', related_name='+', null=True, on_delete=protect_with_blockers)
+ agent3 = models.ForeignKey('Agent', db_column='Agent3ID', related_name='+', null=True, on_delete=protect_with_blockers)
+ agent4 = models.ForeignKey('Agent', db_column='Agent4ID', related_name='+', null=True, on_delete=protect_with_blockers)
+ agent5 = models.ForeignKey('Agent', db_column='Agent5ID', related_name='+', null=True, on_delete=protect_with_blockers)
createdbyagent = models.ForeignKey('Agent', db_column='CreatedByAgentID', related_name='+', null=True, on_delete=protect_with_blockers)
discipline = models.ForeignKey('Discipline', db_column='DisciplineID', related_name='+', null=False, on_delete=protect_with_blockers)
division = models.ForeignKey('Division', db_column='DivisionID', related_name='+', null=True, on_delete=protect_with_blockers)
From 59d63accd5d0b197ffbc6b63c92d9f47a4629e9d Mon Sep 17 00:00:00 2001
From: Grant Fitzsimmons <37256050+grantfitzsimmons@users.noreply.github.com>
Date: Fri, 8 Aug 2025 14:17:11 +0000
Subject: [PATCH 2/4] Lint code with ESLint and Prettier
Triggered by 217a13b37c848d788b2265bafb76dd7193bdc75a on branch refs/heads/issue-7090
---
.../lib/components/AppResources/Aside.tsx | 29 +-
.../lib/components/AppResources/Tabs.tsx | 56 +-
.../AppResources/__tests__/Tabs.test.tsx | 60 +-
.../__tests__/AttachmentCell.test.tsx | 258 +-
.../js_src/lib/localization/attachments.ts | 1054 +++---
.../js_src/lib/localization/backEnd.ts | 790 ++--
.../js_src/lib/localization/batchEdit.ts | 704 ++--
.../js_src/lib/localization/common.ts | 1282 +++----
.../js_src/lib/localization/development.ts | 44 +-
.../frontend/js_src/lib/localization/forms.ts | 1968 +++++-----
.../js_src/lib/localization/header.ts | 688 ++--
.../js_src/lib/localization/interactions.ts | 624 ++--
.../js_src/lib/localization/merging.ts | 382 +-
.../js_src/lib/localization/notifications.ts | 200 +-
.../js_src/lib/localization/preferences.ts | 3260 ++++++++---------
.../js_src/lib/localization/report.ts | 274 +-
.../js_src/lib/localization/resources.ts | 1456 ++++----
.../frontend/js_src/lib/localization/tree.ts | 1040 +++---
.../js_src/lib/localization/workbench.ts | 2398 ++++++------
19 files changed, 8288 insertions(+), 8279 deletions(-)
diff --git a/specifyweb/frontend/js_src/lib/components/AppResources/Aside.tsx b/specifyweb/frontend/js_src/lib/components/AppResources/Aside.tsx
index 28188fe280f..371c50ab03f 100644
--- a/specifyweb/frontend/js_src/lib/components/AppResources/Aside.tsx
+++ b/specifyweb/frontend/js_src/lib/components/AppResources/Aside.tsx
@@ -71,7 +71,8 @@ export function AppResourcesAside({
@@ -4812,7 +4798,21 @@
|
|
|
- -->
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+ |
+ |
+ |
+ |
+
-->
From 5b450b63da57efc9c4948f35ce9fb09fa27815d1 Mon Sep 17 00:00:00 2001
From: Grant Fitzsimmons <37256050+grantfitzsimmons@users.noreply.github.com>
Date: Thu, 14 Aug 2025 12:34:43 -0500
Subject: [PATCH 4/4] fix(migrations): update migration order
---
..._loan_and_gift.py => 0039_agent_fields_for_loan_and_gift.py} | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
rename specifyweb/specify/migrations/{0038_agent_fields_for_loan_and_gift.py => 0039_agent_fields_for_loan_and_gift.py} (99%)
diff --git a/specifyweb/specify/migrations/0038_agent_fields_for_loan_and_gift.py b/specifyweb/specify/migrations/0039_agent_fields_for_loan_and_gift.py
similarity index 99%
rename from specifyweb/specify/migrations/0038_agent_fields_for_loan_and_gift.py
rename to specifyweb/specify/migrations/0039_agent_fields_for_loan_and_gift.py
index e8ef0a5e0ce..fa1b172bce3 100644
--- a/specifyweb/specify/migrations/0038_agent_fields_for_loan_and_gift.py
+++ b/specifyweb/specify/migrations/0039_agent_fields_for_loan_and_gift.py
@@ -91,7 +91,7 @@ def consolidated_0038_backward(apps, schema_editor):
class Migration(migrations.Migration):
dependencies = [
- ('specify', '0037_make_smushed_default_false'),
+ ('specify', '0038_make_countonly_default_false'),
]
operations = [