From 0b7cc25e8899c4b7633352066917b989be4646c3 Mon Sep 17 00:00:00 2001 From: Caroline D <108160931+CarolineDenis@users.noreply.github.com> Date: Thu, 27 Mar 2025 10:48:51 -0700 Subject: [PATCH 1/9] Add save blocker when trying to delete a loaned prep Fixes #1431 --- .../lib/components/DataModel/businessRuleDefs.ts | 12 ++++++++++++ .../frontend/js_src/lib/localization/resources.ts | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts index 0d25da2710a..d82b467175e 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts @@ -596,4 +596,16 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { }, }, }, + Preparation: { + onRemoved: (preparation, collection): void => { + if (preparation.get('isOnLoan') === true) { + setSaveBlockers( + collection.related ?? preparation, + preparation.specifyTable.field.isOnLoan, + [resourcesText.deleteLoanedPrep()], + resourcesText.loanedPrepDeletionMessage() + ) + } + } + } }; diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index 2d4ec3bf1a6..8d72d3e5ba1 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -853,6 +853,12 @@ export const resourcesText = createDictionary({ 'en-us': 'This record cannot be deleted as it is the primary record of the Collection Object Group. Please reload the page, then assign another CO as the primary record if a change is desired.', }, + deleteLoanedPrep: { + 'en-us': 'A loaned preparation cannot be deleted' + }, + loanedPrepDeletionMessage: { + 'en-us': 'This record cannot be delted because it is used in a loan.' + }, invalidDeterminationTaxon: { 'en-us': 'Determination does not belong to the taxon tree associated with the Collection Object Type', From 0cef958e725fae102061eb8e8bdca3ae30ce4eb8 Mon Sep 17 00:00:00 2001 From: Caroline D <108160931+CarolineDenis@users.noreply.github.com> Date: Thu, 27 Mar 2025 17:53:01 +0000 Subject: [PATCH 2/9] Lint code with ESLint and Prettier Triggered by 0b7cc25e8899c4b7633352066917b989be4646c3 on branch refs/heads/issue-1431 --- specifyweb/frontend/js_src/lib/localization/resources.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index 8d72d3e5ba1..058d0c5b3ef 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -854,10 +854,10 @@ export const resourcesText = createDictionary({ 'This record cannot be deleted as it is the primary record of the Collection Object Group. Please reload the page, then assign another CO as the primary record if a change is desired.', }, deleteLoanedPrep: { - 'en-us': 'A loaned preparation cannot be deleted' + 'en-us': 'A loaned preparation cannot be deleted', }, loanedPrepDeletionMessage: { - 'en-us': 'This record cannot be delted because it is used in a loan.' + 'en-us': 'This record cannot be delted because it is used in a loan.', }, invalidDeterminationTaxon: { 'en-us': From 02df75d1ec377557341e84c24a1cbb8aeccf198e Mon Sep 17 00:00:00 2001 From: Caroline D <108160931+CarolineDenis@users.noreply.github.com> Date: Fri, 28 Mar 2025 11:57:17 -0700 Subject: [PATCH 3/9] Use business rules key utils --- .../js_src/lib/components/DataModel/businessRuleDefs.ts | 5 +++-- .../js_src/lib/components/DataModel/businessRuleUtils.ts | 2 ++ specifyweb/frontend/js_src/lib/localization/resources.ts | 7 ------- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts index d82b467175e..b452a46d83a 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts @@ -9,6 +9,7 @@ import { DETERMINATION_TAXON_KEY, ensureSingleCollectionObjectCheck, hasNoCurrentDetermination, + PREPARATION_LOANED_KEY, } from './businessRuleUtils'; import { cogTypes } from './helpers'; import type { AnySchema, CommonFields, TableFields } from './helperTypes'; @@ -332,7 +333,7 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { collection.related ?? cojo, cojo.specifyTable.field.parentCog, [resourcesText.deletePrimaryRecord()], - resourcesText.primaryDeletionErrorMessage() + COJO_PRIMARY_DELETE_KEY ); } if (collection?.related?.specifyTable === tables.CollectionObjectGroup) { @@ -603,7 +604,7 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { collection.related ?? preparation, preparation.specifyTable.field.isOnLoan, [resourcesText.deleteLoanedPrep()], - resourcesText.loanedPrepDeletionMessage() + PREPARATION_LOANED_KEY ) } } diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts index 554d4c7ac96..25a6324587b 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts @@ -8,6 +8,8 @@ export const COG_TOITSELF = 'cog-toItself'; export const PARENTCOG_KEY = 'cog-parentCog'; export const COG_PRIMARY_KEY = 'cog-isPrimary'; export const DETERMINATION_TAXON_KEY = 'determination-Taxon'; +export const PREPARATION_LOANED_KEY = 'preparation-isLoaned'; +export const COJO_PRIMARY_DELETE_KEY = 'primary-cojo-delete' /** * diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index 058d0c5b3ef..9422ea919d2 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -849,16 +849,9 @@ export const resourcesText = createDictionary({ deletePrimaryRecord: { 'en-us': 'Primary record CO cannot be deleted.', }, - primaryDeletionErrorMessage: { - 'en-us': - 'This record cannot be deleted as it is the primary record of the Collection Object Group. Please reload the page, then assign another CO as the primary record if a change is desired.', - }, deleteLoanedPrep: { 'en-us': 'A loaned preparation cannot be deleted', }, - loanedPrepDeletionMessage: { - 'en-us': 'This record cannot be delted because it is used in a loan.', - }, invalidDeterminationTaxon: { 'en-us': 'Determination does not belong to the taxon tree associated with the Collection Object Type', From 14a8987802ef256930debebbe48b781819baa796 Mon Sep 17 00:00:00 2001 From: Caroline D <108160931+CarolineDenis@users.noreply.github.com> Date: Fri, 28 Mar 2025 12:30:58 -0700 Subject: [PATCH 4/9] Import --- .../frontend/js_src/lib/components/DataModel/businessRuleDefs.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts index b452a46d83a..beae985169e 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts @@ -5,6 +5,7 @@ import type { BusinessRuleResult } from './businessRules'; import { COG_PRIMARY_KEY, COG_TOITSELF, + COJO_PRIMARY_DELETE_KEY, CURRENT_DETERMINATION_KEY, DETERMINATION_TAXON_KEY, ensureSingleCollectionObjectCheck, From 50f051f21725316da301ee093dbae88b5847f645 Mon Sep 17 00:00:00 2001 From: Caroline D <108160931+CarolineDenis@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:20:33 -0700 Subject: [PATCH 5/9] Add isOnGift readonly attribute --- .../js_src/lib/components/DataModel/businessRuleDefs.ts | 9 +++++++++ .../js_src/lib/components/DataModel/businessRuleUtils.ts | 1 + .../js_src/lib/components/DataModel/schemaExtras.ts | 8 ++++++++ .../frontend/js_src/lib/components/DataModel/types.ts | 1 + specifyweb/frontend/js_src/lib/localization/resources.ts | 3 +++ 5 files changed, 22 insertions(+) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts index beae985169e..14612a9b97a 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts @@ -10,6 +10,7 @@ import { DETERMINATION_TAXON_KEY, ensureSingleCollectionObjectCheck, hasNoCurrentDetermination, + PREPARATION_GIFTED_KEY, PREPARATION_LOANED_KEY, } from './businessRuleUtils'; import { cogTypes } from './helpers'; @@ -608,6 +609,14 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { PREPARATION_LOANED_KEY ) } + if (preparation.get('isOnGift') === true) { + setSaveBlockers( + collection.related ?? preparation, + preparation.specifyTable.field.isOnGift, + [resourcesText.deleteGiftedPrep()], + PREPARATION_GIFTED_KEY + ) + } } } }; diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts index 25a6324587b..61a7b64efbd 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts @@ -9,6 +9,7 @@ export const PARENTCOG_KEY = 'cog-parentCog'; export const COG_PRIMARY_KEY = 'cog-isPrimary'; export const DETERMINATION_TAXON_KEY = 'determination-Taxon'; export const PREPARATION_LOANED_KEY = 'preparation-isLoaned'; +export const PREPARATION_GIFTED_KEY = 'preparation-isGifted'; export const COJO_PRIMARY_DELETE_KEY = 'primary-cojo-delete' /** diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts b/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts index 00977172648..c7f79156e4a 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts @@ -330,6 +330,14 @@ export const schemaExtras: { indexed: false, unique: false, }), + new LiteralField(table, { + name: 'isOnGift', + required: false, + readOnly: true, + type: 'java.lang.Boolean', + indexed: false, + unique: false, + }), new LiteralField(table, { name: 'actualCountAmt', required: false, diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts index af34a091780..bc09d77ef35 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts @@ -4386,6 +4386,7 @@ export type Preparation = { readonly integer1: number | null; readonly integer2: number | null; readonly isOnLoan: boolean | null; + readonly isOnGift: boolean | null; readonly number1: number | null; readonly number2: number | null; readonly preparedDate: string | null; diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index 9422ea919d2..e0013c7fb86 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -852,6 +852,9 @@ export const resourcesText = createDictionary({ deleteLoanedPrep: { 'en-us': 'A loaned preparation cannot be deleted', }, + deleteGiftedPrep: { + 'en-us': 'A gifted preparation cannot be deleted', + }, invalidDeterminationTaxon: { 'en-us': 'Determination does not belong to the taxon tree associated with the Collection Object Type', From ed1d453f0fd1e9f0f10e7b4dde60bd06dbb7adfc Mon Sep 17 00:00:00 2001 From: Caroline D <108160931+CarolineDenis@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:31:54 -0700 Subject: [PATCH 6/9] Add isOnDisposal readonly attribute --- .../js_src/lib/components/DataModel/businessRuleDefs.ts | 9 +++++++++ .../js_src/lib/components/DataModel/businessRuleUtils.ts | 1 + .../js_src/lib/components/DataModel/schemaExtras.ts | 8 ++++++++ .../frontend/js_src/lib/components/DataModel/types.ts | 1 + specifyweb/frontend/js_src/lib/localization/resources.ts | 3 +++ 5 files changed, 22 insertions(+) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts index 14612a9b97a..062cf494723 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts @@ -10,6 +10,7 @@ import { DETERMINATION_TAXON_KEY, ensureSingleCollectionObjectCheck, hasNoCurrentDetermination, + PREPARATION_DISPOSED_KEY, PREPARATION_GIFTED_KEY, PREPARATION_LOANED_KEY, } from './businessRuleUtils'; @@ -617,6 +618,14 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { PREPARATION_GIFTED_KEY ) } + if (preparation.get('isOnDisposal') === true) { + setSaveBlockers( + collection.related ?? preparation, + preparation.specifyTable.field.isOnDisposal, + [resourcesText.deleteDisposedPrep()], + PREPARATION_DISPOSED_KEY + ) + } } } }; diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts index 61a7b64efbd..963f8a6ecfb 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts @@ -10,6 +10,7 @@ export const COG_PRIMARY_KEY = 'cog-isPrimary'; export const DETERMINATION_TAXON_KEY = 'determination-Taxon'; export const PREPARATION_LOANED_KEY = 'preparation-isLoaned'; export const PREPARATION_GIFTED_KEY = 'preparation-isGifted'; +export const PREPARATION_DISPOSED_KEY = 'preparation-isDisposed'; export const COJO_PRIMARY_DELETE_KEY = 'primary-cojo-delete' /** diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts b/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts index c7f79156e4a..c46679f121c 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts @@ -338,6 +338,14 @@ export const schemaExtras: { indexed: false, unique: false, }), + new LiteralField(table, { + name: 'isOnDisposal', + required: false, + readOnly: true, + type: 'java.lang.Boolean', + indexed: false, + unique: false, + }), new LiteralField(table, { name: 'actualCountAmt', required: false, diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts index bc09d77ef35..16abe2d91d8 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts @@ -4387,6 +4387,7 @@ export type Preparation = { readonly integer2: number | null; readonly isOnLoan: boolean | null; readonly isOnGift: boolean | null; + readonly isOnDisposal: boolean | null; readonly number1: number | null; readonly number2: number | null; readonly preparedDate: string | null; diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index e0013c7fb86..a668f457651 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -855,6 +855,9 @@ export const resourcesText = createDictionary({ deleteGiftedPrep: { 'en-us': 'A gifted preparation cannot be deleted', }, + deleteDisposedPrep: { + 'en-us': 'A disposed preparation cannot be deleted', + }, invalidDeterminationTaxon: { 'en-us': 'Determination does not belong to the taxon tree associated with the Collection Object Type', From 669afd167b483fe49ab41962b8504177d77d0314 Mon Sep 17 00:00:00 2001 From: Caroline D <108160931+CarolineDenis@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:41:50 -0700 Subject: [PATCH 7/9] Add isOnexchangeOut readonly attribute --- .../js_src/lib/components/DataModel/businessRuleDefs.ts | 9 +++++++++ .../js_src/lib/components/DataModel/businessRuleUtils.ts | 1 + .../js_src/lib/components/DataModel/schemaExtras.ts | 8 ++++++++ .../frontend/js_src/lib/components/DataModel/types.ts | 1 + specifyweb/frontend/js_src/lib/localization/resources.ts | 3 +++ 5 files changed, 22 insertions(+) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts index 062cf494723..accbbe8babd 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts @@ -11,6 +11,7 @@ import { ensureSingleCollectionObjectCheck, hasNoCurrentDetermination, PREPARATION_DISPOSED_KEY, + PREPARATION_EXCHANGED_OUT_KEY, PREPARATION_GIFTED_KEY, PREPARATION_LOANED_KEY, } from './businessRuleUtils'; @@ -626,6 +627,14 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { PREPARATION_DISPOSED_KEY ) } + if (preparation.get('isOnExchangeOut') === true) { + setSaveBlockers( + collection.related ?? preparation, + preparation.specifyTable.field.isOnExchangeOut, + [resourcesText.deleteExchangeOutPrep()], + PREPARATION_EXCHANGED_OUT_KEY + ) + } } } }; diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts index 963f8a6ecfb..e0f4fccbc16 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts @@ -11,6 +11,7 @@ export const DETERMINATION_TAXON_KEY = 'determination-Taxon'; export const PREPARATION_LOANED_KEY = 'preparation-isLoaned'; export const PREPARATION_GIFTED_KEY = 'preparation-isGifted'; export const PREPARATION_DISPOSED_KEY = 'preparation-isDisposed'; +export const PREPARATION_EXCHANGED_OUT_KEY = 'preparation-isExchangedOut'; export const COJO_PRIMARY_DELETE_KEY = 'primary-cojo-delete' /** diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts b/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts index c46679f121c..3dc6545adf4 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts @@ -346,6 +346,14 @@ export const schemaExtras: { indexed: false, unique: false, }), + new LiteralField(table, { + name: 'isOnExchangeOut', + required: false, + readOnly: true, + type: 'java.lang.Boolean', + indexed: false, + unique: false, + }), new LiteralField(table, { name: 'actualCountAmt', required: false, diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts index 16abe2d91d8..c8cc7171805 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts @@ -4388,6 +4388,7 @@ export type Preparation = { readonly isOnLoan: boolean | null; readonly isOnGift: boolean | null; readonly isOnDisposal: boolean | null; + readonly isOnExchangeOut: boolean | null; readonly number1: number | null; readonly number2: number | null; readonly preparedDate: string | null; diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index a668f457651..45ce75c2901 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -858,6 +858,9 @@ export const resourcesText = createDictionary({ deleteDisposedPrep: { 'en-us': 'A disposed preparation cannot be deleted', }, + deleteExchangeOutPrep: { + 'en-us': 'A exchanged out preparation cannot be deleted', + }, invalidDeterminationTaxon: { 'en-us': 'Determination does not belong to the taxon tree associated with the Collection Object Type', From 95e92ce746db2f21c188c48ff9cbf89dfb1c766d Mon Sep 17 00:00:00 2001 From: Caroline D <108160931+CarolineDenis@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:47:23 -0700 Subject: [PATCH 8/9] Add isOnExchnageIn readonly attribute --- .../js_src/lib/components/DataModel/businessRuleDefs.ts | 9 +++++++++ .../js_src/lib/components/DataModel/businessRuleUtils.ts | 1 + .../js_src/lib/components/DataModel/schemaExtras.ts | 8 ++++++++ .../frontend/js_src/lib/components/DataModel/types.ts | 1 + specifyweb/frontend/js_src/lib/localization/resources.ts | 3 +++ 5 files changed, 22 insertions(+) diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts index accbbe8babd..5fddc3320ea 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleDefs.ts @@ -11,6 +11,7 @@ import { ensureSingleCollectionObjectCheck, hasNoCurrentDetermination, PREPARATION_DISPOSED_KEY, + PREPARATION_EXCHANGED_IN_KEY, PREPARATION_EXCHANGED_OUT_KEY, PREPARATION_GIFTED_KEY, PREPARATION_LOANED_KEY, @@ -635,6 +636,14 @@ export const businessRuleDefs: MappedBusinessRuleDefs = { PREPARATION_EXCHANGED_OUT_KEY ) } + if (preparation.get('isOnExchangeIn') === true) { + setSaveBlockers( + collection.related ?? preparation, + preparation.specifyTable.field.isOnExchangeIn, + [resourcesText.deleteExchangeInPrep()], + PREPARATION_EXCHANGED_IN_KEY + ) + } } } }; diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts index e0f4fccbc16..3b67d92bdf6 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/businessRuleUtils.ts @@ -12,6 +12,7 @@ export const PREPARATION_LOANED_KEY = 'preparation-isLoaned'; export const PREPARATION_GIFTED_KEY = 'preparation-isGifted'; export const PREPARATION_DISPOSED_KEY = 'preparation-isDisposed'; export const PREPARATION_EXCHANGED_OUT_KEY = 'preparation-isExchangedOut'; +export const PREPARATION_EXCHANGED_IN_KEY = 'preparation-isExchangedIn'; export const COJO_PRIMARY_DELETE_KEY = 'primary-cojo-delete' /** diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts b/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts index 3dc6545adf4..5a8af38f5d8 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/schemaExtras.ts @@ -354,6 +354,14 @@ export const schemaExtras: { indexed: false, unique: false, }), + new LiteralField(table, { + name: 'isOnExchangeIn', + required: false, + readOnly: true, + type: 'java.lang.Boolean', + indexed: false, + unique: false, + }), new LiteralField(table, { name: 'actualCountAmt', required: false, diff --git a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts index c8cc7171805..28152becfc6 100644 --- a/specifyweb/frontend/js_src/lib/components/DataModel/types.ts +++ b/specifyweb/frontend/js_src/lib/components/DataModel/types.ts @@ -4389,6 +4389,7 @@ export type Preparation = { readonly isOnGift: boolean | null; readonly isOnDisposal: boolean | null; readonly isOnExchangeOut: boolean | null; + readonly isOnExchangeIn: boolean | null; readonly number1: number | null; readonly number2: number | null; readonly preparedDate: string | null; diff --git a/specifyweb/frontend/js_src/lib/localization/resources.ts b/specifyweb/frontend/js_src/lib/localization/resources.ts index 45ce75c2901..c9335b9b8fd 100644 --- a/specifyweb/frontend/js_src/lib/localization/resources.ts +++ b/specifyweb/frontend/js_src/lib/localization/resources.ts @@ -861,6 +861,9 @@ export const resourcesText = createDictionary({ deleteExchangeOutPrep: { 'en-us': 'A exchanged out preparation cannot be deleted', }, + deleteExchangeInPrep: { + 'en-us': 'A exchanged in preparation cannot be deleted', + }, invalidDeterminationTaxon: { 'en-us': 'Determination does not belong to the taxon tree associated with the Collection Object Type', From 731a36d0f29f7fbc480d232f6925b7cc7071ee12 Mon Sep 17 00:00:00 2001 From: Caroline D <108160931+CarolineDenis@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:48:07 -0700 Subject: [PATCH 9/9] Add backend --- specifyweb/specify/calculated_fields.py | 4 ++ specifyweb/specify/model_extras.py | 64 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/specifyweb/specify/calculated_fields.py b/specifyweb/specify/calculated_fields.py index e433c952d3b..bbe27f511e1 100644 --- a/specifyweb/specify/calculated_fields.py +++ b/specifyweb/specify/calculated_fields.py @@ -60,6 +60,10 @@ def calculate_extra_fields(obj, data: Dict[str, Any]) -> Dict[str, Any]: extra["actualCountAmt"] = actual_count_amount extra["isonloan"] = obj.isonloan() + extra["isongift"] = obj.isongift() + extra["isondisposal"] = obj.isondisposal() + extra["isonexchangeout"] = obj.isonexchangeout() + extra["isonexchangein"] = obj.isonexchangein() elif isinstance(obj, Specifyuser): extra["isadmin"] = obj.is_admin() diff --git a/specifyweb/specify/model_extras.py b/specifyweb/specify/model_extras.py index 2e2a65e3ff6..49329694766 100644 --- a/specifyweb/specify/model_extras.py +++ b/specifyweb/specify/model_extras.py @@ -144,6 +144,70 @@ def isonloan(self): result = cursor.fetchone() return result[0] > 0 + def isongift(self): + # TODO: needs unit tests + from django.db import connection + cursor = connection.cursor() + + cursor.execute(""" + SELECT COALESCE( + SUM({GREATEST}(0, COALESCE(Quantity, 0))), + 0) + FROM giftpreparation + WHERE PreparationID = %s + """.format(GREATEST='MAX' if connection.vendor == 'sqlite' else 'GREATEST'), [self.id]) + + result = cursor.fetchone() + return result[0] > 0 + + def isondisposal(self): + # TODO: needs unit tests + from django.db import connection + cursor = connection.cursor() + + cursor.execute(""" + SELECT COALESCE( + SUM({GREATEST}(0, COALESCE(Quantity, 0))), + 0) + FROM disposalpreparation + WHERE PreparationID = %s + """.format(GREATEST='MAX' if connection.vendor == 'sqlite' else 'GREATEST'), [self.id]) + + result = cursor.fetchone() + return result[0] > 0 + + def isonexchangeout(self): + # TODO: needs unit tests + from django.db import connection + cursor = connection.cursor() + + cursor.execute(""" + SELECT COALESCE( + SUM({GREATEST}(0, COALESCE(Quantity, 0))), + 0) + FROM exchangeoutprep + WHERE PreparationID = %s + """.format(GREATEST='MAX' if connection.vendor == 'sqlite' else 'GREATEST'), [self.id]) + + result = cursor.fetchone() + return result[0] > 0 + + def isonexchangein(self): + # TODO: needs unit tests + from django.db import connection + cursor = connection.cursor() + + cursor.execute(""" + SELECT COALESCE( + SUM({GREATEST}(0, COALESCE(Quantity, 0))), + 0) + FROM exchangeinprep + WHERE PreparationID = %s + """.format(GREATEST='MAX' if connection.vendor == 'sqlite' else 'GREATEST'), [self.id]) + + result = cursor.fetchone() + return result[0] > 0 + class Meta: abstract = True