From 387aa328496309d92e41c140aaa9a55ba4e74202 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Fri, 13 Sep 2024 15:00:41 -0400 Subject: [PATCH 01/19] Add cogtype helpers --- .../frontend/js_src/lib/components/DataModel/helpers.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/helpers.ts b/specifyweb/frontend/js_src/lib/components/DataModel/helpers.ts index 8ccdfdb534b..a3e1cbfdfbf 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/helpers.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/helpers.ts @@ -216,3 +216,10 @@ export async function fetchDistantRelated( field, }; } + +// Cog types: Discrete, Consolidated, Drill Core +export const cogTypes = { + DISCRETE: 'Discrete', + CONSOLIDATED: 'Consolidated', + DRILL_CORE: 'Drill Core' +} \ No newline at end of file From c1e8978f5ccc54f8b827d70949605a24c44bc724 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Fri, 13 Sep 2024 15:00:53 -0400 Subject: [PATCH 02/19] Add COG business rules --- .../components/DataModel/businessRuleDefs.ts | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts index 8e98a9ec793..c902f9fe72f 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts @@ -2,6 +2,7 @@ import { formsText } from '../../localization/forms'; import { resourcesText } from '../../localization/resources'; import { f } from '../../utils/functools'; import type { BusinessRuleResult } from './businessRules'; +import { cogTypes } from './helpers'; import type { AnySchema, TableFields } from './helperTypes'; import { checkPrepAvailability, @@ -20,6 +21,8 @@ import type { Address, BorrowMaterial, CollectionObject, + CollectionObjectGroup, + CollectionObjectGroupJoin, Determination, DNASequence, LoanPreparation, @@ -202,6 +205,54 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { }, }, + CollectionObjectGroup: { + fieldChecks: { + cogtype: (cog: SpecifyResource) => { + // The first COJO CO will automatically have isPrimary set to True when the COG type is 'consolidated' + cog.rgetPromise('cogtype').then((cogtype) => { + if (cogtype.get('type') === cogTypes.CONSOLIDATED) { + const cojos = cog.getDependentResource('cojo'); + // Set first CO in COG to primary + cojos?.models + .filter((cojo) => cojo.get('childco'))[0] + .set('isprimary', true); + } + }); + }, + }, + }, + + CollectionObjectGroupJoin: { + fieldChecks: { + // Only a single CO in a COG can be set as primary. + // When checking a CO as primary, other COs in that COG will get unchecked. + isprimary: (cojo: SpecifyResource) => { + if (cojo.get('isprimary') && cojo.collection !== undefined) { + cojo.collection.models + .filter((resource) => resource.get('childco')) + .map((other: SpecifyResource) => { + if (other.cid !== cojo.cid) { + other.set('isprimary', false); + } + }); + } + }, + // Only a single CO in a COG can be set as substrate. + // When checking a CO as substrate, other COs in that COG will get unchecked. + issubstrate: (cojo: SpecifyResource) => { + if (cojo.get('issubstrate') && cojo.collection !== undefined) { + cojo.collection.models + .filter((resource) => resource.get('childco')) + .map((other: SpecifyResource) => { + if (other.cid !== cojo.cid) { + other.set('issubstrate', false); + } + }); + } + }, + }, + }, + Determination: { fieldChecks: { taxon: async ( From fda0c2962c0a250062613d9dac24fc4d39b14087 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Fri, 13 Sep 2024 19:05:38 +0000 Subject: [PATCH 03/19] Lint code with ESLint and Prettier Triggered by c1e8978f5ccc54f8b827d70949605a24c44bc724 on branch refs/heads/issue-5246 --- .../lib/components/DataModel/businessRuleDefs.ts | 14 +++++++++----- .../js_src/lib/components/DataModel/helpers.ts | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts index c902f9fe72f..d7697f8a896 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts @@ -214,7 +214,7 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { const cojos = cog.getDependentResource('cojo'); // Set first CO in COG to primary cojos?.models - .filter((cojo) => cojo.get('childco'))[0] + .find((cojo) => cojo.get('childco')) .set('isprimary', true); } }); @@ -224,8 +224,10 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { CollectionObjectGroupJoin: { fieldChecks: { - // Only a single CO in a COG can be set as primary. - // When checking a CO as primary, other COs in that COG will get unchecked. + /* + * Only a single CO in a COG can be set as primary. + * When checking a CO as primary, other COs in that COG will get unchecked. + */ isprimary: (cojo: SpecifyResource) => { if (cojo.get('isprimary') && cojo.collection !== undefined) { cojo.collection.models @@ -237,8 +239,10 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { }); } }, - // Only a single CO in a COG can be set as substrate. - // When checking a CO as substrate, other COs in that COG will get unchecked. + /* + * Only a single CO in a COG can be set as substrate. + * When checking a CO as substrate, other COs in that COG will get unchecked. + */ issubstrate: (cojo: SpecifyResource) => { if (cojo.get('issubstrate') && cojo.collection !== undefined) { cojo.collection.models diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/helpers.ts b/specifyweb/frontend/js_src/lib/components/DataModel/helpers.ts index a3e1cbfdfbf..3a09c959b1f 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/helpers.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/helpers.ts @@ -221,5 +221,5 @@ export async function fetchDistantRelated( export const cogTypes = { DISCRETE: 'Discrete', CONSOLIDATED: 'Consolidated', - DRILL_CORE: 'Drill Core' -} \ No newline at end of file + DRILL_CORE: 'Drill Core', +}; From 381cefc1a6b38c0d0422d03e7de63f4ee6955f7c Mon Sep 17 00:00:00 2001 From: Sharad S Date: Mon, 16 Sep 2024 16:40:58 -0400 Subject: [PATCH 04/19] Generate new types - frontend and backend fieldNames were out of sync --- .../js_src/lib/components/DataModel/types.ts | 180 +++++++++--------- 1 file changed, 90 insertions(+), 90 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts index 01deed86547..66d0fdddfc4 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts @@ -3,8 +3,8 @@ * Afterward, some manual edits have been made. Those are marked with * "NOTE:" comments * - * Schema version: 2.11 - * Date generated: July 17, 2024 + * Schema version: 2.12 + * Date generated: September 16, 2024 * * The dataModel types were generated using the following code snippet. * After schema changes, it needs to be regenerated like this: @@ -233,20 +233,20 @@ export type Tables = { export type Accession = { readonly tableName: 'Accession'; readonly fields: { - readonly accessionNumber: string; readonly accessionCondition: string | null; - readonly dateAccessioned: string | null; + readonly accessionNumber: string; readonly actualTotalCountAmt: number | null; readonly collectionObjectCount: number | null; + readonly dateAccessioned: string | null; readonly dateAcknowledged: string | null; - readonly remarks: string | null; + readonly dateReceived: string | null; readonly integer1: number | null; readonly integer2: number | null; readonly integer3: number | null; readonly number1: number | null; readonly number2: number | null; readonly preparationCount: number | null; - readonly dateReceived: string | null; + readonly remarks: string | null; readonly status: string | null; readonly text1: string | null; readonly text2: string | null; @@ -452,10 +452,10 @@ export type Agent = { readonly initials: string | null; readonly integer1: number | null; readonly integer2: number | null; + readonly interests: string | null; readonly jobTitle: string | null; readonly lastName: string | null; readonly middleInitial: string | null; - readonly interests: string | null; readonly remarks: string | null; readonly suffix: string | null; readonly text1: string | null; @@ -488,8 +488,8 @@ export type Agent = { readonly agentAttachments: RA; readonly agentGeographies: RA; readonly agentSpecialties: RA; - readonly identifiers: RA; readonly groups: RA; + readonly identifiers: RA; readonly variants: RA; }; readonly toManyIndependent: { @@ -920,13 +920,13 @@ export type BorrowMaterial = { readonly tableName: 'BorrowMaterial'; readonly fields: { readonly collectionMemberId: number; + readonly description: string | null; readonly inComments: string | null; readonly materialNumber: string; readonly outComments: string | null; readonly quantity: number | null; readonly quantityResolved: number | null; readonly quantityReturned: number | null; - readonly description: string | null; readonly text1: string | null; readonly text2: string | null; readonly timestampCreated: string; @@ -968,22 +968,24 @@ export type BorrowReturnMaterial = { export type CollectingEvent = { readonly tableName: 'CollectingEvent'; readonly fields: { - readonly startDate: string | null; + readonly verbatimDate: string | null; + readonly remarks: string | null; readonly endDate: string | null; readonly endDatePrecision: number | null; readonly endDateVerbatim: string | null; readonly endTime: number | null; - readonly stationFieldNumber: string | null; - readonly method: string | null; readonly guid: string | null; readonly integer1: number | null; readonly integer2: number | null; - readonly remarks: string | null; + readonly stationFieldNumber: string | null; + readonly verbatimLocality: string | null; + readonly method: string | null; readonly reservedInteger3: number | null; readonly reservedInteger4: number | null; readonly reservedText1: string | null; readonly reservedText2: string | null; readonly sgrStatus: number | null; + readonly startDate: string | null; readonly startDatePrecision: number | null; readonly startDateVerbatim: string | null; readonly startTime: number | null; @@ -1001,8 +1003,6 @@ export type CollectingEvent = { readonly timestampCreated: string; readonly timestampModified: string | null; readonly uniqueIdentifier: string | null; - readonly verbatimDate: string | null; - readonly verbatimLocality: string | null; readonly version: number | null; readonly visibility: number | null; }; @@ -1070,10 +1070,6 @@ export type CollectingEventAttr = { export type CollectingEventAttribute = { readonly tableName: 'CollectingEventAttribute'; readonly fields: { - readonly text8: string | null; - readonly text5: string | null; - readonly text4: string | null; - readonly text9: string | null; readonly integer1: number | null; readonly integer10: number | null; readonly integer2: number | null; @@ -1084,11 +1080,11 @@ export type CollectingEventAttribute = { readonly integer7: number | null; readonly integer8: number | null; readonly integer9: number | null; - readonly number12: number | null; - readonly number13: number | null; readonly number1: number | null; readonly number10: number | null; readonly number11: number | null; + readonly number12: number | null; + readonly number13: number | null; readonly number2: number | null; readonly number3: number | null; readonly number4: number | null; @@ -1098,22 +1094,26 @@ export type CollectingEventAttribute = { readonly number8: number | null; readonly number9: number | null; readonly remarks: string | null; - readonly text6: string | null; readonly text1: string | null; readonly text10: string | null; readonly text11: string | null; + readonly text12: string | null; readonly text13: string | null; readonly text14: string | null; readonly text15: string | null; readonly text16: string | null; readonly text17: string | null; readonly text2: string | null; + readonly text3: string | null; + readonly text4: string | null; + readonly text5: string | null; + readonly text6: string | null; readonly text7: string | null; + readonly text8: string | null; + readonly text9: string | null; readonly timestampCreated: string; readonly timestampModified: string | null; - readonly text12: string | null; readonly version: number | null; - readonly text3: string | null; readonly yesNo1: boolean | null; readonly yesNo2: boolean | null; readonly yesNo3: boolean | null; @@ -1154,7 +1154,6 @@ export type CollectingTrip = { readonly tableName: 'CollectingTrip'; readonly fields: { readonly cruise: string | null; - readonly text2: string | null; readonly date1: string | null; readonly date1Precision: number | null; readonly date2: string | null; @@ -1172,6 +1171,8 @@ export type CollectingTrip = { readonly startDatePrecision: number | null; readonly startDateVerbatim: string | null; readonly startTime: number | null; + readonly text1: string | null; + readonly text2: string | null; readonly text3: string | null; readonly text4: string | null; readonly text5: string | null; @@ -1183,7 +1184,6 @@ export type CollectingTrip = { readonly timestampModified: string | null; readonly collectingTripName: string | null; readonly version: number | null; - readonly text1: string | null; readonly vessel: string | null; readonly yesNo1: boolean | null; readonly yesNo2: boolean | null; @@ -1359,15 +1359,18 @@ export type Collection = { export type CollectionObject = { readonly tableName: 'CollectionObject'; readonly fields: { + readonly yesNo1: boolean | null; readonly actualTotalCountAmt: number | null; + readonly altCatalogNumber: string | null; readonly availability: string | null; - readonly catalogNumber: string | null; readonly catalogedDate: string | null; readonly catalogedDatePrecision: number | null; readonly catalogedDateVerbatim: string | null; + readonly catalogNumber: string | null; + readonly remarks: string | null; readonly collectionMemberId: number; readonly countAmt: number | null; - readonly reservedText: string | null; + readonly timestampCreated: string; readonly timestampModified: string | null; readonly date1: string | null; readonly date1Precision: number | null; @@ -1380,9 +1383,10 @@ export type CollectionObject = { readonly guid: string | null; readonly integer1: number | null; readonly integer2: number | null; - readonly text2: string | null; readonly inventoryDate: string | null; readonly inventoryDatePrecision: number | null; + readonly text2: string | null; + readonly fieldNumber: string | null; readonly modifier: string | null; readonly name: string | null; readonly notifications: string | null; @@ -1391,16 +1395,15 @@ export type CollectionObject = { readonly number2: number | null; readonly objectCondition: string | null; readonly ocr: string | null; - readonly altCatalogNumber: string | null; + readonly text1: string | null; readonly projectNumber: string | null; - readonly remarks: string | null; readonly reservedInteger3: number | null; readonly reservedInteger4: number | null; + readonly reservedText: string | null; readonly reservedText2: string | null; readonly reservedText3: string | null; readonly restrictions: string | null; readonly sgrStatus: number | null; - readonly text1: string | null; readonly description: string | null; readonly text3: string | null; readonly text4: string | null; @@ -1408,14 +1411,11 @@ export type CollectionObject = { readonly text6: string | null; readonly text7: string | null; readonly text8: string | null; - readonly timestampCreated: string; readonly totalCountAmt: number | null; readonly totalValue: number | null; readonly uniqueIdentifier: string | null; readonly version: number | null; readonly visibility: number | null; - readonly fieldNumber: string | null; - readonly yesNo1: boolean | null; readonly yesNo2: boolean | null; readonly yesNo3: boolean | null; readonly yesNo4: boolean | null; @@ -1430,17 +1430,17 @@ export type CollectionObject = { readonly agent1: Agent | null; readonly appraisal: Appraisal | null; readonly cataloger: Agent | null; + readonly collectingEvent: CollectingEvent | null; readonly collection: Collection; readonly collectionObjectType: CollectionObjectType; readonly container: Container | null; readonly containerOwner: Container | null; readonly createdByAgent: Agent | null; readonly currentDetermination: Determination | null; - readonly modifiedByAgent: Agent | null; readonly embargoAuthority: Agent | null; - readonly collectingEvent: CollectingEvent | null; readonly fieldNotebookPage: FieldNotebookPage | null; readonly inventorizedBy: Agent | null; + readonly modifiedByAgent: Agent | null; readonly paleoContext: PaleoContext | null; readonly visibilitySetBy: SpecifyUser | null; }; @@ -1521,11 +1521,11 @@ export type CollectionObjectAttribute = { readonly integer7: number | null; readonly integer8: number | null; readonly integer9: number | null; - readonly number12: number | null; - readonly number13: number | null; readonly number1: number | null; readonly number10: number | null; readonly number11: number | null; + readonly number12: number | null; + readonly number13: number | null; readonly number14: number | null; readonly number15: number | null; readonly number16: number | null; @@ -1563,19 +1563,20 @@ export type CollectionObjectAttribute = { readonly number7: number | null; readonly number8: number | null; readonly number9: number | null; - readonly text13: string | null; - readonly text14: string | null; - readonly text1: string | null; readonly positionState: string | null; - readonly text10: string | null; readonly remarks: string | null; - readonly text8: string | null; + readonly text1: string | null; + readonly text10: string | null; readonly text11: string | null; + readonly text12: string | null; + readonly text13: string | null; + readonly text14: string | null; readonly text15: string | null; readonly text16: string | null; readonly text17: string | null; readonly text18: string | null; readonly text19: string | null; + readonly text2: string | null; readonly text20: string | null; readonly text21: string | null; readonly text22: string | null; @@ -1602,13 +1603,12 @@ export type CollectionObjectAttribute = { readonly text5: string | null; readonly text6: string | null; readonly text7: string | null; + readonly text8: string | null; readonly text9: string | null; readonly timestampCreated: string; readonly timestampModified: string | null; - readonly text12: string | null; readonly topDistance: number | null; readonly version: number | null; - readonly text2: string | null; readonly yesNo1: boolean | null; readonly yesNo10: boolean | null; readonly yesNo11: boolean | null; @@ -1646,10 +1646,10 @@ export type CollectionObjectCitation = { readonly fields: { readonly collectionMemberId: number; readonly figureNumber: string | null; + readonly remarks: string | null; readonly isFigured: boolean | null; readonly pageNumber: string | null; readonly plateNumber: string | null; - readonly remarks: string | null; readonly timestampCreated: string; readonly timestampModified: string | null; readonly version: number | null; @@ -2182,9 +2182,7 @@ export type DNASequence = { readonly compT: number | null; readonly extractionDate: string | null; readonly extractionDatePrecision: number | null; - readonly text2: string | null; readonly genbankAccessionNumber: string | null; - readonly text1: string | null; readonly geneSequence: string | null; readonly moleculeType: string | null; readonly number1: number | null; @@ -2194,6 +2192,8 @@ export type DNASequence = { readonly sequenceDate: string | null; readonly sequenceDatePrecision: number | null; readonly targetMarker: string | null; + readonly text1: string | null; + readonly text2: string | null; readonly text3: string | null; readonly timestampCreated: string; readonly timestampModified: string | null; @@ -2286,8 +2286,8 @@ export type DNASequencingRun = { readonly runByAgent: Agent | null; }; readonly toManyDependent: { - readonly citations: RA; readonly attachments: RA; + readonly citations: RA; }; readonly toManyIndependent: RR; }; @@ -2449,13 +2449,11 @@ export type Determination = { readonly addendum: string | null; readonly alternateName: string | null; readonly collectionMemberId: number; - readonly confidence: string | null; readonly isCurrent: boolean; readonly determinedDate: string | null; readonly determinedDatePrecision: number | null; readonly featureOrBasis: string | null; readonly guid: string | null; - readonly yesNo1: boolean | null; readonly integer1: number | null; readonly integer2: number | null; readonly integer3: number | null; @@ -2468,10 +2466,11 @@ export type Determination = { readonly number3: number | null; readonly number4: number | null; readonly number5: number | null; + readonly text1: string | null; readonly qualifier: string | null; + readonly confidence: string | null; readonly remarks: string | null; readonly subSpQualifier: string | null; - readonly text1: string | null; readonly text2: string | null; readonly text3: string | null; readonly text4: string | null; @@ -2484,6 +2483,7 @@ export type Determination = { readonly typeStatusName: string | null; readonly varQualifier: string | null; readonly version: number | null; + readonly yesNo1: boolean | null; readonly yesNo2: boolean | null; readonly yesNo3: boolean | null; readonly yesNo4: boolean | null; @@ -2510,9 +2510,9 @@ export type DeterminationCitation = { readonly collectionMemberId: number; readonly figureNumber: string | null; readonly isFigured: boolean | null; + readonly remarks: string | null; readonly pageNumber: string | null; readonly plateNumber: string | null; - readonly remarks: string | null; readonly timestampCreated: string; readonly timestampModified: string | null; readonly version: number | null; @@ -3079,12 +3079,13 @@ export type FundingAgent = { export type GeoCoordDetail = { readonly tableName: 'GeoCoordDetail'; readonly fields: { + readonly validation: string | null; + readonly source: string | null; readonly errorPolygon: string | null; readonly geoRefAccuracy: number | null; readonly geoRefAccuracyUnits: string | null; readonly geoRefCompiledDate: string | null; readonly geoRefDetDate: string | null; - readonly geoRefDetRef: string | null; readonly geoRefRemarks: string | null; readonly geoRefVerificationStatus: string | null; readonly integer1: number | null; @@ -3103,7 +3104,7 @@ export type GeoCoordDetail = { readonly number5: number | null; readonly originalCoordSystem: string | null; readonly protocol: string | null; - readonly source: string | null; + readonly geoRefDetRef: string | null; readonly text1: string | null; readonly text2: string | null; readonly text3: string | null; @@ -3112,7 +3113,6 @@ export type GeoCoordDetail = { readonly timestampCreated: string; readonly timestampModified: string | null; readonly uncertaintyPolygon: string | null; - readonly validation: string | null; readonly version: number | null; readonly yesNo1: boolean | null; readonly yesNo2: boolean | null; @@ -3334,8 +3334,8 @@ export type Gift = { readonly srcTaxonomy: string | null; readonly specialConditions: string | null; readonly status: string | null; - readonly text2: string | null; readonly text1: string | null; + readonly text2: string | null; readonly text3: string | null; readonly text4: string | null; readonly text5: string | null; @@ -3704,7 +3704,6 @@ export type Loan = { readonly dateClosed: string | null; readonly dateReceived: string | null; readonly yesNo1: boolean | null; - readonly text2: string | null; readonly integer1: number | null; readonly integer2: number | null; readonly integer3: number | null; @@ -3726,6 +3725,7 @@ export type Loan = { readonly specialConditions: string | null; readonly status: string | null; readonly text1: string | null; + readonly text2: string | null; readonly text3: string | null; readonly text4: string | null; readonly text5: string | null; @@ -3851,22 +3851,26 @@ export type LoanReturnPreparation = { export type Locality = { readonly tableName: 'Locality'; readonly fields: { + readonly timestampCreated: string; + readonly timestampModified: string | null; readonly datum: string | null; readonly elevationAccuracy: number | null; + readonly elevationMethod: string | null; readonly gml: string | null; readonly guid: string | null; readonly latLongMethod: string | null; readonly lat1text: string | null; readonly lat2text: string | null; - readonly latLongAccuracy: number | null; readonly latLongType: string | null; readonly latitude1: number | null; readonly latitude2: number | null; + readonly latLongAccuracy: number | null; readonly localityName: string; readonly long1text: string | null; readonly long2text: string | null; readonly longitude1: number | null; readonly longitude2: number | null; + readonly text1: string | null; readonly maxElevation: number | null; readonly minElevation: number | null; readonly namedPlace: string | null; @@ -3877,20 +3881,16 @@ export type Locality = { readonly sgrStatus: number | null; readonly shortName: string | null; readonly srcLatLongUnit: number; - readonly text1: string | null; readonly text2: string | null; readonly text3: string | null; readonly text4: string | null; readonly text5: string | null; - readonly timestampCreated: string; - readonly timestampModified: string | null; readonly uniqueIdentifier: string | null; readonly verbatimElevation: string | null; readonly verbatimLatitude: string | null; readonly verbatimLongitude: string | null; readonly version: number | null; readonly visibility: number | null; - readonly elevationMethod: string | null; readonly yesNo1: boolean | null; readonly yesNo2: boolean | null; readonly yesNo3: boolean | null; @@ -3963,7 +3963,6 @@ export type LocalityCitation = { export type LocalityDetail = { readonly tableName: 'LocalityDetail'; readonly fields: { - readonly baseMeridian: string | null; readonly drainage: string | null; readonly endDepth: number | null; readonly endDepthUnit: string | null; @@ -3972,6 +3971,7 @@ export type LocalityDetail = { readonly hucCode: string | null; readonly island: string | null; readonly islandGroup: string | null; + readonly text1: string | null; readonly mgrsZone: string | null; readonly nationalParkName: string | null; readonly number1: number | null; @@ -3988,7 +3988,7 @@ export type LocalityDetail = { readonly startDepth: number | null; readonly startDepthUnit: string | null; readonly startDepthVerbatim: string | null; - readonly text1: string | null; + readonly baseMeridian: string | null; readonly text2: string | null; readonly text3: string | null; readonly text4: string | null; @@ -4165,6 +4165,7 @@ export type PaleoContext = { readonly tableName: 'PaleoContext'; readonly fields: { readonly text1: string | null; + readonly text2: string | null; readonly paleoContextName: string | null; readonly number1: number | null; readonly number2: number | null; @@ -4172,7 +4173,6 @@ export type PaleoContext = { readonly number4: number | null; readonly number5: number | null; readonly remarks: string | null; - readonly text2: string | null; readonly text3: string | null; readonly text4: string | null; readonly text5: string | null; @@ -4371,6 +4371,7 @@ export type Preparation = { readonly date4Precision: number | null; readonly description: string | null; readonly guid: string | null; + readonly text1: string | null; readonly integer1: number | null; readonly integer2: number | null; readonly isOnLoan: boolean | null; @@ -4388,6 +4389,7 @@ export type Preparation = { readonly text11: string | null; readonly text12: string | null; readonly text13: string | null; + readonly text2: string | null; readonly text3: string | null; readonly text4: string | null; readonly text5: string | null; @@ -4397,10 +4399,8 @@ export type Preparation = { readonly text9: string | null; readonly timestampCreated: string; readonly timestampModified: string | null; - readonly text1: string | null; - readonly yesNo1: boolean | null; readonly version: number | null; - readonly text2: string | null; + readonly yesNo1: boolean | null; readonly yesNo2: boolean | null; readonly yesNo3: boolean | null; }; @@ -4777,10 +4777,7 @@ export type RecordSetItem = { export type ReferenceWork = { readonly tableName: 'ReferenceWork'; readonly fields: { - readonly text1: string | null; - readonly workDate: string | null; readonly doi: string | null; - readonly text2: string | null; readonly guid: string | null; readonly isPublished: boolean | null; readonly isbn: string | null; @@ -4791,6 +4788,8 @@ export type ReferenceWork = { readonly placeOfPublication: string | null; readonly publisher: string | null; readonly remarks: string | null; + readonly text1: string | null; + readonly text2: string | null; readonly timestampCreated: string; readonly timestampModified: string | null; readonly title: string; @@ -4799,6 +4798,7 @@ export type ReferenceWork = { readonly url: string | null; readonly version: number | null; readonly volume: string | null; + readonly workDate: string | null; readonly yesNo1: boolean | null; readonly yesNo2: boolean | null; }; @@ -4896,9 +4896,9 @@ export type RepositoryAgreementAttachment = { export type Shipment = { readonly tableName: 'Shipment'; readonly fields: { - readonly numberOfPackages: number | null; readonly insuredForAmount: string | null; readonly shipmentMethod: string | null; + readonly numberOfPackages: number | null; readonly number1: number | null; readonly number2: number | null; readonly remarks: string | null; @@ -5612,6 +5612,7 @@ export type Taxon = { readonly colStatus: string | null; readonly commonName: string | null; readonly cultivarName: string | null; + readonly environmentalProtectionStatus: string | null; readonly esaStatus: string | null; readonly fullName: string | null; readonly groupNumber: string | null; @@ -5635,7 +5636,6 @@ export type Taxon = { readonly number3: number | null; readonly number4: number | null; readonly number5: number | null; - readonly environmentalProtectionStatus: string | null; readonly rankId: number; readonly remarks: string | null; readonly source: string | null; @@ -6460,8 +6460,8 @@ export type CollectionObjectType = { readonly text1: string | null; readonly text2: string | null; readonly text3: string | null; - readonly timestampcreated: string; - readonly timestampmodified: string | null; + readonly timestampCreated: string; + readonly timestampModified: string | null; readonly version: number | null; }; readonly toOneDependent: RR; @@ -6490,8 +6490,8 @@ export type CollectionObjectGroup = { readonly text1: string | null; readonly text2: string | null; readonly text3: string | null; - readonly timestampcreated: string; - readonly timestampmodified: string | null; + readonly timestampCreated: string; + readonly timestampModified: string | null; readonly version: number | null; readonly yesno1: boolean | null; readonly yesno2: boolean | null; @@ -6499,14 +6499,14 @@ export type CollectionObjectGroup = { }; readonly toOneDependent: RR; readonly toOneIndependent: { - readonly cogtype: CollectionObjectGroupType; + readonly cogType: CollectionObjectGroupType; readonly collection: Collection | null; readonly createdByAgent: Agent | null; readonly modifiedByAgent: Agent | null; }; readonly toManyDependent: { readonly cojo: RA; - readonly parentcojos: RA; + readonly parentCojos: RA; }; readonly toManyIndependent: RR; }; @@ -6516,14 +6516,14 @@ export type CollectionObjectGroupJoin = { readonly integer1: number | null; readonly integer2: number | null; readonly integer3: number | null; - readonly isprimary: boolean; - readonly issubstrate: boolean; + readonly isPrimary: boolean; + readonly isSubstrate: boolean; readonly precedence: number; readonly text1: string | null; readonly text2: string | null; readonly text3: string | null; - readonly timestampcreated: string; - readonly timestampmodified: string | null; + readonly timestampCreated: string; + readonly timestampModified: string | null; readonly version: number | null; readonly yesno1: boolean | null; readonly yesno2: boolean | null; @@ -6531,9 +6531,9 @@ export type CollectionObjectGroupJoin = { }; readonly toOneDependent: RR; readonly toOneIndependent: { - readonly childco: CollectionObject | null; - readonly childcog: CollectionObjectGroup | null; - readonly parentcog: CollectionObjectGroup; + readonly childCo: CollectionObject | null; + readonly childCog: CollectionObjectGroup | null; + readonly parentCog: CollectionObjectGroup; }; readonly toManyDependent: RR; readonly toManyIndependent: RR; @@ -6542,8 +6542,8 @@ export type CollectionObjectGroupType = { readonly tableName: 'CollectionObjectGroupType'; readonly fields: { readonly name: string; - readonly timestampcreated: string; - readonly timestampmodified: string | null; + readonly timestampCreated: string; + readonly timestampModified: string | null; readonly type: string; readonly version: number | null; }; From dbaa39040ab0db169c7b53337f670bc5c753f264 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Mon, 16 Sep 2024 16:56:32 -0400 Subject: [PATCH 05/19] Change business rules to new fieldnames --- .../components/DataModel/businessRuleDefs.ts | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts index d7697f8a896..d04de256b47 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts @@ -21,7 +21,6 @@ import type { Address, BorrowMaterial, CollectionObject, - CollectionObjectGroup, CollectionObjectGroupJoin, Determination, DNASequence, @@ -207,15 +206,15 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { CollectionObjectGroup: { fieldChecks: { - cogtype: (cog: SpecifyResource) => { + cogType: (cog): void => { // The first COJO CO will automatically have isPrimary set to True when the COG type is 'consolidated' - cog.rgetPromise('cogtype').then((cogtype) => { + cog.rgetPromise('cogType').then((cogtype) => { if (cogtype.get('type') === cogTypes.CONSOLIDATED) { - const cojos = cog.getDependentResource('cojo'); + const cojos = cog.getDependentResource('parentCojos'); // Set first CO in COG to primary cojos?.models - .find((cojo) => cojo.get('childco')) - .set('isprimary', true); + .find((cojo) => cojo.get('childCo') !== null) + ?.set('isPrimary', true); } }); }, @@ -228,13 +227,13 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { * Only a single CO in a COG can be set as primary. * When checking a CO as primary, other COs in that COG will get unchecked. */ - isprimary: (cojo: SpecifyResource) => { - if (cojo.get('isprimary') && cojo.collection !== undefined) { + isPrimary: (cojo: SpecifyResource) => { + if (cojo.get('isPrimary') && cojo.collection !== undefined) { cojo.collection.models - .filter((resource) => resource.get('childco')) + .filter((resource) => resource.get('childCo') !== null) .map((other: SpecifyResource) => { if (other.cid !== cojo.cid) { - other.set('isprimary', false); + other.set('isPrimary', false); } }); } @@ -243,13 +242,13 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { * Only a single CO in a COG can be set as substrate. * When checking a CO as substrate, other COs in that COG will get unchecked. */ - issubstrate: (cojo: SpecifyResource) => { - if (cojo.get('issubstrate') && cojo.collection !== undefined) { + isSubstrate: (cojo: SpecifyResource) => { + if (cojo.get('isSubstrate') && cojo.collection !== undefined) { cojo.collection.models - .filter((resource) => resource.get('childco')) + .filter((resource) => resource.get('childCo') !== null) .map((other: SpecifyResource) => { if (other.cid !== cojo.cid) { - other.set('issubstrate', false); + other.set('isSubstrate', false); } }); } From 44f430042ae2907b990a089d16e02c642d4c470d Mon Sep 17 00:00:00 2001 From: Sharad S Date: Mon, 16 Sep 2024 17:22:21 -0400 Subject: [PATCH 06/19] fix typecheck --- specifyweb/frontend/js_src/lib/components/Forms/parentTables.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/components/Forms/parentTables.ts b/specifyweb/frontend/js_src/lib/components/Forms/parentTables.ts index 172b03af809..d7dc69bbd9d 100644 --- a/specifyweb/frontend/js_src/lib/components/Forms/parentTables.ts +++ b/specifyweb/frontend/js_src/lib/components/Forms/parentTables.ts @@ -71,7 +71,7 @@ const overrides: { CommonNameTx: 'taxon', BorrowReturnMaterial: 'borrowMaterial', CollectionObject: undefined, - CollectionObjectGroupJoin: 'parentcog', + CollectionObjectGroupJoin: 'parentCog', CollectionRelationship: undefined, Collector: 'collectingEvent', DNASequencingRun: 'dnaSequence', From cf933570fe345d1a567027279c125cd0ab8d30d6 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Tue, 17 Sep 2024 11:00:19 -0400 Subject: [PATCH 07/19] cleanup --- .../components/DataModel/businessRuleDefs.ts | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts index d04de256b47..04c1b8467bd 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts @@ -60,6 +60,21 @@ const hasNoCurrentDetermination = (collection: Collection) => determination.get('isCurrent') ); +const ensureSingleCollectionObjectCheck = ( + cojo: SpecifyResource, + field: 'isPrimary' | 'isSubstrate' +) => { + if (cojo.get(field) && cojo.collection !== undefined) { + cojo.collection.models + .filter((resource) => resource.get('childCo') !== null) + .map((other: SpecifyResource) => { + if (other.cid !== cojo.cid) { + other.set(field, false); + } + }); + } +}; + export const businessRuleDefs: MappedBusinessRuleDefs = { Address: { customInit: (address) => { @@ -228,30 +243,14 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { * When checking a CO as primary, other COs in that COG will get unchecked. */ isPrimary: (cojo: SpecifyResource) => { - if (cojo.get('isPrimary') && cojo.collection !== undefined) { - cojo.collection.models - .filter((resource) => resource.get('childCo') !== null) - .map((other: SpecifyResource) => { - if (other.cid !== cojo.cid) { - other.set('isPrimary', false); - } - }); - } + ensureSingleCollectionObjectCheck(cojo, 'isPrimary'); }, /* * Only a single CO in a COG can be set as substrate. * When checking a CO as substrate, other COs in that COG will get unchecked. */ isSubstrate: (cojo: SpecifyResource) => { - if (cojo.get('isSubstrate') && cojo.collection !== undefined) { - cojo.collection.models - .filter((resource) => resource.get('childCo') !== null) - .map((other: SpecifyResource) => { - if (other.cid !== cojo.cid) { - other.set('isSubstrate', false); - } - }); - } + ensureSingleCollectionObjectCheck(cojo, 'isSubstrate'); }, }, }, From 2658814a6a389f7c8e97900a2c357800958b7625 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Fri, 20 Sep 2024 15:22:41 -0400 Subject: [PATCH 08/19] Create utils for business rules --- .../components/DataModel/businessRuleDefs.ts | 34 ++++------------ .../components/DataModel/businessRuleUtils.ts | 40 +++++++++++++++++++ 2 files changed, 48 insertions(+), 26 deletions(-) create mode 100644 specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts index 04c1b8467bd..1b784702712 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts @@ -2,6 +2,12 @@ import { formsText } from '../../localization/forms'; import { resourcesText } from '../../localization/resources'; import { f } from '../../utils/functools'; import type { BusinessRuleResult } from './businessRules'; +import { + CURRENT_DETERMINATION_KEY, + DETERMINATION_TAXON_KEY, + ensureSingleCollectionObjectCheck, + hasNoCurrentDetermination, +} from './businessRuleUtils'; import { cogTypes } from './helpers'; import type { AnySchema, TableFields } from './helperTypes'; import { @@ -51,30 +57,6 @@ type MappedBusinessRuleDefs = { readonly [TABLE in keyof Tables]?: BusinessRuleDefs; }; -const CURRENT_DETERMINATION_KEY = 'determination-isCurrent'; -const DETERMINATION_TAXON_KEY = 'determination-taxon'; - -const hasNoCurrentDetermination = (collection: Collection) => - collection.models.length > 0 && - !collection.models.some((determination: SpecifyResource) => - determination.get('isCurrent') - ); - -const ensureSingleCollectionObjectCheck = ( - cojo: SpecifyResource, - field: 'isPrimary' | 'isSubstrate' -) => { - if (cojo.get(field) && cojo.collection !== undefined) { - cojo.collection.models - .filter((resource) => resource.get('childCo') !== null) - .map((other: SpecifyResource) => { - if (other.cid !== cojo.cid) { - other.set(field, false); - } - }); - } -}; - export const businessRuleDefs: MappedBusinessRuleDefs = { Address: { customInit: (address) => { @@ -242,14 +224,14 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { * Only a single CO in a COG can be set as primary. * When checking a CO as primary, other COs in that COG will get unchecked. */ - isPrimary: (cojo: SpecifyResource) => { + isPrimary: (cojo: SpecifyResource): void => { ensureSingleCollectionObjectCheck(cojo, 'isPrimary'); }, /* * Only a single CO in a COG can be set as substrate. * When checking a CO as substrate, other COs in that COG will get unchecked. */ - isSubstrate: (cojo: SpecifyResource) => { + isSubstrate: (cojo: SpecifyResource): void => { ensureSingleCollectionObjectCheck(cojo, 'isSubstrate'); }, }, diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts new file mode 100644 index 00000000000..a6c8f33125a --- /dev/null +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts @@ -0,0 +1,40 @@ +import { SpecifyResource } from './legacyTypes'; +import { Collection } from './specifyTable'; +import { CollectionObjectGroupJoin, Determination } from './types'; + +// Save blocker keys used in businessRuleDefs.ts +export const CURRENT_DETERMINATION_KEY = 'determination-isCurrent'; +export const DETERMINATION_TAXON_KEY = 'determination-taxon'; + +/** + * + * Calculates whether a collection of determinations has any current determinations or not + * Used in CO -> Determination -> isCurrent business rule + */ +export const hasNoCurrentDetermination = ( + collection: Collection +) => + collection.models.length > 0 && + !collection.models.some((determination: SpecifyResource) => + determination.get('isCurrent') + ); + +/** + * + * Ensures only one CO in a COG can be checked as isPrimary or isSubstrate + * Used in COG business rules: https://github.com/specify/specify7/issues/5246 + */ +export const ensureSingleCollectionObjectCheck = ( + cojo: SpecifyResource, + field: 'isPrimary' | 'isSubstrate' +) => { + if (cojo.get(field) && cojo.collection !== undefined) { + cojo.collection.models + .filter((resource) => resource.get('childCo') !== null) + .map((other: SpecifyResource) => { + if (other.cid !== cojo.cid) { + other.set(field, false); + } + }); + } +}; From 58a257391e977643db58f0a21a37089f9b020ed0 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Fri, 20 Sep 2024 15:23:02 -0400 Subject: [PATCH 09/19] add cog business rule test --- .../DataModel/__tests__/businessRules.test.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts index 2441c7431cb..adf5585ff59 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts @@ -210,6 +210,30 @@ describe('Collection Object business rules', () => { }); }); +describe('CollectionObjectGroup business rules', () => { + test('Only one CO COJO can be primary', () => { + const cog = new tables.CollectionObjectGroup.Resource({ + id: 1, + cogType: getResourceApiUrl('CollectionObjectGroupType', 1), + }); + + const cojo1 = new tables.CollectionObjectGroupJoin.Resource({ + isPrimary: true, + childCo: getResourceApiUrl('CollectionObject', 1), + parentCog: getResourceApiUrl('CollectionObjectGroup', 1), + }); + const cojo2 = new tables.CollectionObjectGroupJoin.Resource({ + isPrimary: false, + childCo: getResourceApiUrl('CollectionObject', 2), + parentCog: getResourceApiUrl('CollectionObjectGroup', 1), + }); + cog.set('parentCojos', [cojo1, cojo2]); + cojo2.set('isPrimary', true); + + expect(cojo1.get('isPrimary')).toBe(false); + }); +}); + describe('DNASequence business rules', () => { test('fieldCheck geneSequence', async () => { const dNASequence = new tables.DNASequence.Resource({ From e9f30e374bb5b34313dca320fd0e842d14a9cbd6 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Tue, 24 Sep 2024 16:15:00 -0400 Subject: [PATCH 10/19] Add empty check for value conditions --- .../frontend/js_src/lib/components/FormCells/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormCells/index.tsx b/specifyweb/frontend/js_src/lib/components/FormCells/index.tsx index b228daff8ed..df751482253 100644 --- a/specifyweb/frontend/js_src/lib/components/FormCells/index.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormCells/index.tsx @@ -235,7 +235,10 @@ const cellRenderers: { break; } const value = await fetchPathAsString(resource, condition.field); - if (!destructorCalled && value === condition.value) { + if ( + (!destructorCalled && value === condition.value) || + (condition.value === 'EMPTY' && value === '') + ) { foundIndex = Number.parseInt(index); break; } From 719c01ae860adde897f8406c6a40a2b2d67ce85b Mon Sep 17 00:00:00 2001 From: melton-jason Date: Wed, 25 Sep 2024 10:03:10 -0500 Subject: [PATCH 11/19] Regenerate datamodel test file --- .../tests/ajax/static/context/datamodel.json | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/tests/ajax/static/context/datamodel.json b/specifyweb/frontend/js_src/lib/tests/ajax/static/context/datamodel.json index a4b3542b624..dd0438cbf64 100644 --- a/specifyweb/frontend/js_src/lib/tests/ajax/static/context/datamodel.json +++ b/specifyweb/frontend/js_src/lib/tests/ajax/static/context/datamodel.json @@ -29588,7 +29588,7 @@ "classname": "edu.ku.brc.specify.datamodel.SpAuditLog", "table": "spauditlog", "tableId": 530, - "system": true, + "system": false, "idColumn": "SpAuditLogID", "idFieldName": "spAuditLogId", "fields": [ @@ -38130,7 +38130,7 @@ "type": "java.lang.Integer" }, { - "name": "timestampcreated", + "name": "timestampCreated", "column": "TimestampCreated", "indexed": false, "unique": false, @@ -38138,7 +38138,7 @@ "type": "java.sql.Timestamp" }, { - "name": "timestampmodified", + "name": "timestampModified", "column": "TimestampModified", "indexed": false, "unique": false, @@ -38261,7 +38261,7 @@ "type": "java.lang.Integer" }, { - "name": "timestampcreated", + "name": "timestampCreated", "column": "TimestampCreated", "indexed": false, "unique": false, @@ -38269,7 +38269,7 @@ "type": "java.sql.Timestamp" }, { - "name": "timestampmodified", + "name": "timestampModified", "column": "TimestampModified", "indexed": false, "unique": false, @@ -38386,7 +38386,7 @@ "column": "CollectionID" }, { - "name": "cogtype", + "name": "cogType", "type": "many-to-one", "required": true, "dependent": false, @@ -38394,12 +38394,12 @@ "column": "COGTypeID" }, { - "name": "parentcojos", + "name": "parentCojos", "type": "one-to-many", "required": false, "dependent": true, "relatedModelName": "CollectionObjectGroupJoin", - "otherSideName": "parentcog" + "otherSideName": "parentCog" }, { "name": "cojo", @@ -38407,7 +38407,7 @@ "required": false, "dependent": true, "relatedModelName": "CollectionObjectGroupJoin", - "otherSideName": "childcog" + "otherSideName": "childCog" }, { "name": "createdByAgent", @@ -38437,7 +38437,7 @@ "idFieldName": "collectionObjectGroupJoinId", "fields": [ { - "name": "isprimary", + "name": "isPrimary", "column": "IsPrimary", "indexed": false, "unique": false, @@ -38445,7 +38445,7 @@ "type": "java.lang.Boolean" }, { - "name": "issubstrate", + "name": "isSubstrate", "column": "IsSubstrate", "indexed": false, "unique": false, @@ -38469,7 +38469,7 @@ "type": "java.lang.Integer" }, { - "name": "timestampcreated", + "name": "timestampCreated", "column": "TimestampCreated", "indexed": false, "unique": false, @@ -38477,7 +38477,7 @@ "type": "java.sql.Timestamp" }, { - "name": "timestampmodified", + "name": "timestampModified", "column": "TimestampModified", "indexed": false, "unique": false, @@ -38562,7 +38562,7 @@ ], "relationships": [ { - "name": "parentcog", + "name": "parentCog", "type": "many-to-one", "required": true, "dependent": false, @@ -38571,8 +38571,8 @@ "otherSideName": "parentcojos" }, { - "name": "childcog", - "type": "many-to-one", + "name": "childCog", + "type": "one-to-one", "required": false, "dependent": false, "relatedModelName": "CollectionObjectGroup", @@ -38580,8 +38580,8 @@ "otherSideName": "cojo" }, { - "name": "childco", - "type": "many-to-one", + "name": "childCo", + "type": "one-to-one", "required": false, "dependent": false, "relatedModelName": "CollectionObject", @@ -38626,7 +38626,7 @@ "type": "java.lang.Integer" }, { - "name": "timestampcreated", + "name": "timestampCreated", "column": "TimestampCreated", "indexed": false, "unique": false, @@ -38634,7 +38634,7 @@ "type": "java.sql.Timestamp" }, { - "name": "timestampmodified", + "name": "timestampModified", "column": "TimestampModified", "indexed": false, "unique": false, From 3a1224c6f5273606b021d13d94649726aa6540b0 Mon Sep 17 00:00:00 2001 From: melton-jason Date: Wed, 25 Sep 2024 10:32:37 -0500 Subject: [PATCH 12/19] Update tests to be inline with updated datamodel test file --- .../DataModel/__tests__/__snapshots__/specifyTable.test.ts.snap | 2 +- .../js_src/lib/components/DataModel/businessRuleUtils.ts | 2 +- .../FormEditor/__tests__/__snapshots__/createView.test.ts.snap | 1 + .../js_src/lib/components/Forms/__tests__/parentTables.test.tsx | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/__snapshots__/specifyTable.test.ts.snap b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/__snapshots__/specifyTable.test.ts.snap index 38b0269a18c..530e2b6d3e1 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/__snapshots__/specifyTable.test.ts.snap +++ b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/__snapshots__/specifyTable.test.ts.snap @@ -1293,7 +1293,7 @@ exports[`tableScoping 1`] = ` "CollectionObjectAttribute": undefined, "CollectionObjectCitation": "collectionObject", "CollectionObjectGroup": "collection", - "CollectionObjectGroupJoin": "parentcog > collection", + "CollectionObjectGroupJoin": "parentCog > collection", "CollectionObjectGroupType": "collection", "CollectionObjectProperty": "collectionObject", "CollectionObjectType": "collection", diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts index a6c8f33125a..f613db0ec31 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts @@ -31,7 +31,7 @@ export const ensureSingleCollectionObjectCheck = ( if (cojo.get(field) && cojo.collection !== undefined) { cojo.collection.models .filter((resource) => resource.get('childCo') !== null) - .map((other: SpecifyResource) => { + .forEach((other: SpecifyResource) => { if (other.cid !== cojo.cid) { other.set(field, false); } diff --git a/specifyweb/frontend/js_src/lib/components/FormEditor/__tests__/__snapshots__/createView.test.ts.snap b/specifyweb/frontend/js_src/lib/components/FormEditor/__tests__/__snapshots__/createView.test.ts.snap index e1d9258ce0c..8d11c38c162 100644 --- a/specifyweb/frontend/js_src/lib/components/FormEditor/__tests__/__snapshots__/createView.test.ts.snap +++ b/specifyweb/frontend/js_src/lib/components/FormEditor/__tests__/__snapshots__/createView.test.ts.snap @@ -127,6 +127,7 @@ exports[`Tables with form tables computed correctly 1`] = ` "[table RepositoryAgreement]", "[table RepositoryAgreementAttachment]", "[table Shipment]", + "[table SpAuditLog]", "[table Storage]", "[table StorageAttachment]", "[table StorageTreeDef]", diff --git a/specifyweb/frontend/js_src/lib/components/Forms/__tests__/parentTables.test.tsx b/specifyweb/frontend/js_src/lib/components/Forms/__tests__/parentTables.test.tsx index 455b91b22b9..66f10effad8 100644 --- a/specifyweb/frontend/js_src/lib/components/Forms/__tests__/parentTables.test.tsx +++ b/specifyweb/frontend/js_src/lib/components/Forms/__tests__/parentTables.test.tsx @@ -31,7 +31,7 @@ test('Parent table relationships are calculated properly', () => "CollectionObjectAttachment": "[relationship CollectionObjectAttachment.collectionObject]", "CollectionObjectAttr": "[relationship CollectionObjectAttr.collectionObject]", "CollectionObjectCitation": "[relationship CollectionObjectCitation.collectionObject]", - "CollectionObjectGroupJoin": "[relationship CollectionObjectGroupJoin.parentcog]", + "CollectionObjectGroupJoin": "[relationship CollectionObjectGroupJoin.parentCog]", "CollectionObjectProperty": "[relationship CollectionObjectProperty.collectionObject]", "Collector": "[relationship Collector.collectingEvent]", "CommonNameTx": "[relationship CommonNameTx.taxon]", From 836b4c2618d46317b8dcbe9d6a3cf16eea6ee000 Mon Sep 17 00:00:00 2001 From: melton-jason Date: Wed, 25 Sep 2024 15:37:01 +0000 Subject: [PATCH 13/19] Lint code with ESLint and Prettier Triggered by 3a1224c6f5273606b021d13d94649726aa6540b0 on branch refs/heads/issue-5246 --- .../js_src/lib/components/DataModel/businessRuleUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts index f613db0ec31..f0a811a1e7e 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts @@ -1,6 +1,6 @@ -import { SpecifyResource } from './legacyTypes'; -import { Collection } from './specifyTable'; -import { CollectionObjectGroupJoin, Determination } from './types'; +import type { SpecifyResource } from './legacyTypes'; +import type { Collection } from './specifyTable'; +import type { CollectionObjectGroupJoin, Determination } from './types'; // Save blocker keys used in businessRuleDefs.ts export const CURRENT_DETERMINATION_KEY = 'determination-isCurrent'; From 6d680a18d5b76fa1af4c1877c6ee29ef46e9e2c0 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Wed, 25 Sep 2024 14:34:25 -0400 Subject: [PATCH 14/19] Add test case for substrate --- .../DataModel/__tests__/businessRules.test.ts | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts index adf5585ff59..2e7ec80be95 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts @@ -211,26 +211,44 @@ describe('Collection Object business rules', () => { }); describe('CollectionObjectGroup business rules', () => { - test('Only one CO COJO can be primary', () => { + const getBaseCOG = () => { const cog = new tables.CollectionObjectGroup.Resource({ id: 1, cogType: getResourceApiUrl('CollectionObjectGroupType', 1), + resource_uri: getResourceApiUrl('CollectionObjectGroup', 1), }); const cojo1 = new tables.CollectionObjectGroupJoin.Resource({ - isPrimary: true, + isPrimary: false, + isSubstrate: true, childCo: getResourceApiUrl('CollectionObject', 1), parentCog: getResourceApiUrl('CollectionObjectGroup', 1), }); const cojo2 = new tables.CollectionObjectGroupJoin.Resource({ - isPrimary: false, + isPrimary: true, + isSubstrate: false, childCo: getResourceApiUrl('CollectionObject', 2), parentCog: getResourceApiUrl('CollectionObjectGroup', 1), }); + cog.set('parentCojos', [cojo1, cojo2]); - cojo2.set('isPrimary', true); + return { cog, cojo1, cojo2 }; + }; + + test('Only one CO COJO can be primary', () => { + const { cojo1, cojo2 } = getBaseCOG(); + cojo1.set('isPrimary', true); + + expect(cojo1.get('isPrimary')).toBe(true); + expect(cojo2.get('isPrimary')).toBe(false); + }); + + test('Only one CO COJO can be substrate', () => { + const { cojo1, cojo2 } = getBaseCOG(); + cojo2.set('isSubstrate', true); - expect(cojo1.get('isPrimary')).toBe(false); + expect(cojo1.get('isSubstrate')).toBe(false); + expect(cojo2.get('isSubstrate')).toBe(true); }); }); From 1a81c71bac9cc226c73ab437b28ed310ada44090 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Tue, 1 Oct 2024 10:57:24 -0400 Subject: [PATCH 15/19] Move empty value keyword to a variable --- specifyweb/frontend/js_src/lib/components/FormCells/index.tsx | 4 ++-- specifyweb/frontend/js_src/lib/components/FormParse/index.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormCells/index.tsx b/specifyweb/frontend/js_src/lib/components/FormCells/index.tsx index df751482253..ca6afd40365 100644 --- a/specifyweb/frontend/js_src/lib/components/FormCells/index.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormCells/index.tsx @@ -18,7 +18,7 @@ import { softFail } from '../Errors/Crash'; import { fetchPathAsString } from '../Formatters/formatters'; import { UiCommand } from '../FormCommands'; import { FormField } from '../FormFields'; -import type { FormType } from '../FormParse'; +import { EMPTY_VALUE_CONDITION, FormType } from '../FormParse'; import { fetchView, resolveViewDefinition } from '../FormParse'; import type { cellAlign, @@ -237,7 +237,7 @@ const cellRenderers: { const value = await fetchPathAsString(resource, condition.field); if ( (!destructorCalled && value === condition.value) || - (condition.value === 'EMPTY' && value === '') + (condition.value === EMPTY_VALUE_CONDITION && value === '') ) { foundIndex = Number.parseInt(index); break; diff --git a/specifyweb/frontend/js_src/lib/components/FormParse/index.ts b/specifyweb/frontend/js_src/lib/components/FormParse/index.ts index bfad30f0e1e..49f16c40914 100644 --- a/specifyweb/frontend/js_src/lib/components/FormParse/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FormParse/index.ts @@ -468,6 +468,8 @@ export type FormCondition = | State<'Always'> | undefined; +export const EMPTY_VALUE_CONDITION="_EMPTY"; + export type ConditionalFormDefinition = RA<{ readonly condition: FormCondition; readonly definition: ParsedFormDefinition; From a757e04673529cf903a7fea3a7c3acfb3d5ece46 Mon Sep 17 00:00:00 2001 From: Sharad S Date: Tue, 1 Oct 2024 15:01:25 +0000 Subject: [PATCH 16/19] Lint code with ESLint and Prettier Triggered by 1a81c71bac9cc226c73ab437b28ed310ada44090 on branch refs/heads/issue-5246 --- specifyweb/frontend/js_src/lib/components/FormCells/index.tsx | 3 ++- specifyweb/frontend/js_src/lib/components/FormParse/index.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormCells/index.tsx b/specifyweb/frontend/js_src/lib/components/FormCells/index.tsx index ca6afd40365..6d5f96c9c97 100644 --- a/specifyweb/frontend/js_src/lib/components/FormCells/index.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormCells/index.tsx @@ -18,7 +18,8 @@ import { softFail } from '../Errors/Crash'; import { fetchPathAsString } from '../Formatters/formatters'; import { UiCommand } from '../FormCommands'; import { FormField } from '../FormFields'; -import { EMPTY_VALUE_CONDITION, FormType } from '../FormParse'; +import type { FormType } from '../FormParse'; +import { EMPTY_VALUE_CONDITION } from '../FormParse'; import { fetchView, resolveViewDefinition } from '../FormParse'; import type { cellAlign, diff --git a/specifyweb/frontend/js_src/lib/components/FormParse/index.ts b/specifyweb/frontend/js_src/lib/components/FormParse/index.ts index 49f16c40914..a4b2f0591ac 100644 --- a/specifyweb/frontend/js_src/lib/components/FormParse/index.ts +++ b/specifyweb/frontend/js_src/lib/components/FormParse/index.ts @@ -468,7 +468,7 @@ export type FormCondition = | State<'Always'> | undefined; -export const EMPTY_VALUE_CONDITION="_EMPTY"; +export const EMPTY_VALUE_CONDITION = '_EMPTY'; export type ConditionalFormDefinition = RA<{ readonly condition: FormCondition; From 217fd70181cb3c2124009be0fca46953a66f24ba Mon Sep 17 00:00:00 2001 From: Sharad S <16229739+sharadsw@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:40:40 -0400 Subject: [PATCH 17/19] Change casing Co-authored-by: Max Patiiuk --- .../lib/components/DataModel/__tests__/businessRules.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts index 2e7ec80be95..5bd0b6c17bc 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts @@ -211,7 +211,7 @@ describe('Collection Object business rules', () => { }); describe('CollectionObjectGroup business rules', () => { - const getBaseCOG = () => { + const getBaseCog = () => { const cog = new tables.CollectionObjectGroup.Resource({ id: 1, cogType: getResourceApiUrl('CollectionObjectGroupType', 1), From b41828beba3ef7f09262881ef624519c31f5c14c Mon Sep 17 00:00:00 2001 From: Sharad S Date: Wed, 2 Oct 2024 13:49:18 -0400 Subject: [PATCH 18/19] Fix casing --- .../lib/components/DataModel/__tests__/businessRules.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts index 5bd0b6c17bc..2f5cb63df72 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts @@ -236,7 +236,7 @@ describe('CollectionObjectGroup business rules', () => { }; test('Only one CO COJO can be primary', () => { - const { cojo1, cojo2 } = getBaseCOG(); + const { cojo1, cojo2 } = getBaseCog(); cojo1.set('isPrimary', true); expect(cojo1.get('isPrimary')).toBe(true); @@ -244,7 +244,7 @@ describe('CollectionObjectGroup business rules', () => { }); test('Only one CO COJO can be substrate', () => { - const { cojo1, cojo2 } = getBaseCOG(); + const { cojo1, cojo2 } = getBaseCog(); cojo2.set('isSubstrate', true); expect(cojo1.get('isSubstrate')).toBe(false); From c0f0887cbf77087e0013719ca44daf1211dd0bfc Mon Sep 17 00:00:00 2001 From: Sharad S Date: Wed, 2 Oct 2024 14:27:21 -0400 Subject: [PATCH 19/19] Change parentCojos to cojo --- .../lib/components/DataModel/__tests__/businessRules.test.ts | 2 +- .../js_src/lib/components/DataModel/businessRuleDefs.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts index 2f5cb63df72..0700746be93 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts @@ -231,7 +231,7 @@ describe('CollectionObjectGroup business rules', () => { parentCog: getResourceApiUrl('CollectionObjectGroup', 1), }); - cog.set('parentCojos', [cojo1, cojo2]); + cog.set('cojo', [cojo1, cojo2]); return { cog, cojo1, cojo2 }; }; diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts index 1b784702712..7aaa79daf64 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts @@ -207,7 +207,7 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { // The first COJO CO will automatically have isPrimary set to True when the COG type is 'consolidated' cog.rgetPromise('cogType').then((cogtype) => { if (cogtype.get('type') === cogTypes.CONSOLIDATED) { - const cojos = cog.getDependentResource('parentCojos'); + const cojos = cog.getDependentResource('cojo'); // Set first CO in COG to primary cojos?.models .find((cojo) => cojo.get('childCo') !== null)