From 65c080c573f6e874ac4e105f8b27cd4802875ad3 Mon Sep 17 00:00:00 2001 From: Fergus Roache Date: Wed, 6 Nov 2024 16:45:12 +1300 Subject: [PATCH 01/25] Add graphql end point for indicator value mutations --- server/graphql/programs/src/lib.rs | 12 +++++ server/graphql/programs/src/mutations/mod.rs | 1 + .../src/mutations/program_indicator/mod.rs | 1 + .../program_indicator/update_value.rs | 52 +++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 server/graphql/programs/src/mutations/program_indicator/mod.rs create mode 100644 server/graphql/programs/src/mutations/program_indicator/update_value.rs diff --git a/server/graphql/programs/src/lib.rs b/server/graphql/programs/src/lib.rs index 3af4234f75..8633241c2c 100644 --- a/server/graphql/programs/src/lib.rs +++ b/server/graphql/programs/src/lib.rs @@ -52,6 +52,9 @@ use mutations::program_enrolment::insert::InsertProgramEnrolmentResponse; use mutations::program_enrolment::update::update_program_enrolment; use mutations::program_enrolment::update::UpdateProgramEnrolmentInput; use mutations::program_enrolment::update::UpdateProgramEnrolmentResponse; +use mutations::program_indicator::update_value::update_indicator_value; +use mutations::program_indicator::update_value::UpdateIndicatorValueInput; +use mutations::program_indicator::update_value::UpdateIndicatorValueResponse; use mutations::program_patient::insert::*; use mutations::program_patient::update::update_program_patient; use mutations::program_patient::update::UpdateProgramPatientInput; @@ -506,4 +509,13 @@ impl ProgramsMutations { ) -> Result { update_vaccination(ctx, store_id, input) } + + pub async fn update_indicator_value( + &self, + ctx: &Context<'_>, + store_id: String, + input: UpdateIndicatorValueInput, + ) -> Result { + update_indicator_value(ctx, store_id, input) + } } diff --git a/server/graphql/programs/src/mutations/mod.rs b/server/graphql/programs/src/mutations/mod.rs index 9eaa9f7da5..7bfda56f62 100644 --- a/server/graphql/programs/src/mutations/mod.rs +++ b/server/graphql/programs/src/mutations/mod.rs @@ -4,6 +4,7 @@ pub mod encounter; pub mod insert_document_registry; pub mod patient; pub mod program_enrolment; +pub mod program_indicator; pub mod program_patient; pub mod rnr_form; pub mod vaccination; diff --git a/server/graphql/programs/src/mutations/program_indicator/mod.rs b/server/graphql/programs/src/mutations/program_indicator/mod.rs new file mode 100644 index 0000000000..02ffbeffed --- /dev/null +++ b/server/graphql/programs/src/mutations/program_indicator/mod.rs @@ -0,0 +1 @@ +pub mod update_value; diff --git a/server/graphql/programs/src/mutations/program_indicator/update_value.rs b/server/graphql/programs/src/mutations/program_indicator/update_value.rs new file mode 100644 index 0000000000..098e94d2e0 --- /dev/null +++ b/server/graphql/programs/src/mutations/program_indicator/update_value.rs @@ -0,0 +1,52 @@ +use async_graphql::*; +use graphql_core::{ + simple_generic_errors::RecordNotFound, standard_graphql_error::validate_auth, ContextExt, +}; +use graphql_types::types::program_indicator::IndicatorValueNode; +use service::auth::{Resource, ResourceAccessRequest}; + +#[derive(InputObject)] +pub struct UpdateIndicatorValueInput { + pub id: String, + pub value: String, +} + +#[derive(Interface)] +#[graphql(name = "UpdateIndicatorValueErrorInterface")] +#[graphql(field(name = "description", ty = "String"))] +pub enum UpdateErrorInterface { + RecordNotFound(RecordNotFound), +} + +#[derive(SimpleObject)] +#[graphql(name = "UpdateIndicatorValueError")] +pub struct UpdateError { + pub error: UpdateErrorInterface, +} + +#[derive(Union)] +pub enum UpdateIndicatorValueResponse { + Response(IndicatorValueNode), + Error(UpdateError), +} + +pub fn update_indicator_value( + ctx: &Context<'_>, + store_id: String, + input: UpdateIndicatorValueInput, +) -> Result { + let user = validate_auth( + ctx, + &ResourceAccessRequest { + resource: Resource::MutateProgram, + store_id: Some(store_id.clone()), + }, + )?; + + let service_provider = ctx.service_provider(); + let service_context = service_provider.context(store_id.to_string(), user.user_id)?; + + return Ok(UpdateIndicatorValueResponse::Error(UpdateError { + error: UpdateErrorInterface::RecordNotFound(RecordNotFound), + })); +} From f6dcc5f43be2e5bf38ef3d2915068511588988c0 Mon Sep 17 00:00:00 2001 From: Fergus Roache Date: Thu, 7 Nov 2024 14:26:51 +1300 Subject: [PATCH 02/25] add update indicator value mutation --- .../program_indicator/update_value.rs | 19 +++- .../src/db_diesel/indicator_value.rs | 7 ++ .../src/programs/indicator_value/mod.rs | 19 ++++ .../src/programs/indicator_value/update.rs | 86 +++++++++++++++++++ server/service/src/programs/mod.rs | 1 + server/service/src/service_provider.rs | 4 +- 6 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 server/service/src/programs/indicator_value/mod.rs create mode 100644 server/service/src/programs/indicator_value/update.rs diff --git a/server/graphql/programs/src/mutations/program_indicator/update_value.rs b/server/graphql/programs/src/mutations/program_indicator/update_value.rs index 098e94d2e0..d89b50d63a 100644 --- a/server/graphql/programs/src/mutations/program_indicator/update_value.rs +++ b/server/graphql/programs/src/mutations/program_indicator/update_value.rs @@ -3,6 +3,7 @@ use graphql_core::{ simple_generic_errors::RecordNotFound, standard_graphql_error::validate_auth, ContextExt, }; use graphql_types::types::program_indicator::IndicatorValueNode; +use repository::indicator_line; use service::auth::{Resource, ResourceAccessRequest}; #[derive(InputObject)] @@ -38,7 +39,7 @@ pub fn update_indicator_value( let user = validate_auth( ctx, &ResourceAccessRequest { - resource: Resource::MutateProgram, + resource: Resource::MutateRequisition, store_id: Some(store_id.clone()), }, )?; @@ -46,7 +47,17 @@ pub fn update_indicator_value( let service_provider = ctx.service_provider(); let service_context = service_provider.context(store_id.to_string(), user.user_id)?; - return Ok(UpdateIndicatorValueResponse::Error(UpdateError { - error: UpdateErrorInterface::RecordNotFound(RecordNotFound), - })); + let response = match service_provider + .indicator_value_service + .update_indicator_value(ctx, input) + { + Ok(incator_value) => { + UpdateIndicatorValueResponse::Response(IndicatorValueNode::from_domain(indicator_value)) + } + Err(error) => UpdateResponse::Error(UpdateError { + error: map_error(error)?, + }), + }; + + Ok(response) } diff --git a/server/repository/src/db_diesel/indicator_value.rs b/server/repository/src/db_diesel/indicator_value.rs index f5d8cd4e73..410967c20e 100644 --- a/server/repository/src/db_diesel/indicator_value.rs +++ b/server/repository/src/db_diesel/indicator_value.rs @@ -65,6 +65,13 @@ impl<'a> IndicatorValueRepository<'a> { .get_result(self.connection.lock().connection())?) } + pub fn query_one( + &self, + filter: IndicatorValueFilter, + ) -> Result, RepositoryError> { + Ok(self.query_by_filter(filter)?.pop()) + } + pub fn query_by_filter( &self, filter: IndicatorValueFilter, diff --git a/server/service/src/programs/indicator_value/mod.rs b/server/service/src/programs/indicator_value/mod.rs new file mode 100644 index 0000000000..dcc7b0a819 --- /dev/null +++ b/server/service/src/programs/indicator_value/mod.rs @@ -0,0 +1,19 @@ +pub mod update; +pub use update::*; + +use repository::IndicatorValueRow; + +use crate::service_provider::ServiceContext; + +pub trait IndicatorValueServiceTrait: Sync + Send { + fn update_indicator_value( + &self, + ctx: &ServiceContext, + input: UpdateIndicatorValue, + ) -> Result { + update_indicator_value(ctx, input) + } +} + +pub struct IndicatorValueService {} +impl IndicatorValueServiceTrait for IndicatorValueService {} diff --git a/server/service/src/programs/indicator_value/update.rs b/server/service/src/programs/indicator_value/update.rs new file mode 100644 index 0000000000..2bedec4273 --- /dev/null +++ b/server/service/src/programs/indicator_value/update.rs @@ -0,0 +1,86 @@ +use actix_web::{web::ServiceConfig, ResponseError}; +use repository::{ + indicator_value::{IndicatorValueFilter, IndicatorValueRepository}, + EqualFilter, IndicatorValueRow, IndicatorValueRowRepository, RepositoryError, + StorageConnection, +}; + +use crate::service_provider::{ServiceContext, ServiceProvider}; + +#[derive(Debug, PartialEq, Clone, Default)] +pub struct UpdateIndicatorValue { + pub id: String, + pub value: String, +} + +#[derive(Debug, PartialEq)] +pub enum UpdateIndicatorValueError { + DatabaseError(RepositoryError), + IndicatorValueDoesNotExist, +} + +type OutError = UpdateIndicatorValueError; + +pub fn update_indicator_value( + ctx: &ServiceContext, + input: UpdateIndicatorValue, +) -> Result { + // let indicator_value = ctx.connection.transaction + let indicator_value = ctx + .connection + .transaction_sync(|connection| { + let indicator_value_row = validate(connection, &input)?; + + let updated_row = generate(indicator_value_row, input); + + IndicatorValueRowRepository::new(connection).upsert_one(&updated_row)?; + + IndicatorValueRepository::new(connection) + .query_one(IndicatorValueFilter::new().id(EqualFilter::equal_to(&updated_row.id))) + .map_err(OutError::DatabaseError)? + .ok_or(OutError::IndicatorValueDoesNotExist) + }) + .map_err(|error| error.to_inner_error())?; + Ok(indicator_value) +} + +fn validate( + connection: &StorageConnection, + input: &UpdateIndicatorValue, +) -> Result { + let indicator_value_row = check_indicator_value_exists(connection, &input.id)? + .ok_or(OutError::IndicatorValueDoesNotExist)?; + + // TODO add validations as per requisition + + Ok(indicator_value_row) +} + +fn check_indicator_value_exists( + connection: &StorageConnection, + id: &str, +) -> Result, RepositoryError> { + IndicatorValueRepository::new(connection) + .query_one(IndicatorValueFilter::new().id(EqualFilter::equal_to(id))) +} + +fn generate( + indicator_value_row: IndicatorValueRow, + input: UpdateIndicatorValue, +) -> IndicatorValueRow { + IndicatorValueRow { + id: indicator_value_row.id, + customer_name_link_id: indicator_value_row.customer_name_link_id, + supplier_store_id: indicator_value_row.supplier_store_id, + period_id: indicator_value_row.period_id, + indicator_line_id: indicator_value_row.indicator_line_id, + indicator_column_id: indicator_value_row.indicator_column_id, + value: input.value, + } +} + +impl From for UpdateIndicatorValueError { + fn from(error: RepositoryError) -> Self { + UpdateIndicatorValueError::DatabaseError(error) + } +} diff --git a/server/service/src/programs/mod.rs b/server/service/src/programs/mod.rs index 59fea461e1..dbf86156fb 100644 --- a/server/service/src/programs/mod.rs +++ b/server/service/src/programs/mod.rs @@ -1,5 +1,6 @@ pub mod contact_trace; pub mod encounter; +pub mod indicator_value; pub mod patient; pub mod program_enrolment; pub mod program_event; diff --git a/server/service/src/service_provider.rs b/server/service/src/service_provider.rs index 1e17d9f36d..d8491fc3ee 100644 --- a/server/service/src/service_provider.rs +++ b/server/service/src/service_provider.rs @@ -37,6 +37,7 @@ use crate::{ programs::{ contact_trace::{ContactTraceService, ContactTraceServiceTrait}, encounter::{EncounterService, EncounterServiceTrait}, + indicator_value::{IndicatorValueService, IndicatorValueServiceTrait}, patient::{PatientService, PatientServiceTrait}, program_enrolment::{ProgramEnrolmentService, ProgramEnrolmentServiceTrait}, program_event::{ProgramEventService, ProgramEventServiceTrait}, @@ -115,7 +116,7 @@ pub struct ServiceProvider { pub program_event_service: Box, pub contact_trace_service: Box, pub program_indicator_service: Box, - + pub indicator_value_service: Box, // Settings pub settings: Box, // App Data Service @@ -213,6 +214,7 @@ impl ServiceProvider { patient_service: Box::new(PatientService {}), program_enrolment_service: Box::new(ProgramEnrolmentService {}), program_indicator_service: Box::new(ProgramIndicatorService {}), + indicator_value_service: Box::new(IndicatorValueService {}), program_event_service: Box::new(ProgramEventService {}), encounter_service: Box::new(EncounterService {}), contact_trace_service: Box::new(ContactTraceService {}), From cff73f26abfa2d929611c86c6fd655b21b65cbb9 Mon Sep 17 00:00:00 2001 From: Fergus Roache Date: Thu, 7 Nov 2024 14:55:21 +1300 Subject: [PATCH 03/25] Fix types --- .../program_indicator/update_value.rs | 30 ++++++++++++++++--- .../src/programs/indicator_value/update.rs | 3 +- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/server/graphql/programs/src/mutations/program_indicator/update_value.rs b/server/graphql/programs/src/mutations/program_indicator/update_value.rs index d89b50d63a..04d6329461 100644 --- a/server/graphql/programs/src/mutations/program_indicator/update_value.rs +++ b/server/graphql/programs/src/mutations/program_indicator/update_value.rs @@ -1,10 +1,12 @@ use async_graphql::*; +use graphql_core::standard_graphql_error::StandardGraphqlError; use graphql_core::{ simple_generic_errors::RecordNotFound, standard_graphql_error::validate_auth, ContextExt, }; use graphql_types::types::program_indicator::IndicatorValueNode; -use repository::indicator_line; use service::auth::{Resource, ResourceAccessRequest}; +use service::programs::indicator_value::UpdateIndicatorValue; +use service::programs::indicator_value::UpdateIndicatorValueError; #[derive(InputObject)] pub struct UpdateIndicatorValueInput { @@ -49,15 +51,35 @@ pub fn update_indicator_value( let response = match service_provider .indicator_value_service - .update_indicator_value(ctx, input) + .update_indicator_value(&service_context, input.to_domain()) { - Ok(incator_value) => { + Ok(indicator_value) => { UpdateIndicatorValueResponse::Response(IndicatorValueNode::from_domain(indicator_value)) } - Err(error) => UpdateResponse::Error(UpdateError { + Err(error) => UpdateIndicatorValueResponse::Error(UpdateError { error: map_error(error)?, }), }; Ok(response) } + +impl UpdateIndicatorValueInput { + pub fn to_domain(self) -> UpdateIndicatorValue { + let UpdateIndicatorValueInput { id, value } = self; + UpdateIndicatorValue { id, value } + } +} + +fn map_error(error: UpdateIndicatorValueError) -> Result { + use StandardGraphqlError::*; + let formatted_error = format!("{:?}", error); + let graphql_error = match error { + UpdateIndicatorValueError::IndicatorValueDoesNotExist => { + return Ok(UpdateErrorInterface::RecordNotFound(RecordNotFound {})) + } + UpdateIndicatorValueError::DatabaseError(_) => InternalError(formatted_error), + }; + + Err(graphql_error.extend()) +} diff --git a/server/service/src/programs/indicator_value/update.rs b/server/service/src/programs/indicator_value/update.rs index 2bedec4273..8fb181d5cd 100644 --- a/server/service/src/programs/indicator_value/update.rs +++ b/server/service/src/programs/indicator_value/update.rs @@ -1,11 +1,10 @@ -use actix_web::{web::ServiceConfig, ResponseError}; use repository::{ indicator_value::{IndicatorValueFilter, IndicatorValueRepository}, EqualFilter, IndicatorValueRow, IndicatorValueRowRepository, RepositoryError, StorageConnection, }; -use crate::service_provider::{ServiceContext, ServiceProvider}; +use crate::service_provider::ServiceContext; #[derive(Debug, PartialEq, Clone, Default)] pub struct UpdateIndicatorValue { From d0147bbb31d9679212f5e735543962ac581ec210 Mon Sep 17 00:00:00 2001 From: Fergus Roache Date: Fri, 8 Nov 2024 10:24:44 +1300 Subject: [PATCH 04/25] Add validation checks --- .../program_indicator/update_value.rs | 9 ++- .../src/programs/indicator_value/update.rs | 57 ++++++++++++++++--- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/server/graphql/programs/src/mutations/program_indicator/update_value.rs b/server/graphql/programs/src/mutations/program_indicator/update_value.rs index 04d6329461..8adcd29702 100644 --- a/server/graphql/programs/src/mutations/program_indicator/update_value.rs +++ b/server/graphql/programs/src/mutations/program_indicator/update_value.rs @@ -12,6 +12,7 @@ use service::programs::indicator_value::UpdateIndicatorValueError; pub struct UpdateIndicatorValueInput { pub id: String, pub value: String, + pub requisition_id: String, } #[derive(Interface)] @@ -66,8 +67,12 @@ pub fn update_indicator_value( impl UpdateIndicatorValueInput { pub fn to_domain(self) -> UpdateIndicatorValue { - let UpdateIndicatorValueInput { id, value } = self; - UpdateIndicatorValue { id, value } + let UpdateIndicatorValueInput { + id, + value, + requisition_id, + } = self; + UpdateIndicatorValue { id, value, requisition_id } } } diff --git a/server/service/src/programs/indicator_value/update.rs b/server/service/src/programs/indicator_value/update.rs index 8fb181d5cd..a3fb686c90 100644 --- a/server/service/src/programs/indicator_value/update.rs +++ b/server/service/src/programs/indicator_value/update.rs @@ -1,21 +1,31 @@ use repository::{ indicator_value::{IndicatorValueFilter, IndicatorValueRepository}, - EqualFilter, IndicatorValueRow, IndicatorValueRowRepository, RepositoryError, - StorageConnection, + requisition_row, EqualFilter, IndicatorValueRow, IndicatorValueRowRepository, RepositoryError, + RequisitionStatus, RequisitionType, StorageConnection, }; -use crate::service_provider::ServiceContext; +use crate::{ + requisition::common::{check_approval_status, check_requisition_exists}, + service_provider::ServiceContext, +}; #[derive(Debug, PartialEq, Clone, Default)] pub struct UpdateIndicatorValue { pub id: String, pub value: String, + pub requisition_id: String, } #[derive(Debug, PartialEq)] pub enum UpdateIndicatorValueError { DatabaseError(RepositoryError), IndicatorValueDoesNotExist, + NoRequisitionForIndicator, + ValueNotOfUsersStore, + RequisitionOfDifferentStore, + RequisitionHasNoPeriod, + ValuePeriodNotRequisitionPeriod, + CannotEditRequisition, } type OutError = UpdateIndicatorValueError; @@ -28,7 +38,7 @@ pub fn update_indicator_value( let indicator_value = ctx .connection .transaction_sync(|connection| { - let indicator_value_row = validate(connection, &input)?; + let indicator_value_row = validate(connection, &input, &ctx.store_id)?; let updated_row = generate(indicator_value_row, input); @@ -46,11 +56,44 @@ pub fn update_indicator_value( fn validate( connection: &StorageConnection, input: &UpdateIndicatorValue, + store_id: &String, ) -> Result { let indicator_value_row = check_indicator_value_exists(connection, &input.id)? .ok_or(OutError::IndicatorValueDoesNotExist)?; - // TODO add validations as per requisition + let requisition = check_requisition_exists(connection, &input.requisition_id)? + .ok_or(OutError::NoRequisitionForIndicator)?; + + // todo rename to store_id as it is store_id + if store_id.to_owned() != indicator_value_row.supplier_store_id { + return Err(OutError::ValueNotOfUsersStore); + } + + if requisition.requisition_row.store_id != store_id.to_owned() { + return Err(OutError::RequisitionOfDifferentStore); + } + + match requisition.period { + Some(period) => { + if period.id != indicator_value_row.period_id { + return Err(OutError::ValuePeriodNotRequisitionPeriod); + } + } + None => return Err(OutError::RequisitionHasNoPeriod), + } + + match requisition.requisition_row.r#type { + RequisitionType::Response => { + if check_approval_status(&requisition.requisition_row) { + return Err(OutError::CannotEditRequisition); + } + } + RequisitionType::Request => { + if requisition.requisition_row.status != RequisitionStatus::Draft { + return Err(OutError::CannotEditRequisition); + } + } + } Ok(indicator_value_row) } @@ -59,8 +102,8 @@ fn check_indicator_value_exists( connection: &StorageConnection, id: &str, ) -> Result, RepositoryError> { - IndicatorValueRepository::new(connection) - .query_one(IndicatorValueFilter::new().id(EqualFilter::equal_to(id))) + Ok(IndicatorValueRepository::new(connection) + .query_one(IndicatorValueFilter::new().id(EqualFilter::equal_to(id)))?) } fn generate( From 0abb6ec6b7d4da437b95219b2cd3b89854a46544 Mon Sep 17 00:00:00 2001 From: Fergus Roache Date: Fri, 8 Nov 2024 11:56:51 +1300 Subject: [PATCH 05/25] Add error matching in graphql --- .../program_indicator/update_value.rs | 25 ++++++++++++++++++- .../src/programs/indicator_value/update.rs | 10 ++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/server/graphql/programs/src/mutations/program_indicator/update_value.rs b/server/graphql/programs/src/mutations/program_indicator/update_value.rs index 8adcd29702..10e4718757 100644 --- a/server/graphql/programs/src/mutations/program_indicator/update_value.rs +++ b/server/graphql/programs/src/mutations/program_indicator/update_value.rs @@ -1,4 +1,5 @@ use async_graphql::*; +use graphql_core::simple_generic_errors::{CannotEditRequisition, ForeignKey, ForeignKeyError}; use graphql_core::standard_graphql_error::StandardGraphqlError; use graphql_core::{ simple_generic_errors::RecordNotFound, standard_graphql_error::validate_auth, ContextExt, @@ -20,6 +21,8 @@ pub struct UpdateIndicatorValueInput { #[graphql(field(name = "description", ty = "String"))] pub enum UpdateErrorInterface { RecordNotFound(RecordNotFound), + RequisitionDoesNotExist(ForeignKeyError), + CannotEditRequisition(CannotEditRequisition), } #[derive(SimpleObject)] @@ -72,7 +75,11 @@ impl UpdateIndicatorValueInput { value, requisition_id, } = self; - UpdateIndicatorValue { id, value, requisition_id } + UpdateIndicatorValue { + id, + value, + requisition_id, + } } } @@ -80,10 +87,26 @@ fn map_error(error: UpdateIndicatorValueError) -> Result { use StandardGraphqlError::*; let formatted_error = format!("{:?}", error); let graphql_error = match error { + // Structured Errors UpdateIndicatorValueError::IndicatorValueDoesNotExist => { return Ok(UpdateErrorInterface::RecordNotFound(RecordNotFound {})) } + UpdateIndicatorValueError::NoRequisitionForIndicator => { + return Ok(UpdateErrorInterface::RequisitionDoesNotExist( + ForeignKeyError(ForeignKey::RequisitionId), + )) + } + UpdateIndicatorValueError::ValuePeriodNotRequisitionPeriod => BadUserInput(formatted_error), + UpdateIndicatorValueError::CannotEditRequisition => { + return Ok(UpdateErrorInterface::CannotEditRequisition( + CannotEditRequisition {}, + )) + } + // Standard graphql errors UpdateIndicatorValueError::DatabaseError(_) => InternalError(formatted_error), + UpdateIndicatorValueError::NotThisStoreValue => BadUserInput(formatted_error), + UpdateIndicatorValueError::NotThisStoreRequisition => BadUserInput(formatted_error), + UpdateIndicatorValueError::RequisitionHasNoPeriod => BadUserInput(formatted_error), }; Err(graphql_error.extend()) diff --git a/server/service/src/programs/indicator_value/update.rs b/server/service/src/programs/indicator_value/update.rs index a3fb686c90..2318aa0f55 100644 --- a/server/service/src/programs/indicator_value/update.rs +++ b/server/service/src/programs/indicator_value/update.rs @@ -1,6 +1,6 @@ use repository::{ indicator_value::{IndicatorValueFilter, IndicatorValueRepository}, - requisition_row, EqualFilter, IndicatorValueRow, IndicatorValueRowRepository, RepositoryError, + EqualFilter, IndicatorValueRow, IndicatorValueRowRepository, RepositoryError, RequisitionStatus, RequisitionType, StorageConnection, }; @@ -21,8 +21,8 @@ pub enum UpdateIndicatorValueError { DatabaseError(RepositoryError), IndicatorValueDoesNotExist, NoRequisitionForIndicator, - ValueNotOfUsersStore, - RequisitionOfDifferentStore, + NotThisStoreRequisition, + NotThisStoreValue, RequisitionHasNoPeriod, ValuePeriodNotRequisitionPeriod, CannotEditRequisition, @@ -66,11 +66,11 @@ fn validate( // todo rename to store_id as it is store_id if store_id.to_owned() != indicator_value_row.supplier_store_id { - return Err(OutError::ValueNotOfUsersStore); + return Err(OutError::NotThisStoreValue); } if requisition.requisition_row.store_id != store_id.to_owned() { - return Err(OutError::RequisitionOfDifferentStore); + return Err(OutError::NotThisStoreRequisition); } match requisition.period { From 3e8067f49d639d5b163d810ddfab92f84bfc5b32 Mon Sep 17 00:00:00 2001 From: Fergus Roache Date: Fri, 8 Nov 2024 15:03:24 +1300 Subject: [PATCH 06/25] Add basic tests --- server/repository/src/mock/indicator_value.rs | 33 +++++ server/repository/src/mock/mod.rs | 61 ++++++--- .../src/mock/test_requisition_repository.rs | 1 + .../src/mock/test_requisition_service.rs | 27 +++- .../src/programs/indicator_value/update.rs | 119 ++++++++++++++++-- 5 files changed, 215 insertions(+), 26 deletions(-) create mode 100644 server/repository/src/mock/indicator_value.rs diff --git a/server/repository/src/mock/indicator_value.rs b/server/repository/src/mock/indicator_value.rs new file mode 100644 index 0000000000..57b07cf9c5 --- /dev/null +++ b/server/repository/src/mock/indicator_value.rs @@ -0,0 +1,33 @@ +use crate::IndicatorValueRow; + +use super::{ + mock_indicator_column_a, mock_indicator_line_a, mock_period, mock_store_a, mock_store_b, +}; + +pub fn mock_indicator_value_a() -> IndicatorValueRow { + IndicatorValueRow { + id: String::from("id_a"), + customer_name_link_id: mock_store_b().name_link_id, + supplier_store_id: mock_store_a().id, + period_id: mock_period().id, + indicator_line_id: mock_indicator_line_a().id, + indicator_column_id: mock_indicator_column_a().id, + value: String::from("test_value"), + } +} + +pub fn mock_indicator_value_b() -> IndicatorValueRow { + IndicatorValueRow { + id: String::from("id_b"), + customer_name_link_id: mock_store_a().name_link_id, + supplier_store_id: mock_store_b().id, + period_id: mock_period().id, + indicator_line_id: mock_indicator_line_a().id, + indicator_column_id: mock_indicator_column_a().id, + value: String::from("test_value"), + } +} + +pub fn mock_indicator_values() -> Vec { + vec![mock_indicator_value_a(), mock_indicator_value_b()] +} diff --git a/server/repository/src/mock/mod.rs b/server/repository/src/mock/mod.rs index d945fb5e20..94073d9414 100644 --- a/server/repository/src/mock/mod.rs +++ b/server/repository/src/mock/mod.rs @@ -17,6 +17,7 @@ mod full_invoice; mod full_master_list; mod indicator_column; mod indicator_line; +mod indicator_value; mod invoice; mod invoice_line; mod item; @@ -80,6 +81,7 @@ pub use full_invoice::*; pub use full_master_list::*; pub use indicator_column::*; pub use indicator_line::*; +pub use indicator_value::*; pub use invoice::*; pub use invoice_line::*; pub use item::*; @@ -140,22 +142,23 @@ use crate::{ ContextRowRepository, CurrencyRow, DemographicRow, Document, DocumentRegistryRow, DocumentRegistryRowRepository, DocumentRepository, EncounterRow, EncounterRowRepository, FormSchema, FormSchemaRowRepository, IndicatorColumnRow, IndicatorColumnRowRepository, - IndicatorLineRow, IndicatorLineRowRepository, InventoryAdjustmentReasonRow, - InventoryAdjustmentReasonRowRepository, InvoiceLineRow, InvoiceLineRowRepository, InvoiceRow, - ItemLinkRowRepository, ItemRow, KeyValueStoreRepository, KeyValueStoreRow, LocationRow, - LocationRowRepository, MasterListNameJoinRepository, MasterListNameJoinRow, MasterListRow, - MasterListRowRepository, NameLinkRow, NameLinkRowRepository, NameTagJoinRepository, - NameTagJoinRow, NameTagRow, NameTagRowRepository, NumberRow, NumberRowRepository, PeriodRow, - PeriodRowRepository, PeriodScheduleRow, PeriodScheduleRowRepository, PluginDataRow, - PluginDataRowRepository, ProgramEnrolmentRow, ProgramEnrolmentRowRepository, - ProgramIndicatorRow, ProgramIndicatorRowRepository, ProgramRequisitionOrderTypeRow, - ProgramRequisitionOrderTypeRowRepository, ProgramRequisitionSettingsRow, - ProgramRequisitionSettingsRowRepository, ProgramRow, ProgramRowRepository, PropertyRow, - PropertyRowRepository, RequisitionLineRow, RequisitionLineRowRepository, RequisitionRow, - RequisitionRowRepository, ReturnReasonRow, ReturnReasonRowRepository, RnRFormLineRow, - RnRFormLineRowRepository, RnRFormRow, RnRFormRowRepository, SensorRow, SensorRowRepository, - StockLineRowRepository, StocktakeLineRowRepository, StocktakeRowRepository, SyncBufferRow, - SyncBufferRowRepository, SyncLogRow, SyncLogRowRepository, TemperatureBreachConfigRow, + IndicatorLineRow, IndicatorLineRowRepository, IndicatorValueRow, IndicatorValueRowRepository, + InventoryAdjustmentReasonRow, InventoryAdjustmentReasonRowRepository, InvoiceLineRow, + InvoiceLineRowRepository, InvoiceRow, ItemLinkRowRepository, ItemRow, KeyValueStoreRepository, + KeyValueStoreRow, LocationRow, LocationRowRepository, MasterListNameJoinRepository, + MasterListNameJoinRow, MasterListRow, MasterListRowRepository, NameLinkRow, + NameLinkRowRepository, NameTagJoinRepository, NameTagJoinRow, NameTagRow, NameTagRowRepository, + NumberRow, NumberRowRepository, PeriodRow, PeriodRowRepository, PeriodScheduleRow, + PeriodScheduleRowRepository, PluginDataRow, PluginDataRowRepository, ProgramEnrolmentRow, + ProgramEnrolmentRowRepository, ProgramIndicatorRow, ProgramIndicatorRowRepository, + ProgramRequisitionOrderTypeRow, ProgramRequisitionOrderTypeRowRepository, + ProgramRequisitionSettingsRow, ProgramRequisitionSettingsRowRepository, ProgramRow, + ProgramRowRepository, PropertyRow, PropertyRowRepository, RequisitionLineRow, + RequisitionLineRowRepository, RequisitionRow, RequisitionRowRepository, ReturnReasonRow, + ReturnReasonRowRepository, RnRFormLineRow, RnRFormLineRowRepository, RnRFormRow, + RnRFormRowRepository, SensorRow, SensorRowRepository, StockLineRowRepository, + StocktakeLineRowRepository, StocktakeRowRepository, SyncBufferRow, SyncBufferRowRepository, + SyncLogRow, SyncLogRowRepository, TemperatureBreachConfigRow, TemperatureBreachConfigRowRepository, TemperatureBreachRow, TemperatureBreachRowRepository, TemperatureLogRow, TemperatureLogRowRepository, UserAccountRow, UserAccountRowRepository, UserPermissionRow, UserPermissionRowRepository, UserStoreJoinRow, UserStoreJoinRowRepository, @@ -237,6 +240,7 @@ pub struct MockData { pub program_indicators: Vec, pub indicator_lines: Vec, pub indicator_columns: Vec, + pub indicator_values: Vec, } impl MockData { @@ -316,6 +320,7 @@ pub struct MockDataInserts { pub program_indicators: bool, pub indicator_lines: bool, pub indicator_columns: bool, + pub indicator_values: bool, } impl MockDataInserts { @@ -384,6 +389,7 @@ impl MockDataInserts { program_indicators: true, indicator_lines: true, indicator_columns: true, + indicator_values: true, } } @@ -701,6 +707,20 @@ impl MockDataInserts { self.indicator_columns = true; self } + + pub fn indicator_values(mut self) -> Self { + self.contexts = true; + self.programs = true; + self.program_indicators = true; + self.indicator_lines = true; + self.indicator_columns = true; + self.period_schedules = true; + self.periods = true; + self.names = true; + self.stores = true; + self.indicator_values = true; + self + } } #[derive(Default)] @@ -790,6 +810,7 @@ pub(crate) fn all_mock_data() -> MockDataCollection { program_indicators: mock_program_indicators(), indicator_lines: mock_indicator_lines(), indicator_columns: mock_indicator_columns(), + indicator_values: mock_indicator_values(), ..Default::default() }, ); @@ -1295,6 +1316,12 @@ pub fn insert_mock_data( repo.upsert_one(row).unwrap(); } } + if inserts.indicator_values { + let repo = IndicatorValueRowRepository::new(connection); + for row in &mock_data.indicator_values { + repo.upsert_one(row).unwrap(); + } + } } mock_data } @@ -1367,6 +1394,7 @@ impl MockData { mut program_indicators, mut indicator_lines, mut indicator_columns, + mut indicator_values, } = other; self.user_accounts.append(&mut user_accounts); @@ -1435,6 +1463,7 @@ impl MockData { self.program_indicators.append(&mut program_indicators); self.indicator_lines.append(&mut indicator_lines); self.indicator_columns.append(&mut indicator_columns); + self.indicator_values.append(&mut indicator_values); self } } diff --git a/server/repository/src/mock/test_requisition_repository.rs b/server/repository/src/mock/test_requisition_repository.rs index a365f7de28..6c54632d76 100644 --- a/server/repository/src/mock/test_requisition_repository.rs +++ b/server/repository/src/mock/test_requisition_repository.rs @@ -31,6 +31,7 @@ pub fn mock_request_draft_requisition() -> RequisitionRow { .unwrap(); r.max_months_of_stock = 1.0; r.min_months_of_stock = 0.9; + r.period_id = Some("period_a".to_string()); }) } diff --git a/server/repository/src/mock/test_requisition_service.rs b/server/repository/src/mock/test_requisition_service.rs index 7ea0ac0632..d1e5182e73 100644 --- a/server/repository/src/mock/test_requisition_service.rs +++ b/server/repository/src/mock/test_requisition_service.rs @@ -9,8 +9,8 @@ use crate::{ use super::{ common::{FullMockInvoice, FullMockInvoiceLine, FullMockMasterList, FullMockRequisition}, - mock_item_a, mock_item_b, mock_item_c, mock_item_d, mock_name_a, mock_program_a, - mock_stock_line_a, mock_store_a, MockData, + mock_item_a, mock_item_b, mock_item_c, mock_item_d, mock_name_a, mock_period, mock_program_a, + mock_stock_line_a, mock_store_a, mock_store_b, MockData, }; pub fn mock_test_requisition_service() -> MockData { @@ -35,6 +35,10 @@ pub fn mock_test_requisition_service() -> MockData { .requisitions .push(mock_finalised_response_requisition()); result.requisitions.push(mock_new_response_requisition()); + result + .requisitions + .push(mock_new_response_requisition_store_b()); + result.requisitions.push(mock_sent_request_requisition()); result .full_requisitions @@ -140,6 +144,7 @@ pub fn mock_finalised_response_requisition() -> RequisitionRow { .unwrap(); r.max_months_of_stock = 1.0; r.min_months_of_stock = 0.9; + r.period_id = Some(mock_period().id); }) } @@ -189,6 +194,24 @@ pub fn mock_new_response_requisition() -> RequisitionRow { }) } +pub fn mock_new_response_requisition_store_b() -> RequisitionRow { + inline_init(|r: &mut RequisitionRow| { + r.id = "mock_new_response_requisition_store_b".to_string(); + r.requisition_number = 3; + r.name_link_id = "name_a".to_string(); + r.store_id = mock_store_b().id; + r.r#type = RequisitionType::Response; + r.status = RequisitionStatus::New; + r.created_datetime = NaiveDate::from_ymd_opt(2021, 1, 1) + .unwrap() + .and_hms_opt(0, 0, 0) + .unwrap(); + r.max_months_of_stock = 1.0; + r.min_months_of_stock = 0.9; + r.period_id = Some(mock_period().id); + }) +} + pub fn mock_new_response_requisition_for_update_test_line() -> RequisitionLineRow { inline_init(|r: &mut RequisitionLineRow| { r.id = "mock_new_response_requisition_for_update_test_line".to_string(); diff --git a/server/service/src/programs/indicator_value/update.rs b/server/service/src/programs/indicator_value/update.rs index 2318aa0f55..9d79e4b971 100644 --- a/server/service/src/programs/indicator_value/update.rs +++ b/server/service/src/programs/indicator_value/update.rs @@ -4,10 +4,7 @@ use repository::{ RequisitionStatus, RequisitionType, StorageConnection, }; -use crate::{ - requisition::common::{check_approval_status, check_requisition_exists}, - service_provider::ServiceContext, -}; +use crate::{requisition::common::check_requisition_exists, service_provider::ServiceContext}; #[derive(Debug, PartialEq, Clone, Default)] pub struct UpdateIndicatorValue { @@ -65,11 +62,10 @@ fn validate( .ok_or(OutError::NoRequisitionForIndicator)?; // todo rename to store_id as it is store_id - if store_id.to_owned() != indicator_value_row.supplier_store_id { + if store_id.to_string() != indicator_value_row.supplier_store_id { return Err(OutError::NotThisStoreValue); } - - if requisition.requisition_row.store_id != store_id.to_owned() { + if requisition.requisition_row.store_id != store_id.to_string() { return Err(OutError::NotThisStoreRequisition); } @@ -84,7 +80,7 @@ fn validate( match requisition.requisition_row.r#type { RequisitionType::Response => { - if check_approval_status(&requisition.requisition_row) { + if requisition.requisition_row.status != RequisitionStatus::New { return Err(OutError::CannotEditRequisition); } } @@ -126,3 +122,110 @@ impl From for UpdateIndicatorValueError { UpdateIndicatorValueError::DatabaseError(error) } } + +#[cfg(test)] +mod test { + use repository::{ + mock::{ + mock_finalised_response_requisition, mock_indicator_value_a, mock_indicator_value_b, + mock_new_response_requisition, mock_new_response_requisition_store_b, + mock_request_draft_requisition, mock_store_a, mock_store_b, mock_user_account_b, + MockDataInserts, + }, + test_db::setup_all, + }; + use util::inline_init; + + use crate::{ + programs::indicator_value::{UpdateIndicatorValue, UpdateIndicatorValueError}, + service_provider::ServiceProvider, + }; + + #[actix_rt::test] + async fn update_indicator_value_errors() { + let (_, _, connection_manager, _) = + setup_all("update_indicator_value_errors", MockDataInserts::all()).await; + + let service_provider = ServiceProvider::new(connection_manager, "app_data"); + let mut context = service_provider + .context(mock_store_a().id, "".to_string()) + .unwrap(); + let service = service_provider.indicator_value_service; + + // IndicatorValueDoesNotExist + assert_eq!( + service.update_indicator_value( + &context, + inline_init(|r: &mut UpdateIndicatorValue| { + r.id = "invalid_id".to_string(); + r.requisition_id = mock_new_response_requisition().id; + r.value = String::from("new value"); + }), + ), + Err(UpdateIndicatorValueError::IndicatorValueDoesNotExist) + ); + + // CannotEditRequisition + assert_eq!( + service.update_indicator_value( + &context, + inline_init(|r: &mut UpdateIndicatorValue| { + r.id = mock_indicator_value_a().id; + r.requisition_id = mock_finalised_response_requisition().id; + r.value = String::from("new value"); + }), + ), + Err(UpdateIndicatorValueError::CannotEditRequisition) + ); + + // NotThisStoreRequisition + context.store_id = mock_store_b().id; + assert_eq!( + service.update_indicator_value( + &context, + inline_init(|r: &mut UpdateIndicatorValue| { + r.id = mock_indicator_value_b().id; + r.requisition_id = mock_new_response_requisition().id; + r.value = String::from("new value"); + }), + ), + Err(UpdateIndicatorValueError::NotThisStoreRequisition) + ); + + // NotThisStoreValue + assert_eq!( + service.update_indicator_value( + &context, + inline_init(|r: &mut UpdateIndicatorValue| { + r.id = mock_indicator_value_a().id; + r.requisition_id = mock_new_response_requisition_store_b().id; + r.value = String::from("new value"); + }), + ), + Err(UpdateIndicatorValueError::NotThisStoreValue) + ); + } + + #[actix_rt::test] + async fn update_indicator_value_success() { + let (_, _, connection_manager, _) = + setup_all("update_indicator_value_success", MockDataInserts::all()).await; + + let service_provider = ServiceProvider::new(connection_manager, "app_data"); + let context = service_provider + .context(mock_store_a().id, "".to_string()) + .unwrap(); + let service = service_provider.indicator_value_service; + + service + .update_indicator_value( + &context, + UpdateIndicatorValue { + id: mock_indicator_value_a().id, + value: "new_test_value".to_string(), + requisition_id: mock_request_draft_requisition().id, + }, + ) + .unwrap(); + } +} From e5b63976c8cf2b611e283afc124aeae3c0509ba6 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Wed, 13 Nov 2024 10:00:49 +1300 Subject: [PATCH 07/25] Rename supplier_store_id -> store_id --- server/repository/src/mock/indicator_value.rs | 4 ++-- .../src/programs/indicator_value/update.rs | 18 ++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/server/repository/src/mock/indicator_value.rs b/server/repository/src/mock/indicator_value.rs index 57b07cf9c5..f535e16bc5 100644 --- a/server/repository/src/mock/indicator_value.rs +++ b/server/repository/src/mock/indicator_value.rs @@ -8,7 +8,7 @@ pub fn mock_indicator_value_a() -> IndicatorValueRow { IndicatorValueRow { id: String::from("id_a"), customer_name_link_id: mock_store_b().name_link_id, - supplier_store_id: mock_store_a().id, + store_id: mock_store_a().id, period_id: mock_period().id, indicator_line_id: mock_indicator_line_a().id, indicator_column_id: mock_indicator_column_a().id, @@ -20,7 +20,7 @@ pub fn mock_indicator_value_b() -> IndicatorValueRow { IndicatorValueRow { id: String::from("id_b"), customer_name_link_id: mock_store_a().name_link_id, - supplier_store_id: mock_store_b().id, + store_id: mock_store_b().id, period_id: mock_period().id, indicator_line_id: mock_indicator_line_a().id, indicator_column_id: mock_indicator_column_a().id, diff --git a/server/service/src/programs/indicator_value/update.rs b/server/service/src/programs/indicator_value/update.rs index 9d79e4b971..d182df8f88 100644 --- a/server/service/src/programs/indicator_value/update.rs +++ b/server/service/src/programs/indicator_value/update.rs @@ -35,7 +35,7 @@ pub fn update_indicator_value( let indicator_value = ctx .connection .transaction_sync(|connection| { - let indicator_value_row = validate(connection, &input, &ctx.store_id)?; + let indicator_value_row = validate(connection, &input, ctx.store_id.clone())?; let updated_row = generate(indicator_value_row, input); @@ -53,7 +53,7 @@ pub fn update_indicator_value( fn validate( connection: &StorageConnection, input: &UpdateIndicatorValue, - store_id: &String, + store_id: String, ) -> Result { let indicator_value_row = check_indicator_value_exists(connection, &input.id)? .ok_or(OutError::IndicatorValueDoesNotExist)?; @@ -61,11 +61,10 @@ fn validate( let requisition = check_requisition_exists(connection, &input.requisition_id)? .ok_or(OutError::NoRequisitionForIndicator)?; - // todo rename to store_id as it is store_id - if store_id.to_string() != indicator_value_row.supplier_store_id { + if store_id != indicator_value_row.store_id { return Err(OutError::NotThisStoreValue); } - if requisition.requisition_row.store_id != store_id.to_string() { + if requisition.requisition_row.store_id != store_id { return Err(OutError::NotThisStoreRequisition); } @@ -98,8 +97,8 @@ fn check_indicator_value_exists( connection: &StorageConnection, id: &str, ) -> Result, RepositoryError> { - Ok(IndicatorValueRepository::new(connection) - .query_one(IndicatorValueFilter::new().id(EqualFilter::equal_to(id)))?) + IndicatorValueRepository::new(connection) + .query_one(IndicatorValueFilter::new().id(EqualFilter::equal_to(id))) } fn generate( @@ -109,7 +108,7 @@ fn generate( IndicatorValueRow { id: indicator_value_row.id, customer_name_link_id: indicator_value_row.customer_name_link_id, - supplier_store_id: indicator_value_row.supplier_store_id, + store_id: indicator_value_row.store_id, period_id: indicator_value_row.period_id, indicator_line_id: indicator_value_row.indicator_line_id, indicator_column_id: indicator_value_row.indicator_column_id, @@ -129,8 +128,7 @@ mod test { mock::{ mock_finalised_response_requisition, mock_indicator_value_a, mock_indicator_value_b, mock_new_response_requisition, mock_new_response_requisition_store_b, - mock_request_draft_requisition, mock_store_a, mock_store_b, mock_user_account_b, - MockDataInserts, + mock_request_draft_requisition, mock_store_a, mock_store_b, MockDataInserts, }, test_db::setup_all, }; From 53af261cdef40b2f4a4e6eb3add285104e079e73 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Wed, 13 Nov 2024 10:27:24 +1300 Subject: [PATCH 08/25] Remove unnecessary code --- server/service/src/programs/indicator_value/update.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/server/service/src/programs/indicator_value/update.rs b/server/service/src/programs/indicator_value/update.rs index d182df8f88..696ff5b97e 100644 --- a/server/service/src/programs/indicator_value/update.rs +++ b/server/service/src/programs/indicator_value/update.rs @@ -31,7 +31,6 @@ pub fn update_indicator_value( ctx: &ServiceContext, input: UpdateIndicatorValue, ) -> Result { - // let indicator_value = ctx.connection.transaction let indicator_value = ctx .connection .transaction_sync(|connection| { From a5a76b1ab54078fec72fd107a1b5b3554eb0ce37 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Wed, 13 Nov 2024 11:40:53 +1300 Subject: [PATCH 09/25] Fix tests --- .../src/mock/test_requisition_repository.rs | 1 - .../src/mock/test_requisition_service.rs | 27 ++----------------- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/server/repository/src/mock/test_requisition_repository.rs b/server/repository/src/mock/test_requisition_repository.rs index 6c54632d76..a365f7de28 100644 --- a/server/repository/src/mock/test_requisition_repository.rs +++ b/server/repository/src/mock/test_requisition_repository.rs @@ -31,7 +31,6 @@ pub fn mock_request_draft_requisition() -> RequisitionRow { .unwrap(); r.max_months_of_stock = 1.0; r.min_months_of_stock = 0.9; - r.period_id = Some("period_a".to_string()); }) } diff --git a/server/repository/src/mock/test_requisition_service.rs b/server/repository/src/mock/test_requisition_service.rs index d1e5182e73..7ea0ac0632 100644 --- a/server/repository/src/mock/test_requisition_service.rs +++ b/server/repository/src/mock/test_requisition_service.rs @@ -9,8 +9,8 @@ use crate::{ use super::{ common::{FullMockInvoice, FullMockInvoiceLine, FullMockMasterList, FullMockRequisition}, - mock_item_a, mock_item_b, mock_item_c, mock_item_d, mock_name_a, mock_period, mock_program_a, - mock_stock_line_a, mock_store_a, mock_store_b, MockData, + mock_item_a, mock_item_b, mock_item_c, mock_item_d, mock_name_a, mock_program_a, + mock_stock_line_a, mock_store_a, MockData, }; pub fn mock_test_requisition_service() -> MockData { @@ -35,10 +35,6 @@ pub fn mock_test_requisition_service() -> MockData { .requisitions .push(mock_finalised_response_requisition()); result.requisitions.push(mock_new_response_requisition()); - result - .requisitions - .push(mock_new_response_requisition_store_b()); - result.requisitions.push(mock_sent_request_requisition()); result .full_requisitions @@ -144,7 +140,6 @@ pub fn mock_finalised_response_requisition() -> RequisitionRow { .unwrap(); r.max_months_of_stock = 1.0; r.min_months_of_stock = 0.9; - r.period_id = Some(mock_period().id); }) } @@ -194,24 +189,6 @@ pub fn mock_new_response_requisition() -> RequisitionRow { }) } -pub fn mock_new_response_requisition_store_b() -> RequisitionRow { - inline_init(|r: &mut RequisitionRow| { - r.id = "mock_new_response_requisition_store_b".to_string(); - r.requisition_number = 3; - r.name_link_id = "name_a".to_string(); - r.store_id = mock_store_b().id; - r.r#type = RequisitionType::Response; - r.status = RequisitionStatus::New; - r.created_datetime = NaiveDate::from_ymd_opt(2021, 1, 1) - .unwrap() - .and_hms_opt(0, 0, 0) - .unwrap(); - r.max_months_of_stock = 1.0; - r.min_months_of_stock = 0.9; - r.period_id = Some(mock_period().id); - }) -} - pub fn mock_new_response_requisition_for_update_test_line() -> RequisitionLineRow { inline_init(|r: &mut RequisitionLineRow| { r.id = "mock_new_response_requisition_for_update_test_line".to_string(); From 1cfb54a4d1c5ad9c7dbeab93bbe6d7db54477483 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Wed, 13 Nov 2024 13:15:30 +1300 Subject: [PATCH 10/25] Generate indicator values for program requisition inserts --- .../response_requisition/insert_program.rs | 92 +++++++++++++++++-- 1 file changed, 82 insertions(+), 10 deletions(-) diff --git a/server/service/src/requisition/response_requisition/insert_program.rs b/server/service/src/requisition/response_requisition/insert_program.rs index 345fe72bff..7a250b24ab 100644 --- a/server/service/src/requisition/response_requisition/insert_program.rs +++ b/server/service/src/requisition/response_requisition/insert_program.rs @@ -1,6 +1,9 @@ +use std::collections::HashMap; + use crate::{ activity_log::activity_log_entry, number::next_number, + programs::program_indicator::query::{program_indicators, ProgramIndicator}, requisition::{ common::check_requisition_row_exists, program_settings::get_customer_program_requisition_settings, query::get_requisition, @@ -11,10 +14,12 @@ use crate::{ use chrono::Utc; use repository::{ requisition_row::{RequisitionRow, RequisitionStatus, RequisitionType}, - ActivityLogType, EqualFilter, MasterListLineFilter, MasterListLineRepository, NumberRowType, - ProgramRequisitionOrderTypeRow, ProgramRow, RepositoryError, Requisition, RequisitionLineRow, - RequisitionLineRowRepository, RequisitionRowRepository, + ActivityLogType, EqualFilter, IndicatorValueRow, IndicatorValueRowRepository, + MasterListLineFilter, MasterListLineRepository, NumberRowType, Pagination, + ProgramIndicatorFilter, ProgramRequisitionOrderTypeRow, ProgramRow, RepositoryError, + Requisition, RequisitionLineRow, RequisitionLineRowRepository, RequisitionRowRepository, }; +use util::uuid::uuid; #[derive(Debug, PartialEq)] pub enum InsertProgramResponseRequisitionError { @@ -47,7 +52,11 @@ pub fn insert_program_response_requisition( .connection .transaction_sync(|connection| { let (program, order_type) = validate(ctx, &input)?; - let (new_requisition, requisition_lines) = generate(ctx, program, order_type, input)?; + let GenerateResult { + requisition: new_requisition, + requisition_lines, + indicator_values, + } = generate(ctx, program, order_type, input)?; RequisitionRowRepository::new(connection).upsert_one(&new_requisition)?; let requisition_line_repo = RequisitionLineRowRepository::new(connection); @@ -55,6 +64,11 @@ pub fn insert_program_response_requisition( requisition_line_repo.upsert_one(&requisition_line)?; } + let indicator_value_repo = IndicatorValueRowRepository::new(connection); + for indicator_value in indicator_values { + indicator_value_repo.upsert_one(&indicator_value)?; + } + activity_log_entry( ctx, ActivityLogType::RequisitionCreated, @@ -132,6 +146,12 @@ fn validate( )) } +pub struct GenerateResult { + pub(crate) requisition: RequisitionRow, + pub(crate) requisition_lines: Vec, + pub(crate) indicator_values: Vec, +} + fn generate( ctx: &ServiceContext, program: ProgramRow, @@ -142,7 +162,7 @@ fn generate( program_order_type_id: _, period_id, }: InsertProgramResponseRequisition, -) -> Result<(RequisitionRow, Vec), RepositoryError> { +) -> Result { let connection = &ctx.connection; let requisition = RequisitionRow { @@ -153,15 +173,15 @@ fn generate( &NumberRowType::ResponseRequisition, &ctx.store_id, )?, - name_link_id: other_party_id, + name_link_id: other_party_id.clone(), store_id: ctx.store_id.clone(), r#type: RequisitionType::Response, status: RequisitionStatus::New, created_datetime: Utc::now().naive_utc(), max_months_of_stock: order_type.max_mos, min_months_of_stock: order_type.threshold_mos, - program_id: Some(program.id), - period_id: Some(period_id), + program_id: Some(program.id.clone()), + period_id: Some(period_id.clone()), order_type: Some(order_type.name), // Default colour: None, @@ -184,10 +204,62 @@ fn generate( .map(|line| line.item_id) .collect(); - let requisition_line_rows = + let requisition_lines = generate_requisition_lines(ctx, &ctx.store_id, &requisition, program_item_ids)?; - Ok((requisition, requisition_line_rows)) + let program_indicators = program_indicators( + connection, + Pagination::all(), + None, + Some(ProgramIndicatorFilter::new().program_id(EqualFilter::equal_to(&program.id))), + )?; + + let indicator_values = generate_program_indicator_values( + &ctx.store_id, + &period_id, + &other_party_id, + program_indicators, + ); + + Ok(GenerateResult { + requisition, + requisition_lines, + indicator_values, + }) +} + +fn generate_program_indicator_values( + store_id: &str, + period_id: &str, + other_party_id: &str, + program_indicator: HashMap, +) -> Vec { + let mut indicator_values = vec![]; + + for (_, program_indicator) in program_indicator { + for line in program_indicator.lines { + for column in line.columns { + let value = match column.value_type { + Some(_) => column.default_value.clone(), + None => line.line.default_value.clone(), + }; + + let indicator_value = IndicatorValueRow { + id: uuid(), + customer_name_link_id: other_party_id.to_string(), + store_id: store_id.to_string(), + period_id: period_id.to_string(), + indicator_line_id: line.line.id.to_string(), + indicator_column_id: column.id.to_string(), + value, + }; + + indicator_values.push(indicator_value); + } + } + } + + indicator_values } impl From for InsertProgramResponseRequisitionError { From df2e4c2bd243e72131f145237437ec5b34c228b2 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Wed, 13 Nov 2024 13:21:33 +1300 Subject: [PATCH 11/25] Fix imports --- .../program_indicator/update_value.rs | 3 +- .../src/types/program/program_indicator.rs | 2 +- .../src/programs/indicator_value/update.rs | 228 ------------------ server/service/src/programs/mod.rs | 2 - .../indicator_value/mod.rs | 0 .../src/requisition/indicator_value/update.rs | 228 ++++++++++++++++++ server/service/src/requisition/mod.rs | 2 + .../program_indicator/mod.rs | 0 .../program_indicator/query.rs | 0 .../response_requisition/insert_program.rs | 5 +- server/service/src/service_provider.rs | 8 +- 11 files changed, 240 insertions(+), 238 deletions(-) delete mode 100644 server/service/src/programs/indicator_value/update.rs rename server/service/src/{programs => requisition}/indicator_value/mod.rs (100%) create mode 100644 server/service/src/requisition/indicator_value/update.rs rename server/service/src/{programs => requisition}/program_indicator/mod.rs (100%) rename server/service/src/{programs => requisition}/program_indicator/query.rs (100%) diff --git a/server/graphql/programs/src/mutations/program_indicator/update_value.rs b/server/graphql/programs/src/mutations/program_indicator/update_value.rs index 10e4718757..d98cc0cc5b 100644 --- a/server/graphql/programs/src/mutations/program_indicator/update_value.rs +++ b/server/graphql/programs/src/mutations/program_indicator/update_value.rs @@ -6,8 +6,7 @@ use graphql_core::{ }; use graphql_types::types::program_indicator::IndicatorValueNode; use service::auth::{Resource, ResourceAccessRequest}; -use service::programs::indicator_value::UpdateIndicatorValue; -use service::programs::indicator_value::UpdateIndicatorValueError; +use service::requisition::indicator_value::{UpdateIndicatorValue, UpdateIndicatorValueError}; #[derive(InputObject)] pub struct UpdateIndicatorValueInput { diff --git a/server/graphql/types/src/types/program/program_indicator.rs b/server/graphql/types/src/types/program/program_indicator.rs index bb92825a3e..d04ddec690 100644 --- a/server/graphql/types/src/types/program/program_indicator.rs +++ b/server/graphql/types/src/types/program/program_indicator.rs @@ -13,7 +13,7 @@ use repository::{ EqualFilter, IndicatorColumnRow, IndicatorLineRow, IndicatorValueRow, IndicatorValueType, ProgramIndicatorFilter, ProgramIndicatorSort, ProgramIndicatorSortField, }; -use service::programs::program_indicator::query::{IndicatorLine, ProgramIndicator}; +use service::requisition::program_indicator::query::{IndicatorLine, ProgramIndicator}; use super::program_node::ProgramNode; diff --git a/server/service/src/programs/indicator_value/update.rs b/server/service/src/programs/indicator_value/update.rs deleted file mode 100644 index 696ff5b97e..0000000000 --- a/server/service/src/programs/indicator_value/update.rs +++ /dev/null @@ -1,228 +0,0 @@ -use repository::{ - indicator_value::{IndicatorValueFilter, IndicatorValueRepository}, - EqualFilter, IndicatorValueRow, IndicatorValueRowRepository, RepositoryError, - RequisitionStatus, RequisitionType, StorageConnection, -}; - -use crate::{requisition::common::check_requisition_exists, service_provider::ServiceContext}; - -#[derive(Debug, PartialEq, Clone, Default)] -pub struct UpdateIndicatorValue { - pub id: String, - pub value: String, - pub requisition_id: String, -} - -#[derive(Debug, PartialEq)] -pub enum UpdateIndicatorValueError { - DatabaseError(RepositoryError), - IndicatorValueDoesNotExist, - NoRequisitionForIndicator, - NotThisStoreRequisition, - NotThisStoreValue, - RequisitionHasNoPeriod, - ValuePeriodNotRequisitionPeriod, - CannotEditRequisition, -} - -type OutError = UpdateIndicatorValueError; - -pub fn update_indicator_value( - ctx: &ServiceContext, - input: UpdateIndicatorValue, -) -> Result { - let indicator_value = ctx - .connection - .transaction_sync(|connection| { - let indicator_value_row = validate(connection, &input, ctx.store_id.clone())?; - - let updated_row = generate(indicator_value_row, input); - - IndicatorValueRowRepository::new(connection).upsert_one(&updated_row)?; - - IndicatorValueRepository::new(connection) - .query_one(IndicatorValueFilter::new().id(EqualFilter::equal_to(&updated_row.id))) - .map_err(OutError::DatabaseError)? - .ok_or(OutError::IndicatorValueDoesNotExist) - }) - .map_err(|error| error.to_inner_error())?; - Ok(indicator_value) -} - -fn validate( - connection: &StorageConnection, - input: &UpdateIndicatorValue, - store_id: String, -) -> Result { - let indicator_value_row = check_indicator_value_exists(connection, &input.id)? - .ok_or(OutError::IndicatorValueDoesNotExist)?; - - let requisition = check_requisition_exists(connection, &input.requisition_id)? - .ok_or(OutError::NoRequisitionForIndicator)?; - - if store_id != indicator_value_row.store_id { - return Err(OutError::NotThisStoreValue); - } - if requisition.requisition_row.store_id != store_id { - return Err(OutError::NotThisStoreRequisition); - } - - match requisition.period { - Some(period) => { - if period.id != indicator_value_row.period_id { - return Err(OutError::ValuePeriodNotRequisitionPeriod); - } - } - None => return Err(OutError::RequisitionHasNoPeriod), - } - - match requisition.requisition_row.r#type { - RequisitionType::Response => { - if requisition.requisition_row.status != RequisitionStatus::New { - return Err(OutError::CannotEditRequisition); - } - } - RequisitionType::Request => { - if requisition.requisition_row.status != RequisitionStatus::Draft { - return Err(OutError::CannotEditRequisition); - } - } - } - - Ok(indicator_value_row) -} - -fn check_indicator_value_exists( - connection: &StorageConnection, - id: &str, -) -> Result, RepositoryError> { - IndicatorValueRepository::new(connection) - .query_one(IndicatorValueFilter::new().id(EqualFilter::equal_to(id))) -} - -fn generate( - indicator_value_row: IndicatorValueRow, - input: UpdateIndicatorValue, -) -> IndicatorValueRow { - IndicatorValueRow { - id: indicator_value_row.id, - customer_name_link_id: indicator_value_row.customer_name_link_id, - store_id: indicator_value_row.store_id, - period_id: indicator_value_row.period_id, - indicator_line_id: indicator_value_row.indicator_line_id, - indicator_column_id: indicator_value_row.indicator_column_id, - value: input.value, - } -} - -impl From for UpdateIndicatorValueError { - fn from(error: RepositoryError) -> Self { - UpdateIndicatorValueError::DatabaseError(error) - } -} - -#[cfg(test)] -mod test { - use repository::{ - mock::{ - mock_finalised_response_requisition, mock_indicator_value_a, mock_indicator_value_b, - mock_new_response_requisition, mock_new_response_requisition_store_b, - mock_request_draft_requisition, mock_store_a, mock_store_b, MockDataInserts, - }, - test_db::setup_all, - }; - use util::inline_init; - - use crate::{ - programs::indicator_value::{UpdateIndicatorValue, UpdateIndicatorValueError}, - service_provider::ServiceProvider, - }; - - #[actix_rt::test] - async fn update_indicator_value_errors() { - let (_, _, connection_manager, _) = - setup_all("update_indicator_value_errors", MockDataInserts::all()).await; - - let service_provider = ServiceProvider::new(connection_manager, "app_data"); - let mut context = service_provider - .context(mock_store_a().id, "".to_string()) - .unwrap(); - let service = service_provider.indicator_value_service; - - // IndicatorValueDoesNotExist - assert_eq!( - service.update_indicator_value( - &context, - inline_init(|r: &mut UpdateIndicatorValue| { - r.id = "invalid_id".to_string(); - r.requisition_id = mock_new_response_requisition().id; - r.value = String::from("new value"); - }), - ), - Err(UpdateIndicatorValueError::IndicatorValueDoesNotExist) - ); - - // CannotEditRequisition - assert_eq!( - service.update_indicator_value( - &context, - inline_init(|r: &mut UpdateIndicatorValue| { - r.id = mock_indicator_value_a().id; - r.requisition_id = mock_finalised_response_requisition().id; - r.value = String::from("new value"); - }), - ), - Err(UpdateIndicatorValueError::CannotEditRequisition) - ); - - // NotThisStoreRequisition - context.store_id = mock_store_b().id; - assert_eq!( - service.update_indicator_value( - &context, - inline_init(|r: &mut UpdateIndicatorValue| { - r.id = mock_indicator_value_b().id; - r.requisition_id = mock_new_response_requisition().id; - r.value = String::from("new value"); - }), - ), - Err(UpdateIndicatorValueError::NotThisStoreRequisition) - ); - - // NotThisStoreValue - assert_eq!( - service.update_indicator_value( - &context, - inline_init(|r: &mut UpdateIndicatorValue| { - r.id = mock_indicator_value_a().id; - r.requisition_id = mock_new_response_requisition_store_b().id; - r.value = String::from("new value"); - }), - ), - Err(UpdateIndicatorValueError::NotThisStoreValue) - ); - } - - #[actix_rt::test] - async fn update_indicator_value_success() { - let (_, _, connection_manager, _) = - setup_all("update_indicator_value_success", MockDataInserts::all()).await; - - let service_provider = ServiceProvider::new(connection_manager, "app_data"); - let context = service_provider - .context(mock_store_a().id, "".to_string()) - .unwrap(); - let service = service_provider.indicator_value_service; - - service - .update_indicator_value( - &context, - UpdateIndicatorValue { - id: mock_indicator_value_a().id, - value: "new_test_value".to_string(), - requisition_id: mock_request_draft_requisition().id, - }, - ) - .unwrap(); - } -} diff --git a/server/service/src/programs/mod.rs b/server/service/src/programs/mod.rs index dbf86156fb..f3114eb621 100644 --- a/server/service/src/programs/mod.rs +++ b/server/service/src/programs/mod.rs @@ -1,8 +1,6 @@ pub mod contact_trace; pub mod encounter; -pub mod indicator_value; pub mod patient; pub mod program_enrolment; pub mod program_event; -pub mod program_indicator; pub mod update_program_document; diff --git a/server/service/src/programs/indicator_value/mod.rs b/server/service/src/requisition/indicator_value/mod.rs similarity index 100% rename from server/service/src/programs/indicator_value/mod.rs rename to server/service/src/requisition/indicator_value/mod.rs diff --git a/server/service/src/requisition/indicator_value/update.rs b/server/service/src/requisition/indicator_value/update.rs new file mode 100644 index 0000000000..d70945e4e2 --- /dev/null +++ b/server/service/src/requisition/indicator_value/update.rs @@ -0,0 +1,228 @@ +use repository::{ + indicator_value::{IndicatorValueFilter, IndicatorValueRepository}, + EqualFilter, IndicatorValueRow, IndicatorValueRowRepository, RepositoryError, + RequisitionStatus, RequisitionType, StorageConnection, +}; + +use crate::{requisition::common::check_requisition_exists, service_provider::ServiceContext}; + +#[derive(Debug, PartialEq, Clone, Default)] +pub struct UpdateIndicatorValue { + pub id: String, + pub value: String, + pub requisition_id: String, +} + +#[derive(Debug, PartialEq)] +pub enum UpdateIndicatorValueError { + DatabaseError(RepositoryError), + IndicatorValueDoesNotExist, + NoRequisitionForIndicator, + NotThisStoreRequisition, + NotThisStoreValue, + RequisitionHasNoPeriod, + ValuePeriodNotRequisitionPeriod, + CannotEditRequisition, +} + +type OutError = UpdateIndicatorValueError; + +pub fn update_indicator_value( + ctx: &ServiceContext, + input: UpdateIndicatorValue, +) -> Result { + let indicator_value = ctx + .connection + .transaction_sync(|connection| { + let indicator_value_row = validate(connection, &input, ctx.store_id.clone())?; + + let updated_row = generate(indicator_value_row, input); + + IndicatorValueRowRepository::new(connection).upsert_one(&updated_row)?; + + IndicatorValueRepository::new(connection) + .query_one(IndicatorValueFilter::new().id(EqualFilter::equal_to(&updated_row.id))) + .map_err(OutError::DatabaseError)? + .ok_or(OutError::IndicatorValueDoesNotExist) + }) + .map_err(|error| error.to_inner_error())?; + Ok(indicator_value) +} + +fn validate( + connection: &StorageConnection, + input: &UpdateIndicatorValue, + store_id: String, +) -> Result { + let indicator_value_row = check_indicator_value_exists(connection, &input.id)? + .ok_or(OutError::IndicatorValueDoesNotExist)?; + + let requisition = check_requisition_exists(connection, &input.requisition_id)? + .ok_or(OutError::NoRequisitionForIndicator)?; + + if store_id != indicator_value_row.store_id { + return Err(OutError::NotThisStoreValue); + } + if requisition.requisition_row.store_id != store_id { + return Err(OutError::NotThisStoreRequisition); + } + + match requisition.period { + Some(period) => { + if period.id != indicator_value_row.period_id { + return Err(OutError::ValuePeriodNotRequisitionPeriod); + } + } + None => return Err(OutError::RequisitionHasNoPeriod), + } + + match requisition.requisition_row.r#type { + RequisitionType::Response => { + if requisition.requisition_row.status != RequisitionStatus::New { + return Err(OutError::CannotEditRequisition); + } + } + RequisitionType::Request => { + if requisition.requisition_row.status != RequisitionStatus::Draft { + return Err(OutError::CannotEditRequisition); + } + } + } + + Ok(indicator_value_row) +} + +fn check_indicator_value_exists( + connection: &StorageConnection, + id: &str, +) -> Result, RepositoryError> { + IndicatorValueRepository::new(connection) + .query_one(IndicatorValueFilter::new().id(EqualFilter::equal_to(id))) +} + +fn generate( + indicator_value_row: IndicatorValueRow, + input: UpdateIndicatorValue, +) -> IndicatorValueRow { + IndicatorValueRow { + id: indicator_value_row.id, + customer_name_link_id: indicator_value_row.customer_name_link_id, + store_id: indicator_value_row.store_id, + period_id: indicator_value_row.period_id, + indicator_line_id: indicator_value_row.indicator_line_id, + indicator_column_id: indicator_value_row.indicator_column_id, + value: input.value, + } +} + +impl From for UpdateIndicatorValueError { + fn from(error: RepositoryError) -> Self { + UpdateIndicatorValueError::DatabaseError(error) + } +} + +// #[cfg(test)] +// mod test { +// use repository::{ +// mock::{ +// mock_finalised_response_requisition, mock_indicator_value_a, mock_indicator_value_b, +// mock_new_response_requisition, mock_new_response_requisition_store_b, +// mock_request_draft_requisition, mock_store_a, mock_store_b, MockDataInserts, +// }, +// test_db::setup_all, +// }; +// use util::inline_init; + +// use crate::{ +// programs::indicator_value::{UpdateIndicatorValue, UpdateIndicatorValueError}, +// service_provider::ServiceProvider, +// }; + +// #[actix_rt::test] +// async fn update_indicator_value_errors() { +// let (_, _, connection_manager, _) = +// setup_all("update_indicator_value_errors", MockDataInserts::all()).await; + +// let service_provider = ServiceProvider::new(connection_manager, "app_data"); +// let mut context = service_provider +// .context(mock_store_a().id, "".to_string()) +// .unwrap(); +// let service = service_provider.indicator_value_service; + +// // IndicatorValueDoesNotExist +// assert_eq!( +// service.update_indicator_value( +// &context, +// inline_init(|r: &mut UpdateIndicatorValue| { +// r.id = "invalid_id".to_string(); +// r.requisition_id = mock_new_response_requisition().id; +// r.value = String::from("new value"); +// }), +// ), +// Err(UpdateIndicatorValueError::IndicatorValueDoesNotExist) +// ); + +// // CannotEditRequisition +// assert_eq!( +// service.update_indicator_value( +// &context, +// inline_init(|r: &mut UpdateIndicatorValue| { +// r.id = mock_indicator_value_a().id; +// r.requisition_id = mock_finalised_response_requisition().id; +// r.value = String::from("new value"); +// }), +// ), +// Err(UpdateIndicatorValueError::CannotEditRequisition) +// ); + +// // NotThisStoreRequisition +// context.store_id = mock_store_b().id; +// assert_eq!( +// service.update_indicator_value( +// &context, +// inline_init(|r: &mut UpdateIndicatorValue| { +// r.id = mock_indicator_value_b().id; +// r.requisition_id = mock_new_response_requisition().id; +// r.value = String::from("new value"); +// }), +// ), +// Err(UpdateIndicatorValueError::NotThisStoreRequisition) +// ); + +// // NotThisStoreValue +// assert_eq!( +// service.update_indicator_value( +// &context, +// inline_init(|r: &mut UpdateIndicatorValue| { +// r.id = mock_indicator_value_a().id; +// r.requisition_id = mock_new_response_requisition_store_b().id; +// r.value = String::from("new value"); +// }), +// ), +// Err(UpdateIndicatorValueError::NotThisStoreValue) +// ); +// } + +// #[actix_rt::test] +// async fn update_indicator_value_success() { +// let (_, _, connection_manager, _) = +// setup_all("update_indicator_value_success", MockDataInserts::all()).await; + +// let service_provider = ServiceProvider::new(connection_manager, "app_data"); +// let context = service_provider +// .context(mock_store_a().id, "".to_string()) +// .unwrap(); +// let service = service_provider.indicator_value_service; + +// service +// .update_indicator_value( +// &context, +// UpdateIndicatorValue { +// id: mock_indicator_value_a().id, +// value: "new_test_value".to_string(), +// requisition_id: mock_request_draft_requisition().id, +// }, +// ) +// .unwrap(); +// } +// } diff --git a/server/service/src/requisition/mod.rs b/server/service/src/requisition/mod.rs index 15273fb2d3..6cf8be44ba 100644 --- a/server/service/src/requisition/mod.rs +++ b/server/service/src/requisition/mod.rs @@ -36,6 +36,8 @@ use response_requisition::{ }; pub mod common; +pub mod indicator_value; +pub mod program_indicator; pub mod program_settings; pub mod query; pub mod request_requisition; diff --git a/server/service/src/programs/program_indicator/mod.rs b/server/service/src/requisition/program_indicator/mod.rs similarity index 100% rename from server/service/src/programs/program_indicator/mod.rs rename to server/service/src/requisition/program_indicator/mod.rs diff --git a/server/service/src/programs/program_indicator/query.rs b/server/service/src/requisition/program_indicator/query.rs similarity index 100% rename from server/service/src/programs/program_indicator/query.rs rename to server/service/src/requisition/program_indicator/query.rs diff --git a/server/service/src/requisition/response_requisition/insert_program.rs b/server/service/src/requisition/response_requisition/insert_program.rs index 7a250b24ab..4f12223826 100644 --- a/server/service/src/requisition/response_requisition/insert_program.rs +++ b/server/service/src/requisition/response_requisition/insert_program.rs @@ -3,10 +3,11 @@ use std::collections::HashMap; use crate::{ activity_log::activity_log_entry, number::next_number, - programs::program_indicator::query::{program_indicators, ProgramIndicator}, requisition::{ common::check_requisition_row_exists, - program_settings::get_customer_program_requisition_settings, query::get_requisition, + program_indicator::query::{program_indicators, ProgramIndicator}, + program_settings::get_customer_program_requisition_settings, + query::get_requisition, request_requisition::generate_requisition_lines, }, service_provider::ServiceContext, diff --git a/server/service/src/service_provider.rs b/server/service/src/service_provider.rs index d8491fc3ee..560eb7bfd0 100644 --- a/server/service/src/service_provider.rs +++ b/server/service/src/service_provider.rs @@ -37,15 +37,17 @@ use crate::{ programs::{ contact_trace::{ContactTraceService, ContactTraceServiceTrait}, encounter::{EncounterService, EncounterServiceTrait}, - indicator_value::{IndicatorValueService, IndicatorValueServiceTrait}, patient::{PatientService, PatientServiceTrait}, program_enrolment::{ProgramEnrolmentService, ProgramEnrolmentServiceTrait}, program_event::{ProgramEventService, ProgramEventServiceTrait}, - program_indicator::{ProgramIndicatorService, ProgramIndicatorServiceTrait}, }, repack::{RepackService, RepackServiceTrait}, report::report_service::{ReportService, ReportServiceTrait}, - requisition::{RequisitionService, RequisitionServiceTrait}, + requisition::{ + indicator_value::{IndicatorValueService, IndicatorValueServiceTrait}, + program_indicator::{ProgramIndicatorService, ProgramIndicatorServiceTrait}, + RequisitionService, RequisitionServiceTrait, + }, requisition_line::{RequisitionLineService, RequisitionLineServiceTrait}, rnr_form::{RnRFormService, RnRFormServiceTrait}, sensor::{SensorService, SensorServiceTrait}, From 100ae9bc4ca75cd4bbc301ec6eb2d3585903f04f Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Wed, 13 Nov 2024 18:10:18 +1300 Subject: [PATCH 12/25] Only generate indicator lines if the customer has a store --- .../response_requisition/insert_program.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/server/service/src/requisition/response_requisition/insert_program.rs b/server/service/src/requisition/response_requisition/insert_program.rs index 4f12223826..8d7c305e87 100644 --- a/server/service/src/requisition/response_requisition/insert_program.rs +++ b/server/service/src/requisition/response_requisition/insert_program.rs @@ -19,6 +19,7 @@ use repository::{ MasterListLineFilter, MasterListLineRepository, NumberRowType, Pagination, ProgramIndicatorFilter, ProgramRequisitionOrderTypeRow, ProgramRow, RepositoryError, Requisition, RequisitionLineRow, RequisitionLineRowRepository, RequisitionRowRepository, + StoreFilter, StoreRepository, }; use util::uuid::uuid; @@ -65,9 +66,11 @@ pub fn insert_program_response_requisition( requisition_line_repo.upsert_one(&requisition_line)?; } - let indicator_value_repo = IndicatorValueRowRepository::new(connection); - for indicator_value in indicator_values { - indicator_value_repo.upsert_one(&indicator_value)?; + if !indicator_values.is_empty() { + let indicator_value_repo = IndicatorValueRowRepository::new(connection); + for indicator_value in indicator_values { + indicator_value_repo.upsert_one(&indicator_value)?; + } } activity_log_entry( @@ -215,13 +218,21 @@ fn generate( Some(ProgramIndicatorFilter::new().program_id(EqualFilter::equal_to(&program.id))), )?; - let indicator_values = generate_program_indicator_values( + let customer_store = StoreRepository::new(connection) + .query_one(StoreFilter::new().name_id(EqualFilter::equal_to(&other_party_id)))?; + + let generate_indicator_values = generate_program_indicator_values( &ctx.store_id, &period_id, &other_party_id, program_indicators, ); + let indicator_values = match customer_store { + Some(_) => generate_indicator_values, + None => vec![], + }; + Ok(GenerateResult { requisition, requisition_lines, From 7f7dc8a4f6457c887d98938ddc14201a8dcd0437 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Wed, 13 Nov 2024 18:13:22 +1300 Subject: [PATCH 13/25] Remove requisition_id from input + add check to ensure number type is correct --- .../program_indicator/update_value.rs | 35 +++-------- .../src/requisition/indicator_value/update.rs | 60 ++++++++++--------- 2 files changed, 38 insertions(+), 57 deletions(-) diff --git a/server/graphql/programs/src/mutations/program_indicator/update_value.rs b/server/graphql/programs/src/mutations/program_indicator/update_value.rs index d98cc0cc5b..83786cc40d 100644 --- a/server/graphql/programs/src/mutations/program_indicator/update_value.rs +++ b/server/graphql/programs/src/mutations/program_indicator/update_value.rs @@ -1,5 +1,4 @@ use async_graphql::*; -use graphql_core::simple_generic_errors::{CannotEditRequisition, ForeignKey, ForeignKeyError}; use graphql_core::standard_graphql_error::StandardGraphqlError; use graphql_core::{ simple_generic_errors::RecordNotFound, standard_graphql_error::validate_auth, ContextExt, @@ -12,7 +11,6 @@ use service::requisition::indicator_value::{UpdateIndicatorValue, UpdateIndicato pub struct UpdateIndicatorValueInput { pub id: String, pub value: String, - pub requisition_id: String, } #[derive(Interface)] @@ -20,8 +18,6 @@ pub struct UpdateIndicatorValueInput { #[graphql(field(name = "description", ty = "String"))] pub enum UpdateErrorInterface { RecordNotFound(RecordNotFound), - RequisitionDoesNotExist(ForeignKeyError), - CannotEditRequisition(CannotEditRequisition), } #[derive(SimpleObject)] @@ -69,16 +65,8 @@ pub fn update_indicator_value( impl UpdateIndicatorValueInput { pub fn to_domain(self) -> UpdateIndicatorValue { - let UpdateIndicatorValueInput { - id, - value, - requisition_id, - } = self; - UpdateIndicatorValue { - id, - value, - requisition_id, - } + let UpdateIndicatorValueInput { id, value } = self; + UpdateIndicatorValue { id, value } } } @@ -90,22 +78,13 @@ fn map_error(error: UpdateIndicatorValueError) -> Result { UpdateIndicatorValueError::IndicatorValueDoesNotExist => { return Ok(UpdateErrorInterface::RecordNotFound(RecordNotFound {})) } - UpdateIndicatorValueError::NoRequisitionForIndicator => { - return Ok(UpdateErrorInterface::RequisitionDoesNotExist( - ForeignKeyError(ForeignKey::RequisitionId), - )) - } - UpdateIndicatorValueError::ValuePeriodNotRequisitionPeriod => BadUserInput(formatted_error), - UpdateIndicatorValueError::CannotEditRequisition => { - return Ok(UpdateErrorInterface::CannotEditRequisition( - CannotEditRequisition {}, - )) - } // Standard graphql errors + UpdateIndicatorValueError::NotThisStoreRequisition + | UpdateIndicatorValueError::NotThisStoreValue + | UpdateIndicatorValueError::IndicatorColumnDoesNotExist + | UpdateIndicatorValueError::ValueNotCorrectType + | UpdateIndicatorValueError::IndicatorLineDoesNotExist => BadUserInput(formatted_error), UpdateIndicatorValueError::DatabaseError(_) => InternalError(formatted_error), - UpdateIndicatorValueError::NotThisStoreValue => BadUserInput(formatted_error), - UpdateIndicatorValueError::NotThisStoreRequisition => BadUserInput(formatted_error), - UpdateIndicatorValueError::RequisitionHasNoPeriod => BadUserInput(formatted_error), }; Err(graphql_error.extend()) diff --git a/server/service/src/requisition/indicator_value/update.rs b/server/service/src/requisition/indicator_value/update.rs index d70945e4e2..5a5a446961 100644 --- a/server/service/src/requisition/indicator_value/update.rs +++ b/server/service/src/requisition/indicator_value/update.rs @@ -1,28 +1,28 @@ use repository::{ + indicator_column::{IndicatorColumnFilter, IndicatorColumnRepository}, + indicator_line::{IndicatorLineFilter, IndicatorLineRepository}, indicator_value::{IndicatorValueFilter, IndicatorValueRepository}, - EqualFilter, IndicatorValueRow, IndicatorValueRowRepository, RepositoryError, - RequisitionStatus, RequisitionType, StorageConnection, + EqualFilter, IndicatorValueRow, IndicatorValueRowRepository, IndicatorValueType, + RepositoryError, StorageConnection, }; -use crate::{requisition::common::check_requisition_exists, service_provider::ServiceContext}; +use crate::service_provider::ServiceContext; #[derive(Debug, PartialEq, Clone, Default)] pub struct UpdateIndicatorValue { pub id: String, pub value: String, - pub requisition_id: String, } #[derive(Debug, PartialEq)] pub enum UpdateIndicatorValueError { DatabaseError(RepositoryError), IndicatorValueDoesNotExist, - NoRequisitionForIndicator, NotThisStoreRequisition, NotThisStoreValue, - RequisitionHasNoPeriod, - ValuePeriodNotRequisitionPeriod, - CannotEditRequisition, + ValueNotCorrectType, + IndicatorLineDoesNotExist, + IndicatorColumnDoesNotExist, } type OutError = UpdateIndicatorValueError; @@ -57,34 +57,36 @@ fn validate( let indicator_value_row = check_indicator_value_exists(connection, &input.id)? .ok_or(OutError::IndicatorValueDoesNotExist)?; - let requisition = check_requisition_exists(connection, &input.requisition_id)? - .ok_or(OutError::NoRequisitionForIndicator)?; + let indicator_line = IndicatorLineRepository::new(connection) + .query_by_filter(IndicatorLineFilter::new().id(EqualFilter::equal_to( + &indicator_value_row.indicator_line_id, + )))? + .pop() + .ok_or(OutError::IndicatorLineDoesNotExist)?; if store_id != indicator_value_row.store_id { return Err(OutError::NotThisStoreValue); } - if requisition.requisition_row.store_id != store_id { - return Err(OutError::NotThisStoreRequisition); - } - - match requisition.period { - Some(period) => { - if period.id != indicator_value_row.period_id { - return Err(OutError::ValuePeriodNotRequisitionPeriod); - } - } - None => return Err(OutError::RequisitionHasNoPeriod), - } - match requisition.requisition_row.r#type { - RequisitionType::Response => { - if requisition.requisition_row.status != RequisitionStatus::New { - return Err(OutError::CannotEditRequisition); + let indicator_column = IndicatorColumnRepository::new(connection) + .query_by_filter(IndicatorColumnFilter::new().id(EqualFilter::equal_to( + &indicator_value_row.indicator_column_id, + )))? + .pop() + .ok_or(OutError::IndicatorColumnDoesNotExist)?; + + if let Some(column_value_type) = indicator_column.value_type { + if column_value_type == IndicatorValueType::Number { + match input.value.parse::() { + Ok(_) => {} + Err(_) => return Err(OutError::ValueNotCorrectType), } } - RequisitionType::Request => { - if requisition.requisition_row.status != RequisitionStatus::Draft { - return Err(OutError::CannotEditRequisition); + } else if let Some(line_value_type) = indicator_line.value_type { + if line_value_type == IndicatorValueType::Number { + match input.value.parse::() { + Ok(_) => {} + Err(_) => return Err(OutError::ValueNotCorrectType), } } } From 756a91a72d6d798e78a6f4a2006f9309a0ff155c Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Thu, 14 Nov 2024 08:24:33 +1300 Subject: [PATCH 14/25] Remove Requisition error --- .../programs/src/mutations/program_indicator/update_value.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/graphql/programs/src/mutations/program_indicator/update_value.rs b/server/graphql/programs/src/mutations/program_indicator/update_value.rs index 83786cc40d..a54c2364f7 100644 --- a/server/graphql/programs/src/mutations/program_indicator/update_value.rs +++ b/server/graphql/programs/src/mutations/program_indicator/update_value.rs @@ -79,8 +79,7 @@ fn map_error(error: UpdateIndicatorValueError) -> Result { return Ok(UpdateErrorInterface::RecordNotFound(RecordNotFound {})) } // Standard graphql errors - UpdateIndicatorValueError::NotThisStoreRequisition - | UpdateIndicatorValueError::NotThisStoreValue + UpdateIndicatorValueError::NotThisStoreValue | UpdateIndicatorValueError::IndicatorColumnDoesNotExist | UpdateIndicatorValueError::ValueNotCorrectType | UpdateIndicatorValueError::IndicatorLineDoesNotExist => BadUserInput(formatted_error), From bc04b090ae141414fe6833c9ceb7bea8bd1919a4 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Thu, 14 Nov 2024 08:45:35 +1300 Subject: [PATCH 15/25] Update indicator value tests --- .../src/requisition/indicator_value/update.rs | 246 ++++++++++-------- 1 file changed, 136 insertions(+), 110 deletions(-) diff --git a/server/service/src/requisition/indicator_value/update.rs b/server/service/src/requisition/indicator_value/update.rs index 5a5a446961..9aad96cda4 100644 --- a/server/service/src/requisition/indicator_value/update.rs +++ b/server/service/src/requisition/indicator_value/update.rs @@ -18,7 +18,6 @@ pub struct UpdateIndicatorValue { pub enum UpdateIndicatorValueError { DatabaseError(RepositoryError), IndicatorValueDoesNotExist, - NotThisStoreRequisition, NotThisStoreValue, ValueNotCorrectType, IndicatorLineDoesNotExist, @@ -57,6 +56,10 @@ fn validate( let indicator_value_row = check_indicator_value_exists(connection, &input.id)? .ok_or(OutError::IndicatorValueDoesNotExist)?; + if store_id != indicator_value_row.store_id { + return Err(OutError::NotThisStoreValue); + } + let indicator_line = IndicatorLineRepository::new(connection) .query_by_filter(IndicatorLineFilter::new().id(EqualFilter::equal_to( &indicator_value_row.indicator_line_id, @@ -64,10 +67,6 @@ fn validate( .pop() .ok_or(OutError::IndicatorLineDoesNotExist)?; - if store_id != indicator_value_row.store_id { - return Err(OutError::NotThisStoreValue); - } - let indicator_column = IndicatorColumnRepository::new(connection) .query_by_filter(IndicatorColumnFilter::new().id(EqualFilter::equal_to( &indicator_value_row.indicator_column_id, @@ -123,108 +122,135 @@ impl From for UpdateIndicatorValueError { } } -// #[cfg(test)] -// mod test { -// use repository::{ -// mock::{ -// mock_finalised_response_requisition, mock_indicator_value_a, mock_indicator_value_b, -// mock_new_response_requisition, mock_new_response_requisition_store_b, -// mock_request_draft_requisition, mock_store_a, mock_store_b, MockDataInserts, -// }, -// test_db::setup_all, -// }; -// use util::inline_init; - -// use crate::{ -// programs::indicator_value::{UpdateIndicatorValue, UpdateIndicatorValueError}, -// service_provider::ServiceProvider, -// }; - -// #[actix_rt::test] -// async fn update_indicator_value_errors() { -// let (_, _, connection_manager, _) = -// setup_all("update_indicator_value_errors", MockDataInserts::all()).await; - -// let service_provider = ServiceProvider::new(connection_manager, "app_data"); -// let mut context = service_provider -// .context(mock_store_a().id, "".to_string()) -// .unwrap(); -// let service = service_provider.indicator_value_service; - -// // IndicatorValueDoesNotExist -// assert_eq!( -// service.update_indicator_value( -// &context, -// inline_init(|r: &mut UpdateIndicatorValue| { -// r.id = "invalid_id".to_string(); -// r.requisition_id = mock_new_response_requisition().id; -// r.value = String::from("new value"); -// }), -// ), -// Err(UpdateIndicatorValueError::IndicatorValueDoesNotExist) -// ); - -// // CannotEditRequisition -// assert_eq!( -// service.update_indicator_value( -// &context, -// inline_init(|r: &mut UpdateIndicatorValue| { -// r.id = mock_indicator_value_a().id; -// r.requisition_id = mock_finalised_response_requisition().id; -// r.value = String::from("new value"); -// }), -// ), -// Err(UpdateIndicatorValueError::CannotEditRequisition) -// ); - -// // NotThisStoreRequisition -// context.store_id = mock_store_b().id; -// assert_eq!( -// service.update_indicator_value( -// &context, -// inline_init(|r: &mut UpdateIndicatorValue| { -// r.id = mock_indicator_value_b().id; -// r.requisition_id = mock_new_response_requisition().id; -// r.value = String::from("new value"); -// }), -// ), -// Err(UpdateIndicatorValueError::NotThisStoreRequisition) -// ); - -// // NotThisStoreValue -// assert_eq!( -// service.update_indicator_value( -// &context, -// inline_init(|r: &mut UpdateIndicatorValue| { -// r.id = mock_indicator_value_a().id; -// r.requisition_id = mock_new_response_requisition_store_b().id; -// r.value = String::from("new value"); -// }), -// ), -// Err(UpdateIndicatorValueError::NotThisStoreValue) -// ); -// } - -// #[actix_rt::test] -// async fn update_indicator_value_success() { -// let (_, _, connection_manager, _) = -// setup_all("update_indicator_value_success", MockDataInserts::all()).await; - -// let service_provider = ServiceProvider::new(connection_manager, "app_data"); -// let context = service_provider -// .context(mock_store_a().id, "".to_string()) -// .unwrap(); -// let service = service_provider.indicator_value_service; - -// service -// .update_indicator_value( -// &context, -// UpdateIndicatorValue { -// id: mock_indicator_value_a().id, -// value: "new_test_value".to_string(), -// requisition_id: mock_request_draft_requisition().id, -// }, -// ) -// .unwrap(); -// } -// } +#[cfg(test)] +mod test { + use crate::{ + requisition::indicator_value::{UpdateIndicatorValue, UpdateIndicatorValueError}, + service_provider::ServiceProvider, + }; + use chrono::NaiveDate; + use repository::{ + mock::{ + mock_indicator_column_a, mock_indicator_line_c, mock_indicator_value_a, + mock_name_store_b, mock_period, mock_store_a, mock_store_b, MockData, MockDataInserts, + }, + test_db::setup_all_with_data, + IndicatorValueRow, RequisitionRow, RequisitionStatus, RequisitionType, + }; + use util::inline_init; + + fn response_program_req() -> RequisitionRow { + inline_init(|r: &mut RequisitionRow| { + r.id = "response_program_req".to_string(); + r.requisition_number = 3; + r.name_link_id = mock_name_store_b().id; + r.store_id = mock_store_a().id; + r.r#type = RequisitionType::Response; + r.status = RequisitionStatus::New; + r.created_datetime = NaiveDate::from_ymd_opt(2021, 1, 1) + .unwrap() + .and_hms_opt(0, 0, 0) + .unwrap(); + r.max_months_of_stock = 1.0; + r.min_months_of_stock = 0.9; + r.period_id = Some(mock_period().id); + }) + } + + fn test_indicator_value() -> IndicatorValueRow { + IndicatorValueRow { + id: "test_indicator_value".to_string(), + customer_name_link_id: mock_name_store_b().id, + store_id: mock_store_a().id, + period_id: mock_period().id, + indicator_line_id: mock_indicator_line_c().id, + indicator_column_id: mock_indicator_column_a().id, + value: "2".to_string(), + } + } + + #[actix_rt::test] + async fn update_indicator_value_errors() { + let (_, _, connection_manager, _) = setup_all_with_data( + "update_indicator_value_errors", + MockDataInserts::all(), + inline_init(|r: &mut MockData| { + r.requisitions = vec![response_program_req()]; + r.indicator_values = vec![test_indicator_value()]; + }), + ) + .await; + + let service_provider = ServiceProvider::new(connection_manager, "app_data"); + let mut context = service_provider + .context(mock_store_a().id, "".to_string()) + .unwrap(); + let service = service_provider.indicator_value_service; + + // IndicatorValueDoesNotExist + assert_eq!( + service.update_indicator_value( + &context, + inline_init(|r: &mut UpdateIndicatorValue| { + r.id = "invalid_id".to_string(); + r.value = "new_value".to_string(); + }), + ), + Err(UpdateIndicatorValueError::IndicatorValueDoesNotExist) + ); + + // ValueNotCorrectType + assert_eq!( + service.update_indicator_value( + &context, + inline_init(|r: &mut UpdateIndicatorValue| { + r.id = test_indicator_value().id; + r.value = "new value".to_string(); + }), + ), + Err(UpdateIndicatorValueError::ValueNotCorrectType) + ); + + context.store_id = mock_store_b().id; + // NotThisStoreValue + assert_eq!( + service.update_indicator_value( + &context, + inline_init(|r: &mut UpdateIndicatorValue| { + r.id = mock_indicator_value_a().id; + r.value = "new value".to_string(); + }), + ), + Err(UpdateIndicatorValueError::NotThisStoreValue) + ); + } + + #[actix_rt::test] + async fn update_indicator_value_success() { + let (_, _, connection_manager, _) = setup_all_with_data( + "update_indicator_value_success", + MockDataInserts::all(), + inline_init(|r: &mut MockData| { + r.requisitions = vec![response_program_req()]; + r.indicator_values = vec![test_indicator_value()]; + }), + ) + .await; + + let service_provider = ServiceProvider::new(connection_manager, "app_data"); + let context = service_provider + .context(mock_store_a().id, "".to_string()) + .unwrap(); + let service = service_provider.indicator_value_service; + + service + .update_indicator_value( + &context, + UpdateIndicatorValue { + id: test_indicator_value().id, + value: "6".to_string(), + }, + ) + .unwrap(); + } +} From 7e178547302f452827a61d0294f947e5ac8cf729 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Thu, 14 Nov 2024 08:54:44 +1300 Subject: [PATCH 16/25] Move indicators graphql folders/file to correct place --- server/graphql/programs/src/lib.rs | 22 --------------- server/graphql/programs/src/mutations/mod.rs | 1 - .../src/mutations/program_indicator/mod.rs | 1 - server/graphql/programs/src/queries/mod.rs | 2 -- server/graphql/requisition/src/lib.rs | 27 +++++++++++++++++++ .../graphql/requisition/src/mutations/mod.rs | 1 + .../src/mutations/update_indicator_value.rs} | 2 +- .../src}/program_indicator.rs | 0 8 files changed, 29 insertions(+), 27 deletions(-) delete mode 100644 server/graphql/programs/src/mutations/program_indicator/mod.rs rename server/graphql/{programs/src/mutations/program_indicator/update_value.rs => requisition/src/mutations/update_indicator_value.rs} (98%) rename server/graphql/{programs/src/queries => requisition/src}/program_indicator.rs (100%) diff --git a/server/graphql/programs/src/lib.rs b/server/graphql/programs/src/lib.rs index 8513e5d0d1..a4412c945b 100644 --- a/server/graphql/programs/src/lib.rs +++ b/server/graphql/programs/src/lib.rs @@ -51,9 +51,6 @@ use mutations::program_enrolment::insert::InsertProgramEnrolmentResponse; use mutations::program_enrolment::update::update_program_enrolment; use mutations::program_enrolment::update::UpdateProgramEnrolmentInput; use mutations::program_enrolment::update::UpdateProgramEnrolmentResponse; -use mutations::program_indicator::update_value::update_indicator_value; -use mutations::program_indicator::update_value::UpdateIndicatorValueInput; -use mutations::program_indicator::update_value::UpdateIndicatorValueResponse; use mutations::program_patient::insert::*; use mutations::program_patient::update::update_program_patient; use mutations::program_patient::update::UpdateProgramPatientInput; @@ -314,16 +311,6 @@ impl ProgramsQueries { ) -> Result { vaccination_card(ctx, store_id, program_enrolment_id) } - - pub async fn program_indicators( - &self, - ctx: &Context<'_>, - store_id: String, - sort: Option, - filter: Option, - ) -> Result { - program_indicators(ctx, store_id, sort, filter) - } } #[derive(Default, Clone)] @@ -501,13 +488,4 @@ impl ProgramsMutations { ) -> Result { update_vaccination(ctx, store_id, input) } - - pub async fn update_indicator_value( - &self, - ctx: &Context<'_>, - store_id: String, - input: UpdateIndicatorValueInput, - ) -> Result { - update_indicator_value(ctx, store_id, input) - } } diff --git a/server/graphql/programs/src/mutations/mod.rs b/server/graphql/programs/src/mutations/mod.rs index 7bfda56f62..9eaa9f7da5 100644 --- a/server/graphql/programs/src/mutations/mod.rs +++ b/server/graphql/programs/src/mutations/mod.rs @@ -4,7 +4,6 @@ pub mod encounter; pub mod insert_document_registry; pub mod patient; pub mod program_enrolment; -pub mod program_indicator; pub mod program_patient; pub mod rnr_form; pub mod vaccination; diff --git a/server/graphql/programs/src/mutations/program_indicator/mod.rs b/server/graphql/programs/src/mutations/program_indicator/mod.rs deleted file mode 100644 index 02ffbeffed..0000000000 --- a/server/graphql/programs/src/mutations/program_indicator/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod update_value; diff --git a/server/graphql/programs/src/queries/mod.rs b/server/graphql/programs/src/queries/mod.rs index 11cde59a65..10b2a9697f 100644 --- a/server/graphql/programs/src/queries/mod.rs +++ b/server/graphql/programs/src/queries/mod.rs @@ -27,5 +27,3 @@ pub use self::program::*; pub use self::r_and_r_form::*; pub mod vaccination; pub use self::vaccination::*; -pub mod program_indicator; -pub use self::program_indicator::*; diff --git a/server/graphql/requisition/src/lib.rs b/server/graphql/requisition/src/lib.rs index 8ca90d43c7..91510df656 100644 --- a/server/graphql/requisition/src/lib.rs +++ b/server/graphql/requisition/src/lib.rs @@ -1,9 +1,14 @@ pub mod mutations; +mod program_indicator; mod program_settings; mod requisition_queries; use async_graphql::*; use graphql_core::pagination::PaginationInput; +use graphql_types::types::program_indicator::{ + ProgramIndicatorFilterInput, ProgramIndicatorResponse, ProgramIndicatorSortInput, +}; use graphql_types::types::RequisitionNodeType; +use program_indicator::program_indicators; use program_settings::{ get_customer_program_requisition_settings, get_supplier_program_requisition_settings, CustomerProgramRequisitionSettingNode, SupplierProgramRequisitionSettingNode, @@ -11,6 +16,9 @@ use program_settings::{ use self::mutations::{request_requisition, response_requisition}; use self::requisition_queries::*; +use mutations::update_indicator_value::{ + self, UpdateIndicatorValueInput, UpdateIndicatorValueResponse, +}; #[derive(Default, Clone)] pub struct RequisitionQueries; @@ -61,6 +69,16 @@ impl RequisitionQueries { ) -> Result> { get_customer_program_requisition_settings(ctx, &store_id) } + + pub async fn program_indicators( + &self, + ctx: &Context<'_>, + store_id: String, + sort: Option, + filter: Option, + ) -> Result { + program_indicators(ctx, store_id, sort, filter) + } } #[derive(Default, Clone)] @@ -188,6 +206,15 @@ impl RequisitionMutations { ctx, &store_id, input, ) } + + pub async fn update_indicator_value( + &self, + ctx: &Context<'_>, + store_id: String, + input: UpdateIndicatorValueInput, + ) -> Result { + update_indicator_value::update(ctx, store_id, input) + } } #[cfg(test)] diff --git a/server/graphql/requisition/src/mutations/mod.rs b/server/graphql/requisition/src/mutations/mod.rs index 1ef8a29c5d..373115b9c5 100644 --- a/server/graphql/requisition/src/mutations/mod.rs +++ b/server/graphql/requisition/src/mutations/mod.rs @@ -1,3 +1,4 @@ pub mod errors; pub mod request_requisition; pub mod response_requisition; +pub mod update_indicator_value; diff --git a/server/graphql/programs/src/mutations/program_indicator/update_value.rs b/server/graphql/requisition/src/mutations/update_indicator_value.rs similarity index 98% rename from server/graphql/programs/src/mutations/program_indicator/update_value.rs rename to server/graphql/requisition/src/mutations/update_indicator_value.rs index a54c2364f7..573d0eb6a9 100644 --- a/server/graphql/programs/src/mutations/program_indicator/update_value.rs +++ b/server/graphql/requisition/src/mutations/update_indicator_value.rs @@ -32,7 +32,7 @@ pub enum UpdateIndicatorValueResponse { Error(UpdateError), } -pub fn update_indicator_value( +pub fn update( ctx: &Context<'_>, store_id: String, input: UpdateIndicatorValueInput, diff --git a/server/graphql/programs/src/queries/program_indicator.rs b/server/graphql/requisition/src/program_indicator.rs similarity index 100% rename from server/graphql/programs/src/queries/program_indicator.rs rename to server/graphql/requisition/src/program_indicator.rs From 3e5667c03496d3e383773659f8a874c455857349 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Thu, 14 Nov 2024 09:01:45 +1300 Subject: [PATCH 17/25] Remove unused mock indicator value --- server/repository/src/mock/indicator_value.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/server/repository/src/mock/indicator_value.rs b/server/repository/src/mock/indicator_value.rs index f535e16bc5..b79c8573a4 100644 --- a/server/repository/src/mock/indicator_value.rs +++ b/server/repository/src/mock/indicator_value.rs @@ -16,18 +16,6 @@ pub fn mock_indicator_value_a() -> IndicatorValueRow { } } -pub fn mock_indicator_value_b() -> IndicatorValueRow { - IndicatorValueRow { - id: String::from("id_b"), - customer_name_link_id: mock_store_a().name_link_id, - store_id: mock_store_b().id, - period_id: mock_period().id, - indicator_line_id: mock_indicator_line_a().id, - indicator_column_id: mock_indicator_column_a().id, - value: String::from("test_value"), - } -} - pub fn mock_indicator_values() -> Vec { - vec![mock_indicator_value_a(), mock_indicator_value_b()] + vec![mock_indicator_value_a()] } From a9207401d5ff50c246c7c46fbfb59a9520277e00 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Thu, 14 Nov 2024 09:02:51 +1300 Subject: [PATCH 18/25] Remove insert bools --- server/repository/src/mock/mod.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/server/repository/src/mock/mod.rs b/server/repository/src/mock/mod.rs index 94073d9414..bf10d61006 100644 --- a/server/repository/src/mock/mod.rs +++ b/server/repository/src/mock/mod.rs @@ -700,24 +700,11 @@ impl MockDataInserts { } pub fn program_indicators(mut self) -> Self { - self.contexts = true; - self.programs = true; self.program_indicators = true; - self.indicator_lines = true; - self.indicator_columns = true; self } pub fn indicator_values(mut self) -> Self { - self.contexts = true; - self.programs = true; - self.program_indicators = true; - self.indicator_lines = true; - self.indicator_columns = true; - self.period_schedules = true; - self.periods = true; - self.names = true; - self.stores = true; self.indicator_values = true; self } From 0c473a0fb103d1a805d303e58c76e757dc4a5390 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Thu, 14 Nov 2024 11:42:12 +1300 Subject: [PATCH 19/25] Plural Co-Authored-By: Chris Petty --- .../src/requisition/response_requisition/insert_program.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/service/src/requisition/response_requisition/insert_program.rs b/server/service/src/requisition/response_requisition/insert_program.rs index 8d7c305e87..f4b25d78fb 100644 --- a/server/service/src/requisition/response_requisition/insert_program.rs +++ b/server/service/src/requisition/response_requisition/insert_program.rs @@ -244,11 +244,11 @@ fn generate_program_indicator_values( store_id: &str, period_id: &str, other_party_id: &str, - program_indicator: HashMap, + program_indicators: HashMap, ) -> Vec { let mut indicator_values = vec![]; - for (_, program_indicator) in program_indicator { + for (_, program_indicator) in program_indicators { for line in program_indicator.lines { for column in line.columns { let value = match column.value_type { From 95c088079533c9a160978781c6429ea2e4beb052 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Thu, 14 Nov 2024 14:39:21 +1300 Subject: [PATCH 20/25] Change Hashmap to Vec --- .../src/requisition/response_requisition/insert_program.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/server/service/src/requisition/response_requisition/insert_program.rs b/server/service/src/requisition/response_requisition/insert_program.rs index f4b25d78fb..1ed85d0113 100644 --- a/server/service/src/requisition/response_requisition/insert_program.rs +++ b/server/service/src/requisition/response_requisition/insert_program.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use crate::{ activity_log::activity_log_entry, number::next_number, @@ -244,11 +242,11 @@ fn generate_program_indicator_values( store_id: &str, period_id: &str, other_party_id: &str, - program_indicators: HashMap, + program_indicators: Vec, ) -> Vec { let mut indicator_values = vec![]; - for (_, program_indicator) in program_indicators { + for program_indicator in program_indicators { for line in program_indicator.lines { for column in line.columns { let value = match column.value_type { From 4e90e6af0bd2d53e9cbb20281053ca74b69bc26b Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Thu, 14 Nov 2024 14:39:47 +1300 Subject: [PATCH 21/25] Return None for value if doesn't exist --- .../src/types/program/program_indicator.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/server/graphql/types/src/types/program/program_indicator.rs b/server/graphql/types/src/types/program/program_indicator.rs index d5e54b3050..4252738e91 100644 --- a/server/graphql/types/src/types/program/program_indicator.rs +++ b/server/graphql/types/src/types/program/program_indicator.rs @@ -6,7 +6,6 @@ use graphql_core::{ loader::{ IndicatorValueLoader, IndicatorValueLoaderInput, IndicatorValuePayload, ProgramByIdLoader, }, - standard_graphql_error::StandardGraphqlError, ContextExt, }; use repository::{ @@ -203,15 +202,13 @@ impl IndicatorColumnNode { &self.column.id, payload, )) - .await? - .ok_or_else(|| { - StandardGraphqlError::InternalError(format!( - "Cannot find value for column {} with header {}", - &self.line_id, &self.column.id, - )) - })?; - - Ok(Some(IndicatorValueNode::from_domain(result))) + .await?; + + if let Some(value) = result { + Ok(Some(IndicatorValueNode::from_domain(value))) + } else { + Ok(None) + } } } From a81359dc2e043fefc55c2626ec323372b40bfe6d Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Thu, 14 Nov 2024 14:40:03 +1300 Subject: [PATCH 22/25] Remove unused imports --- server/graphql/programs/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/server/graphql/programs/src/lib.rs b/server/graphql/programs/src/lib.rs index a4412c945b..a61b6ab079 100644 --- a/server/graphql/programs/src/lib.rs +++ b/server/graphql/programs/src/lib.rs @@ -19,9 +19,6 @@ use graphql_types::types::program_enrolment::ProgramEnrolmentSortInput; use graphql_types::types::program_enrolment::ProgramEventFilterInput; use graphql_types::types::program_event::ProgramEventResponse; use graphql_types::types::program_event::ProgramEventSortInput; -use graphql_types::types::program_indicator::ProgramIndicatorFilterInput; -use graphql_types::types::program_indicator::ProgramIndicatorResponse; -use graphql_types::types::program_indicator::ProgramIndicatorSortInput; use graphql_types::types::vaccination::VaccinationNode; use mutations::allocate_number::allocate_program_number; use mutations::allocate_number::AllocateProgramNumberInput; From 70ebd5584b2b345d09cdd71254f6dc3bb6bb0443 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Thu, 14 Nov 2024 14:44:22 +1300 Subject: [PATCH 23/25] Fix import --- server/service/src/requisition/program_indicator/query.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/service/src/requisition/program_indicator/query.rs b/server/service/src/requisition/program_indicator/query.rs index 39df8f6b13..d1923d060c 100644 --- a/server/service/src/requisition/program_indicator/query.rs +++ b/server/service/src/requisition/program_indicator/query.rs @@ -64,12 +64,11 @@ pub fn program_indicators( #[cfg(test)] mod query { + use crate::requisition::program_indicator::query::IndicatorLine; + use crate::service_provider::ServiceProvider; use repository::Pagination; use repository::{mock::MockDataInserts, test_db::setup_all}; - use crate::programs::program_indicator::query::IndicatorLine; - use crate::service_provider::ServiceProvider; - #[actix_rt::test] async fn program_indicator_query() { let (_, connection, connection_manager, _) = From aade948cfede1fcb656af5873475892ecb114669 Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Fri, 15 Nov 2024 08:21:25 +1300 Subject: [PATCH 24/25] Simplify --- .../response_requisition/insert_program.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/server/service/src/requisition/response_requisition/insert_program.rs b/server/service/src/requisition/response_requisition/insert_program.rs index 1ed85d0113..b61a38c295 100644 --- a/server/service/src/requisition/response_requisition/insert_program.rs +++ b/server/service/src/requisition/response_requisition/insert_program.rs @@ -219,15 +219,13 @@ fn generate( let customer_store = StoreRepository::new(connection) .query_one(StoreFilter::new().name_id(EqualFilter::equal_to(&other_party_id)))?; - let generate_indicator_values = generate_program_indicator_values( - &ctx.store_id, - &period_id, - &other_party_id, - program_indicators, - ); - let indicator_values = match customer_store { - Some(_) => generate_indicator_values, + Some(_) => generate_program_indicator_values( + &ctx.store_id, + &period_id, + &other_party_id, + program_indicators, + ), None => vec![], }; From 7c7636c9d8088454a47685087d78e54f7f4ad47b Mon Sep 17 00:00:00 2001 From: roxy-dao Date: Fri, 15 Nov 2024 10:31:31 +1300 Subject: [PATCH 25/25] Optional value type --- .../types/src/types/program/program_indicator.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/server/graphql/types/src/types/program/program_indicator.rs b/server/graphql/types/src/types/program/program_indicator.rs index 4252738e91..ad56d70794 100644 --- a/server/graphql/types/src/types/program/program_indicator.rs +++ b/server/graphql/types/src/types/program/program_indicator.rs @@ -155,6 +155,10 @@ impl IndicatorLineRowNode { pub async fn line_number(&self) -> i32 { self.line.line_number } + + pub async fn value_type(&self) -> Option { + IndicatorValueTypeNode::from_domain(&self.line.value_type) + } } impl IndicatorColumnNode { @@ -174,7 +178,7 @@ impl IndicatorColumnNode { &self.column.header } - pub async fn value_type(&self) -> IndicatorValueTypeNode { + pub async fn value_type(&self) -> Option { IndicatorValueTypeNode::from_domain(&self.column.value_type) } @@ -219,11 +223,11 @@ pub enum IndicatorValueTypeNode { } impl IndicatorValueTypeNode { - pub fn from_domain(r#type: &Option) -> Self { + pub fn from_domain(r#type: &Option) -> Option { match r#type { - Some(IndicatorValueType::Number) => IndicatorValueTypeNode::Number, - Some(IndicatorValueType::String) => IndicatorValueTypeNode::String, - None => IndicatorValueTypeNode::String, + Some(IndicatorValueType::Number) => Some(IndicatorValueTypeNode::Number), + Some(IndicatorValueType::String) => Some(IndicatorValueTypeNode::String), + None => None, } } }