Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Billing automation and Clearinghouse Uploader #4068

Closed

Conversation

juggernautsei
Copy link
Member

@juggernautsei juggernautsei commented Nov 27, 2020

This feature was built for behavioral health practices that assisted them in billing. The clinician by closing out the appointment fires off the billing to be entered into the billing table. The way billing does not have to be manually entered for each visit.

Short description of what this resolves:

This feature allows a practice to fill in the fee sheet from calendar entries.

Changes proposed in this pull request:

This adds the functionality of automating part of the billing so that the calendar is coupled to filling out the fee sheet with basic information for very simple claims.

…them in billing. The clinician by closing out the appointment fires off the billing to be entered into the billing table. The way billing does not have to be manually entered for each visit.
@juggernautsei juggernautsei changed the title This feature was built for behavioral health practices that assisted … Billing automation and Clearinghouse Uploader Nov 28, 2020
namespace OpenEMR\Billing;

use OpenEMR\Common\Logging\EventAuditLogger;
use phpseclib\Net\SFTP;
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pretty cool @juggernautsei , wouldn't you want to add this to composer.json?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed it was already there

"phpseclib/phpseclib": "2.0.29",

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, we got it already :)

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, just looks weird uncapitalized and not alpha sorted

public function sendBilling($bat_filename)
{
$cryptoGen = new Crypto\CryptoGen();
$fileLocal = dirname(__DIR__, 2) . '/sites/default/documents/edi/' . $bat_filename;
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should use instead of sites/default

$GLOBALS['OE_SITE_DIR']

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do!

…emoved the unused logging statement

The system needs to create the encounter irrespective of the date.
@bradymiller
Copy link
Sponsor Member

@juggernautsei , Ignore the automated testing fails. There is a pesky bug in the e2e testing which is unrelated to your code.

} else {
$clearinghouse = new ClearingHouse();
$response = $clearinghouse->sendBilling($bat_filename);
if ($response == 'Sucesss') {
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Success

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh wait, disregard that since not part of front end (ie. does not need to be spelled correctly)

@@ -52,11 +52,14 @@

use OpenEMR\Common\Acl\AclMain;
use OpenEMR\Core\Header;
use OpenEMR\Billing\AutoBilling;
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should sort/order the above use alphabetically


//Check that a diagnosis has been set for the patient before entering calendar entry Sherwin 2020/11/21
$doBillingPid = $_POST['form_pid'];
if (!empty($doBillingPid && $GLOBALS['enable_autobilling'] == 1)) {
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you mean:

if (!empty($doBillingPid) && ($GLOBALS['enable_autobilling'] == 1)) {

if (!empty($doBillingPid && $GLOBALS['enable_autobilling'] == 1)) {
$isDiagnosed = $doBilling->getDiagnosis($doBillingPid);
if (empty($isDiagnosed['title'])) {
die("<h4>Please enter a diagnosis for the patient before calendar event record</h4>");
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

translate

if ($event_date == date('Y-m-d') && !$GLOBALS['enable_autobilling']) {
$evd = true; //The encounter should be created irrespective of the event date
} elseif ($GLOBALS['enable_autobilling']) {
$evd = true;
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think you meant to put that comment on line 235
(the logic makes sense)

if ($_POST['form_apptstatus'] === '>') {
$bInfo = add_escape_custom($_POST['form_title']); //calendar appointment type billing information
$pid = add_escape_custom($_POST['form_pid']); //patient id
$userid = add_escape_custom($_SESSION['authUserID']); //user who entered the information
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the add_escape_custom() wrappers above (note you are properly sql escaping them via binding downstream)

try {
$doBilling->generateBilling($event_date, $bInfo, $pid, $userid);
} catch (Exception $e) {
error_log("Auto Billing failed to insert line 472 add_edit_event" . $e);
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

escape for log:

error_log("Auto Billing failed to insert line 472 add_edit_event" . errorLogEscape($e));


'ch_sftp_pwd' => array(
xl('Clearinghouse account password'),
'encrypted', // data type
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2DV

}
//see if there is a diagnosis in the patient chart
//returns an array
$icd10 = self::getDiagnosis($pid);
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am surprised that the self:: works here since those are not static functions. Recommend changing all the self:: calls in this class to $this->

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note this then allows you to set up variables within the class such as $this->pid so then can use this within the functions rather than needing to pass the parameter.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the PHP documentation, it said that self can be used. However, it can only be used within the class. In the Symfony courses, I have taken. They use it this way as well. I will change them.

use Exception;

class AutoBilling
{
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets throw in a blank _construct function at the top of the class:

    public function __construct()
    {
    }

self::insertICDBilling($event_date, $icd10, $pid, $provider, $userid, $enc, $desc);
}
} catch (Exception $e) {
echo xlt('Autobilling failed ' . $e);
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no trailing space in constants and need to html escape the $e:

echo (xlt('Autobilling failed') . ' ' . text($e));

public function sendBilling($bat_filename)
{
$cryptoGen = new Crypto\CryptoGen();
$fileLocal = $GLOBALS['OE_SITE_DIR'] . '/sites/default/documents/edi/' . $bat_filename;
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drop the sites/default/

@kchapple
Copy link
Sponsor Contributor

@bradymiller @juggernautsei Should the SFTP remote call be asynchronous? If the remote connection takes a long time, this will block the end user. Also, should the SFTP credentials go in the x-12 partner form since they are not global, but are specific to the x-12 partner? I am currently working on something similar to this with @growlingflea and we’ll have to integrate with what you’ve done if and when this gets into the project.

}
//see if there is a diagnosis in the patient chart
//returns an array
$icd10 = $this->getDiagnosis($pid);
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

next step is to make these variables part of the class. For example, will have:

$this->pid = $pid;

then when call the function do:

$icd10 = $this->getDiagnosis();

then inside the getDiagnosis() function, use the $this->pid rather than $pid.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and then at top of class will have for this variable:

private $pid;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason the $pid is sent in rather than referenced by the session is that the user will not be in the patient chart. The user will be in the calendar only closing the appointment. So, the user is not forced to open the chart to set the PID variable in the session. If the appointment is being closed from the calendar the PID is a post variable. The only thing I can think of is that @bradymiller is suggesting that the variable be set this way.

Inside the add edit event do this:

        $doBilling->pid = $_POST['pid']
     $doBilling->bInfo = $_POST['form_title']
    $doBilling->userid = $_SESSION['authUserID']

Inside AutoBilling class do this

  class AutoBilling
  {
    public $pid;
    public $bInfo;
    public $userid;
    public $event_date;;

The reason that the credentials are in the globals is that I was thinking to make it generic enough that any clearinghouse providing the SFTP information to their client. The client could plug in the information. It is not meant to be specific to a clearinghouse. Also, most practice use only one clearinghouse in my experience. But if there is a practice that uses more than one clearinghouse at a time. It should be moved to practice settings.

Also, I trying to leverage what is in the system already. The system already stores the file. The ability to leverage that existing tech is why I went with the SFTP over building an API. SFTP was the path of least resistance. And anyone using the program now has to manually upload the x12 file to the clearinghouse. This takes that step out.

The upload should be async. If it is not set that way. I did not find any setting here http://phpseclib.sourceforge.net/sftp/2.0/examples.html#put

I tried cURL and grizzle both. I was only able to get phpseclib to work on the ubuntu 18.04 server.

Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing wrong with SFTP as a protocol. I believe that logically and functionally, the credentials belong with the x-12 partner. Dan already has code for that.

To make the transfer asynchronous, it would need to be a background task. I would propose putting references to files in a table, as a queue, and processing transfers from the queue rather than doing them synchronously based on a user action. That way new files, as well as files that haven’t been sent successfully can be tracked and processed.

Dan and I are wrapping up very similar functionality that we were going to commit this week, so this stuff is on the top of my brain. @growlingflea

Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can chat this week on how we can coordinate our effort since it sound like we’re working on similar things?

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi guys (@juggernautsei , @kchapple ), could I listen in as well please? we could use the openemr zoom setup

thank you

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if current code still has it but 'Electronic Report' checked all configured SFTP laboratory connections every time that screen was called. This would cause delays even if the user was just looking for history. SFTP connections need a decent timeout so any down hosts would cause major wait.

Ended up extracting fetch and update code in a separate script for use by cron AND added a button to run the same when the user was ok with expected short delay as results needed to be fetched stat.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mdsupport in this workflow. The SFTP is only activated when there is a file to send. If no file exists, then the SFTP call is not made to put the file on the remote server. Also, the users' action has to be purposeful to transmit a claim. Without the user selecting the transmit of the claim the sFTP is not fired.

@mdsupport, I looked it up and to add a time out to the sFTP. All I have to add is

 $ssh->setTimeout(20);

In your experience, is 20 seconds a good time out for the login to fail or go through? I have pushed the commit for that update.

thanks for the comment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@juggernautsei 20s for clearinghouse should be more than adequate.

I was just narrating our user experience years ago when labs interface was activated. Without knowing the billing workflow my guess was clearinghouse was not that different from labs - get inbound and send outbound. Obviously that does not seem to be the case for billing.

Best.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kchapple How is your project coming along? I paused work on this because you stated that you have a similar project that you are working on. Have you posted your project?

@kchapple
Copy link
Sponsor Contributor

kchapple commented Jan 21, 2021

Hi @juggernautsei yes. We are still cleaning up, testing and I need to write some user documentation, but I was hoping to have a pull request ready to start this week. I think I need to have a call with @sjpadgett and @growlingflea and @stephenwaite because I have some questions. This code queues the SFTP uploads using the background service API. Anyways here is the branch: https://github.com/mi-squared/openemr/tree/billing-processor-direct-x12-auto-sftp

Screen Shot 2021-01-18 at 4 37 34 PM

@juggernautsei
Copy link
Member Author

Something better came along and made this obsolete.

@sjpadgett
Copy link
Sponsor Member

I think I need to have a call with @sjpadgett and @growlingflea and @stephenwaite because I have some questions.
@mdsupport
If we're going to talk this through let's do today sooner than later.

@kchapple
Copy link
Sponsor Contributor

Sure @sjpadgett you want to do a zoom thing?

@sjpadgett
Copy link
Sponsor Member

I guess we can use the sat call zoom unless you have an account. @stephenwaite you avail?

@stephenwaite
Copy link
Sponsor Member

@sjpadgett
Copy link
Sponsor Member

Okay, I'll set up another account here soon and donate to openemr or help Brady defray costs. So @kchapple say in 15 minutes? @growlingflea you there?

@kchapple
Copy link
Sponsor Contributor

Can we do 2:30 EST (in 40 minutes?) I have a Zoom account if needed, but if you have access to the OpenEMR one, that's good too.

@sjpadgett
Copy link
Sponsor Member

okay all, 2:30. we'll just use the acct stephen posted. hear ya then:)

@stephenwaite
Copy link
Sponsor Member

only thing is we can't do screen share, want to try a jitsi?

@stephenwaite
Copy link
Sponsor Member

let's try this, https://meet.jit.si/openemr-mtg
it's been working well for me

@sjpadgett
Copy link
Sponsor Member

okay with me. @stephenwaite i'll meet you 15 min early so we can talk labs

@juggernautsei juggernautsei deleted the Autobilling_Clearinghouse branch February 21, 2021 13:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants