Untitled
both fucntions those are calling submissioncheck methodpackage Clinical::P4P::Submission; # Utility (non-model-specific) functions related to QM submission functionality, # which is comprised of 3 components, generally speaking: # 1. The creation and maintenance of denormalized measure/program satisfaction # information in P4PSUBMISSION/DATA. # 2. The final attainment by a provider of satisfaction for a program. # 3. The generation of provider measure/program satisfaction information in XML format # and the submission of this to interested or regulating parties. # See Clinical/P4P/Submission/* for further generic and submission-model-specific functions. use Athena::Policy; use constant ALLPROVIDERID => -1; use constant ALLFEDERALIDNUMBER => 'ALL'; use constant LOG_SEPARATOR => '=' x 90 . "\n"; use Athena::P4P::Entity::Result::ResultReportConfiguration; use Athena::P4P::App::PerformanceData; use Athena::P4P::Persistence::PerformanceData; use Athena::P4P::Persistence::Result::ResultReport; use Athena::RolloutToggle; use Athena::ServiceBus::Dispatcher qw( P4P::GetProgramDefinition ); use Athena::ServiceBus; use Athena::Util::Assert; use Athena::Util::Database; use Athena::Util::Hash; use Athena::Util::Text; use Athena::Util::SQL; use AthenaDate; use Athena::DateTime; use DateTime; use AthenaUtils; use Athena::ServiceBus::Attributes; use Cache; use Clinical::P4P::Attestation; use Clinical::P4P::LegacyEHR; use Clinical::P4P::Log; use Clinical::P4P::Measure; use Clinical::P4P::Persistence::MultiProviderSubmission; use Clinical::P4P::Persistence::ProgramSubmission; use Clinical::P4P::Provider; use Clinical::P4P::ReportingPeriod; use Clinical::P4P::Result; use Clinical::P4P::Tech::Data; use Clinical::P4P::Utils; use Enrollment::P4P; use Global; use List::MoreUtils; use List::Util qw( first ); use List::Compare; use PageCache; use Proc; use Provider; use SQL::Select; use SQL; use Time::HiRes qw( gettimeofday tv_interval ); use Try::Tiny; use WorkUnit::AthenaDB; use Athena::Conf::AthenaNet; use Athena::Util::Database; use Athena::Util::List; use Clinical::P4P::Attestation::ProgramTaskAttestationDetails; use Athena::Util::Carp qw( carp cluck confess croak reconfess ); # SC stands for Submission Check. our $UserName = 'ATHENASCRUB_SC'; our $LocalDBH; our %SubStatusMap = ( ATTESTED => { # NOQCS: Unable to calculate a Quality Composite Score based on the Submission's available data NOQCS => 1, }, ATTESTEDELSEWHERE => { MULTITABLESPACE => 1, }, ATTESTINGELSEWHERE => { BLACKLISTED => 1, COUNTMISMATCH => 1, HOSPITAL => 1, MULTITABLESPACE => 1, NOPHR => 1, OUTSIDEATHENA => 1, UNSCRUBBEDPATIENTS => 1, }, COMPLETEDBYCLIENT => { }, FAILED => { # BADENROLLMENT: Failing reporting requirements due to bad enrollment (e.g. No Cross Cutting measure for PQRS Registry) BADENROLLMENT => 1, BLACKLISTED => 1, COUNTMISMATCH => 1, FAILINGBEHAVIORAL => 1, FAILINGCOREMEASURES => 1, FAILINGMENUMEASURES => 1, FAILINGREGISTRYMEASURES => 1, HOSPITAL => 1, IMMREGENROLLMENTTASK => 1, LEGACYNOTENABLED => 1, MULTITABLESPACE => 1, # INSUFFICIENTDATA: Not meeting minimum reporting requirements INSUFFICIENTDATA => 1, # INSUFFICIENTTIMEONATHENA: Not meeting minimum time on athenaNet. INSUFFICIENTTIMEONATHENA => 1, # INVALIDENROLLMENT: Submission includes inappropriate enrollments INVALIDENROLLMENT => 1, INVALIDLEGACYPORTALDATA => 1, # NOPATIENTS: Submission does not have enough patients to satisfy reporting requirements NOPATIENTS => 1, NOCONTACTINFO => 1, NOLEGACYSIGNOFF => 1, NOLEGACYDATA => 1, NOLEGACYPORTALSIGNOFF => 1, NOLEGACYPORTALDATA => 1, NOPATIENTLIST => 1, NOPHR => 1, NOPROXY => 1, NOSRA => 1, # NOQCS: Unable to calculate a Quality Composite Score based on the Submission's available data NOQCS => 1, OUTSIDEATHENA => 1, SRANOTDONE => 1, UNSCRUBBEDPATIENTS => 1, # ZEROPERFORMANCERATE: Not meeting minimum reporting requirements because at least one measure has a 0 performance rate ZEROPERFORMANCERATE => 1, }, NOTSATISFIED => { # BADENROLLMENT: Failing reporting requirements due to bad enrollment (e.g. No Cross Cutting measure for PQRS Registry) BADENROLLMENT => 1, BLACKLISTED => 1, COUNTMISMATCH => 1, # FAILINGBEHAVIORAL: Indicates the provider is failing their core and/or menu measures # and that their failure cannot be corrected by a simple provider or athena action (such as # completing their SRA). FAILINGBEHAVIORAL => 1, # FAILINGCOREMEASURES: Indicates the provider is failing their core measures. Unlike FAILINGBEHAVIORAL, # this does not account for potential provider actions. A provider who has not completed their SRA but # is otherwise passing will have FAILINGCOREMEASURES set but not FAILINGBEHAVIORAL. FAILINGCOREMEASURES => 1, # FAILINGMENUMEASURES: The same as FAILINGCOREMEASURES, except for menu measures. FAILINGMENUMEASURES => 1, FAILINGREGISTRYMEASURES => 1, HOSPITAL => 1, IMMREGENROLLMENTTASK => 1, LEGACYNOTENABLED => 1, MULTITABLESPACE => 1, # INSUFFICIENTDATA: Not meeting minimum reporting requirements INSUFFICIENTDATA => 1, # INSUFFICIENTTIMEONATHENA: Not meeting minimum time on athenaNet. INSUFFICIENTTIMEONATHENA => 1, # INVALIDENROLLMENT: Submission includes inappropriate enrollments INVALIDENROLLMENT => 1, INVALIDLEGACYPORTALDATA => 1, NOCONTACTINFO => 1, # NOCROSSCUTTING: Submission is missing a required cross-cutting measure NOCROSSCUTTING => 1, NOLEGACYSIGNOFF => 1, NOLEGACYDATA => 1, NOLEGACYPORTALSIGNOFF => 1, NOLEGACYPORTALDATA => 1, # NOPATIENTS: Submission does not have enough patients to satisfy reporting requirements NOPATIENTS => 1, NOPATIENTLIST => 1, NOPHR => 1, NOPROXY => 1, NOSRA => 1, # NOQCS: Unable to calculate a Quality Composite Score based on the Submission's available data NOQCS => 1, OUTSIDEATHENA => 1, SRANOTDONE => 1, UNSCRUBBEDPATIENTS => 1, # WAITINGONPROVIDER: Indicates the provider may take some simple action to # improve their status, such as completing their SRA. WAITINGONPROVIDER => 1, # WAITINGONATHENA: Indicates athena may take some simple action to improve their # status. WAITINGONATHENA => 1, # ZEROPERFORMANCERATE: Not meeting minimum reporting requirements because at least one measure has a 0 performance rate ZEROPERFORMANCERATE => 1, # BADFCED: Not having first encounter dater for program, provider, federalid combination inside the program's reporting period BADFCED => 1, # MINPERFORMANCEPERIODNOTMET: Not having minimum performance period to for given program to get snapshotted and complete submission MINPERFORMANCEPERIODNOTMET => 1, # POINTSLESSTHANFIFTY : Not having minimum score for Hospital PI POINTSLESSTHANFIFTY => 1, REPORTINGPERIOD_NC => 1, }, OPTOUTMU => { }, PAYMENTRECEIVED => { }, READYTOATTEST => { MULTITABLESPACE => 1, NOCONTACTINFO => 1, NOPROXY => 1, OUTSIDEATHENA => 1, # NOQCS: Unable to calculate a Quality Composite Score based on the Submission's available data NOQCS => 1, }, READYTOATTESTELSEWHERE => { }, SATISFIED_WAITINGONPROVIDER => { HOSPITAL => 1, IMMREGENROLLMENTTASK => 1, MULTITABLESPACE => 1, NOCONTACTINFO => 1, NOLEGACYSIGNOFF => 1, NOLEGACYDATA => 1, NOLEGACYPORTALSIGNOFF => 1, NOLEGACYPORTALDATA => 1, NOPATIENTLIST => 1, NOPROXY => 1, NOSRA => 1, OUTSIDEATHENA => 1, # NOQCS: Unable to calculate a Quality Composite Score based on the Submission's available data NOQCS => 1, INVALIDLEGACYPORTALDATA => 1, }, SATISFIED_WAITINGONATHENA => { BLACKLISTED => 1, BLOCKEDBYCONTRIBUTINGTABLESPACE => 1, COUNTMISMATCH => 1, FAILINGREGISTRYMEASURES => 1, HOSPITAL => 1, IMMREGENROLLMENTTASK => 1, # INVALIDENROLLMENT: Submission includes inappropriate enrollments INVALIDENROLLMENT => 1, # INSUFFICIENTTIMEONATHENA: Not meeting minimum time on athenaNet. INSUFFICIENTTIMEONATHENA => 1, LEGACYNOTENABLED => 1, MULTITABLESPACE => 1, NOCONTACTINFO => 1, NOLEGACYSIGNOFF => 1, NOLEGACYDATA => 1, NOLEGACYPORTALSIGNOFF => 1, NOLEGACYPORTALDATA => 1, NOPATIENTLIST => 1, NOPHR => 1, NOPROXY => 1, NOSRA => 1, # NOQCS: Unable to calculate a Quality Composite Score based on the Submission's available data NOQCS => 1, OUTSIDEATHENA => 1, SNAPSHOTBUFFER => 1, UNSCRUBBEDPATIENTS => 1, BEGINSNAPSHOTTING_IS_N => 1, }, SUBMITTED => { # NOQCS: Unable to calculate a Quality Composite Score based on the Submission's available data NOQCS => 1, }, ); our @AttestationTaskSubStatuses = ( 'NOCONTACTINFO', 'NOPROXY', ); # We sometimes connect to the standbys to access load the # result data (to reduce database load). my %StandbyDBHCache; ################################################################################ # UpdateAndRetrieveAdditionalData # # Description: # Updates measure counts and then returns the new measure counts. # # Parameters: # Required: # P4PPROGRAMID Integer. # At least one of # PROVIDERID Integer. # FEDERALIDNUMBER Integer. # SUBMISSIONNUMBER Integer. # Optional: # INCLUDECHARTIDS Boolean. Used when retrieving submission measure data # INCLUDEPATIENTIDS Boolean. Used when retrieving submission measure data # USERNAME String. # # Returns: # MEASUREDATA Arrayref of Hashrefs: # DENOMEX Integer. # DENOMINATOR Integer. # ENROLLED Boolean. 1 or 0. # EXCLUDED Integer. # GROUPORDERING Integer. # INTERFACEVENDORID String. # IPP Integer. # NUMERATOR Integer. # P4PMEASUREID Integer. # P4PMEASURESUBSCRIPTIONID Integer. # P4PSUBMISSIONGROUPID Integer. # PROGRAMKEY String. # STATUS String. # SUBMISSIONNUMBER Integer. # PREVENTSNAPSHOT Boolean 1/0. If 1, the provider should not be able to snapshot # in the Attesting practice ################################################################################ Expose( 'UpdateAndRetrieveAdditionalData', 'ATHENANETINTERNALWEBSERVICE' ); sub UpdateAndRetrieveAdditionalData { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields($args, [ qw( P4PPROGRAMID SUBMISSIONNUMBER ), { ONEPLUS => [qw( PROVIDERID FEDERALIDNUMBER )], }, ], [qw( INCLUDECHARTIDS INCLUDEPATIENTIDS USERNAME MULTITABLESPACEDATA )], ); my $federalidnumber = $args->{FEDERALIDNUMBER}; my $p4pprogramid = $args->{P4PPROGRAMID}; my $providerid = $args->{PROVIDERID}; my $submissionnumber = $args->{SUBMISSIONNUMBER}; my $username = $args->{USERNAME} || $Global::session{USERNAME}; my $includechartids = $args->{INCLUDECHARTIDS}; my $includepatientids = $args->{INCLUDEPATIENTIDS}; my $multitablespacedata = $args->{MULTITABLESPACEDATA}; my $measuresatisfactionrules = Clinical::P4P::Submission::GetMeasureSatisfactionRules($dbh, { P4PPROGRAMID => $p4pprogramid, }); my $npiprovidermap = Clinical::P4P::Provider::GetNPIProviderMap($dbh, { ALLDUPLICATES => 1, }); my $duplicateproviderids = $npiprovidermap->{ $providerid }; if ( $providerid ) { $providerid = Clinical::P4P::Provider::GetRepresentativeProviderID($dbh, { PROVIDERID => $providerid, FEDERALIDNUMBER => $federalidnumber, PROVIDERENTITYTYPEFLAG => 1, }); } my $quproconf = Athena::Conf::AthenaNet::InternalServices('qupro'); my @mtsimprovement_contexts = (380,1438,2183,4290,7598,8042,8209,9582,9954,10170,11541,12833,13122); if (defined $quproconf->{mtsimprovement_contexts}){ @mtsimprovement_contexts = split (<,>, $quproconf->{mtsimprovement_contexts}); } my $mtsimprovements = (AthenaUtils::InList(Athena::Util::Database::SessionInfo($dbh)->{context}, @mtsimprovement_contexts)) ? 1 : 0; my $resultdata = GetResultData($dbh, { FEDERALIDNUMBER => $federalidnumber, P4PPROGRAMID => $p4pprogramid, PROVIDERID => $providerid, SUBMISSIONNUMBER => $submissionnumber, SUBMISSIONCHECKIMPROVEMENTS => 1, CALLFROMMTS => $mtsimprovements, }); my $submissioncheckresult; AthenaUtils::DBTransaction(sub { $submissioncheckresult = Clinical::P4P::Submission::SubmissionCheck($dbh, { ALLOWCONTRIBUTINGPROVIDERS => 1, DUPLICATEPROVIDERIDS => $duplicateproviderids, FEDERALIDNUMBER => $federalidnumber, MEASURESATISFACTIONRULES => $measuresatisfactionrules, P4PPROGRAMID => $p4pprogramid, PROVIDERID => $providerid, RESULTDATA => $resultdata, RUNALLOVERRIDES => 1, SUBMISSIONNUMBER => $submissionnumber, USERNAME => $username, MULTITABLESPACEDATA => $multitablespacedata, }); }, $dbh); my $submissionobject = Clinical::P4P::Submission::MaybeCreateResult($dbh, { FEDERALIDNUMBER => $federalidnumber, P4PPROGRAMID => $p4pprogramid, PROVIDERID => $providerid, SUBMISSIONNUMBER => $submissionnumber, }); my $measuredata = $submissionobject->GetMeasureData({ KEYS => [qw( DENOMEX DENOMINATOR ENROLLED EXCLUDED IPP NUMERATOR STATUS INTERFACEVENDORID )], INCLUDECHARTIDS => $includechartids, INCLUDEPATIENTIDS => $includepatientids, }); my $submissionmodule = Clinical::P4P::Utils::GetProgramModule($dbh, { P4PPROGRAMID => $p4pprogramid, TYPE => 'Submission', }); my $rp = $submissionobject->ReportingPeriod(); my $preventsnapshot; my $submissionperiodcomplete = $rp->RequiredSubmissionSizeAttained({ SUBMISSIONNUMBER => $submissionnumber, }); if ( ! $submissionperiodcomplete ) { $preventsnapshot = 1; } else { my $substatuses = $submissionobject->GetSubStatusesBySubmissionNumber({ SUBMISSIONNUMBER => $submissionnumber, }) || {}; $preventsnapshot = $submissionmodule->ShouldAdditionalSubStatusesPreventSnapshot($dbh, { SUBSTATUSES => [ keys %{ $substatuses->{SUBSTATUSES} || {} } ], }); } return { MEASUREDATA => $measuredata, PREVENTSNAPSHOT => $preventsnapshot, }; } ############################################################################### # MaybeCreateResult # # Description: # Returns the ID of the row in P4PSUBMISSION corresponding to the specified TIN/provider and program, incorporating 'duplicate provider' logic to find the P4PSUBMISSION # for the 'representative' provider of the specified one, when applicable. # If such a P4PSUBMISSION does not already exist, one will be instantiated, and the SubmissionCheck() executed on it, unless DONOTINSTANTIATE is specified. # # Parameters: # Required # P4PPROGRAMID # PROVIDERID and/or FEDERALIDNUMBER # SUBMISSIONNUMBER # Optional # DONOTINSTANTIATE Boolean. If the provider/program combination specified is not associated with a submission (P4PSUBMISSION row) # do not create one, and return the empty string. # DUPLICATEPROVIDERIDS Listref. The IDs of the PROVIDERs in the specified provider's 'duplicate set'. Will be computed if not provided. # SUBMISSIONNUMBER. Integer. The submission number to find the # corresponding p4psubmission for. If not specified, defaults to # the current submission number for the program, or 1 if we're before # the beginning of the reporting period. # # Return Value: # Integer. The ID of the P4PSUBMISSION row located or created, or the empty string, if no P4PSUBMISSION exists and DONOTINSTANTIATE is specified. ############################################################################### sub MaybeCreateResult : CoveredByAPI("Athena::P4P::API::Submission::MaybeCreateResult") { my ($dbh, $args) = @_; AssertFields( $args, [ qw( P4PPROGRAMID ), { ONEPLUS => [qw( PROVIDERID FEDERALIDNUMBER )], }, ], [qw( DONOTINSTANTIATE DUPLICATEPROVIDERIDS SUBMISSIONNUMBER )], ); my $p4pprogramid = $args->{P4PPROGRAMID}; my $providerid = $args->{PROVIDERID}; my $federalidnumber = $args->{FEDERALIDNUMBER}; my $donotinstantiate = $args->{DONOTINSTANTIATE}; my $duplicateproviderids = $args->{DUPLICATEPROVIDERIDS}; my $submissionnumber = $args->{SUBMISSIONNUMBER}; my $contextid = Athena::Util::Database::SessionInfo($dbh)->{context}; my $submissionobject; DBTransaction( sub { # in case of multiple providers having the same NPI, use the primary one and get a list of the rest if ( ! $duplicateproviderids && $providerid ) { $duplicateproviderids = Clinical::P4P::Provider::GetDuplicateProviderIDs($dbh, { PROVIDERID => $providerid, FEDERALIDNUMBER => $federalidnumber, }); } my $representativeproviderid; if ( $providerid ) { $representativeproviderid = Clinical::P4P::Provider::GetRepresentativeProviderID($dbh, { PROVIDERID => $providerid, FEDERALIDNUMBER => $federalidnumber, PROVIDERENTITYTYPEFLAG => 1, }); if ( $representativeproviderid != $providerid ) { # This function should never be called with a non-primary provider ID, but if it is, # we want to catch and correct those cases. Clinical::P4P::Log::Log($dbh, { LEVEL => 'error', MESSAGE => "MaybeCreateResult called with non-primary duplicate NPI providerid.", SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $contextid, P4PPROGRAMID => $p4pprogramid, PROVIDERID => $providerid, FEDERALIDNUMBER => $federalidnumber, SUBMISSIONNUMBER => $submissionnumber, }, }); } } if ( !$submissionnumber ) { my $reportingperiod = Clinical::P4P::ReportingPeriod::Get($dbh, { P4PPROGRAMID => $p4pprogramid, PROVIDERID => $representativeproviderid, }); $submissionnumber = $reportingperiod->CurrentSubmissionNumber(); } $submissionobject = Clinical::P4P::Persistence::ProgramSubmission->new({ DBH => $dbh, P4PPROGRAMID => $p4pprogramid, PROVIDERID => $representativeproviderid, FEDERALIDNUMBER => $federalidnumber, SUBMISSIONNUMBER => $submissionnumber, }); if ( $submissionobject->NoDenormalizedData({ SUBMISSIONNUMBER => $submissionnumber, }) && ! $donotinstantiate ) { my $submissionmodule = Clinical::P4P::Utils::GetProgramModule($dbh, { P4PPROGRAMID => $p4pprogramid, TYPE => 'Submission', }); my $enrollmentsql = SQL::Select->new( )->Select( 'distinct p4pmeasuresubscriptionid', '1', )->From( 'vp4penrollment', )->Where( ['vp4penrollment.p4pprogramid = ?', $p4pprogramid], ); if ( $duplicateproviderids ) { $enrollmentsql->Where( ['vp4penrollment.providerid in (??)', $duplicateproviderids], ); } elsif ( $federalidnumber ) { # We only want to look for TIN level enrollments if # we are not creating a provider level submission. $enrollmentsql->Where( ['vp4penrollment.federalidnumber = ?', $federalidnumber], ); } my %enrollment = $enrollmentsql->ColumnValues($dbh); if (!$submissionmodule) { # create a stub if the we do not have denormalization logic # for this program or the provider is not enrolled in this program $submissionobject = _CreateResult($dbh, { ENROLLMENTDATA => \%enrollment, SUBMISSIONOBJECT => $submissionobject, SUBMISSIONNUMBER => $submissionnumber, }); # We shouldn't be calling MaybeCreateResult on a program with no submissionmodule # but will be better off cleaning it up later than not returning with a p4psubmissionid Clinical::P4P::Log::Log($dbh, { LEVEL => 'error', MESSAGE => "Stub P4PSUBMISSION row created for program when it shouldn't be.", SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $contextid, P4PPROGRAMID => $p4pprogramid, PROVIDERID => $providerid, FEDERALIDNUMBER => $federalidnumber, SUBMISSIONNUMBER => $submissionnumber, }, }); } else { # get initial denormalized data my $programdefinition = BusCall::P4P::GetProgramDefinition($dbh, { P4PPROGRAMID => $p4pprogramid, }); $submissionobject = _CreateResult($dbh, { PROGRAMDEFINITION => $programdefinition, ENROLLMENTDATA => \%enrollment, SUBMISSIONOBJECT => $submissionobject, SUBMISSIONMODULE => $submissionmodule, SUBMISSIONNUMBER => $submissionnumber, }); # Get Program-specific measure satisfaction rules my $satisfactionrules = GetMeasureSatisfactionRules($dbh, { P4PPROGRAMID => $p4pprogramid, }); # Get Provider Results my $results = GetResultData($dbh, { P4PPROGRAMID => $p4pprogramid, PROVIDERID => $representativeproviderid, FEDERALIDNUMBER => $federalidnumber, SUBMISSIONNUMBER => $submissionnumber, SUBMISSIONCHECKIMPROVEMENTS => 1, }); SubmissionCheck($dbh, { DUPLICATEPROVIDERIDS => $duplicateproviderids, MEASURESATISFACTIONRULES => $satisfactionrules, P4PMEASURESUBSCRIPTIONIDS => [keys %enrollment], P4PPROGRAMID => $p4pprogramid, PROVIDERID => $representativeproviderid, FEDERALIDNUMBER => $federalidnumber, RESULTDATA => $results, SUBMISSIONNUMBER => $submissionnumber, UPDATECOUNTSONLY => 1, USERNAME => 'MAYBECREATERESULT', }); # This function is susceptible to 'Out of Memory!' errors, when run successively on multiple providers, in large practices. # We combat this here by freeing up the memory used by the large '$results' data structure, and by its # page-cached representation, following each submission check. undef $results; PageCache::Clear($dbh, { FILTER => 'Clinical::P4P::Submission::GetResultData', }); } } }); if ( !$submissionobject ) { return; } return $submissionobject; }
Leave a Comment