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
Billing automation and Clearinghouse Uploader #4068
Conversation
…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.
…de users and translated the messages.
…emoved the unused logging statement
namespace OpenEMR\Billing; | ||
|
||
use OpenEMR\Common\Logging\EventAuditLogger; | ||
use phpseclib\Net\SFTP; |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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
Line 48 in 1b975e5
"phpseclib/phpseclib": "2.0.29", |
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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
src/Billing/ClearingHouse.php
Outdated
public function sendBilling($bat_filename) | ||
{ | ||
$cryptoGen = new Crypto\CryptoGen(); | ||
$fileLocal = dirname(__DIR__, 2) . '/sites/default/documents/edi/' . $bat_filename; |
There was a problem hiding this comment.
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']
There was a problem hiding this comment.
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.
@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') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Success
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
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>"); |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/Billing/AutoBilling.php
Outdated
} | ||
//see if there is a diagnosis in the patient chart | ||
//returns an array | ||
$icd10 = self::getDiagnosis($pid); |
There was a problem hiding this comment.
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->
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 | ||
{ |
There was a problem hiding this comment.
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()
{
}
src/Billing/AutoBilling.php
Outdated
self::insertICDBilling($event_date, $icd10, $pid, $provider, $userid, $enc, $desc); | ||
} | ||
} catch (Exception $e) { | ||
echo xlt('Autobilling failed ' . $e); |
There was a problem hiding this comment.
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));
src/Billing/ClearingHouse.php
Outdated
public function sendBilling($bat_filename) | ||
{ | ||
$cryptoGen = new Crypto\CryptoGen(); | ||
$fileLocal = $GLOBALS['OE_SITE_DIR'] . '/sites/default/documents/edi/' . $bat_filename; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drop the sites/default/
@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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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;
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
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 |
Something better came along and made this obsolete. |
|
Sure @sjpadgett you want to do a zoom thing? |
I guess we can use the sat call zoom unless you have an account. @stephenwaite you avail? |
yep, want to use @bradymiller 's gift? https://www.open-emr.org/wiki/index.php/OEMR_Monthly_Conference_Call_Details |
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? |
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. |
okay all, 2:30. we'll just use the acct stephen posted. hear ya then:) |
only thing is we can't do screen share, want to try a jitsi? |
let's try this, https://meet.jit.si/openemr-mtg |
okay with me. @stephenwaite i'll meet you 15 min early so we can talk labs |
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.