From ba11972302ff8a37e8d5f088b1db3a3193042d24 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Fri, 6 Jul 2018 15:58:13 +0530 Subject: [PATCH 01/45] Healthcare Service - Invoice Work Flow - Redesign --- .../doctype/sales_invoice/sales_invoice.js | 210 +++++++++++++++- .../doctype/sales_invoice/sales_invoice.py | 16 ++ erpnext/domains/healthcare.py | 27 +- .../clinical_procedure/clinical_procedure.py | 6 + .../doctype/fee_validity/fee_validity.py | 26 ++ .../healthcare/doctype/lab_test/lab_test.js | 38 +-- .../healthcare/doctype/lab_test/lab_test.py | 55 +---- .../doctype/lab_test/lab_test_list.js | 2 +- .../patient_appointment.js | 29 --- .../patient_appointment.py | 106 +------- .../patient_appointment_dashboard.py | 4 - .../patient_encounter/patient_encounter.js | 30 +-- .../patient_encounter/patient_encounter.py | 72 ------ .../report/lab_test_report/lab_test_report.py | 6 +- erpnext/healthcare/utils.py | 232 ++++++++++++++++++ 15 files changed, 540 insertions(+), 319 deletions(-) create mode 100644 erpnext/healthcare/utils.py diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 619776c674..daf1bfea4b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -16,7 +16,8 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte }, onload: function() { var me = this; - this._super(); + this._super(); + console.log("class erpnext.accounts.SalesInvoiceController, onload this->", this); if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) { // show debit_to in print format @@ -466,7 +467,7 @@ cur_frm.fields_dict.write_off_cost_center.get_query = function(doc) { } } -//project name +// project name //-------------------------- cur_frm.fields_dict['project'].get_query = function(doc, cdt, cdn) { return{ @@ -543,7 +544,7 @@ cur_frm.set_query("asset", "items", function(doc, cdt, cdn) { frappe.ui.form.on('Sales Invoice', { setup: function(frm){ - + frm.custom_make_buttons = { 'Delivery Note': 'Delivery', 'Sales Invoice': 'Sales Return', @@ -625,7 +626,7 @@ frappe.ui.form.on('Sales Invoice', { } }; }, - //When multiple companies are set up. in case company name is changed set default company address + // When multiple companies are set up. in case company name is changed set default company address company:function(frm){ if (frm.doc.company) { @@ -712,8 +713,41 @@ frappe.ui.form.on('Sales Invoice', { } frm.set_value("loyalty_amount", loyalty_amount); } - } + }, + // Healthcare + patient: function(frm) { + if (frappe.boot.active_domains.includes("Healthcare")){ + if(frm.doc.patient){ + frappe.call({ + method: "frappe.client.get_value", + args:{ + doctype: "Patient", + filters: {"name": frm.doc.patient}, + fieldname: "customer" + }, + callback:function(patient_customer) { + if(patient_customer){ + frm.set_value("customer", patient_customer.message.customer); + frm.refresh_fields(); + } + } + }); + } + else{ + frm.set_value("customer", ''); + } + } + }, + refresh: function(frm) { + if (frappe.boot.active_domains.includes("Healthcare")){ + if (cint(frm.doc.docstatus==0) && cur_frm.page.current_view_name!=="pos" && !frm.doc.is_return) { + frm.add_custom_button(__('Healthcare Services'), function() { + get_healthcare_services_to_invoice(frm); + },"Get items from"); + } + } + } }) frappe.ui.form.on('Sales Invoice Timesheet', { @@ -816,3 +850,169 @@ var select_loyalty_program = function(frm, loyalty_programs) { dialog.show(); } + +var get_healthcare_services_to_invoice = function(frm) { + var me = this; + let selected_patient = ''; + var dialog = new frappe.ui.Dialog({ + title: __("Get Items from Healthcare Services"), + fields:[ + { + fieldtype: 'Link', + options: 'Patient', + label: 'Patient', + fieldname: "patient", + reqd: true + }, + { fieldtype: 'Section Break' }, + { fieldtype: 'HTML', fieldname: 'results_area' } + ] + }); + var $wrapper; + var $results; + var $placeholder; + dialog.set_values({ + 'patient': frm.doc.patient + }); + dialog.fields_dict["patient"].df.onchange = () => { + var patient = dialog.fields_dict.patient.input.value; + if(patient && patient!=selected_patient){ + selected_patient = patient; + get_services(frm, patient, $results, $placeholder) + } + else if(!patient){ + selected_patient = ''; + $results.empty(); + $results.append($placeholder); + } + } + $wrapper = dialog.fields_dict.results_area.$wrapper.append(`
`); + $results = $wrapper.find('.results'); + $placeholder = $(`
+ + +

No billable Healthcare Services found

+
+
`); + $results.on('click', '.list-item--head :checkbox', (e) => { + $results.find('.list-item-container .list-row-check') + .prop("checked", ($(e.target).is(':checked'))); + }); + set_primary_action(frm, dialog, $results); + dialog.show(); +}; + +var get_services= function(frm, patient_id, $results, $placeholder) { + var me = this; + $results.empty(); + frappe.call({ + method:"erpnext.healthcare.utils.get_healthcare_services_to_invoice", + args: { + patient: patient_id + }, + callback: function(data) { + if(data.message){ + $results.append(make_list_row()); + for(let i=0; i + ${ + head ? `${__(frappe.model.unscrub(column))}` + + :(column !== "name" ? `${__(result[column])}` + : ` + ${__(result[column])}`) + } + `; + }) + + let $row = $(`
+
+ +
+ ${contents} +
`); + + head ? $row.addClass('list-item--head') + : $row = $(`
+
`).append($row); + return $row; +}; + +var set_primary_action= function(frm, dialog, $results) { + var me = this; + dialog.set_primary_action(__('Add'), function() { + // TODO: Get checked items + let checked_values = get_checked_values($results); + if(checked_values.length > 0){ + frm.set_value("patient", dialog.fields_dict.patient.input.value); + frm.set_value("items", []); + for(let i=0; i 1){ + frappe.model.set_value(si_item.doctype, si_item.name, 'qty', checked_values[i]['qty']); + } + } + frm.refresh_fields(); + dialog.hide(); + } + else{ + frappe.msgprint(__("Please select Healthcare Service")); + } + }); +}; + +var get_checked_values= function($results) { + return $results.find('.list-item-container').map(function() { + let checked_values = {}; + if ($(this).find('.list-row-check:checkbox:checked').length > 0 ) { + checked_values['dn'] = $(this).attr('data-dn'); + checked_values['dt'] = $(this).attr('data-dt'); + checked_values['item'] = $(this).attr('data-item'); + if($(this).attr('data-rate')){ + checked_values['rate'] = $(this).attr('data-rate'); + } + if($(this).attr('data-income-account')){ + checked_values['income_account'] = $(this).attr('data-income-account'); + } + if($(this).attr('data-qty')){ + checked_values['qty'] = $(this).attr('data-qty'); + } + return checked_values; + } + }).get(); +}; diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index a45b953cdd..7aeb86d7af 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -24,6 +24,8 @@ from erpnext.accounts.general_ledger import get_round_off_account_and_cost_cente from erpnext.accounts.doctype.loyalty_program.loyalty_program import \ get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points +from erpnext.healthcare.utils import manage_invoice_submit_cancel + from six import iteritems form_grid_templates = { @@ -179,6 +181,13 @@ class SalesInvoice(SellingController): if self.redeem_loyalty_points and self.loyalty_points: self.apply_loyalty_points() + # Healthcare Service Invoice. + domain_settings = frappe.get_doc('Domain Settings') + active_domains = [d.domain for d in domain_settings.active_domains] + + if "Healthcare" in active_domains: + manage_invoice_submit_cancel(self, "on_submit") + def validate_pos_paid_amount(self): if len(self.payments) == 0 and self.is_pos: frappe.throw(_("At least one mode of payment is required for POS invoice.")) @@ -227,6 +236,13 @@ class SalesInvoice(SellingController): unlink_inter_company_invoice(self.doctype, self.name, self.inter_company_invoice_reference) + # Healthcare Service Invoice. + domain_settings = frappe.get_doc('Domain Settings') + active_domains = [d.domain for d in domain_settings.active_domains] + + if "Healthcare" in active_domains: + manage_invoice_submit_cancel(self, "on_cancel") + def update_status_updater_args(self): if cint(self.update_stock): self.status_updater.extend([{ diff --git a/erpnext/domains/healthcare.py b/erpnext/domains/healthcare.py index 5a54cf67d0..57f30f6301 100644 --- a/erpnext/domains/healthcare.py +++ b/erpnext/domains/healthcare.py @@ -21,9 +21,30 @@ data = { 'Patient' ], 'custom_fields': { - 'Sales Invoice': dict(fieldname='appointment', label='Patient Appointment', - fieldtype='Link', options='Patient Appointment', - insert_after='customer') + 'Sales Invoice': [ + { + 'fieldname': 'patient', 'label': 'Patient', 'fieldtype': 'Link', 'options': 'Patient', + 'insert_after': 'naming_series' + }, + { + 'fieldname': 'patient_name', 'label': 'Patient Name', 'fieldtype': 'Data', 'fetch_from': 'patient.patient_name', + 'insert_after': 'patient', 'read_only': True + }, + { + 'fieldname': 'ref_practitioner', 'label': 'Referring Practitioner', 'fieldtype': 'Link', 'options': 'Practitioner', + 'insert_after': 'customer' + } + ], + 'Sales Invoice Item': [ + { + 'fieldname': 'reference_dt', 'label': 'Reference DocType', 'fieldtype': 'Link', 'options': 'DocType', + 'insert_after': 'edit_references' + }, + { + 'fieldname': 'reference_dn', 'label': 'Reference Name', 'fieldtype': 'Dynamic Link', 'options': 'reference_dt', + 'insert_after': 'reference_dt' + } + ] }, 'on_setup': 'erpnext.healthcare.setup.setup_healthcare' } diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index 97d8a02080..d51d062408 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -24,6 +24,8 @@ class ClinicalProcedure(Document): self.set_actual_qty(); def after_insert(self): + if self.prescription: + frappe.db.set_value("Procedure Prescription", self.prescription, "procedure_created", 1) if self.appointment: frappe.db.set_value("Patient Appointment", self.appointment, "status", "Closed") template = frappe.get_doc("Clinical Procedure Template", self.procedure_template) @@ -130,6 +132,8 @@ def set_stock_items(doc, stock_detail_parent, parenttype): se_child.conversion_factor = flt(d["conversion_factor"]) if d["batch_no"]: se_child.batch_no = d["batch_no"] + if parenttype == "Clinical Procedure Template": + se_child.invoice_separately_as_consumables = d["invoice_separately_as_consumables"] return doc def get_item_dict(table, parent, parenttype): @@ -165,6 +169,8 @@ def create_procedure(appointment): procedure.patient_age = appointment.patient_age procedure.patient_sex = appointment.patient_sex procedure.procedure_template = appointment.procedure_template + procedure.procedure_prescription = appointment.procedure_prescription + procedure.invoiced = appointment.invoiced procedure.medical_department = appointment.department procedure.start_date = appointment.appointment_date procedure.start_time = appointment.appointment_time diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.py b/erpnext/healthcare/doctype/fee_validity/fee_validity.py index 53a17417ce..a707236c86 100644 --- a/erpnext/healthcare/doctype/fee_validity/fee_validity.py +++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.py @@ -4,6 +4,32 @@ from __future__ import unicode_literals from frappe.model.document import Document +import frappe +from frappe.utils import getdate +import datetime class FeeValidity(Document): pass + +def update_fee_validity(fee_validity, date): + max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") + valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") + if not valid_days: + valid_days = 1 + if not max_visit: + max_visit = 1 + date = getdate(date) + valid_till = date + datetime.timedelta(days=int(valid_days)) + fee_validity.max_visit = max_visit + fee_validity.visited = 1 + fee_validity.valid_till = valid_till + fee_validity.save(ignore_permissions=True) + return fee_validity + + +def create_fee_validity(practitioner, patient, date): + fee_validity = frappe.new_doc("Fee Validity") + fee_validity.practitioner = practitioner + fee_validity.patient = patient + fee_validity = update_fee_validity(fee_validity, date) + return fee_validity diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js index c3b069d049..06637bcdcc 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test.js @@ -24,11 +24,6 @@ frappe.ui.form.on('Lab Test', { refresh : function(frm){ refresh_field('normal_test_items'); refresh_field('special_test_items'); - if(!frm.doc.__islocal && !frm.doc.invoice && frappe.user.has_role("Accounts User")){ - frm.add_custom_button(__('Make Invoice'), function() { - make_invoice(frm); - }); - } if(frm.doc.__islocal){ frm.add_custom_button(__('Get from Patient Encounter'), function () { get_lab_test_prescribed(frm); @@ -166,8 +161,8 @@ var show_lab_tests = function(frm, result){ ', {name:y[0], lab_test: y[1], encounter:y[2], invoice:y[3], practitioner:y[4], date:y[5]})).appendTo(html_field); + data-invoiced="%(invoiced)s" href="#">', {name:y[0], lab_test: y[1], encounter:y[2], invoiced:y[3], practitioner:y[4], date:y[5]})).appendTo(html_field); row.find("a").click(function() { frm.doc.template = $(this).attr("data-lab-test"); frm.doc.prescription = $(this).attr("data-name"); @@ -175,14 +170,11 @@ var show_lab_tests = function(frm, result){ frm.set_df_property("template", "read_only", 1); frm.set_df_property("patient", "read_only", 1); frm.set_df_property("practitioner", "read_only", 1); - if($(this).attr("data-invoice") != 'null'){ - frm.doc.invoice = $(this).attr("data-invoice"); - refresh_field("invoice"); - }else { - frm.doc.invoice = ""; - refresh_field("invoice"); + frm.doc.invoiced = 0; + if($(this).attr("data-invoiced") == 1){ + frm.doc.invoiced = 1; } - + refresh_field("invoiced"); refresh_field("template"); d.hide(); return false; @@ -195,24 +187,6 @@ var show_lab_tests = function(frm, result){ d.show(); }; -var make_invoice = function(frm){ - var doc = frm.doc; - frappe.call({ - method: "erpnext.healthcare.doctype.lab_test.lab_test.create_invoice", - args: {company:doc.company, patient:doc.patient, lab_tests: [doc.name], prescriptions:[]}, - callback: function(r){ - if(!r.exc){ - if(r.message){ - /* frappe.show_alert(__('Sales Invoice {0} created', - ['' + r.message+ ''])); */ - frappe.set_route("Form", "Sales Invoice", r.message); - } - cur_frm.reload_doc(); - } - } - }); -}; - cur_frm.cscript.custom_before_submit = function(doc) { if(doc.normal_test_items){ for(let result in doc.normal_test_items){ diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 767581f656..9cba34cd58 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -5,10 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -import json from frappe.utils import getdate, cstr -from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account -from frappe import _ class LabTest(Document): def on_submit(self): @@ -31,6 +28,8 @@ class LabTest(Document): def after_insert(self): if(self.prescription): frappe.db.set_value("Lab Prescription", self.prescription, "test_created", 1) + if frappe.db.get_value("Lab Prescription", self.prescription, 'invoiced') == 1: + self.invoiced = True if not self.test_name and self.template: self.load_test_from_template() self.reload() @@ -64,7 +63,7 @@ def create_lab_test_doc(invoice, encounter, patient, template): #create Test Result for template, copy vals from Invoice lab_test = frappe.new_doc("Lab Test") if(invoice): - lab_test.invoice = invoice + lab_test.invoiced = True if(encounter): lab_test.practitioner = encounter.practitioner lab_test.patient = patient.name @@ -133,7 +132,7 @@ def create_sample_doc(template, patient, invoice): #create Sample Collection for template, copy vals from Invoice sample_collection = frappe.new_doc("Sample Collection") if(invoice): - sample_collection.invoice = invoice + sample_collection.invoiced = True sample_collection.patient = patient.name sample_collection.patient_age = patient.get_age() sample_collection.patient_sex = patient.sex @@ -211,7 +210,7 @@ def load_result_format(lab_test, template, prescription, invoice): if(prescription): lab_test.prescription = prescription if(invoice): - frappe.db.set_value("Lab Prescription", prescription, "invoice", invoice) + frappe.db.set_value("Lab Prescription", prescription, "invoiced", True) lab_test.save(ignore_permissions=True) # insert the result return lab_test @@ -248,49 +247,7 @@ def delete_lab_test_from_medical_record(self): if medical_record_id and medical_record_id[0][0]: frappe.delete_doc("Patient Medical Record", medical_record_id[0][0]) -def create_item_line(test_code, sales_invoice): - if test_code: - item = frappe.get_doc("Item", test_code) - if item: - if not item.disabled: - sales_invoice_line = sales_invoice.append("items") - sales_invoice_line.item_code = item.item_code - sales_invoice_line.item_name = item.item_name - sales_invoice_line.qty = 1.0 - sales_invoice_line.description = item.description - -@frappe.whitelist() -def create_invoice(company, patient, lab_tests, prescriptions): - test_ids = json.loads(lab_tests) - line_ids = json.loads(prescriptions) - if not test_ids and not line_ids: - return - sales_invoice = frappe.new_doc("Sales Invoice") - sales_invoice.customer = frappe.get_value("Patient", patient, "customer") - sales_invoice.due_date = getdate() - sales_invoice.is_pos = '0' - sales_invoice.debit_to = get_receivable_account(company) - for line in line_ids: - test_code = frappe.get_value("Lab Prescription", line, "test_code") - create_item_line(test_code, sales_invoice) - for test in test_ids: - template = frappe.get_value("Lab Test", test, "template") - test_code = frappe.get_value("Lab Test Template", template, "item") - create_item_line(test_code, sales_invoice) - sales_invoice.set_missing_values() - sales_invoice.save() - #set invoice in lab test - for test in test_ids: - frappe.db.set_value("Lab Test", test, "invoice", sales_invoice.name) - prescription = frappe.db.get_value("Lab Test", test, "prescription") - if prescription: - frappe.db.set_value("Lab Prescription", prescription, "invoice", sales_invoice.name) - #set invoice in prescription - for line in line_ids: - frappe.db.set_value("Lab Prescription", line, "invoice", sales_invoice.name) - return sales_invoice.name - @frappe.whitelist() def get_lab_test_prescribed(patient): - return frappe.db.sql("""select cp.name, cp.test_code, cp.parent, cp.invoice, ct.practitioner, ct.encounter_date from `tabPatient Encounter` ct, + return frappe.db.sql("""select cp.name, cp.test_code, cp.parent, cp.invoiced, ct.practitioner, ct.encounter_date from `tabPatient Encounter` ct, `tabLab Prescription` cp where ct.patient=%s and cp.parent=ct.name and cp.test_created=0""", (patient)) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test_list.js b/erpnext/healthcare/doctype/lab_test/lab_test_list.js index c36c115f99..1d65e5be66 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test_list.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test_list.js @@ -2,7 +2,7 @@ (c) ESS 2015-16 */ frappe.listview_settings['Lab Test'] = { - add_fields: ["name", "status", "invoice"], + add_fields: ["name", "status", "invoiced"], filters:[["docstatus","=","0"]], get_indicator: function(doc) { if(doc.status=="Approved"){ diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index 9799018cea..c0e5cd0979 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -4,7 +4,6 @@ frappe.provide("erpnext.queries"); frappe.ui.form.on('Patient Appointment', { setup: function(frm) { frm.custom_make_buttons = { - 'Sales Invoice': 'Invoice', 'Vital Signs': 'Vital Signs', 'Patient Encounter': 'Patient Encounter' }; @@ -84,19 +83,6 @@ frappe.ui.form.on('Patient Appointment', { btn_update_status(frm, "Cancelled"); }); } - - if(!frm.doc.__islocal){ - if(frm.doc.sales_invoice && frappe.user.has_role("Accounts User")){ - frm.add_custom_button(__('Invoice'), function() { - frappe.set_route("Form", "Sales Invoice", frm.doc.sales_invoice); - },__("View") ); - } - else if(frm.doc.status != "Cancelled" && frappe.user.has_role("Accounts User")){ - frm.add_custom_button(__('Invoice'), function() { - btn_invoice_encounter(frm); - },__("Create")); - } - } frm.set_df_property("get_procedure_from_encounter", "read_only", frm.doc.__islocal ? 0 : 1); }, check_availability: function(frm) { @@ -339,21 +325,6 @@ var btn_update_status = function(frm, status){ ); }; -var btn_invoice_encounter = function(frm){ - frappe.call({ - doc: frm.doc, - method:"create_invoice", - callback: function(data){ - if(!data.exc){ - if(data.message){ - frappe.set_route("Form", "Sales Invoice", data.message); - } - cur_frm.reload_doc(); - } - } - }); -}; - frappe.ui.form.on("Patient Appointment", "practitioner", function(frm) { if(frm.doc.practitioner){ frappe.call({ diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index ad2a933544..7d0dade5e0 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -10,7 +10,6 @@ from frappe.utils import getdate from frappe import _ import datetime from frappe.core.doctype.sms_settings.sms_settings import send_sms -from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account from erpnext.hr.doctype.employee.employee import is_holiday class PatientAppointment(Document): @@ -38,26 +37,23 @@ class PatientAppointment(Document): visited = fee_validity.visited + 1 frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) if fee_validity.ref_invoice: - frappe.db.set_value("Patient Appointment", appointment.name, "sales_invoice", fee_validity.ref_invoice) + frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", True) frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till)) confirm_sms(self) - def create_invoice(self): - return invoice_appointment(self) - def appointment_cancel(appointment_id): appointment = frappe.get_doc("Patient Appointment", appointment_id) - # If invoice --> fee_validity update with -1 visit - if appointment.sales_invoice: - validity = frappe.db.exists({"doctype": "Fee Validity", "ref_invoice": appointment.sales_invoice}) + # If invoiced --> fee_validity update with -1 visit + if appointment.invoiced: + validity = validity_exists(appointment.practitioner, appointment.patient) if validity: fee_validity = frappe.get_doc("Fee Validity", validity[0][0]) visited = fee_validity.visited - 1 frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) if visited <= 0: frappe.msgprint( - _("Appointment cancelled, Please review and cancel the invoice {0}".format(appointment.sales_invoice)) + _("Appointment cancelled, Please review and cancel the invoice {0}".format(fee_validity.ref_invoice)) ) else: frappe.msgprint(_("Appointment cancelled")) @@ -198,99 +194,12 @@ def confirm_sms(doc): send_message(doc, message) -@frappe.whitelist() -def invoice_appointment(appointment_doc): - if not appointment_doc.name: - return False - sales_invoice = frappe.new_doc("Sales Invoice") - sales_invoice.customer = frappe.get_value("Patient", appointment_doc.patient, "customer") - sales_invoice.appointment = appointment_doc.name - sales_invoice.due_date = getdate() - sales_invoice.is_pos = '0' - sales_invoice.company = appointment_doc.company - sales_invoice.debit_to = get_receivable_account(appointment_doc.company) - - fee_validity = get_fee_validity(appointment_doc.practitioner, appointment_doc.patient, appointment_doc.appointment_date) - procedure_template = False - if appointment_doc.procedure_template: - procedure_template = appointment_doc.procedure_template - create_invoice_items(appointment_doc.practitioner, appointment_doc.company, sales_invoice, procedure_template) - - sales_invoice.save(ignore_permissions=True) - frappe.db.sql("""update `tabPatient Appointment` set sales_invoice=%s where name=%s""", (sales_invoice.name, appointment_doc.name)) - frappe.db.set_value("Fee Validity", fee_validity.name, "ref_invoice", sales_invoice.name) - encounter = frappe.db.exists({ - "doctype": "Patient Encounter", - "appointment": appointment_doc.name}) - if encounter: - frappe.db.set_value("Patient Encounter", encounter[0][0], "invoice", sales_invoice.name) - return sales_invoice.name - - -def get_fee_validity(practitioner, patient, date): - validity_exist = validity_exists(practitioner, patient) - if validity_exist: - fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0]) - fee_validity = update_fee_validity(fee_validity, date) - else: - fee_validity = create_fee_validity(practitioner, patient, date) - return fee_validity - - def validity_exists(practitioner, patient): return frappe.db.exists({ "doctype": "Fee Validity", "practitioner": practitioner, "patient": patient}) - -def update_fee_validity(fee_validity, date): - max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") - valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") - if not valid_days: - valid_days = 1 - if not max_visit: - max_visit = 1 - date = getdate(date) - valid_till = date + datetime.timedelta(days=int(valid_days)) - fee_validity.max_visit = max_visit - fee_validity.visited = 1 - fee_validity.valid_till = valid_till - fee_validity.save(ignore_permissions=True) - return fee_validity - - -def create_fee_validity(practitioner, patient, date): - fee_validity = frappe.new_doc("Fee Validity") - fee_validity.practitioner = practitioner - fee_validity.patient = patient - fee_validity = update_fee_validity(fee_validity, date) - return fee_validity - - -def create_invoice_items(practitioner, company, invoice, procedure_template): - item_line = invoice.append("items") - if procedure_template: - procedure_template_obj = frappe.get_doc("Clinical Procedure Template", procedure_template) - item_line.item_code = procedure_template_obj.item_code - item_line.item_name = procedure_template_obj.template - item_line.description = procedure_template_obj.description - else: - item_line.item_name = "Consulting Charges" - item_line.description = "Consulting Charges: " + practitioner - item_line.uom = "Nos" - item_line.conversion_factor = 1 - item_line.income_account = get_income_account(practitioner, company) - op_consulting_charge = frappe.db.get_value("Healthcare Practitioner", practitioner, "op_consulting_charge") - if op_consulting_charge: - item_line.rate = op_consulting_charge - item_line.amount = op_consulting_charge - item_line.qty = 1 - - - return invoice - - @frappe.whitelist() def create_encounter(appointment): appointment = frappe.get_doc("Patient Appointment", appointment) @@ -301,8 +210,8 @@ def create_encounter(appointment): encounter.visit_department = appointment.department encounter.patient_sex = appointment.patient_sex encounter.encounter_date = appointment.appointment_date - if appointment.sales_invoice: - encounter.invoice = appointment.sales_invoice + if appointment.invoiced: + encounter.invoiced = True return encounter.as_dict() @@ -359,6 +268,7 @@ def get_events(start, end, filters=None): item.appointment_datetime = item.appointment_date + datetime.timedelta(minutes = item.duration) return data + @frappe.whitelist() def get_procedure_prescribed(patient): return frappe.db.sql("""select pp.name, pp.procedure, pp.parent, ct.practitioner, diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py index f9ef1cbe77..a030f19e95 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py @@ -10,10 +10,6 @@ def get_data(): { 'label': _('Consultations'), 'items': ['Patient Encounter', 'Vital Signs', 'Patient Medical Record'] - }, - { - 'label': _('Billing'), - 'items': ['Sales Invoice'] } ] } diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 47c9cada6b..2dd3512bc0 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -94,11 +94,6 @@ frappe.ui.form.on('Patient Encounter', { } }; }); - if(!frm.doc.__islocal && !frm.doc.invoice && (frappe.user.has_role("Accounts User"))){ - frm.add_custom_button(__('Invoice'), function() { - btn_invoice_encounter(frm); - },__("Create")); - } frm.set_df_property("appointment", "read_only", frm.doc.__islocal ? 0:1); frm.set_df_property("patient", "read_only", frm.doc.__islocal ? 0:1); frm.set_df_property("patient_age", "read_only", frm.doc.__islocal ? 0:1); @@ -139,23 +134,6 @@ var schedule_discharge = function(frm) { }); }; -var btn_invoice_encounter = function(frm){ - var doc = frm.doc; - frappe.call({ - method: - "erpnext.healthcare.doctype.encounter.encounter.create_invoice", - args: {company: doc.company, patient: doc.patient, practitioner: doc.practitioner, encounter_id: doc.name }, - callback: function(data){ - if(!data.exc){ - if(data.message){ - frappe.set_route("Form", "Sales Invoice", data.message); - } - cur_frm.reload_doc(); - } - } - }); -}; - var create_medical_record = function (frm) { if(!frm.doc.patient){ frappe.throw(__("Please select patient")); @@ -203,10 +181,16 @@ frappe.ui.form.on("Patient Encounter", "appointment", function(frm){ frappe.model.set_value(frm.doctype,frm.docname, "patient", data.message.patient); frappe.model.set_value(frm.doctype,frm.docname, "type", data.message.appointment_type); frappe.model.set_value(frm.doctype,frm.docname, "practitioner", data.message.practitioner); - frappe.model.set_value(frm.doctype,frm.docname, "invoice", data.message.sales_invoice); + frappe.model.set_value(frm.doctype,frm.docname, "invoiced", data.message.invoiced); } }); } + else{ + frappe.model.set_value(frm.doctype,frm.docname, "patient", ""); + frappe.model.set_value(frm.doctype,frm.docname, "type", ""); + frappe.model.set_value(frm.doctype,frm.docname, "practitioner", ""); + frappe.model.set_value(frm.doctype,frm.docname, "invoiced", 0); + } }); frappe.ui.form.on("Patient Encounter", "practitioner", function(frm) { diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py index 3d8f95298b..5aa7867167 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py @@ -7,7 +7,6 @@ import frappe from frappe.model.document import Document from frappe.utils import getdate, cstr import json -from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account class PatientEncounter(Document): def on_update(self): @@ -23,77 +22,6 @@ class PatientEncounter(Document): frappe.db.set_value("Patient Appointment", self.appointment, "status", "Open") delete_medical_record(self) -def set_sales_invoice_fields(company, patient): - sales_invoice = frappe.new_doc("Sales Invoice") - sales_invoice.customer = frappe.get_value("Patient", patient, "customer") - # patient is custom field in sales inv. - sales_invoice.due_date = getdate() - sales_invoice.is_pos = '0' - sales_invoice.debit_to = get_receivable_account(company) - - return sales_invoice - -def create_sales_invoice_item_lines(item, sales_invoice): - sales_invoice_line = sales_invoice.append("items") - sales_invoice_line.item_code = item.item_code - sales_invoice_line.item_name = item.item_name - sales_invoice_line.qty = 1.0 - sales_invoice_line.description = item.description - return sales_invoice_line - -@frappe.whitelist() -def create_drug_invoice(company, patient, prescriptions): - list_ids = json.loads(prescriptions) - if not (company or patient or prescriptions): - return False - - sales_invoice = set_sales_invoice_fields(company, patient) - sales_invoice.update_stock = 1 - - for line_id in list_ids: - line_obj = frappe.get_doc("Drug Prescription", line_id) - if line_obj: - if(line_obj.drug_code): - item = frappe.get_doc("Item", line_obj.drug_code) - sales_invoice_line = create_sales_invoice_item_lines(item, sales_invoice) - sales_invoice_line.qty = line_obj.get_quantity() - #income_account and cost_center in itemlines - by set_missing_values() - sales_invoice.set_missing_values() - return sales_invoice.as_dict() - -@frappe.whitelist() -def create_invoice(company, patient, practitioner, encounter_id): - if not encounter_id: - return False - sales_invoice = frappe.new_doc("Sales Invoice") - sales_invoice.customer = frappe.get_value("Patient", patient, "customer") - sales_invoice.due_date = getdate() - sales_invoice.is_pos = '0' - sales_invoice.debit_to = get_receivable_account(company) - - create_invoice_items(practitioner, sales_invoice, company) - - sales_invoice.save(ignore_permissions=True) - frappe.db.sql("""update `tabPatient Encounter` set invoice=%s where name=%s""", (sales_invoice.name, encounter_id)) - appointment = frappe.db.get_value("Patient Encounter", encounter_id, "appointment") - if appointment: - frappe.db.set_value("Patient Appointment", appointment, "sales_invoice", sales_invoice.name) - return sales_invoice.name - -def create_invoice_items(practitioner, invoice, company): - item_line = invoice.append("items") - item_line.item_name = "Consulting Charges" - item_line.description = "Consulting Charges: " + practitioner - item_line.qty = 1 - item_line.uom = "Nos" - item_line.conversion_factor = 1 - item_line.income_account = get_income_account(practitioner, company) - op_consulting_charge = frappe.get_value("Healthcare Practitioner", practitioner, "op_consulting_charge") - if op_consulting_charge: - item_line.rate = op_consulting_charge - item_line.amount = op_consulting_charge - return invoice - def insert_encounter_to_medical_record(doc): subject = set_subject_field(doc) medical_record = frappe.new_doc("Patient Medical Record") diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.py b/erpnext/healthcare/report/lab_test_report/lab_test_report.py index e4771c56bb..b9a26dfcad 100644 --- a/erpnext/healthcare/report/lab_test_report/lab_test_report.py +++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.py @@ -17,7 +17,7 @@ def execute(filters=None): data = [] for lab_test in lab_test_list: - row = [ lab_test.test_name, lab_test.patient, lab_test.practitioner, lab_test.invoice, lab_test.status, lab_test.result_date, lab_test.department] + row = [ lab_test.test_name, lab_test.patient, lab_test.practitioner, lab_test.invoiced, lab_test.status, lab_test.result_date, lab_test.department] data.append(row) return columns, data @@ -28,7 +28,7 @@ def get_columns(): _("Test") + ":Data:120", _("Patient") + ":Link/Patient:180", _("Healthcare Practitioner") + ":Link/Healthcare Practitioner:120", - _("Invoice") + ":Link/Sales Invoice:120", + _("Invoiced") + ":Check:100", _("Status") + ":Data:120", _("Result Date") + ":Date:120", _("Department") + ":Data:120", @@ -52,7 +52,7 @@ def get_conditions(filters): def get_lab_test(filters): conditions = get_conditions(filters) - return frappe.db.sql("""select name, patient, test_name, patient_name, status, result_date, practitioner, invoice, department + return frappe.db.sql("""select name, patient, test_name, patient_name, status, result_date, practitioner, invoiced, department from `tabLab Test` where docstatus<2 %s order by submitted_date desc, name desc""" % conditions, filters, as_dict=1) diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py new file mode 100644 index 0000000000..5a7c7e3680 --- /dev/null +++ b/erpnext/healthcare/utils.py @@ -0,0 +1,232 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, earthians and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import datetime +from frappe import _ +from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account +from erpnext.healthcare.doctype.patient_appointment.patient_appointment import validity_exists +from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity, update_fee_validity + +@frappe.whitelist() +def get_healthcare_services_to_invoice(patient): + patient = frappe.get_doc("Patient", patient) + if patient: + if patient.customer: + item_to_invoice = [] + patient_appointments = frappe.get_list("Patient Appointment",{'patient': patient.name, 'invoiced': False}, + order_by="appointment_date") + if patient_appointments: + fee_validity_details = [] + valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") + max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") + for patient_appointment in patient_appointments: + patient_appointment_obj = frappe.get_doc("Patient Appointment", patient_appointment['name']) + + if patient_appointment_obj.procedure_template: + if frappe.db.get_value("Clinical Procedure Template", patient_appointment_obj.procedure_template, "is_billable") == 1: + item_to_invoice.append({'reference_type': 'Patient Appointment', 'reference_name': patient_appointment_obj.name, 'service': patient_appointment_obj.procedure_template}) + else: + practitioner_exist_in_list = False + skip_invoice = False + if fee_validity_details: + for validity in fee_validity_details: + if validity['practitioner'] == patient_appointment_obj.practitioner: + practitioner_exist_in_list = True + if validity['valid_till'] >= patient_appointment_obj.appointment_date: + validity['visits'] = validity['visits']+1 + if int(max_visit) > validity['visits']: + skip_invoice = True + if not skip_invoice: + validity['visits'] = 1 + validity['valid_till'] = patient_appointment_obj.appointment_date + datetime.timedelta(days=int(valid_days)) + if not practitioner_exist_in_list: + valid_till = patient_appointment_obj.appointment_date + datetime.timedelta(days=int(valid_days)) + visits = 0 + validity_exist = validity_exists(patient_appointment_obj.practitioner, patient_appointment_obj.patient) + if validity_exist: + fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0]) + valid_till = fee_validity.valid_till + visits = fee_validity.visited + fee_validity_details.append({'practitioner': patient_appointment_obj.practitioner, + 'valid_till': valid_till, 'visits': visits}) + + if not skip_invoice: + practitioner_charge = 0 + income_account = None + if patient_appointment_obj.practitioner: + practitioner_charge = get_practitioner_charge(patient_appointment_obj.practitioner) + income_account = get_income_account(patient_appointment_obj.practitioner, patient_appointment_obj.company) + item_to_invoice.append({'reference_type': 'Patient Appointment', 'reference_name': patient_appointment_obj.name, + 'service': 'Consulting Charges', 'rate': practitioner_charge, + 'income_account': income_account}) + + encounters = frappe.get_list("Patient Encounter", {'patient': patient.name, 'invoiced': False, 'docstatus': 1}) + if encounters: + for encounter in encounters: + encounter_obj = frappe.get_doc("Patient Encounter", encounter['name']) + if not encounter_obj.appointment: + practitioner_charge = 0 + income_account = None + if encounter_obj.practitioner: + practitioner_charge = get_practitioner_charge(encounter_obj.practitioner) + income_account = get_income_account(encounter_obj.practitioner, encounter_obj.company) + item_to_invoice.append({'reference_type': 'Patient Encounter', 'reference_name': encounter_obj.name, + 'service': 'Consulting Charges', 'rate': practitioner_charge, + 'income_account': income_account}) + + lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False}) + if lab_tests: + for lab_test in lab_tests: + lab_test_obj = frappe.get_doc("Lab Test", lab_test['name']) + if frappe.db.get_value("Lab Test Template", lab_test_obj.template, "is_billable") == 1: + item_to_invoice.append({'reference_type': 'Lab Test', 'reference_name': lab_test_obj.name, 'service': lab_test_obj.template}) + + lab_rxs = frappe.db.sql("""select lp.name from `tabPatient Encounter` et, + `tabLab Prescription` lp where et.patient=%s and lp.parent=et.name and lp.test_created=0 and lp.invoiced=0""", (patient.name)) + if lab_rxs: + for lab_rx in lab_rxs: + rx_obj = frappe.get_doc("Lab Prescription", lab_rx[0]) + if rx_obj.test_code and (frappe.db.get_value("Lab Test Template", rx_obj.test_code, "is_billable") == 1): + item_to_invoice.append({'reference_type': 'Lab Prescription', 'reference_name': rx_obj.name, 'service': rx_obj.test_code}) + + procedures = frappe.get_list("Clinical Procedure", {'patient': patient.name, 'invoiced': False}) + if procedures: + for procedure in procedures: + procedure_obj = frappe.get_doc("Clinical Procedure", procedure['name']) + if not procedure_obj.appointment: + if procedure_obj.procedure_template and (frappe.db.get_value("Clinical Procedure Template", procedure_obj.procedure_template, "is_billable") == 1): + item_to_invoice.append({'reference_type': 'Clinical Procedure', 'reference_name': procedure_obj.name, 'service': procedure_obj.procedure_template}) + + procedure_rxs = frappe.db.sql("""select pp.name from `tabPatient Encounter` et, + `tabProcedure Prescription` pp where et.patient=%s and pp.parent=et.name and + pp.procedure_created=0 and pp.invoiced=0 and pp.appointment_booked=0""", (patient.name)) + if procedure_rxs: + for procedure_rx in procedure_rxs: + rx_obj = frappe.get_doc("Procedure Prescription", procedure_rx[0]) + if frappe.db.get_value("Clinical Procedure Template", rx_obj.procedure, "is_billable") == 1: + item_to_invoice.append({'reference_type': 'Procedure Prescription', 'reference_name': rx_obj.name, 'service': rx_obj.procedure}) + + procedure_consumables = frappe.db.sql("""select pc.name from `tabClinical Procedure` cp, + `tabClinical Procedure Item` pc where cp.patient=%s and pc.parent=cp.name and + pc.invoice_separately_as_consumables=1 and pc.invoiced=0""", (patient.name)) + if procedure_consumables: + for procedure_consumable in procedure_consumables: + procedure_consumable_obj = frappe.get_doc("Clinical Procedure Item", procedure_consumable[0]) + item_to_invoice.append({'reference_type': 'Clinical Procedure Item', 'reference_name': procedure_consumable_obj.name, + 'service': procedure_consumable_obj.item_code, 'qty': procedure_consumable_obj.qty}) + + return item_to_invoice + else: + frappe.throw(_("The Patient {0} do not have customer refrence to invoice").format(patient.name)) + +def get_practitioner_charge(practitioner): + practitioner_charge = frappe.db.get_value("Healthcare Practitioner", practitioner, "op_consulting_charge") + if practitioner_charge: + return practitioner_charge + +def manage_invoice_submit_cancel(doc, method): + if doc.items: + for item in doc.items: + if item.reference_dt and item.reference_dn: + if frappe.get_meta(item.reference_dt).has_field("invoiced"): + set_invoiced(item, method) + +def set_invoiced(item, method): + invoiced = False + if(method=="on_submit"): + validate_invoiced_on_submit(item) + invoiced = True + + frappe.db.set_value(item.reference_dt, item.reference_dn, "invoiced", invoiced) + if item.reference_dt == 'Patient Appointment': + if frappe.db.get_value('Patient Appointment', item.reference_dn, 'procedure_template'): + dt_from_appointment = "Clinical Procedure" + else: + manage_fee_validity(item.reference_dn, method) + dt_from_appointment = "Patient Encounter" + manage_doc_for_appoitnment(dt_from_appointment, item.reference_dn, invoiced) + + elif item.reference_dt == 'Lab Prescription': + manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, "Lab Test", "test_created") + + elif item.reference_dt == 'Procedure Prescription': + manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, "Clinical Procedure", "procedure_created") + +def validate_invoiced_on_submit(item): + is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, "invoiced") + if is_invoiced == 1: + frappe.throw(_("The item referenced by {0} - {1} is already invoiced"\ + ).format(item.reference_dt, item.reference_dn)) + +def manage_prescriptions(invoiced, ref_dt, ref_dn, dt, created_check_field): + created = frappe.db.get_value(ref_dt, ref_dn, created_check_field) + if created == 1: + # Fetch the doc created for the prescription + doc_created = frappe.db.get_value(dt, {'prescription': item.reference_dn}) + frappe.db.set_value(dt, doc_created, 'invoiced', invoiced) + +def manage_fee_validity(appointment_name, method): + appointment_doc = frappe.get_doc("Patient Appointment", appointment_name) + validity_exist = validity_exists(appointment_doc.practitioner, appointment_doc.patient) + do_not_update = False + visited = 0 + if validity_exist: + fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0]) + # Check if the validity is valid + if (fee_validity.valid_till >= appointment_doc.appointment_date): + if (method == "on_cancel" and appointment_doc.status != "Closed"): + visited = fee_validity.visited - 1 + if visited < 0: + visited = 0 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + do_not_update = True + elif (fee_validity.visited < fee_validity.max_visit): + visited = fee_validity.visited + 1 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + do_not_update = True + else: + do_not_update = False + + if not do_not_update: + fee_validity = update_fee_validity(fee_validity, appointment_doc.appointment_date) + visited = fee_validity.visited + else: + fee_validity = create_fee_validity(appointment_doc.practitioner, appointment_doc.patient, appointment_doc.appointment_date) + visited = fee_validity.visited + + # Mark All Patient Appointment invoiced = True in the validity range do not cross the max visit + if (method == "on_cancel"): + invoiced = True + else: + invoiced = False + patient_appointments = frappe.get_list("Patient Appointment",{'patient': fee_validity.patient, 'invoiced': invoiced, + 'appointment_date':("<=", fee_validity.valid_till), 'practitioner': fee_validity.practitioner}, order_by="appointment_date") + if patient_appointments and fee_validity: + visit = visited + for appointment in patient_appointments: + if (method == "on_cancel" and appointment.status != "Closed"): + visited = visited - 1 + if visited < 0: + visited = 0 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", False) + manage_doc_for_appoitnment("Patient Encounter", appointment.name, False) + elif int(fee_validity.max_visit) > visit: + visited = visited + 1 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", True) + manage_doc_for_appoitnment("Patient Encounter", appointment.name, True) + visit = visit + 1 + +def manage_doc_for_appoitnment(dt_from_appointment, appointment, invoiced): + dn_from_appointment = frappe.db.exists( + dt_from_appointment, + { + "appointment": appointment + } + ) + if dn_from_appointment: + frappe.db.set_value(dt_from_appointment, dn_from_appointment, "invoiced", invoiced) From bab55390f8b5c2d6674dc633b3278c3191b5d71d Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 23 Jul 2018 16:50:06 +0530 Subject: [PATCH 02/45] Sales Invoice in Patient Dashboard --- erpnext/healthcare/doctype/patient/patient_dashboard.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/healthcare/doctype/patient/patient_dashboard.py b/erpnext/healthcare/doctype/patient/patient_dashboard.py index 098497c39b..46b10136a3 100644 --- a/erpnext/healthcare/doctype/patient/patient_dashboard.py +++ b/erpnext/healthcare/doctype/patient/patient_dashboard.py @@ -13,6 +13,10 @@ def get_data(): { 'label': _('Lab Tests and Vital Signs'), 'items': ['Lab Test', 'Sample Collection', 'Vital Signs'] + }, + { + 'label': _('Billing'), + 'items': ['Sales Invoice'] } ] } From 8cb142bf2908674e62aecd854202364cf6fe782b Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Fri, 6 Jul 2018 16:04:01 +0530 Subject: [PATCH 03/45] Field Changes - Healthcare Invoice Work Flow Redesign --- .../clinical_procedure.json | 42 +++++++++- .../clinical_procedure_item.json | 79 ++++++++++++++++++- .../lab_prescription/lab_prescription.json | 33 +++++--- .../healthcare/doctype/lab_test/lab_test.json | 13 +-- .../patient_appointment.json | 11 +-- .../patient_encounter/patient_encounter.json | 11 +-- .../procedure_prescription.json | 69 +++++++++++++++- .../sample_collection/sample_collection.json | 12 +-- .../lab_test_print/lab_test_print.json | 6 +- .../web_form/lab_test/lab_test.json | 13 +-- 10 files changed, 238 insertions(+), 51 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json index 04b96e960a..0856606359 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json @@ -245,6 +245,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "prescription", + "fieldtype": "Link", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Procedure Prescription", + "length": 0, + "no_copy": 0, + "options": "Procedure Prescription", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -480,7 +513,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "is_invoiced", + "default": "0", + "fieldname": "invoiced", "fieldtype": "Check", "hidden": 0, "ignore_user_permissions": 0, @@ -489,9 +523,9 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Is Invoiced", + "label": "Invoiced", "length": 0, - "no_copy": 0, + "no_copy": 1, "permlevel": 0, "precision": "", "print_hide": 0, @@ -751,4 +785,4 @@ "track_changes": 1, "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json b/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json index c0a3247993..467fb7ae12 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json +++ b/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json @@ -14,6 +14,7 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 1, "collapsible": 0, @@ -46,6 +47,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -77,6 +79,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -108,6 +111,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -139,6 +143,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -171,6 +176,39 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "invoice_separately_as_consumables", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Invoice Separately as Consumables", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -201,6 +239,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -233,6 +272,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -264,6 +304,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -296,6 +337,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -327,6 +369,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -355,6 +398,40 @@ "set_only_once": 0, "translatable": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "depends_on": "invoice_separately_as_consumables", + "fieldname": "invoiced", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Invoiced", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 } ], "has_web_view": 0, @@ -367,7 +444,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-03-28 14:34:03.796229", + "modified": "2018-07-04 19:01:39.353654", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedure Item", diff --git a/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json b/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json index 127bebf627..2fbe58b7d8 100644 --- a/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json +++ b/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json @@ -13,6 +13,7 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -40,16 +41,17 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fetch_from": "test_code.test_name", + "fetch_from": "test_code.test_name", "fieldname": "test_name", "fieldtype": "Data", "hidden": 0, @@ -73,17 +75,19 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "invoice", - "fieldtype": "Link", + "default": "0", + "fieldname": "invoiced", + "fieldtype": "Check", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -91,10 +95,10 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Invoice", + "label": "Invoiced", "length": 0, - "no_copy": 0, - "options": "Sales Invoice", + "no_copy": 1, + "options": "", "permlevel": 0, "precision": "", "print_hide": 0, @@ -105,11 +109,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -135,11 +140,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -166,11 +172,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -197,7 +204,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 } ], @@ -211,7 +218,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-05-16 22:43:39.014193", + "modified": "2018-07-05 12:05:50.883490", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Prescription", diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json index 89b513f470..9db3ae50e2 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.json +++ b/erpnext/healthcare/doctype/lab_test/lab_test.json @@ -88,8 +88,9 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "invoice", - "fieldtype": "Link", + "default": "0", + "fieldname": "invoiced", + "fieldtype": "Check", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -97,10 +98,10 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Invoice", + "label": "Invoiced", "length": 0, "no_copy": 1, - "options": "Sales Invoice", + "options": "", "permlevel": 0, "precision": "", "print_hide": 0, @@ -1586,7 +1587,7 @@ "read_only": 0, "read_only_onload": 0, "restrict_to_domain": "Healthcare", - "search_fields": "patient,invoice,practitioner,test_name,sample", + "search_fields": "patient,practitioner,test_name,sample", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", @@ -1594,4 +1595,4 @@ "track_changes": 1, "track_seen": 1, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index 960648bb69..fc7b69d5e8 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -712,8 +712,9 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "sales_invoice", - "fieldtype": "Link", + "default": "0", + "fieldname": "invoiced", + "fieldtype": "Check", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -721,10 +722,10 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Sales Invoice", + "label": "Invoiced", "length": 0, "no_copy": 0, - "options": "Sales Invoice", + "options": "", "permlevel": 0, "precision": "", "print_hide": 0, @@ -989,4 +990,4 @@ "track_changes": 1, "track_seen": 1, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index d11d1a7c5f..84350883ce 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -482,8 +482,9 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "invoice", - "fieldtype": "Link", + "default": "0", + "fieldname": "invoiced", + "fieldtype": "Check", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -491,10 +492,10 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Invoice", + "label": "Invoiced", "length": 0, "no_copy": 1, - "options": "Sales Invoice", + "options": "", "permlevel": 0, "precision": "", "print_hide": 0, @@ -1145,4 +1146,4 @@ "track_changes": 1, "track_seen": 1, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json b/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json index b4c453280e..205d31dc88 100644 --- a/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json +++ b/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json @@ -240,6 +240,71 @@ "set_only_once": 0, "translatable": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "procedure_created", + "fieldtype": "Check", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Procedure Created", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fieldname": "invoiced", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Invoiced", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 } ], "has_web_view": 0, @@ -252,7 +317,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-07-16 13:08:15.499491", + "modified": "2018-07-17 13:08:15.499491", "modified_by": "Administrator", "module": "Healthcare", "name": "Procedure Prescription", @@ -267,4 +332,4 @@ "sort_order": "DESC", "track_changes": 1, "track_seen": 0 -} \ No newline at end of file +} diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json index 7655685cd6..783fc3d044 100644 --- a/erpnext/healthcare/doctype/sample_collection/sample_collection.json +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.json @@ -88,8 +88,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "invoice", - "fieldtype": "Link", + "fieldname": "invoiced", + "fieldtype": "Check", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -97,10 +97,10 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Invoice", + "label": "Invoiced", "length": 0, - "no_copy": 0, - "options": "Sales Invoice", + "no_copy": 1, + "options": "", "permlevel": 0, "precision": "", "print_hide": 0, @@ -684,4 +684,4 @@ "track_changes": 1, "track_seen": 1, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/healthcare/print_format/lab_test_print/lab_test_print.json b/erpnext/healthcare/print_format/lab_test_print/lab_test_print.json index 2f85ff6378..d3ad4409db 100644 --- a/erpnext/healthcare/print_format/lab_test_print/lab_test_print.json +++ b/erpnext/healthcare/print_format/lab_test_print/lab_test_print.json @@ -7,10 +7,10 @@ "docstatus": 0, "doctype": "Print Format", "font": "Default", - "html": "
\n {% if letter_head and not no_letterhead -%}\n
{{ letter_head }}
\n
\n {%- endif %}\n\n {% if (doc.docstatus != 1) %}\n Lab Tests have to be Submitted for Print .. !\n {% elif (frappe.db.get_value(\"Healthcare Settings\", \"None\", \"require_test_result_approval\") == '1' and doc.approval_status != \"Approved\") %}\n Lab Tests have to be Approved for Print .. !\n {%- else -%}\n
\n
\n {% if doc.invoice %}\n
\n
\n \n
\n
\n : {{doc.invoice}}\n
\n
\n {%- endif -%}\n\n
\n
\n \n
\n {% if doc.patient %}\n
\n : {{doc.patient}}\n
\n {% else %}\n
\n : Patient Name\n
\n {%- endif -%}\n
\n\n
\n
\n \n
\n
\n : {{doc.patient_age}}\n
\n
\n\n
\n
\n \n
\n
\n : {{doc.patient_sex}}\n
\n
\n\n
\n\n
\n\n
\n
\n \n
\n {% if doc.practitioner %}\n
\n : {{doc.practitioner}}\n
\n {%- endif -%}\n
\n\n {% if doc.sample_date %}\n
\n
\n \n
\n
\n : {{doc.sample_date}}\n
\n
\n {%- endif -%}\n\n {% if doc.result_date %}\n
\n
\n \n
\n
\n : {{doc.result_date}}\n
\n
\n {%- endif -%}\n\n
\n\n
\n\n
\n

Department of {{doc.department}}

\n
\n\n \n \n {%- if doc.normal_test_items -%}\n \n \n \n \n \n\n {%- if doc.normal_test_items|length > 1 %}\n \n {%- endif -%}\n\n {%- for row in doc.normal_test_items -%}\n \n \n\n \n\n \n \n\n {%- endfor -%}\n {%- endif -%}\n \n
Name of TestResultNormal Range
{{ doc.test_name }}
\n {%- if doc.normal_test_items|length > 1 %}  {%- endif -%}\n {%- if row.test_name -%}{{ row.test_name }}\n {%- else -%}   {%- endif -%}\n {%- if row.test_event -%}   {{ row.test_event }}{%- endif -%}\n \n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%} \n {%- if row.test_uom -%}{{ row.test_uom }}{%- endif -%}\n \n
\n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n
\n
\n\n \n \n {%- if doc.special_test_items -%}\n \n \n \n \n \n {%- for row in doc.special_test_items -%}\n \n \n \n \n\n {%- endfor -%}\n {%- endif -%}\n\n {%- if doc.sensitivity_test_items -%}\n \n \n \n \n {%- for row in doc.sensitivity_test_items -%}\n \n \n \n \n\n {%- endfor -%}\n {%- endif -%}\n\n \n
Name of TestResult
{{ doc.test_name }}
  {{ row.test_particulars }} \n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%}\n
AntibioticSensitivity
{{ row.antibiotic }} {{ row.antibiotic_sensitivity }}
\n {%- endif -%}\n\n
\n {%- if (frappe.db.get_value(\"Healthcare Settings\", \"None\", \"employee_name_and_designation_in_print\") == '1') -%}\n
{{doc.employee_name}}
\n
{{doc.employee_designation}}
\n {%- else -%}\n
{{frappe.db.get_value(\"Healthcare Settings\", \"None\", \"custom_signature_in_print\") }}
\n {%- endif -%}\n
\n
\n", + "html": "
\n {% if letter_head and not no_letterhead -%}\n
{{ letter_head }}
\n
\n {%- endif %}\n\n {% if (doc.docstatus != 1) %}\n Lab Tests have to be Submitted for Print .. !\n {% elif (frappe.db.get_value(\"Healthcare Settings\", \"None\", \"require_test_result_approval\") == '1' and doc.approval_status != \"Approved\") %}\n Lab Tests have to be Approved for Print .. !\n {%- else -%}\n
\n
\n\n
\n
\n \n
\n {% if doc.patient %}\n
\n : {{doc.patient}}\n
\n {% else %}\n
\n : Patient Name\n
\n {%- endif -%}\n
\n\n
\n
\n \n
\n
\n : {{doc.patient_age}}\n
\n
\n\n
\n
\n \n
\n
\n : {{doc.patient_sex}}\n
\n
\n\n
\n\n
\n\n
\n
\n \n
\n {% if doc.practitioner %}\n
\n : {{doc.practitioner}}\n
\n {%- endif -%}\n
\n\n {% if doc.sample_date %}\n
\n
\n \n
\n
\n : {{doc.sample_date}}\n
\n
\n {%- endif -%}\n\n {% if doc.result_date %}\n
\n
\n \n
\n
\n : {{doc.result_date}}\n
\n
\n {%- endif -%}\n\n
\n\n
\n\n
\n

Department of {{doc.department}}

\n
\n\n \n \n {%- if doc.normal_test_items -%}\n \n \n \n \n \n\n {%- if doc.normal_test_items|length > 1 %}\n \n {%- endif -%}\n\n {%- for row in doc.normal_test_items -%}\n \n \n\n \n\n \n \n\n {%- endfor -%}\n {%- endif -%}\n \n
Name of TestResultNormal Range
{{ doc.test_name }}
\n {%- if doc.normal_test_items|length > 1 %}  {%- endif -%}\n {%- if row.test_name -%}{{ row.test_name }}\n {%- else -%}   {%- endif -%}\n {%- if row.test_event -%}   {{ row.test_event }}{%- endif -%}\n \n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%} \n {%- if row.test_uom -%}{{ row.test_uom }}{%- endif -%}\n \n
\n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n
\n
\n\n \n \n {%- if doc.special_test_items -%}\n \n \n \n \n \n {%- for row in doc.special_test_items -%}\n \n \n \n \n\n {%- endfor -%}\n {%- endif -%}\n\n {%- if doc.sensitivity_test_items -%}\n \n \n \n \n {%- for row in doc.sensitivity_test_items -%}\n \n \n \n \n\n {%- endfor -%}\n {%- endif -%}\n\n \n
Name of TestResult
{{ doc.test_name }}
  {{ row.test_particulars }} \n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%}\n
AntibioticSensitivity
{{ row.antibiotic }} {{ row.antibiotic_sensitivity }}
\n {%- endif -%}\n\n
\n {%- if (frappe.db.get_value(\"Healthcare Settings\", \"None\", \"employee_name_and_designation_in_print\") == '1') -%}\n
{{doc.employee_name}}
\n
{{doc.employee_designation}}
\n {%- else -%}\n
{{frappe.db.get_value(\"Healthcare Settings\", \"None\", \"custom_signature_in_print\") }}
\n {%- endif -%}\n
\n
\n", "idx": 0, "line_breaks": 0, - "modified": "2018-07-10 11:29:24.167265", + "modified": "2018-07-13 12:51:25.750441", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test Print", @@ -19,4 +19,4 @@ "print_format_type": "Server", "show_section_headings": 0, "standard": "Yes" -} \ No newline at end of file +} diff --git a/erpnext/healthcare/web_form/lab_test/lab_test.json b/erpnext/healthcare/web_form/lab_test/lab_test.json index 89029fac75..f6f51b2fad 100644 --- a/erpnext/healthcare/web_form/lab_test/lab_test.json +++ b/erpnext/healthcare/web_form/lab_test/lab_test.json @@ -18,7 +18,7 @@ "is_standard": 1, "login_required": 1, "max_attachment_size": 0, - "modified": "2018-07-16 13:10:47.940128", + "modified": "2018-07-17 13:10:47.940128", "modified_by": "Administrator", "module": "Healthcare", "name": "lab-test", @@ -44,13 +44,14 @@ "reqd": 1 }, { - "fieldname": "invoice", - "fieldtype": "Link", + "default": "0", + "fieldname": "invoiced", + "fieldtype": "Check", "hidden": 0, - "label": "Invoice", + "label": "Invoiced", "max_length": 0, "max_value": 0, - "options": "Sales Invoice", + "options": "", "read_only": 0, "reqd": 0 }, @@ -232,4 +233,4 @@ "reqd": 0 } ] -} \ No newline at end of file +} From 54ae74eb28112f4f9c875017a039d3f1c91b259c Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Fri, 13 Jul 2018 21:10:14 +0530 Subject: [PATCH 04/45] Inpatient Billing --- .../doctype/sales_invoice/sales_invoice.js | 2 +- erpnext/domains/healthcare.py | 2 +- .../inpatient_occupancy.json | 37 +++++++++++++- .../inpatient_record/inpatient_record.py | 50 ++++++++++++++++++- erpnext/healthcare/utils.py | 15 ++++++ 5 files changed, 100 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index daf1bfea4b..facfa53d98 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -984,7 +984,7 @@ var set_primary_action= function(frm, dialog, $results) { frappe.model.set_value(si_item.doctype, si_item.name, 'reference_dn', checked_values[i]['dn']); frappe.model.set_value(si_item.doctype, si_item.name, 'qty', 1); if(checked_values[i]['qty'] > 1){ - frappe.model.set_value(si_item.doctype, si_item.name, 'qty', checked_values[i]['qty']); + frappe.model.set_value(si_item.doctype, si_item.name, 'qty', parseFloat(checked_values[i]['qty'])); } } frm.refresh_fields(); diff --git a/erpnext/domains/healthcare.py b/erpnext/domains/healthcare.py index 57f30f6301..ee8dc8161b 100644 --- a/erpnext/domains/healthcare.py +++ b/erpnext/domains/healthcare.py @@ -31,7 +31,7 @@ data = { 'insert_after': 'patient', 'read_only': True }, { - 'fieldname': 'ref_practitioner', 'label': 'Referring Practitioner', 'fieldtype': 'Link', 'options': 'Practitioner', + 'fieldname': 'ref_practitioner', 'label': 'Referring Practitioner', 'fieldtype': 'Link', 'options': 'Healthcare Practitioner', 'insert_after': 'customer' } ], diff --git a/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.json b/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.json index 2ac498df11..3e10143942 100644 --- a/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.json +++ b/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.json @@ -140,6 +140,39 @@ "set_only_once": 0, "translatable": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fieldname": "invoiced", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Invoiced", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 } ], "has_web_view": 0, @@ -152,7 +185,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-07-17 18:26:46.009878", + "modified": "2018-07-18 18:26:46.009878", "modified_by": "Administrator", "module": "Healthcare", "name": "Inpatient Occupancy", @@ -167,4 +200,4 @@ "sort_order": "DESC", "track_changes": 1, "track_seen": 0 -} \ No newline at end of file +} diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index 07cd9e467e..2ed3f8e065 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -69,17 +69,20 @@ def schedule_inpatient(patient, encounter_id, practitioner): inpatient_record.save(ignore_permissions = True) @frappe.whitelist() -def schedule_discharge(patient, encounter_id, practitioner): +def schedule_discharge(patient, encounter_id=None, practitioner=None): inpatient_record_id = frappe.db.get_value('Patient', patient, 'inpatient_record') if inpatient_record_id: inpatient_record = frappe.get_doc("Inpatient Record", inpatient_record_id) inpatient_record.discharge_practitioner = practitioner inpatient_record.discharge_encounter = encounter_id inpatient_record.status = "Discharge Scheduled" + + check_out_inpatient(inpatient_record) + inpatient_record.save(ignore_permissions = True) frappe.db.set_value("Patient", patient, "inpatient_status", "Discharge Scheduled") -def discharge_patient(inpatient_record): +def check_out_inpatient(inpatient_record): if inpatient_record.inpatient_occupancies: for inpatient_occupancy in inpatient_record.inpatient_occupancies: if inpatient_occupancy.left != 1: @@ -87,11 +90,54 @@ def discharge_patient(inpatient_record): inpatient_occupancy.check_out = now_datetime() frappe.db.set_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "occupied", False) +def discharge_patient(inpatient_record): + validate_invoiced_inpatient(inpatient_record) inpatient_record.discharge_date = today() inpatient_record.status = "Discharged" inpatient_record.save(ignore_permissions = True) +def validate_invoiced_inpatient(inpatient_record): + pending_invoices = [] + if inpatient_record.inpatient_occupancies: + service_unit_names = False + for inpatient_occupancy in inpatient_record.inpatient_occupancies: + if inpatient_occupancy.invoiced != 1: + if service_unit_names: + service_unit_names += ", " + inpatient_occupancy.service_unit + else: + service_unit_names = inpatient_occupancy.service_unit + if service_unit_names: + pending_invoices.append("Inpatient Occupancy (" + service_unit_names + ")") + + docs = ["Patient Appointment", "Patient Encounter", "Lab Test", "Clinical Procedure"] + + for doc in docs: + doc_name_list = get_inpatient_docs_not_invoiced(doc, inpatient_record) + if doc_name_list: + pending_invoices = get_doc_pendig(doc, doc_name_list, pending_invoices) + + if pending_invoices: + frappe.throw(_("Can not mark Inpatient Record Discharged, there are Unpaid Invoices {0}").format(", " + .join(map(lambda x: """ {0}""".format(x), pending_invoices)))) + +def get_doc_pendig(doc, doc_name_list, pending_invoices): + if doc_name_list: + doc_ids = False + for doc_name in doc_name_list: + if doc_ids: + doc_ids += ", "+doc_name.name + else: + doc_ids = doc_name.name + if doc_ids: + pending_invoices.append(doc + " (" + doc_ids + ")") + + return pending_invoices + +def get_inpatient_docs_not_invoiced(doc, inpatient_record): + return frappe.db.get_list(doc, filters = {"patient": inpatient_record.patient, + "inpatient_record": inpatient_record.name, "invoiced": 0}) + def admit_patient(inpatient_record, service_unit, check_in, expected_discharge=None): inpatient_record.admitted_datetime = check_in inpatient_record.status = "Admitted" diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 5a7c7e3680..5145c85076 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe import datetime from frappe import _ +from frappe.utils import date_diff, getdate from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account from erpnext.healthcare.doctype.patient_appointment.patient_appointment import validity_exists from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity, update_fee_validity @@ -118,6 +119,20 @@ def get_healthcare_services_to_invoice(patient): item_to_invoice.append({'reference_type': 'Clinical Procedure Item', 'reference_name': procedure_consumable_obj.name, 'service': procedure_consumable_obj.item_code, 'qty': procedure_consumable_obj.qty}) + inpatient_services = frappe.db.sql("""select io.name, io.parent from `tabInpatient Record` ip, + `tabInpatient Occupancy` io where ip.patient=%s and io.parent=ip.name and + io.left=1 and io.invoiced=0""", (patient.name)) + if inpatient_services: + for inpatient_service in inpatient_services: + inpatient_occupancy = frappe.get_doc("Inpatient Occupancy", inpatient_service[0]) + service_unit_type = frappe.get_doc("Healthcare Service Unit Type", frappe.db.get_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "service_unit_type")) + if service_unit_type and service_unit_type.is_billable == 1: + qty = date_diff(getdate(inpatient_occupancy.check_out), getdate(inpatient_occupancy.check_in)) + if qty < 1: + qty = 1 + item_to_invoice.append({'reference_type': 'Inpatient Occupancy', 'reference_name': inpatient_occupancy.name, + 'service': service_unit_type.item, 'qty': qty}) + return item_to_invoice else: frappe.throw(_("The Patient {0} do not have customer refrence to invoice").format(patient.name)) From f7a6531fb32c448b93d15038725484e00db988a8 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Tue, 24 Jul 2018 16:45:14 +0530 Subject: [PATCH 05/45] Inpatient Record - Test - Updated --- .../inpatient_record/test_inpatient_record.py | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py index b192064f61..f912ddb043 100644 --- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py @@ -7,10 +7,11 @@ import frappe import unittest from frappe.utils import now_datetime, today from frappe.utils.make_random import get_random -from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient +from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge class TestInpatientRecord(unittest.TestCase): def test_admit_and_discharge(self): + frappe.db.sql("""delete from `tabInpatient Record`""") patient = get_patient() # Schedule Admission ip_record = create_inpatient(patient) @@ -25,10 +26,18 @@ class TestInpatientRecord(unittest.TestCase): self.assertEqual(1, frappe.db.get_value("Healthcare Service Unit", service_unit, "occupied")) # Discharge - discharge_patient(ip_record) + schedule_discharge(patient=patient) + self.assertEqual(0, frappe.db.get_value("Healthcare Service Unit", service_unit, "occupied")) + + ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name) + # Validate Pending Invoices + self.assertRaises(frappe.ValidationError, ip_record.discharge) + mark_invoiced_inpatient_occupancy(ip_record1) + + discharge_patient(ip_record1) + self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_record")) self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_status")) - self.assertEqual(0, frappe.db.get_value("Healthcare Service Unit", service_unit, "occupied")) def test_validate_overlap_admission(self): frappe.db.sql("""delete from `tabInpatient Record`""") @@ -45,6 +54,12 @@ class TestInpatientRecord(unittest.TestCase): self.assertRaises(frappe.ValidationError, ip_record_new.save) frappe.db.sql("""delete from `tabInpatient Record`""") +def mark_invoiced_inpatient_occupancy(ip_record): + if ip_record.inpatient_occupancies: + for inpatient_occupancy in ip_record.inpatient_occupancies: + inpatient_occupancy.invoiced = 1 + ip_record.save(ignore_permissions = True) + def create_inpatient(patient): patient_obj = frappe.get_doc('Patient', patient) inpatient_record = frappe.new_doc('Inpatient Record') From 146683bbffecd7e2f7fce558611ba35672a15972 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 25 Jul 2018 11:30:30 +0530 Subject: [PATCH 06/45] Fee Validity - invoice reference --- .../healthcare/doctype/fee_validity/fee_validity.py | 7 ++++--- erpnext/healthcare/utils.py | 12 ++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.py b/erpnext/healthcare/doctype/fee_validity/fee_validity.py index a707236c86..90285459ce 100644 --- a/erpnext/healthcare/doctype/fee_validity/fee_validity.py +++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.py @@ -11,7 +11,7 @@ import datetime class FeeValidity(Document): pass -def update_fee_validity(fee_validity, date): +def update_fee_validity(fee_validity, date, ref_invoice=None): max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") if not valid_days: @@ -23,13 +23,14 @@ def update_fee_validity(fee_validity, date): fee_validity.max_visit = max_visit fee_validity.visited = 1 fee_validity.valid_till = valid_till + fee_validity.ref_invoice = ref_invoice fee_validity.save(ignore_permissions=True) return fee_validity -def create_fee_validity(practitioner, patient, date): +def create_fee_validity(practitioner, patient, date, ref_invoice=None): fee_validity = frappe.new_doc("Fee Validity") fee_validity.practitioner = practitioner fee_validity.patient = patient - fee_validity = update_fee_validity(fee_validity, date) + fee_validity = update_fee_validity(fee_validity, date, ref_invoice) return fee_validity diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 5145c85076..b038d8f0a5 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -147,9 +147,9 @@ def manage_invoice_submit_cancel(doc, method): for item in doc.items: if item.reference_dt and item.reference_dn: if frappe.get_meta(item.reference_dt).has_field("invoiced"): - set_invoiced(item, method) + set_invoiced(item, method, doc.name) -def set_invoiced(item, method): +def set_invoiced(item, method, ref_invoice=None): invoiced = False if(method=="on_submit"): validate_invoiced_on_submit(item) @@ -160,7 +160,7 @@ def set_invoiced(item, method): if frappe.db.get_value('Patient Appointment', item.reference_dn, 'procedure_template'): dt_from_appointment = "Clinical Procedure" else: - manage_fee_validity(item.reference_dn, method) + manage_fee_validity(item.reference_dn, method, ref_invoice) dt_from_appointment = "Patient Encounter" manage_doc_for_appoitnment(dt_from_appointment, item.reference_dn, invoiced) @@ -183,7 +183,7 @@ def manage_prescriptions(invoiced, ref_dt, ref_dn, dt, created_check_field): doc_created = frappe.db.get_value(dt, {'prescription': item.reference_dn}) frappe.db.set_value(dt, doc_created, 'invoiced', invoiced) -def manage_fee_validity(appointment_name, method): +def manage_fee_validity(appointment_name, method, ref_invoice=None): appointment_doc = frappe.get_doc("Patient Appointment", appointment_name) validity_exist = validity_exists(appointment_doc.practitioner, appointment_doc.patient) do_not_update = False @@ -206,10 +206,10 @@ def manage_fee_validity(appointment_name, method): do_not_update = False if not do_not_update: - fee_validity = update_fee_validity(fee_validity, appointment_doc.appointment_date) + fee_validity = update_fee_validity(fee_validity, appointment_doc.appointment_date, ref_invoice) visited = fee_validity.visited else: - fee_validity = create_fee_validity(appointment_doc.practitioner, appointment_doc.patient, appointment_doc.appointment_date) + fee_validity = create_fee_validity(appointment_doc.practitioner, appointment_doc.patient, appointment_doc.appointment_date, ref_invoice) visited = fee_validity.visited # Mark All Patient Appointment invoiced = True in the validity range do not cross the max visit From 94efed161ab704b2d8d810bf600e3cdbfa313ab0 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 25 Jul 2018 11:31:14 +0530 Subject: [PATCH 07/45] Fee Validity - Test - Updated --- .../doctype/fee_validity/test_fee_validity.py | 65 +++++++++++++++---- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py index 64222adac3..b8305d748f 100644 --- a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py +++ b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py @@ -5,33 +5,35 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.healthcare.doctype.patient_appointment.patient_appointment import invoice_appointment from frappe.utils.make_random import get_random -from frappe.utils import nowdate, add_days -# test_records = frappe.get_test_records('Fee Validity') +from frappe.utils import nowdate, add_days, getdate + +test_dependencies = ["Company"] class TestFeeValidity(unittest.TestCase): def test_fee_validity(self): + frappe.db.sql("""delete from `tabPatient Appointment`""") + frappe.db.sql("""delete from `tabFee Validity`""") patient = get_random("Patient") practitioner = get_random("Healthcare Practitioner") department = get_random("Medical Department") if not patient: patient = frappe.new_doc("Patient") - patient.patient_name = "Test Patient" + patient.patient_name = "_Test Patient" patient.sex = "Male" patient.save(ignore_permissions=True) patient = patient.name if not department: medical_department = frappe.new_doc("Medical Department") - medical_department.department = "Test Medical Department" + medical_department.department = "_Test Medical Department" medical_department.save(ignore_permissions=True) department = medical_department.name if not practitioner: practitioner = frappe.new_doc("Healthcare Practitioner") - practitioner.first_name = "Amit Jain" + practitioner.first_name = "_Test Healthcare Practitioner" practitioner.department = department practitioner.save(ignore_permissions=True) practitioner = practitioner.name @@ -42,18 +44,22 @@ class TestFeeValidity(unittest.TestCase): frappe.db.set_value("Healthcare Settings", None, "valid_days", 7) appointment = create_appointment(patient, practitioner, nowdate(), department) - invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice") - self.assertEqual(invoice, None) + invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced") + self.assertEqual(invoiced, 0) + invoice_appointment(appointment) + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4), department) - invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice") - self.assertTrue(invoice) + invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced") + self.assertTrue(invoiced) + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 5), department) - invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice") - self.assertEqual(invoice, None) + invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced") + self.assertEqual(invoiced, 0) + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 10), department) - invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice") - self.assertEqual(invoice, None) + invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced") + self.assertEqual(invoiced, 0) def create_appointment(patient, practitioner, appointment_date, department): appointment = frappe.new_doc("Patient Appointment") @@ -64,3 +70,34 @@ def create_appointment(patient, practitioner, appointment_date, department): appointment.company = "_Test Company" appointment.save(ignore_permissions=True) return appointment + +def invoice_appointment(appointment_doc): + if not appointment_doc.name: + return False + sales_invoice = frappe.new_doc("Sales Invoice") + sales_invoice.customer = frappe.get_value("Patient", appointment_doc.patient, "customer") + sales_invoice.due_date = getdate() + sales_invoice.is_pos = 0 + sales_invoice.company = appointment_doc.company + sales_invoice.debit_to = "_Test Receivable - _TC" + + create_invoice_items(appointment_doc, sales_invoice) + + sales_invoice.save(ignore_permissions=True) + sales_invoice.submit() + +def create_invoice_items(appointment, invoice): + item_line = invoice.append("items") + item_line.item_name = "Consulting Charges" + item_line.description = "Consulting Charges: " + appointment.practitioner + item_line.uom = "Nos" + item_line.conversion_factor = 1 + item_line.income_account = "_Test Account Cost for Goods Sold - _TC" + item_line.cost_center = "_Test Cost Center - _TC" + item_line.rate = 250 + item_line.amount = 250 + item_line.qty = 1 + item_line.reference_dt = "Patient Appointment" + item_line.reference_dn = appointment.name + + return invoice From c6974a79a82366f2ce877f19efa6ff0fd1ea5d74 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 25 Jul 2018 12:02:39 +0530 Subject: [PATCH 08/45] Sales Invoice - show Healthcare Fields - Healthcare in active Domains --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index facfa53d98..fe58e80c5b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -741,12 +741,20 @@ frappe.ui.form.on('Sales Invoice', { }, refresh: function(frm) { if (frappe.boot.active_domains.includes("Healthcare")){ + frm.set_df_property("patient", "hidden", 0); + frm.set_df_property("patient_name", "hidden", 0); + frm.set_df_property("ref_practitioner", "hidden", 0); if (cint(frm.doc.docstatus==0) && cur_frm.page.current_view_name!=="pos" && !frm.doc.is_return) { frm.add_custom_button(__('Healthcare Services'), function() { get_healthcare_services_to_invoice(frm); },"Get items from"); } } + else{ + frm.set_df_property("patient", "hidden", 1); + frm.set_df_property("patient_name", "hidden", 1); + frm.set_df_property("ref_practitioner", "hidden", 1); + } } }) From fd2879c07754d31da009fcfd09fdc5c3000a9e90 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Tue, 17 Jul 2018 12:14:39 +0530 Subject: [PATCH 09/45] Patch - Healthcare - Redesign Billing --- erpnext/patches.txt | 1 + .../redesign_healthcare_billing_work_flow.py | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1af75e8397..017eecc07b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -563,3 +563,4 @@ erpnext.patches.v11_0.reset_publish_in_hub_for_all_items erpnext.patches.v11_0.update_hub_url # 2018-08-31 erpnext.patches.v10_0.set_discount_amount erpnext.patches.v10_0.recalculate_gross_margin_for_project +erpnext.patches.v11_0.redesign_healthcare_billing_work_flow diff --git a/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py b/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py new file mode 100644 index 0000000000..719d23eec6 --- /dev/null +++ b/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py @@ -0,0 +1,53 @@ +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields +from erpnext.domains.healthcare import data +from frappe.modules import scrub, get_doctype_module + +sales_invoice_referenced_doc = { + "Patient Appointment": "sales_invoice", + "Patient Encounter": "invoice", + "Lab Test": "invoice", + "Lab Prescription": "invoice", + "Sample Collection": "invoice" +} + +def execute(): + healthcare_custom_field_in_sales_invoice() + for si_ref_doc in sales_invoice_referenced_doc: + if frappe.db.exists('DocType', si_ref_doc): + frappe.reload_doc(get_doctype_module(si_ref_doc), 'doctype', scrub(si_ref_doc)) + + if frappe.db.has_column(si_ref_doc, sales_invoice_referenced_doc[si_ref_doc]) \ + and frappe.db.has_column(si_ref_doc, 'invoiced'): + doc_list = frappe.db.sql(""" + select name from `tab{0}` + where {1} is not null + """.format(si_ref_doc, sales_invoice_referenced_doc[si_ref_doc])) + if doc_list: + frappe.reload_doc(get_doctype_module("Sales Invoice"), 'doctype', 'sales_invoice') + for doc_id in doc_list: + invoice_id = frappe.db.get_value(si_ref_doc, doc_id[0][0], sales_invoice_referenced_doc[si_ref_doc]) + invoice = frappe.get_doc("Sales Invoice", invoice_id) + if invoice.docstatus == 1 and invoice.items: + marked = False + if not marked: + for item_line in invoice.items: + marked = True + item_line.reference_dt = si_ref_doc + item_line.reference_dn = doc_id[0][0] + invoice.update() + + + frappe.db.sql(""" + update `tab{0}` set invoiced = 1 + where {1} is not null + """.format(si_ref_doc, sales_invoice_referenced_doc[si_ref_doc])) + +def healthcare_custom_field_in_sales_invoice(): + if data['custom_fields']: + create_custom_fields(data['custom_fields']) + + frappe.db.sql(""" + delete from `tabCustom Field` + where fieldname = 'appointment' and options = 'Patient Appointment' + """) From eafb046fceee6a8382d97a0dc565c7292d216c6f Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 25 Jul 2018 13:15:12 +0530 Subject: [PATCH 10/45] Healthcare Utils - prescription - invoice - fix --- erpnext/healthcare/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index b038d8f0a5..d42a5bb318 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -180,7 +180,7 @@ def manage_prescriptions(invoiced, ref_dt, ref_dn, dt, created_check_field): created = frappe.db.get_value(ref_dt, ref_dn, created_check_field) if created == 1: # Fetch the doc created for the prescription - doc_created = frappe.db.get_value(dt, {'prescription': item.reference_dn}) + doc_created = frappe.db.get_value(dt, {'prescription': ref_dn}) frappe.db.set_value(dt, doc_created, 'invoiced', invoiced) def manage_fee_validity(appointment_name, method, ref_invoice=None): From 7f51ffa5eb6f6ee7141c7d8815a3f864531a95bb Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 25 Jul 2018 15:39:40 +0530 Subject: [PATCH 11/45] Patch - Healthcare - Redesign Billing - fix --- .../redesign_healthcare_billing_work_flow.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py b/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py index 719d23eec6..e6c41ef077 100644 --- a/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py +++ b/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py @@ -19,6 +19,7 @@ def execute(): if frappe.db.has_column(si_ref_doc, sales_invoice_referenced_doc[si_ref_doc]) \ and frappe.db.has_column(si_ref_doc, 'invoiced'): + # Set Reference DocType and Reference Docname doc_list = frappe.db.sql(""" select name from `tab{0}` where {1} is not null @@ -26,21 +27,24 @@ def execute(): if doc_list: frappe.reload_doc(get_doctype_module("Sales Invoice"), 'doctype', 'sales_invoice') for doc_id in doc_list: - invoice_id = frappe.db.get_value(si_ref_doc, doc_id[0][0], sales_invoice_referenced_doc[si_ref_doc]) + invoice_id = frappe.db.get_value(si_ref_doc, doc_id[0], sales_invoice_referenced_doc[si_ref_doc]) invoice = frappe.get_doc("Sales Invoice", invoice_id) - if invoice.docstatus == 1 and invoice.items: + if invoice.items: marked = False if not marked: for item_line in invoice.items: marked = True - item_line.reference_dt = si_ref_doc - item_line.reference_dn = doc_id[0][0] - invoice.update() - + frappe.db.sql(""" + update `tabSales Invoice Item` + set reference_dt = '{0}', reference_dn = '{1}' + where name = '{2}' + """.format(si_ref_doc, doc_id[0], item_line.name)) + # Documents mark invoiced for submitted sales invoice frappe.db.sql(""" - update `tab{0}` set invoiced = 1 - where {1} is not null + update `tab{0}` doc, `tabSales Invoice` si + set doc.invoiced = 1 + where si.docstatus = 1 and doc.{1} = si.name """.format(si_ref_doc, sales_invoice_referenced_doc[si_ref_doc])) def healthcare_custom_field_in_sales_invoice(): From b2a615b8e83d50b5641174e1d46af94edf9a4785 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 25 Jul 2018 15:53:50 +0530 Subject: [PATCH 12/45] Healthcare Practitioner - new field - Inpatient Visit Charge --- .../healthcare_practitioner.json | 65 ++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json index e7c575da9c..7e794cce45 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json @@ -561,6 +561,69 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_18", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "inpatient_visit_charge", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Inpatient Visit Charge", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -798,7 +861,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-07-10 11:18:58.760297", + "modified": "2018-07-20 16:24:40.314459", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Practitioner", From edf8821a4eacbee7ec740620e857babca72e543b Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 25 Jul 2018 12:13:04 +0530 Subject: [PATCH 13/45] Healthcare Settings - fields - service items --- .../healthcare_settings.json | 166 +++++++++++++++++- 1 file changed, 164 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json index 0bd8534f95..a63ed88ed9 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json @@ -305,6 +305,168 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "healthcare_service_items", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Healthcare Service Items", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "inpatient_visit_charge_item", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Inpatient Visit Charge Item", + "length": 0, + "no_copy": 0, + "options": "Item", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "op_consulting_charge_item", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Out Patient Consulting Charge Item", + "length": 0, + "no_copy": 0, + "options": "Item", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_13", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "clinical_procedure_consumable_item", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Clinical Procedure Consumable Item", + "length": 0, + "no_copy": 0, + "options": "Item", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -1099,7 +1261,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2018-07-16 14:00:04.171717", + "modified": "2018-07-20 16:22:51.148456", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Settings", @@ -1135,4 +1297,4 @@ "sort_order": "DESC", "track_changes": 1, "track_seen": 0 -} \ No newline at end of file +} From 0eb64ca38be1fc3bd83eed9b2e48d1d21a41f152 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 25 Jul 2018 12:13:36 +0530 Subject: [PATCH 14/45] Healthcare Settings - Filter For Service Items --- .../healthcare_settings/healthcare_settings.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js index 8e98fee87e..8879d93d43 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js @@ -23,5 +23,18 @@ frappe.ui.form.on('Healthcare Settings', { } }; }); + set_query_service_item(frm, 'inpatient_visit_charge_item'); + set_query_service_item(frm, 'op_consulting_charge_item'); } }); + +var set_query_service_item = function(frm, service_item_field) { + frm.set_query(service_item_field, function() { + return { + filters: { + 'is_sales_item': 1, + 'is_stock_item': 0 + } + }; + }); +} From 1646010059299e6dbfec8a240c161fb18391ba86 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Thu, 26 Jul 2018 20:52:00 +0530 Subject: [PATCH 15/45] Clinical Procedure - Consumables Invoice Separately --- .../clinical_procedure/clinical_procedure.js | 21 ++- .../clinical_procedure.json | 133 ++++++++++++++++++ .../clinical_procedure/clinical_procedure.py | 78 +++++----- 3 files changed, 189 insertions(+), 43 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js index 9fc5b376a5..7f866e1996 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js @@ -264,21 +264,20 @@ frappe.ui.form.on('Clinical Procedure Item', { let args = null; if(d.item_code) { args = { - 'item_code' : d.item_code, - 'transfer_qty' : d.transfer_qty, - 'company' : frm.doc.company, - 'quantity' : d.qty + 'doctype' : "Clinical Procedure", + 'item_code' : d.item_code, + 'company' : frm.doc.company, + 'warehouse': frm.doc.warehouse }; return frappe.call({ - doc: frm.doc, - method: "get_item_details", - args: args, + method: "erpnext.stock.get_item_details.get_item_details", + args: {args: args}, callback: function(r) { if(r.message) { - var d = locals[cdt][cdn]; - $.each(r.message, function(k, v){ - d[k] = v; - }); + frappe.model.set_value(cdt, cdn, "item_name", r.message.item_name); + frappe.model.set_value(cdt, cdn, "stock_uom", r.message.stock_uom); + frappe.model.set_value(cdt, cdn, "conversion_factor", r.message.conversion_factor); + frappe.model.set_value(cdt, cdn, "actual_qty", r.message.actual_qty); refresh_field("items"); } } diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json index 0856606359..a830c37d96 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json @@ -704,6 +704,139 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fieldname": "invoice_separately_as_consumables", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Consumables Invoice Separately", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "invoice_separately_as_consumables", + "fieldname": "consumable_total_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Consumable Total Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "invoice_separately_as_consumables", + "fieldname": "consumption_details", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Consumption Details", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "depends_on": "invoice_separately_as_consumables", + "fieldname": "consumption_invoiced", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Consumption Invoiced", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index d51d062408..4a4bd39153 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -10,6 +10,7 @@ from frappe.utils import cint, flt, nowdate, nowtime from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_account from erpnext.healthcare.doctype.lab_test.lab_test import create_sample_doc from erpnext.stock.stock_ledger import get_previous_sle +from erpnext.stock.get_item_details import get_item_details class ClinicalProcedure(Document): def validate(self): @@ -18,6 +19,12 @@ class ClinicalProcedure(Document): frappe.throw(("Set warehouse for Procedure {0} ").format(self.name)) self.set_actual_qty() + if self.items: + self.invoice_separately_as_consumables = False + for item in self.items: + if item.invoice_separately_as_consumables == 1: + self.invoice_separately_as_consumables = True + def before_insert(self): if self.consume_stock: set_stock_items(self, self.procedure_template, "Clinical Procedure Template") @@ -40,6 +47,36 @@ class ClinicalProcedure(Document): create_stock_entry(self) frappe.db.set_value("Clinical Procedure", self.name, "status", 'Completed') + if self.items: + consumable_total_amount = 0 + consumption_details = False + for item in self.items: + if item.invoice_separately_as_consumables: + price_list, price_list_currency = frappe.db.get_values("Price List", {"selling": 1}, ['name', 'currency'])[0] + args = { + 'doctype': "Sales Invoice", + 'item_code': item.item_code, + 'company': self.company, + 'warehouse': self.warehouse, + 'customer': frappe.db.get_value("Patient", self.patient, "customer"), + 'selling_price_list': price_list, + 'price_list_currency': price_list_currency, + 'plc_conversion_rate': 1.0, + 'conversion_rate': 1.0 + } + item_details = get_item_details(args) + item_price = item_details.price_list_rate * item.transfer_qty + item_consumption_details = item_details.item_name+"\t"+str(item.qty)+" "+item.uom+"\t"+str(item_price) + consumable_total_amount += item_price + if not consumption_details: + consumption_details = "Clinical Procedure ("+self.name+"):\n\t"+item_consumption_details + else: + consumption_details += "\n\t"+item_consumption_details + if consumable_total_amount > 0: + frappe.db.set_value("Clinical Procedure", self.name, "consumable_total_amount", consumable_total_amount) + frappe.db.set_value("Clinical Procedure", self.name, "consumption_details", consumption_details) + + def start(self): allow_start = self.set_actual_qty() if allow_start: @@ -54,16 +91,7 @@ class ClinicalProcedure(Document): allow_start = True for d in self.get('items'): - previous_sle = get_previous_sle({ - "item_code": d.item_code, - "warehouse": self.warehouse, - "posting_date": nowdate(), - "posting_time": nowtime() - }) - - # get actual stock at source warehouse - d.actual_qty = previous_sle.get("qty_after_transaction") or 0 - + d.actual_qty = get_stock_qty(d.item_code, self.warehouse) # validate qty if not allow_negative_stock and d.actual_qty < d.qty: allow_start = False @@ -93,28 +121,14 @@ class ClinicalProcedure(Document): se_child.expense_account = expense_account return stock_entry.as_dict() - def get_item_details(self, args=None): - item = frappe.db.sql("""select stock_uom, description, image, item_name, - expense_account, buying_cost_center, item_group from `tabItem` - where name = %s - and disabled=0 - and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)""", - (args.get('item_code'), nowdate()), as_dict = 1) - if not item: - frappe.throw(_("Item {0} is not active or end of life has been reached").format(args.get('item_code'))) - - item = item[0] - - ret = { - 'uom' : item.stock_uom, - 'stock_uom' : item.stock_uom, - 'item_name' : item.item_name, - 'quantity' : 0, - 'transfer_qty' : 0, - 'conversion_factor' : 1 - } - return ret - +@frappe.whitelist() +def get_stock_qty(item_code, warehouse): + return get_previous_sle({ + "item_code": item_code, + "warehouse": warehouse, + "posting_date": nowdate(), + "posting_time": nowtime() + }).get("qty_after_transaction") or 0 @frappe.whitelist() def set_stock_items(doc, stock_detail_parent, parenttype): From 25e535c66ff7b442f2a509426dcc1e15ae900ce9 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Thu, 26 Jul 2018 20:53:27 +0530 Subject: [PATCH 16/45] Clinical Procedure Item - Remove field invoiced --- .../clinical_procedure_item.json | 39 ++----------------- 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json b/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json index 467fb7ae12..a974f2143d 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json +++ b/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json @@ -398,40 +398,6 @@ "set_only_once": 0, "translatable": 0, "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "depends_on": "invoice_separately_as_consumables", - "fieldname": "invoiced", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Invoiced", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 } ], "has_web_view": 0, @@ -444,7 +410,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-07-04 19:01:39.353654", + "modified": "2018-07-26 17:05:29.402908", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedure Item", @@ -458,5 +424,6 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file From 8da6f4e30ce87ada3a05557c35f29377e1b62a70 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Thu, 26 Jul 2018 21:03:17 +0530 Subject: [PATCH 17/45] Healthcare - Invoice - Redesign --- .../doctype/sales_invoice/sales_invoice.js | 56 +++++---- .../doctype/sales_invoice/sales_invoice.py | 37 ++++++ erpnext/healthcare/utils.py | 108 ++++++++++++++---- 3 files changed, 156 insertions(+), 45 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index fe58e80c5b..42363ce50f 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -859,6 +859,7 @@ var select_loyalty_program = function(frm, loyalty_programs) { dialog.show(); } +// Healthcare var get_healthcare_services_to_invoice = function(frm) { var me = this; let selected_patient = ''; @@ -962,7 +963,8 @@ var make_list_row= function(result={}) { data-dn= "${result.reference_name}" data-dt= "${result.reference_type}" data-item= "${result.service}" data-rate = ${result.rate} data-income-account = "${result.income_account}" - data-qty = ${result.qty}> + data-qty = ${result.qty} + data-description = "${result.description}"> `).append($row); return $row; }; @@ -970,32 +972,21 @@ var make_list_row= function(result={}) { var set_primary_action= function(frm, dialog, $results) { var me = this; dialog.set_primary_action(__('Add'), function() { - // TODO: Get checked items let checked_values = get_checked_values($results); if(checked_values.length > 0){ frm.set_value("patient", dialog.fields_dict.patient.input.value); frm.set_value("items", []); - for(let i=0; i 1){ - frappe.model.set_value(si_item.doctype, si_item.name, 'qty', parseFloat(checked_values[i]['qty'])); - } - } - frm.refresh_fields(); + }); dialog.hide(); } else{ @@ -1011,15 +1002,30 @@ var get_checked_values= function($results) { checked_values['dn'] = $(this).attr('data-dn'); checked_values['dt'] = $(this).attr('data-dt'); checked_values['item'] = $(this).attr('data-item'); - if($(this).attr('data-rate')){ + if($(this).attr('data-rate') != 'undefined'){ checked_values['rate'] = $(this).attr('data-rate'); } - if($(this).attr('data-income-account')){ + else{ + checked_values['rate'] = false; + } + if($(this).attr('data-income-account') != 'undefined'){ checked_values['income_account'] = $(this).attr('data-income-account'); } - if($(this).attr('data-qty')){ + else{ + checked_values['income_account'] = false; + } + if($(this).attr('data-qty') != 'undefined'){ checked_values['qty'] = $(this).attr('data-qty'); } + else{ + checked_values['qty'] = false; + } + if($(this).attr('data-description') != 'undefined'){ + checked_values['description'] = $(this).attr('data-description'); + } + else{ + checked_values['description'] = false; + } return checked_values; } }).get(); diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 7aeb86d7af..ca348b9817 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1191,6 +1191,43 @@ class SalesInvoice(SellingController): from erpnext.accounts.general_ledger import make_gl_entries make_gl_entries(gl_entries, cancel=(self.docstatus == 2), merge_entries=True) + # Healthcare + def set_healthcare_services(self, checked_values): + self.set("items", []) + from erpnext.stock.get_item_details import get_item_details + for checked_item in checked_values: + item_line = self.append("items", {}) + price_list, price_list_currency = frappe.db.get_values("Price List", {"selling": 1}, ['name', 'currency'])[0] + args = { + 'doctype': "Sales Invoice", + 'item_code': checked_item['item'], + 'company': self.company, + 'customer': frappe.db.get_value("Patient", self.patient, "customer"), + 'selling_price_list': price_list, + 'price_list_currency': price_list_currency, + 'plc_conversion_rate': 1.0, + 'conversion_rate': 1.0 + } + item_details = get_item_details(args) + item_line.item_code = checked_item['item'] + item_line.qty = 1 + if checked_item['qty']: + item_line.qty = checked_item['qty'] + if checked_item['rate']: + item_line.rate = checked_item['rate'] + else: + item_line.rate = item_details.price_list_rate + item_line.amount = float(item_line.rate) * float(item_line.qty) + if checked_item['income_account']: + item_line.income_account = checked_item['income_account'] + if checked_item['dt']: + item_line.reference_dt = checked_item['dt'] + if checked_item['dn']: + item_line.reference_dn = checked_item['dn'] + if checked_item['description']: + item_line.description = checked_item['description'] + + self.set_missing_values(for_validate = True) def booked_deferred_revenue(start_date=None, end_date=None): # check for the sales invoice for which GL entries has to be done diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index d42a5bb318..0b300b0cd0 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -57,11 +57,12 @@ def get_healthcare_services_to_invoice(patient): if not skip_invoice: practitioner_charge = 0 income_account = None + service_item = None if patient_appointment_obj.practitioner: - practitioner_charge = get_practitioner_charge(patient_appointment_obj.practitioner) + service_item, practitioner_charge = service_item_and_practitioner_charge(patient_appointment_obj) income_account = get_income_account(patient_appointment_obj.practitioner, patient_appointment_obj.company) item_to_invoice.append({'reference_type': 'Patient Appointment', 'reference_name': patient_appointment_obj.name, - 'service': 'Consulting Charges', 'rate': practitioner_charge, + 'service': service_item, 'rate': practitioner_charge, 'income_account': income_account}) encounters = frappe.get_list("Patient Encounter", {'patient': patient.name, 'invoiced': False, 'docstatus': 1}) @@ -71,11 +72,13 @@ def get_healthcare_services_to_invoice(patient): if not encounter_obj.appointment: practitioner_charge = 0 income_account = None + service_item = None if encounter_obj.practitioner: - practitioner_charge = get_practitioner_charge(encounter_obj.practitioner) + service_item, practitioner_charge = service_item_and_practitioner_charge(encounter_obj) income_account = get_income_account(encounter_obj.practitioner, encounter_obj.company) + item_to_invoice.append({'reference_type': 'Patient Encounter', 'reference_name': encounter_obj.name, - 'service': 'Consulting Charges', 'rate': practitioner_charge, + 'service': service_item, 'rate': practitioner_charge, 'income_account': income_account}) lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False}) @@ -83,7 +86,8 @@ def get_healthcare_services_to_invoice(patient): for lab_test in lab_tests: lab_test_obj = frappe.get_doc("Lab Test", lab_test['name']) if frappe.db.get_value("Lab Test Template", lab_test_obj.template, "is_billable") == 1: - item_to_invoice.append({'reference_type': 'Lab Test', 'reference_name': lab_test_obj.name, 'service': lab_test_obj.template}) + item_to_invoice.append({'reference_type': 'Lab Test', 'reference_name': lab_test_obj.name, + 'service': frappe.db.get_value("Lab Test Template", lab_test_obj.template, "item")}) lab_rxs = frappe.db.sql("""select lp.name from `tabPatient Encounter` et, `tabLab Prescription` lp where et.patient=%s and lp.parent=et.name and lp.test_created=0 and lp.invoiced=0""", (patient.name)) @@ -91,7 +95,8 @@ def get_healthcare_services_to_invoice(patient): for lab_rx in lab_rxs: rx_obj = frappe.get_doc("Lab Prescription", lab_rx[0]) if rx_obj.test_code and (frappe.db.get_value("Lab Test Template", rx_obj.test_code, "is_billable") == 1): - item_to_invoice.append({'reference_type': 'Lab Prescription', 'reference_name': rx_obj.name, 'service': rx_obj.test_code}) + item_to_invoice.append({'reference_type': 'Lab Prescription', 'reference_name': rx_obj.name, + 'service': frappe.db.get_value("Lab Test Template", rx_obj.test_code, "item")}) procedures = frappe.get_list("Clinical Procedure", {'patient': patient.name, 'invoiced': False}) if procedures: @@ -99,7 +104,8 @@ def get_healthcare_services_to_invoice(patient): procedure_obj = frappe.get_doc("Clinical Procedure", procedure['name']) if not procedure_obj.appointment: if procedure_obj.procedure_template and (frappe.db.get_value("Clinical Procedure Template", procedure_obj.procedure_template, "is_billable") == 1): - item_to_invoice.append({'reference_type': 'Clinical Procedure', 'reference_name': procedure_obj.name, 'service': procedure_obj.procedure_template}) + item_to_invoice.append({'reference_type': 'Clinical Procedure', 'reference_name': procedure_obj.name, + 'service': frappe.db.get_value("Clinical Procedure Template", procedure_obj.procedure_template, "item")}) procedure_rxs = frappe.db.sql("""select pp.name from `tabPatient Encounter` et, `tabProcedure Prescription` pp where et.patient=%s and pp.parent=et.name and @@ -108,16 +114,22 @@ def get_healthcare_services_to_invoice(patient): for procedure_rx in procedure_rxs: rx_obj = frappe.get_doc("Procedure Prescription", procedure_rx[0]) if frappe.db.get_value("Clinical Procedure Template", rx_obj.procedure, "is_billable") == 1: - item_to_invoice.append({'reference_type': 'Procedure Prescription', 'reference_name': rx_obj.name, 'service': rx_obj.procedure}) + item_to_invoice.append({'reference_type': 'Procedure Prescription', 'reference_name': rx_obj.name, + 'service': frappe.db.get_value("Clinical Procedure Template", rx_obj.procedure, "item")}) - procedure_consumables = frappe.db.sql("""select pc.name from `tabClinical Procedure` cp, - `tabClinical Procedure Item` pc where cp.patient=%s and pc.parent=cp.name and - pc.invoice_separately_as_consumables=1 and pc.invoiced=0""", (patient.name)) - if procedure_consumables: - for procedure_consumable in procedure_consumables: - procedure_consumable_obj = frappe.get_doc("Clinical Procedure Item", procedure_consumable[0]) - item_to_invoice.append({'reference_type': 'Clinical Procedure Item', 'reference_name': procedure_consumable_obj.name, - 'service': procedure_consumable_obj.item_code, 'qty': procedure_consumable_obj.qty}) + procedures = frappe.get_list("Clinical Procedure", + {'patient': patient.name, 'invoice_separately_as_consumables': True, 'consumption_invoiced': False, + 'consume_stock': True, 'status': 'Completed'}) + if procedures: + service_item = get_healthcare_service_item('clinical_procedure_consumable_item') + if not service_item: + msg = _(("Please Configure {0} in ").format("Clinical Procedure Consumable Item") \ + + """Healthcare Settings""") + frappe.throw(msg) + for procedure in procedures: + procedure_obj = frappe.get_doc("Clinical Procedure", procedure['name']) + item_to_invoice.append({'reference_type': 'Clinical Procedure', 'reference_name': procedure_obj.name, + 'service': service_item, 'rate': procedure_obj.consumable_total_amount, 'description': procedure_obj.consumption_details}) inpatient_services = frappe.db.sql("""select io.name, io.parent from `tabInpatient Record` ip, `tabInpatient Occupancy` io where ip.patient=%s and io.parent=ip.name and @@ -137,10 +149,56 @@ def get_healthcare_services_to_invoice(patient): else: frappe.throw(_("The Patient {0} do not have customer refrence to invoice").format(patient.name)) -def get_practitioner_charge(practitioner): - practitioner_charge = frappe.db.get_value("Healthcare Practitioner", practitioner, "op_consulting_charge") +def service_item_and_practitioner_charge(doc): + is_ip = doc_is_ip(doc) + if is_ip: + service_item = get_healthcare_service_item("inpatient_visit_charge_item") + else: + service_item = get_healthcare_service_item("op_consulting_charge_item") + if not service_item: + throw_config_service_item(is_ip) + + practitioner_charge = get_practitioner_charge(doc.practitioner, is_ip) + if not practitioner_charge: + throw_config_practitioner_charge(is_ip, doc.practitioner) + + return service_item, practitioner_charge + +def throw_config_service_item(is_ip): + service_item_lable = "Out Patient Consulting Charge Item" + if is_ip: + service_item_lable = "Inpatient Visit Charge Item" + + msg = _(("Please Configure {0} in ").format(service_item_lable) \ + + """Healthcare Settings""") + frappe.throw(msg) + +def throw_config_practitioner_charge(is_ip, practitioner): + charge_name = "OP Consulting Charge" + if is_ip: + charge_name = "Inpatient Visit Charge" + + msg = _(("Please Configure {0} for Healthcare Practitioner").format(charge_name) \ + + """ {0}""".format(practitioner)) + frappe.throw(msg) + +def get_healthcare_service_item(service_item_field): + return frappe.db.get_value("Healthcare Settings", None, service_item_field) + +def doc_is_ip(doc): + is_ip = False + if doc.inpatient_record: + is_ip = True + return is_ip + +def get_practitioner_charge(practitioner, is_ip): + if is_ip: + practitioner_charge = frappe.db.get_value("Healthcare Practitioner", practitioner, "inpatient_visit_charge") + else: + practitioner_charge = frappe.db.get_value("Healthcare Practitioner", practitioner, "op_consulting_charge") if practitioner_charge: return practitioner_charge + return False def manage_invoice_submit_cancel(doc, method): if doc.items: @@ -155,7 +213,14 @@ def set_invoiced(item, method, ref_invoice=None): validate_invoiced_on_submit(item) invoiced = True - frappe.db.set_value(item.reference_dt, item.reference_dn, "invoiced", invoiced) + if item.reference_dt == 'Clinical Procedure': + if get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code: + frappe.db.set_value(item.reference_dt, item.reference_dn, "consumption_invoiced", invoiced) + else: + frappe.db.set_value(item.reference_dt, item.reference_dn, "invoiced", invoiced) + else: + frappe.db.set_value(item.reference_dt, item.reference_dn, "invoiced", invoiced) + if item.reference_dt == 'Patient Appointment': if frappe.db.get_value('Patient Appointment', item.reference_dn, 'procedure_template'): dt_from_appointment = "Clinical Procedure" @@ -171,7 +236,10 @@ def set_invoiced(item, method, ref_invoice=None): manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, "Clinical Procedure", "procedure_created") def validate_invoiced_on_submit(item): - is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, "invoiced") + if item.reference_dt == 'Clinical Procedure' and get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code: + is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, "consumption_invoiced") + else: + is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, "invoiced") if is_invoiced == 1: frappe.throw(_("The item referenced by {0} - {1} is already invoiced"\ ).format(item.reference_dt, item.reference_dn)) From a5c3fc69b81a49494aa9ddfe6592791197cde838 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Thu, 26 Jul 2018 21:05:10 +0530 Subject: [PATCH 18/45] Patch - Healthcare - Invoice - fix --- erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py b/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py index e6c41ef077..aed6fc236c 100644 --- a/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py +++ b/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py @@ -48,6 +48,8 @@ def execute(): """.format(si_ref_doc, sales_invoice_referenced_doc[si_ref_doc])) def healthcare_custom_field_in_sales_invoice(): + frappe.reload_doc('healthcare', 'doctype', 'patient') + frappe.reload_doc('healthcare', 'doctype', 'healthcare_practitioner') if data['custom_fields']: create_custom_fields(data['custom_fields']) From 197cd88d36816e95da185956f2aecab1cf0639bf Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Thu, 26 Jul 2018 21:11:07 +0530 Subject: [PATCH 19/45] Lab Test Template - rate updation - fix --- .../doctype/lab_test_template/lab_test_template.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py index d76fb29adc..62a3b306c8 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py @@ -12,13 +12,16 @@ class LabTestTemplate(Document): #Item and Price List update --> if (change_in_item) if(self.change_in_item and self.is_billable == 1 and self.item): updating_item(self) - if not item_price_exist(self): + item_price = item_price_exist(self) + if not item_price: if(self.test_rate != 0.0): price_list_name = frappe.db.get_value("Price List", {"selling": 1}) if(self.test_rate): make_item_price(self.test_code, price_list_name, self.test_rate) else: make_item_price(self.test_code, price_list_name, 0.0) + else: + frappe.db.set_value("Item Price", item_price, "price_list_rate", self.test_rate) frappe.db.set_value(self.doctype,self.name,"change_in_item",0) elif(self.is_billable == 0 and self.item): @@ -43,7 +46,7 @@ def item_price_exist(doc): "doctype": "Item Price", "item_code": doc.test_code}) if(item_price): - return True + return item_price[0][0] else: return False From e82f27ac10c5fc2beed21f6d1a4a45f54883bf01 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 30 Jul 2018 11:28:37 +0530 Subject: [PATCH 20/45] Healthcare - Invoice Drug Prescription --- .../doctype/sales_invoice/sales_invoice.js | 164 ++++++++++++++---- erpnext/healthcare/utils.py | 16 ++ 2 files changed, 147 insertions(+), 33 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 42363ce50f..36d69fa1f6 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -748,6 +748,9 @@ frappe.ui.form.on('Sales Invoice', { frm.add_custom_button(__('Healthcare Services'), function() { get_healthcare_services_to_invoice(frm); },"Get items from"); + frm.add_custom_button(__('Prescriptions'), function() { + get_drugs_to_invoice(frm); + },"Get items from"); } } else{ @@ -887,7 +890,10 @@ var get_healthcare_services_to_invoice = function(frm) { var patient = dialog.fields_dict.patient.input.value; if(patient && patient!=selected_patient){ selected_patient = patient; - get_services(frm, patient, $results, $placeholder) + var method = "erpnext.healthcare.utils.get_healthcare_services_to_invoice"; + var args = {patient: patient}; + var columns = (["service", "reference_name", "reference_type"]); + get_healthcare_items(frm, true, $results, $placeholder, method, args, columns); } else if(!patient){ selected_patient = ''; @@ -908,37 +914,34 @@ var get_healthcare_services_to_invoice = function(frm) { $results.find('.list-item-container .list-row-check') .prop("checked", ($(e.target).is(':checked'))); }); - set_primary_action(frm, dialog, $results); + set_primary_action(frm, dialog, $results, true); dialog.show(); }; -var get_services= function(frm, patient_id, $results, $placeholder) { +var get_healthcare_items = function(frm, invoice_healthcare_services, $results, $placeholder, method, args, columns) { var me = this; $results.empty(); frappe.call({ - method:"erpnext.healthcare.utils.get_healthcare_services_to_invoice", - args: { - patient: patient_id - }, + method: method, + args: args, callback: function(data) { if(data.message){ - $results.append(make_list_row()); + $results.append(make_list_row(columns, invoice_healthcare_services)); for(let i=0; i ${ @@ -958,39 +961,27 @@ var make_list_row= function(result={}) { ${contents} `); - head ? $row.addClass('list-item--head') - : $row = $(`
-
`).append($row); + $row = list_row_data_items(head, $row, result, invoice_healthcare_services); return $row; }; -var set_primary_action= function(frm, dialog, $results) { +var set_primary_action= function(frm, dialog, $results, invoice_healthcare_services) { var me = this; dialog.set_primary_action(__('Add'), function() { let checked_values = get_checked_values($results); if(checked_values.length > 0){ frm.set_value("patient", dialog.fields_dict.patient.input.value); frm.set_value("items", []); - frappe.call({ - doc: frm.doc, - method: "set_healthcare_services", - args:{ - checked_values: checked_values - }, - callback: function() { - frm.trigger("validate"); - frm.refresh_fields(); - } - }); + add_to_item_line(frm, checked_values, invoice_healthcare_services); dialog.hide(); } else{ - frappe.msgprint(__("Please select Healthcare Service")); + if(invoice_healthcare_services){ + frappe.msgprint(__("Please select Healthcare Service")); + } + else{ + frappe.msgprint(__("Please select Drug")); + } } }); }; @@ -1030,3 +1021,110 @@ var get_checked_values= function($results) { } }).get(); }; + +var get_drugs_to_invoice = function(frm) { + var me = this; + let selected_encounter = ''; + var dialog = new frappe.ui.Dialog({ + title: __("Get Items from Prescriptions"), + fields:[ + { fieldtype: 'Link', options: 'Patient', label: 'Patient', fieldname: "patient", reqd: true }, + { fieldtype: 'Link', options: 'Patient Encounter', label: 'Patient Encounter', fieldname: "encounter", reqd: true, + description:'Quantity will be calculated only for items which has "Nos" as UoM. You may change as required for each invoice item.', + get_query: function(doc) { + return { + filters: { patient :dialog.get_value("patient") } + }; + } + }, + { fieldtype: 'Section Break' }, + { fieldtype: 'HTML', fieldname: 'results_area' } + ] + }); + var $wrapper; + var $results; + var $placeholder; + dialog.set_values({ + 'patient': frm.doc.patient, + 'encounter': "" + }); + dialog.fields_dict["encounter"].df.onchange = () => { + var encounter = dialog.fields_dict.encounter.input.value; + if(encounter && encounter!=selected_encounter){ + selected_encounter = encounter; + var method = "erpnext.healthcare.utils.get_drugs_to_invoice"; + var args = {encounter: encounter}; + var columns = (["drug_code", "quantity", "description"]); + get_healthcare_items(frm, false, $results, $placeholder, method, args, columns); + } + else if(!encounter){ + selected_encounter = ''; + $results.empty(); + $results.append($placeholder); + } + } + $wrapper = dialog.fields_dict.results_area.$wrapper.append(`
`); + $results = $wrapper.find('.results'); + $placeholder = $(`
+ + +

No Drug Prescription found

+
+
`); + $results.on('click', '.list-item--head :checkbox', (e) => { + $results.find('.list-item-container .list-row-check') + .prop("checked", ($(e.target).is(':checked'))); + }); + set_primary_action(frm, dialog, $results, false); + dialog.show(); +}; + +var list_row_data_items = function(head, $row, result, invoice_healthcare_services) { + if(invoice_healthcare_services){ + head ? $row.addClass('list-item--head') + : $row = $(`
+
`).append($row); + } + else{ + head ? $row.addClass('list-item--head') + : $row = $(`
+
`).append($row); + } + return $row +}; + +var add_to_item_line = function(frm, checked_values, invoice_healthcare_services){ + if(invoice_healthcare_services){ + frappe.call({ + doc: frm.doc, + method: "set_healthcare_services", + args:{ + checked_values: checked_values + }, + callback: function() { + frm.trigger("validate"); + frm.refresh_fields(); + } + }); + } + else{ + for(let i=0; i 1){ + frappe.model.set_value(si_item.doctype, si_item.name, 'qty', parseFloat(checked_values[i]['qty'])); + } + } + frm.refresh_fields(); + } +}; diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 0b300b0cd0..b9667214d9 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -313,3 +313,19 @@ def manage_doc_for_appoitnment(dt_from_appointment, appointment, invoiced): ) if dn_from_appointment: frappe.db.set_value(dt_from_appointment, dn_from_appointment, "invoiced", invoiced) + +@frappe.whitelist() +def get_drugs_to_invoice(encounter): + encounter = frappe.get_doc("Patient Encounter", encounter) + if encounter: + patient = frappe.get_doc("Patient", encounter.patient) + if patient and patient.customer: + item_to_invoice = [] + for drug_line in encounter.drug_prescription: + if drug_line.drug_code: + qty = 1 + if frappe.db.get_value("Item", drug_line.drug_code, "stock_uom") == "Nos": + qty = drug_line.get_quantity() + item_to_invoice.append({'drug_code': drug_line.drug_code, 'quantity': qty, + 'description': drug_line.dosage+" for "+drug_line.period}) + return item_to_invoice From 1dbfd5c1e7fb9409d07e8940ab317a669046b6c8 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 30 Jul 2018 12:35:42 +0530 Subject: [PATCH 21/45] Healthcare Settings - Heathcare Service Item - Validation --- .../healthcare_settings.js | 3 ++- .../healthcare_settings.py | 24 +++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js index 8879d93d43..22fbf5019a 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js @@ -25,6 +25,7 @@ frappe.ui.form.on('Healthcare Settings', { }); set_query_service_item(frm, 'inpatient_visit_charge_item'); set_query_service_item(frm, 'op_consulting_charge_item'); + set_query_service_item(frm, 'clinical_procedure_consumable_item'); } }); @@ -37,4 +38,4 @@ var set_query_service_item = function(frm, service_item_field) { } }; }); -} +}; diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py index 8c3cdfe1e7..8555e80f84 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py @@ -10,13 +10,19 @@ from frappe.core.doctype.sms_settings.sms_settings import send_sms import json class HealthcareSettings(Document): - def validate(self): - for key in ["collect_registration_fee","manage_customer","patient_master_name", - "require_test_result_approval","require_sample_collection", "default_medical_code_standard"]: - frappe.db.set_default(key, self.get(key, "")) - if(self.collect_registration_fee): - if self.registration_fee <= 0 : - frappe.throw(_("Registration fee can not be Zero")) + def validate(self): + for key in ["collect_registration_fee","manage_customer","patient_master_name", + "require_test_result_approval","require_sample_collection", "default_medical_code_standard"]: + frappe.db.set_default(key, self.get(key, "")) + if(self.collect_registration_fee): + if self.registration_fee <= 0 : + frappe.throw(_("Registration fee can not be Zero")) + if self.inpatient_visit_charge_item: + validate_service_item(self.inpatient_visit_charge_item, "Configure a service Item for Inpatient Visit Charge Item") + if self.op_consulting_charge_item: + validate_service_item(self.op_consulting_charge_item, "Configure a service Item for Out Patient Consulting Charge Item") + if self.clinical_procedure_consumable_item: + validate_service_item(self.clinical_procedure_consumable_item, "Configure a service Item for Clinical Procedure Consumable Item") @frappe.whitelist() def get_sms_text(doc): @@ -67,3 +73,7 @@ def get_account(parent_type, parent_field, parent, company): if(parent_field): return frappe.db.get_value("Party Account", {"parentfield": parent_field, "parent": parent, "company": company}, "account") + +def validate_service_item(item, msg): + if frappe.db.get_value("Item", item, "is_stock_item") == 1: + frappe.throw(_(msg)) From e1d215352b1f681772d921ee5cc41cf7b52fee39 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 30 Jul 2018 12:38:26 +0530 Subject: [PATCH 22/45] Healthcare Practitioner - Service Item Fields - Filter and Validation --- .../healthcare_practitioner.js | 13 ++++ .../healthcare_practitioner.json | 71 ++++++++++++++++++- .../healthcare_practitioner.py | 8 +++ 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js index f2dc849150..efca484d6a 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js @@ -27,9 +27,22 @@ frappe.ui.form.on('Healthcare Practitioner', { } }; }); + set_query_service_item(frm, 'inpatient_visit_charge_item'); + set_query_service_item(frm, 'op_consulting_charge_item'); } }); +var set_query_service_item = function(frm, service_item_field) { + frm.set_query(service_item_field, function() { + return { + filters: { + 'is_sales_item': 1, + 'is_stock_item': 0 + } + }; + }); +}; + frappe.ui.form.on("Healthcare Practitioner", "user_id",function(frm) { if(frm.doc.user_id){ frappe.call({ diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json index 7e794cce45..f8f4cc0eb0 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json @@ -528,6 +528,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "op_consulting_charge_item", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Out Patient Consulting Charge Item", + "length": 0, + "no_copy": 0, + "options": "Item", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -592,6 +625,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "inpatient_visit_charge_item", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Inpatient Visit Charge Item", + "length": 0, + "no_copy": 0, + "options": "Item", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -861,7 +927,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-07-20 16:24:40.314459", + "modified": "2018-07-30 12:00:20.746580", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Practitioner", @@ -936,5 +1002,6 @@ "sort_order": "DESC", "title_field": "first_name", "track_changes": 1, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py index 753ecd148a..8a087dd1b2 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py @@ -21,6 +21,10 @@ class HealthcarePractitioner(Document): def validate(self): validate_party_accounts(self) + if self.inpatient_visit_charge_item: + validate_service_item(self.inpatient_visit_charge_item, "Configure a service Item for Inpatient Visit Charge Item") + if self.op_consulting_charge_item: + validate_service_item(self.op_consulting_charge_item, "Configure a service Item for Out Patient Consulting Charge Item") if self.user_id: self.validate_for_enabled_user_id() @@ -57,3 +61,7 @@ class HealthcarePractitioner(Document): def on_trash(self): delete_contact_and_address('Healthcare Practitioner', self.name) + +def validate_service_item(item, msg): + if frappe.db.get_value("Item", item, "is_stock_item") == 1: + frappe.throw(_(msg)) From ee5f9c7b8099db1d3742ba890bf0c02a68f61afd Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 30 Jul 2018 12:42:06 +0530 Subject: [PATCH 23/45] Healthcare Invoice - Practitioner Specific Service Item --- erpnext/healthcare/utils.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index b9667214d9..3580003646 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -152,9 +152,13 @@ def get_healthcare_services_to_invoice(patient): def service_item_and_practitioner_charge(doc): is_ip = doc_is_ip(doc) if is_ip: - service_item = get_healthcare_service_item("inpatient_visit_charge_item") + service_item = get_practitioner_service_item(doc.practitioner, "inpatient_visit_charge_item") + if not service_item: + service_item = get_healthcare_service_item("inpatient_visit_charge_item") else: - service_item = get_healthcare_service_item("op_consulting_charge_item") + service_item = get_practitioner_service_item(doc.practitioner, "op_consulting_charge_item") + if not service_item: + service_item = get_healthcare_service_item("op_consulting_charge_item") if not service_item: throw_config_service_item(is_ip) @@ -182,6 +186,9 @@ def throw_config_practitioner_charge(is_ip, practitioner): + """ {0}""".format(practitioner)) frappe.throw(msg) +def get_practitioner_service_item(practitioner, service_item_field): + return frappe.db.get_value("Healthcare Practitioner", practitioner, service_item_field) + def get_healthcare_service_item(service_item_field): return frappe.db.get_value("Healthcare Settings", None, service_item_field) From c8f827be661afb5f1113a342242cd261673c463d Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 30 Jul 2018 14:41:19 +0530 Subject: [PATCH 24/45] Healthcare Service Unit Type - Field - UOM Conversion in Hours --- .../healthcare_service_unit_type.json | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json index 6394ce7361..f8bb75e500 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json @@ -43,7 +43,7 @@ "search_index": 0, "set_only_once": 0, "translatable": 0, - "unique": 0 + "unique": 1 }, { "allow_bulk_edit": 0, @@ -351,8 +351,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "rate", - "fieldtype": "Currency", + "fieldname": "no_of_hours", + "fieldtype": "Int", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -360,7 +360,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Rate / UOM", + "label": "UOM Conversion in Hours", "length": 0, "no_copy": 0, "permlevel": 0, @@ -407,6 +407,38 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "rate", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Rate / UOM", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -515,7 +547,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-07-13 16:54:59.131606", + "modified": "2018-07-30 14:31:58.025819", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit Type", @@ -550,5 +582,6 @@ "sort_order": "DESC", "title_field": "service_unit_type", "track_changes": 0, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file From 9b9518eaaa5d06214b646bbee24caf0f43b28b1e Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 30 Jul 2018 14:44:58 +0530 Subject: [PATCH 25/45] Healthcare Service Unit Type - Validation and Item price updation --- .../healthcare_service_unit_type.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py index e0d380bc5e..727d035a80 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py @@ -8,6 +8,11 @@ from frappe import _ from frappe.model.document import Document class HealthcareServiceUnitType(Document): + def validate(self): + if self.is_billable == 1: + if not self.uom or not self.item_group or not self.description or not self.no_of_hours > 0: + frappe.throw(_("Configure Item Fields like UOM, Item Group, Description and No of Hours.")) + def after_insert(self): if self.inpatient_occupancy and self.is_billable: create_item(self) @@ -22,13 +27,16 @@ class HealthcareServiceUnitType(Document): def on_update(self): if(self.change_in_item and self.is_billable == 1 and self.item): updating_item(self) - if not item_price_exist(self): - if(self.test_rate != 0.0): + item_price = item_price_exist(self) + if not item_price: + if(self.rate != 0.0): price_list_name = frappe.db.get_value("Price List", {"selling": 1}) - if(self.test_rate): - make_item_price(self.test_code, price_list_name, self.test_rate) + if(self.rate): + make_item_price(self.item_code, price_list_name, self.rate) else: - make_item_price(self.test_code, price_list_name, 0.0) + make_item_price(self.item_code, price_list_name, 0.0) + else: + frappe.db.set_value("Item Price", item_price, "price_list_rate", self.rate) frappe.db.set_value(self.doctype,self.name,"change_in_item",0) elif(self.is_billable == 0 and self.item): @@ -40,7 +48,7 @@ def item_price_exist(doc): "doctype": "Item Price", "item_code": doc.item_code}) if(item_price): - return True + return item_price[0][0] else: return False From 25dda3a91a5cd171359b84858b5f93c07eb5c539 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 30 Jul 2018 14:50:16 +0530 Subject: [PATCH 26/45] Invoice inpatient occupancy - Qty updated --- erpnext/healthcare/utils.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 3580003646..045f3bc31f 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -6,7 +6,8 @@ from __future__ import unicode_literals import frappe import datetime from frappe import _ -from frappe.utils import date_diff, getdate +import math +from frappe.utils import time_diff_in_hours, rounded from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account from erpnext.healthcare.doctype.patient_appointment.patient_appointment import validity_exists from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity, update_fee_validity @@ -139,9 +140,18 @@ def get_healthcare_services_to_invoice(patient): inpatient_occupancy = frappe.get_doc("Inpatient Occupancy", inpatient_service[0]) service_unit_type = frappe.get_doc("Healthcare Service Unit Type", frappe.db.get_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "service_unit_type")) if service_unit_type and service_unit_type.is_billable == 1: - qty = date_diff(getdate(inpatient_occupancy.check_out), getdate(inpatient_occupancy.check_in)) - if qty < 1: - qty = 1 + hours_occupied = time_diff_in_hours(inpatient_occupancy.check_out, inpatient_occupancy.check_in) + qty = 0.5 + if hours_occupied > 0: + actual_qty = hours_occupied / service_unit_type.no_of_hours + floor = math.floor(actual_qty) + decimal_part = actual_qty - floor + if decimal_part > 0.5: + qty = rounded(floor + 1, 1) + elif decimal_part < 0.5 and decimal_part > 0: + qty = rounded(floor + 0.5, 1) + if qty <= 0: + qty = 0.5 item_to_invoice.append({'reference_type': 'Inpatient Occupancy', 'reference_name': inpatient_occupancy.name, 'service': service_unit_type.item, 'qty': qty}) From b19993b5bc3bb07387e047f598ca77ced5c574bb Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 30 Jul 2018 15:04:44 +0530 Subject: [PATCH 27/45] Codacy - fixes --- .../doctype/clinical_procedure/clinical_procedure.py | 2 +- .../doctype/inpatient_record/inpatient_record.py | 8 ++++---- .../doctype/patient_encounter/patient_encounter.py | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index 4a4bd39153..6d00c25117 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -16,7 +16,7 @@ class ClinicalProcedure(Document): def validate(self): if self.consume_stock and not self.status == 'Draft': if not self.warehouse: - frappe.throw(("Set warehouse for Procedure {0} ").format(self.name)) + frappe.throw(_("Set warehouse for Procedure {0} ").format(self.name)) self.set_actual_qty() if self.items: diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index 2ed3f8e065..2d928c6c9e 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -115,13 +115,13 @@ def validate_invoiced_inpatient(inpatient_record): for doc in docs: doc_name_list = get_inpatient_docs_not_invoiced(doc, inpatient_record) if doc_name_list: - pending_invoices = get_doc_pendig(doc, doc_name_list, pending_invoices) + pending_invoices = get_pending_doc(doc, doc_name_list, pending_invoices) if pending_invoices: - frappe.throw(_("Can not mark Inpatient Record Discharged, there are Unpaid Invoices {0}").format(", " - .join(map(lambda x: """ {0}""".format(x), pending_invoices)))) + frappe.throw(_("Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}").format(", " + .join(pending_invoices))) -def get_doc_pendig(doc, doc_name_list, pending_invoices): +def get_pending_doc(doc, doc_name_list, pending_invoices): if doc_name_list: doc_ids = False for doc_name in doc_name_list: diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py index 5aa7867167..4c62e57537 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py @@ -5,8 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -from frappe.utils import getdate, cstr -import json +from frappe.utils import cstr class PatientEncounter(Document): def on_update(self): From 2ae06d3a352d766b897f6a4ecd0e69105c8d69fa Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 30 Jul 2018 16:07:19 +0530 Subject: [PATCH 28/45] Healthcare Service Unit - rename field - occupied to occupancy status --- .../healthcare_service_unit.json | 16 +++++++++------- .../doctype/inpatient_record/inpatient_record.js | 4 ++-- .../doctype/inpatient_record/inpatient_record.py | 6 +++--- .../inpatient_record/test_inpatient_record.py | 6 +++--- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json index 945817c8d3..a7e69329de 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json @@ -43,7 +43,7 @@ "search_index": 0, "set_only_once": 0, "translatable": 0, - "unique": 0 + "unique": 1 }, { "allow_bulk_edit": 0, @@ -258,10 +258,10 @@ "bold": 0, "collapsible": 0, "columns": 0, - "default": "0", + "default": "", "depends_on": "eval:doc.inpatient_occupancy == 1", - "fieldname": "occupied", - "fieldtype": "Check", + "fieldname": "occupancy_status", + "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -269,9 +269,10 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Occupied", + "label": "Occupancy Status", "length": 0, "no_copy": 1, + "options": "Vacant\nOccupied", "permlevel": 0, "precision": "", "print_hide": 0, @@ -460,7 +461,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-07-17 17:40:18.867327", + "modified": "2018-07-30 15:09:57.016256", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit", @@ -535,5 +536,6 @@ "sort_order": "DESC", "title_field": "healthcare_service_unit_name", "track_changes": 1, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js index 936c682dca..67c12f6c14 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js @@ -94,7 +94,7 @@ var admit_patient_dialog = function(frm){ filters: { "is_group": 0, "service_unit_type": dialog.get_value("service_unit_type"), - "occupied" : 0 + "occupancy_status" : "Vacant" } }; }; @@ -166,7 +166,7 @@ var transfer_patient_dialog = function(frm){ filters: { "is_group": 0, "service_unit_type": dialog.get_value("service_unit_type"), - "occupied" : 0 + "occupancy_status" : "Vacant" } }; }; diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index 2d928c6c9e..c107cd7335 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -88,7 +88,7 @@ def check_out_inpatient(inpatient_record): if inpatient_occupancy.left != 1: inpatient_occupancy.left = True inpatient_occupancy.check_out = now_datetime() - frappe.db.set_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "occupied", False) + frappe.db.set_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "occupancy_status", "Vacant") def discharge_patient(inpatient_record): validate_invoiced_inpatient(inpatient_record) @@ -156,7 +156,7 @@ def transfer_patient(inpatient_record, service_unit, check_in): inpatient_record.save(ignore_permissions = True) - frappe.db.set_value("Healthcare Service Unit", service_unit, "occupied", True) + frappe.db.set_value("Healthcare Service Unit", service_unit, "occupancy_status", "Occupied") def patient_leave_service_unit(inpatient_record, check_out, leave_from): if inpatient_record.inpatient_occupancies: @@ -164,7 +164,7 @@ def patient_leave_service_unit(inpatient_record, check_out, leave_from): if inpatient_occupancy.left != 1 and inpatient_occupancy.service_unit == leave_from: inpatient_occupancy.left = True inpatient_occupancy.check_out = check_out - frappe.db.set_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "occupied", False) + frappe.db.set_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "occupancy_status", "Vacant") inpatient_record.save(ignore_permissions = True) @frappe.whitelist() diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py index f912ddb043..884974853d 100644 --- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py @@ -23,11 +23,11 @@ class TestInpatientRecord(unittest.TestCase): service_unit = get_healthcare_service_unit() admit_patient(ip_record, service_unit, now_datetime()) self.assertEqual("Admitted", frappe.db.get_value("Patient", patient, "inpatient_status")) - self.assertEqual(1, frappe.db.get_value("Healthcare Service Unit", service_unit, "occupied")) + self.assertEqual("Occupied", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status")) # Discharge schedule_discharge(patient=patient) - self.assertEqual(0, frappe.db.get_value("Healthcare Service Unit", service_unit, "occupied")) + self.assertEqual("Vacant", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status")) ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name) # Validate Pending Invoices @@ -93,7 +93,7 @@ def get_healthcare_service_unit(): service_unit.healthcare_service_unit_name = "Test Service Unit Ip Occupancy" service_unit.service_unit_type = get_service_unit_type() service_unit.inpatient_occupancy = 1 - service_unit.occupied = 0 + service_unit.occupancy_status = "Vacant" service_unit.is_group = 0 service_unit_parent_name = frappe.db.exists({ "doctype": "Healthcare Service Unit", From 80468bec317d9f9bec7897631a36a7075491396a Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 1 Aug 2018 11:30:28 +0530 Subject: [PATCH 29/45] Lab Test - Remove unused methods --- .../healthcare/doctype/lab_test/lab_test.py | 44 ------------------- 1 file changed, 44 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 9cba34cd58..3d78fcdf1b 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -59,26 +59,6 @@ def update_status(status, name): def update_lab_test_print_sms_email_status(print_sms_email, name): frappe.db.set_value("Lab Test",name,print_sms_email,1) -def create_lab_test_doc(invoice, encounter, patient, template): - #create Test Result for template, copy vals from Invoice - lab_test = frappe.new_doc("Lab Test") - if(invoice): - lab_test.invoiced = True - if(encounter): - lab_test.practitioner = encounter.practitioner - lab_test.patient = patient.name - lab_test.patient_age = patient.get_age() - lab_test.patient_sex = patient.sex - lab_test.email = patient.email - lab_test.mobile = patient.mobile - lab_test.department = template.department - lab_test.test_name = template.test_name - lab_test.template = template.name - lab_test.test_group = template.test_group - lab_test.result_date = getdate() - lab_test.report_preference = patient.report_preference - return lab_test - def create_normals(template, lab_test): lab_test.normal_toggle = "1" normal = lab_test.append("normal_test_items") @@ -145,24 +125,6 @@ def create_sample_doc(template, patient, invoice): return sample_collection -@frappe.whitelist() -def create_lab_test_from_desk(patient, template, prescription, invoice=None): - lab_test_exist = frappe.db.exists({ - "doctype": "Lab Test", - "prescription": prescription - }) - if lab_test_exist: - return - template = frappe.get_doc("Lab Test Template", template) - #skip the loop if there is no test_template for Item - if not (template): - return - patient = frappe.get_doc("Patient", patient) - encounter_id = frappe.get_value("Lab Prescription", prescription, "parent") - encounter = frappe.get_doc("Patient Encounter", encounter_id) - lab_test = create_lab_test(patient, template, prescription, encounter, invoice) - return lab_test.name - def create_sample_collection(lab_test, template, patient, invoice): if(frappe.db.get_value("Healthcare Settings", None, "require_sample_collection") == "1"): sample_collection = create_sample_doc(template, patient, invoice) @@ -214,12 +176,6 @@ def load_result_format(lab_test, template, prescription, invoice): lab_test.save(ignore_permissions=True) # insert the result return lab_test -def create_lab_test(patient, template, prescription, encounter, invoice): - lab_test = create_lab_test_doc(invoice, encounter, patient, template) - lab_test = create_sample_collection(lab_test, template, patient, invoice) - lab_test = load_result_format(lab_test, template, prescription, invoice) - return lab_test - @frappe.whitelist() def get_employee_by_user_id(user_id): emp_id = frappe.db.get_value("Employee",{"user_id":user_id}) From 858e026fc07630afea82e0a1e1d724c0eaeadbd0 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 1 Aug 2018 13:26:23 +0530 Subject: [PATCH 30/45] Lab Test - Create Multiple from Sales Invoice or from Patient Encounter --- .../healthcare/doctype/lab_test/lab_test.py | 95 +++++++++++++++++++ .../doctype/lab_test/lab_test_list.js | 47 +++++++++ 2 files changed, 142 insertions(+) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 3d78fcdf1b..f710b83f19 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.model.document import Document from frappe.utils import getdate, cstr @@ -59,6 +60,100 @@ def update_status(status, name): def update_lab_test_print_sms_email_status(print_sms_email, name): frappe.db.set_value("Lab Test",name,print_sms_email,1) +@frappe.whitelist() +def create_multiple(doctype, docname): + lab_test_created = False + if doctype == "Sales Invoice": + lab_test_created = create_lab_test_from_invoice(docname) + elif doctype == "Patient Encounter": + lab_test_created = create_lab_test_from_encounter(docname) + + if lab_test_created: + frappe.msgprint(_("Lab Test(s) "+lab_test_created+" created.")) + else: + frappe.msgprint(_("No Lab Test created")) + +def create_lab_test_from_encounter(encounter_id): + lab_test_created = False + encounter = frappe.get_doc("Patient Encounter", encounter_id) + + lab_test_ids = frappe.db.sql("""select lp.name, lp.test_code, lp.invoiced + from `tabPatient Encounter` et, `tabLab Prescription` lp + where et.patient=%s and lp.parent=%s and + lp.parent=et.name and lp.test_created=0 and et.docstatus=1""", (encounter.patient, encounter_id)) + + if lab_test_ids: + patient = frappe.get_doc("Patient", encounter.patient) + for lab_test_id in lab_test_ids: + template = get_lab_test_template(lab_test_id[1]) + if template: + lab_test = create_lab_test_doc(lab_test_id[2], encounter.practitioner, patient, template) + lab_test.save(ignore_permissions = True) + frappe.db.set_value("Lab Prescription", lab_test_id[0], "test_created", 1) + if not lab_test_created: + lab_test_created = lab_test.name + else: + lab_test_created += ", "+lab_test.name + return lab_test_created + + +def create_lab_test_from_invoice(invoice_name): + lab_test_created = False + invoice = frappe.get_doc("Sales Invoice", invoice_name) + if invoice.patient: + patient = frappe.get_doc("Patient", invoice.patient) + for item in invoice.items: + test_created = 0 + if item.reference_dt == "Lab Prescription": + test_created = frappe.db.get_value("Lab Prescription", item.reference_dn, "test_created") + elif item.reference_dt == "Lab Test": + test_created = 1 + if test_created != 1: + template = get_lab_test_template(item.item_code) + if template: + lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template) + lab_test.save(ignore_permissions = True) + frappe.db.set_value("Sales Invoice Item", item.name, "reference_dt", "Lab Test") + frappe.db.set_value("Sales Invoice Item", item.name, "reference_dn", lab_test.name) + if not lab_test_created: + lab_test_created = lab_test.name + else: + lab_test_created += ", "+lab_test.name + return lab_test_created + +def get_lab_test_template(item): + template_id = check_template_exists(item) + if template_id: + return frappe.get_doc("Lab Test Template", template_id) + return False + +def check_template_exists(item): + template_exists = frappe.db.exists( + "Lab Test Template", + { + 'item': item + } + ) + if template_exists: + return template_exists + return False + +def create_lab_test_doc(invoiced, practitioner, patient, template): + lab_test = frappe.new_doc("Lab Test") + lab_test.invoiced = invoiced + lab_test.practitioner = practitioner + lab_test.patient = patient.name + lab_test.patient_age = patient.get_age() + lab_test.patient_sex = patient.sex + lab_test.email = patient.email + lab_test.mobile = patient.mobile + lab_test.department = template.department + lab_test.template = template.name + lab_test.test_group = template.test_group + lab_test.result_date = getdate() + lab_test.report_preference = patient.report_preference + return lab_test + def create_normals(template, lab_test): lab_test.normal_toggle = "1" normal = lab_test.append("normal_test_items") diff --git a/erpnext/healthcare/doctype/lab_test/lab_test_list.js b/erpnext/healthcare/doctype/lab_test/lab_test_list.js index 1d65e5be66..1f6a12f935 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test_list.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test_list.js @@ -11,5 +11,52 @@ frappe.listview_settings['Lab Test'] = { if(doc.status=="Rejected"){ return [__("Rejected"), "yellow", "status,=,Rejected"]; } + }, + onload: function(listview) { + listview.page.add_menu_item(__("Create Multiple"), function() { + create_multiple_dialog(listview); + }); } }; + +var create_multiple_dialog = function(listview){ + var dialog = new frappe.ui.Dialog({ + title: 'Create Multiple Lab Test', + width: 100, + fields: [ + {fieldtype: "Link", label: "Patient", fieldname: "patient", options: "Patient", reqd: 1}, + {fieldtype: "Select", label: "Invoice / Patient Encounter", fieldname: "doctype", + options: "\nSales Invoice\nPatient Encounter", reqd: 1}, + {fieldtype: "Dynamic Link", fieldname: "docname", options: "doctype", reqd: 1, + get_query: function(){ + return { + filters: { + "patient": dialog.get_value("patient"), + "docstatus": 1 + } + }; + } + } + ], + primary_action_label: __("Create Lab Test"), + primary_action : function(){ + frappe.call({ + method: 'erpnext.healthcare.doctype.lab_test.lab_test.create_multiple', + args:{ + 'doctype': dialog.get_value("doctype"), + 'docname': dialog.get_value("docname") + }, + callback: function(data) { + if(!data.exc){ + listview.refresh(); + } + }, + freeze: true, + freeze_message: "Creating Lab Test..." + }); + dialog.hide(); + } + }); + + dialog.show(); +}; From 61574d18bff4ffe6941db9dd060181cb499e4746 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 1 Aug 2018 14:05:37 +0530 Subject: [PATCH 31/45] Fix - Create Multiple Lab Test from Sales Invoice or from Patient Encounter --- erpnext/healthcare/doctype/lab_test/lab_test.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index f710b83f19..98ab696e90 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -112,9 +112,12 @@ def create_lab_test_from_invoice(invoice_name): template = get_lab_test_template(item.item_code) if template: lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template) + if item.reference_dt == "Lab Prescription": + lab_test.prescription = item.reference_dn lab_test.save(ignore_permissions = True) - frappe.db.set_value("Sales Invoice Item", item.name, "reference_dt", "Lab Test") - frappe.db.set_value("Sales Invoice Item", item.name, "reference_dn", lab_test.name) + if item.reference_dt != "Lab Prescription": + frappe.db.set_value("Sales Invoice Item", item.name, "reference_dt", "Lab Test") + frappe.db.set_value("Sales Invoice Item", item.name, "reference_dn", lab_test.name) if not lab_test_created: lab_test_created = lab_test.name else: From 0ae100b0353e82c527213f0aaf9ee687883f32cc Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 1 Aug 2018 14:29:43 +0530 Subject: [PATCH 32/45] Configuration - Create Lab Test on Sales Invoice Submit --- .../healthcare_settings.json | 39 +++++++++++++++++-- erpnext/healthcare/utils.py | 4 ++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json index a63ed88ed9..cbe49c1ac2 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json @@ -959,6 +959,38 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "create_test_on_si_submit", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Create Lab Test(s) on Sales Invoice Submit", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -1261,7 +1293,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2018-07-20 16:22:51.148456", + "modified": "2018-08-01 13:30:04.448397", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Settings", @@ -1296,5 +1328,6 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1, - "track_seen": 0 -} + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 045f3bc31f..4bcb1334c8 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -11,6 +11,7 @@ from frappe.utils import time_diff_in_hours, rounded from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account from erpnext.healthcare.doctype.patient_appointment.patient_appointment import validity_exists from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity, update_fee_validity +from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple @frappe.whitelist() def get_healthcare_services_to_invoice(patient): @@ -224,6 +225,9 @@ def manage_invoice_submit_cancel(doc, method): if frappe.get_meta(item.reference_dt).has_field("invoiced"): set_invoiced(item, method, doc.name) + if method=="on_submit" and frappe.db.get_value("Healthcare Settings", None, "create_test_on_si_submit") == '1': + create_multiple("Sales Invoice", doc.name) + def set_invoiced(item, method, ref_invoice=None): invoiced = False if(method=="on_submit"): From c64880b5785e3c09ad26779fd958c622c5294ddb Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 1 Aug 2018 14:37:13 +0530 Subject: [PATCH 33/45] Invoice - lab prescription from submitted Patient Encounter --- erpnext/healthcare/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 4bcb1334c8..b6b733025c 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -91,8 +91,8 @@ def get_healthcare_services_to_invoice(patient): item_to_invoice.append({'reference_type': 'Lab Test', 'reference_name': lab_test_obj.name, 'service': frappe.db.get_value("Lab Test Template", lab_test_obj.template, "item")}) - lab_rxs = frappe.db.sql("""select lp.name from `tabPatient Encounter` et, - `tabLab Prescription` lp where et.patient=%s and lp.parent=et.name and lp.test_created=0 and lp.invoiced=0""", (patient.name)) + lab_rxs = frappe.db.sql("""select lp.name from `tabPatient Encounter` et, `tabLab Prescription` lp + where et.patient=%s and lp.parent=et.name and lp.test_created=0 and lp.invoiced=0 and et.docstatus = 1""", (patient.name)) if lab_rxs: for lab_rx in lab_rxs: rx_obj = frappe.get_doc("Lab Prescription", lab_rx[0]) From 4371c7e492ccd4a8c7f1824ba483392971ee98c6 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 1 Aug 2018 18:40:05 +0530 Subject: [PATCH 34/45] Healthcare Service Unit - Tree View Updated --- .../healthcare_service_unit.json | 4 +- .../healthcare_service_unit_tree.js | 34 ++++++++++++- erpnext/healthcare/utils.py | 49 +++++++++++++++++++ 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json index a7e69329de..79af7ae15c 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json @@ -116,7 +116,7 @@ "allow_bulk_edit": 0, "allow_in_quick_entry": 0, "allow_on_submit": 0, - "bold": 0, + "bold": 1, "collapsible": 0, "columns": 0, "depends_on": "eval:doc.is_group != 1", @@ -461,7 +461,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-07-30 15:09:57.016256", + "modified": "2018-08-01 17:32:53.968441", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit", diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js index 4eb9475a33..a03b579c50 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js @@ -1,3 +1,35 @@ frappe.treeview_settings["Healthcare Service Unit"] = { - ignore_fields:["parent_healthcare_service_unit"] + breadcrumbs: "Healthcare Service Unit", + title: __("Healthcare Service Unit"), + get_tree_root: false, + filters: [{ + fieldname: "company", + fieldtype: "Select", + options: erpnext.utils.get_tree_options("company"), + label: __("Company"), + default: erpnext.utils.get_tree_default("company") + }], + get_tree_nodes: 'erpnext.healthcare.utils.get_children', + ignore_fields:["parent_healthcare_service_unit"], + onrender: function(node) { + if (node.data.occupied_out_of_vacant!==undefined){ + $('' + + " " + node.data.occupied_out_of_vacant + + '').insertBefore(node.$ul); + } + if (node.data && node.data.inpatient_occupancy!==undefined) { + if (node.data.inpatient_occupancy == 1){ + if (node.data.occupancy_status == "Occupied"){ + $('' + + " " + node.data.occupancy_status + + '').insertBefore(node.$ul); + } + if (node.data.occupancy_status == "Vacant"){ + $('' + + " " + node.data.occupancy_status + + '').insertBefore(node.$ul); + } + } + } + }, }; diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index b6b733025c..3372e1d81e 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -350,3 +350,52 @@ def get_drugs_to_invoice(encounter): item_to_invoice.append({'drug_code': drug_line.drug_code, 'quantity': qty, 'description': drug_line.dosage+" for "+drug_line.period}) return item_to_invoice + +@frappe.whitelist() +def get_children(doctype, parent, company, is_root=False): + parent_fieldname = 'parent_' + doctype.lower().replace(' ', '_') + fields = [ + 'name as value', + 'is_group as expandable', + 'lft', + 'rgt' + ] + # fields = [ 'name', 'is_group', 'lft', 'rgt' ] + filters = [['ifnull(`{0}`,"")'.format(parent_fieldname), '=', '' if is_root else parent]] + + if is_root: + fields += ['service_unit_type'] if doctype == 'Healthcare Service Unit' else [] + filters.append(['company', '=', company]) + + else: + fields += ['service_unit_type', 'allow_appointments', 'inpatient_occupancy', 'occupancy_status'] if doctype == 'Healthcare Service Unit' else [] + fields += [parent_fieldname + ' as parent'] + + hc_service_units = frappe.get_list(doctype, fields=fields, filters=filters) + + if doctype == 'Healthcare Service Unit': + for each in hc_service_units: + occupancy_msg = "" + if each['expandable'] == 1: + occupied = False + vacant = False + child_list = frappe.db.sql(""" + select name, occupancy_status from `tabHealthcare Service Unit` + where inpatient_occupancy = 1 and + lft > %s and rgt < %s""", + (each['lft'], each['rgt'])) + for child in child_list: + print child[0], child[1] + if not occupied: + occupied = 0 + if child[1] == "Occupied": + occupied += 1 + if not vacant: + vacant = 0 + if child[1] == "Vacant": + vacant += 1 + if vacant and occupied: + occupancy_total = vacant+occupied + occupancy_msg = str(occupied) + " Occupied out of " + str(occupancy_total) + each["occupied_out_of_vacant"] = occupancy_msg + return hc_service_units From 363deb67c3b4d1e04556fd729eaf199a89f55422 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Sat, 4 Aug 2018 12:56:36 +0530 Subject: [PATCH 35/45] Automate - Patient Apointment Invoicing --- .../healthcare_settings.json | 36 +++++- .../patient_appointment.py | 116 +++++++++++++++--- erpnext/healthcare/utils.py | 59 ++++++--- 3 files changed, 175 insertions(+), 36 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json index cbe49c1ac2..24c3cd9033 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json @@ -241,6 +241,40 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "description": "Manage Appointment Invoice submit and cancel automatically for Patient Encounter", + "fieldname": "manage_appointment_invoice_automatically", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Manage Appointment Invoice Automatically", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -1293,7 +1327,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2018-08-01 13:30:04.448397", + "modified": "2018-08-03 15:18:36.631441", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Settings", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 7d0dade5e0..a0ffeb1dfd 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -6,11 +6,13 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document import json -from frappe.utils import getdate +from frappe.utils import getdate, add_days from frappe import _ import datetime from frappe.core.doctype.sms_settings.sms_settings import send_sms from erpnext.hr.doctype.employee.employee import is_holiday +from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account +from erpnext.healthcare.utils import validity_exists, service_item_and_practitioner_charge class PatientAppointment(Document): def on_update(self): @@ -41,23 +43,108 @@ class PatientAppointment(Document): frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till)) confirm_sms(self) + if frappe.db.get_value("Healthcare Settings", None, "manage_appointment_invoice_automatically") == '1' and \ + frappe.db.get_value("Patient Appointment", self.name, "invoiced") != 1: + invoice_appointment(self) + +@frappe.whitelist() +def invoice_appointment(appointment_doc): + if not appointment_doc.name: + return False + sales_invoice = frappe.new_doc("Sales Invoice") + sales_invoice.customer = frappe.get_value("Patient", appointment_doc.patient, "customer") + sales_invoice.appointment = appointment_doc.name + sales_invoice.due_date = getdate() + sales_invoice.is_pos = True + sales_invoice.company = appointment_doc.company + sales_invoice.debit_to = get_receivable_account(appointment_doc.company) + + item_line = sales_invoice.append("items") + service_item, practitioner_charge = service_item_and_practitioner_charge(appointment_doc) + item_line.item_code = service_item + item_line.description = "Consulting Charges: " + appointment_doc.practitioner + item_line.income_account = get_income_account(appointment_doc.practitioner, appointment_doc.company) + item_line.rate = practitioner_charge + item_line.amount = practitioner_charge + item_line.qty = 1 + item_line.reference_dt = "Patient Appointment" + item_line.reference_dn = appointment_doc.name + + payments_line = sales_invoice.append("payments") + payments_line.mode_of_payment = "Cash" + payments_line.amount = practitioner_charge + + sales_invoice.set_missing_values(for_validate = True) + + sales_invoice.save(ignore_permissions=True) + sales_invoice.submit() + frappe.msgprint(_("Sales Invoice {0} created as paid".format(sales_invoice.name)), alert=True) + def appointment_cancel(appointment_id): appointment = frappe.get_doc("Patient Appointment", appointment_id) - # If invoiced --> fee_validity update with -1 visit if appointment.invoiced: - validity = validity_exists(appointment.practitioner, appointment.patient) - if validity: - fee_validity = frappe.get_doc("Fee Validity", validity[0][0]) - visited = fee_validity.visited - 1 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) - if visited <= 0: - frappe.msgprint( - _("Appointment cancelled, Please review and cancel the invoice {0}".format(fee_validity.ref_invoice)) - ) + sales_invoice = exists_sales_invoice(appointment) + if sales_invoice and cancel_sales_invoice(sales_invoice): + frappe.msgprint( + _("Appointment {0} and Sales Invoice {1} cancelled".format(appointment.name, sales_invoice.name)) + ) + else: + validity = validity_exists(appointment.practitioner, appointment.patient) + if validity: + fee_validity = frappe.get_doc("Fee Validity", validity[0][0]) + if appointment_valid_in_fee_validity(appointment, fee_validity.valid_till, True, fee_validity.ref_invoice): + visited = fee_validity.visited - 1 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + frappe.msgprint( + _("Appointment cancelled, Please review and cancel the invoice {0}".format(fee_validity.ref_invoice)) + ) + else: + frappe.msgprint(_("Appointment cancelled")) else: frappe.msgprint(_("Appointment cancelled")) + else: + frappe.msgprint(_("Appointment cancelled")) +def appointment_valid_in_fee_validity(appointment, valid_end_date, invoiced, ref_invoice): + valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") + max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") + valid_start_date = add_days(getdate(valid_end_date), -int(valid_days)) + + # Appointments which has same fee validity range with the appointment + appointments = frappe.get_list("Patient Appointment",{'patient': appointment.patient, 'invoiced': invoiced, + 'appointment_date':("<=", getdate(valid_end_date)), 'appointment_date':(">=", getdate(valid_start_date)), + 'practitioner': appointment.practitioner}, order_by="appointment_date desc", limit=int(max_visit)) + + if appointments and len(appointments) > 0: + appointment_obj = appointments[len(appointments)-1] + sales_invoice = exists_sales_invoice(appointment_obj) + if sales_invoice.name == ref_invoice: + return True + return False + +def cancel_sales_invoice(sales_invoice): + if frappe.db.get_value("Healthcare Settings", None, "manage_appointment_invoice_automatically") == '1': + if len(sales_invoice.items) == 1: + sales_invoice.cancel() + return True + return False + +def exists_sales_invoice_item(appointment): + return frappe.db.exists( + "Sales Invoice Item", + { + "reference_dt": "Patient Appointment", + "reference_dn": appointment.name + } + ) + +def exists_sales_invoice(appointment): + sales_item_exist = exists_sales_invoice_item(appointment) + if sales_item_exist: + sales_invoice = frappe.get_doc("Sales Invoice", frappe.db.get_value("Sales Invoice Item", sales_item_exist, "parent")) + return sales_invoice + return False @frappe.whitelist() def get_availability_data(date, practitioner): @@ -193,13 +280,6 @@ def confirm_sms(doc): message = frappe.db.get_value("Healthcare Settings", None, "app_con_msg") send_message(doc, message) - -def validity_exists(practitioner, patient): - return frappe.db.exists({ - "doctype": "Fee Validity", - "practitioner": practitioner, - "patient": patient}) - @frappe.whitelist() def create_encounter(appointment): appointment = frappe.get_doc("Patient Appointment", appointment) diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 3372e1d81e..3acb000da4 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -7,9 +7,8 @@ import frappe import datetime from frappe import _ import math -from frappe.utils import time_diff_in_hours, rounded +from frappe.utils import time_diff_in_hours, rounded, getdate, add_days from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account -from erpnext.healthcare.doctype.patient_appointment.patient_appointment import validity_exists from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity, update_fee_validity from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple @@ -272,6 +271,12 @@ def manage_prescriptions(invoiced, ref_dt, ref_dn, dt, created_check_field): doc_created = frappe.db.get_value(dt, {'prescription': ref_dn}) frappe.db.set_value(dt, doc_created, 'invoiced', invoiced) +def validity_exists(practitioner, patient): + return frappe.db.exists({ + "doctype": "Fee Validity", + "practitioner": practitioner, + "patient": patient}) + def manage_fee_validity(appointment_name, method, ref_invoice=None): appointment_doc = frappe.get_doc("Patient Appointment", appointment_name) validity_exist = validity_exists(appointment_doc.practitioner, appointment_doc.patient) @@ -282,12 +287,13 @@ def manage_fee_validity(appointment_name, method, ref_invoice=None): # Check if the validity is valid if (fee_validity.valid_till >= appointment_doc.appointment_date): if (method == "on_cancel" and appointment_doc.status != "Closed"): - visited = fee_validity.visited - 1 - if visited < 0: - visited = 0 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + if ref_invoice == fee_validity.ref_invoice: + visited = fee_validity.visited - 1 + if visited < 0: + visited = 0 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) do_not_update = True - elif (fee_validity.visited < fee_validity.max_visit): + elif (method == "on_submit" and fee_validity.visited < fee_validity.max_visit): visited = fee_validity.visited + 1 frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) do_not_update = True @@ -301,29 +307,48 @@ def manage_fee_validity(appointment_name, method, ref_invoice=None): fee_validity = create_fee_validity(appointment_doc.practitioner, appointment_doc.patient, appointment_doc.appointment_date, ref_invoice) visited = fee_validity.visited + print "do_not_update: ", do_not_update + print "visited: ", visited + # Mark All Patient Appointment invoiced = True in the validity range do not cross the max visit if (method == "on_cancel"): invoiced = True else: invoiced = False - patient_appointments = frappe.get_list("Patient Appointment",{'patient': fee_validity.patient, 'invoiced': invoiced, - 'appointment_date':("<=", fee_validity.valid_till), 'practitioner': fee_validity.practitioner}, order_by="appointment_date") + + patient_appointments = appointments_valid_in_fee_validity(appointment_doc, invoiced) if patient_appointments and fee_validity: visit = visited for appointment in patient_appointments: if (method == "on_cancel" and appointment.status != "Closed"): - visited = visited - 1 - if visited < 0: - visited = 0 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + if ref_invoice == fee_validity.ref_invoice: + visited = visited - 1 + if visited < 0: + visited = 0 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", False) manage_doc_for_appoitnment("Patient Encounter", appointment.name, False) - elif int(fee_validity.max_visit) > visit: - visited = visited + 1 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + elif method == "on_submit" and int(fee_validity.max_visit) > visit: + if ref_invoice == fee_validity.ref_invoice: + visited = visited + 1 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", True) manage_doc_for_appoitnment("Patient Encounter", appointment.name, True) - visit = visit + 1 + if ref_invoice == fee_validity.ref_invoice: + visit = visit + 1 + + if method == "on_cancel": + ref_invoice_in_fee_validity = frappe.db.get_value("Fee Validity", fee_validity.name, 'ref_invoice') + if ref_invoice_in_fee_validity == ref_invoice: + frappe.delete_doc("Fee Validity", fee_validity.name) + +def appointments_valid_in_fee_validity(appointment, invoiced): + valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") + max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") + valid_days_date = add_days(getdate(appointment.appointment_date), int(valid_days)) + return frappe.get_list("Patient Appointment",{'patient': appointment.patient, 'invoiced': invoiced, + 'appointment_date':("<=", valid_days_date), 'appointment_date':(">=", getdate(appointment.appointment_date)), + 'practitioner': appointment.practitioner}, order_by="appointment_date", limit=int(max_visit)-1) def manage_doc_for_appoitnment(dt_from_appointment, appointment, invoiced): dn_from_appointment = frappe.db.exists( From 7cc205946c3855c9d0e19b2152f3c07dc2370ccf Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 6 Aug 2018 11:48:03 +0530 Subject: [PATCH 36/45] Restrict to Healthcare --- .../clinical_procedure.json | 1 + .../healthcare_service_unit_type.json | 3 +- .../inpatient_record/inpatient_record.json | 3 +- .../appointment_analytic.json | 34 +++++++++--------- .../page/medical_record/medical_record.json | 36 ++++++++++--------- .../lab_test_report/lab_test_report.json | 12 +++++-- 6 files changed, 51 insertions(+), 38 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json index a830c37d96..c755b7fe79 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json @@ -912,6 +912,7 @@ "quick_entry": 0, "read_only": 0, "read_only_onload": 0, + "restrict_to_domain": "Healthcare", "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json index f8bb75e500..431e66e53d 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json @@ -547,7 +547,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-07-30 14:31:58.025819", + "modified": "2018-08-06 11:43:03.679223", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit Type", @@ -577,6 +577,7 @@ "quick_entry": 0, "read_only": 0, "read_only_onload": 0, + "restrict_to_domain": "Healthcare", "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json index 50f17e9dc4..92c11fbb4d 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json @@ -968,6 +968,7 @@ "quick_entry": 0, "read_only": 0, "read_only_onload": 0, + "restrict_to_domain": "Healthcare", "search_fields": "patient", "show_name_in_global_search": 0, "sort_field": "modified", @@ -976,4 +977,4 @@ "track_changes": 1, "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/healthcare/page/appointment_analytic/appointment_analytic.json b/erpnext/healthcare/page/appointment_analytic/appointment_analytic.json index 4deff806ba..ac5ca1a266 100644 --- a/erpnext/healthcare/page/appointment_analytic/appointment_analytic.json +++ b/erpnext/healthcare/page/appointment_analytic/appointment_analytic.json @@ -1,22 +1,24 @@ { - "content": null, - "creation": "2016-08-18 12:29:52.497819", - "docstatus": 0, - "doctype": "Page", - "idx": 0, - "modified": "2016-08-18 12:29:52.497819", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "appointment-analytic", - "owner": "Administrator", - "page_name": "Appointment Analytics", + "content": null, + "creation": "2016-08-18 12:29:52.497819", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2018-08-06 11:40:53.082863", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "appointment-analytic", + "owner": "Administrator", + "page_name": "Appointment Analytics", + "restrict_to_domain": "Healthcare", "roles": [ { "role": "Physician" } - ], - "script": null, - "standard": "Yes", - "style": null, + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0, "title": "Appointment Analytics" -} +} \ No newline at end of file diff --git a/erpnext/healthcare/page/medical_record/medical_record.json b/erpnext/healthcare/page/medical_record/medical_record.json index 7c786cae6b..ca30c3be29 100644 --- a/erpnext/healthcare/page/medical_record/medical_record.json +++ b/erpnext/healthcare/page/medical_record/medical_record.json @@ -1,23 +1,25 @@ { - "content": null, - "creation": "2016-06-09 11:33:14.025787", - "docstatus": 0, - "doctype": "Page", - "icon": "icon-play", - "idx": 0, - "modified": "2017-03-06 11:20:40.174661", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "medical_record", - "owner": "Administrator", - "page_name": "medical_record", + "content": null, + "creation": "2016-06-09 11:33:14.025787", + "docstatus": 0, + "doctype": "Page", + "icon": "icon-play", + "idx": 0, + "modified": "2018-08-06 11:40:39.705660", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "medical_record", + "owner": "Administrator", + "page_name": "medical_record", + "restrict_to_domain": "Healthcare", "roles": [ { "role": "Physician" } - ], - "script": null, - "standard": "Yes", - "style": null, + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0, "title": "Medical Record" -} +} \ No newline at end of file diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.json b/erpnext/healthcare/report/lab_test_report/lab_test_report.json index f133a8e1b7..30e5a5fd56 100644 --- a/erpnext/healthcare/report/lab_test_report/lab_test_report.json +++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.json @@ -1,17 +1,17 @@ { "add_total_row": 1, - "apply_user_permissions": 1, "creation": "2013-04-23 18:15:29", "disabled": 0, "docstatus": 0, "doctype": "Report", "idx": 1, "is_standard": "Yes", - "modified": "2017-08-23 14:54:12.593140", + "modified": "2018-08-06 11:41:50.218737", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test Report", "owner": "Administrator", + "prepared_report": 0, "ref_doctype": "Lab Test", "report_name": "Lab Test Report", "report_type": "Script Report", @@ -20,7 +20,13 @@ "role": "Laboratory User" }, { - "role": "System Manager" + "role": "Nursing User" + }, + { + "role": "LabTest Approver" + }, + { + "role": "Healthcare Administrator" } ] } \ No newline at end of file From 359bb512b4a003d0f28e578d16f1c12c51e95f55 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 6 Aug 2018 16:41:32 +0530 Subject: [PATCH 37/45] Payment mode and Paid amount in Patient Appointment --- .../patient_appointment.js | 15 +++++ .../patient_appointment.json | 67 ++++++++++++++++++- .../patient_appointment.py | 4 +- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index c0e5cd0979..9338b9f047 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -84,6 +84,20 @@ frappe.ui.form.on('Patient Appointment', { }); } frm.set_df_property("get_procedure_from_encounter", "read_only", frm.doc.__islocal ? 0 : 1); + frappe.db.get_value('Healthcare Settings', {name: 'Healthcare Settings'}, 'manage_appointment_invoice_automatically', (r) => { + if(r.manage_appointment_invoice_automatically == 1){ + frm.set_df_property("mode_of_payment", "hidden", 0); + frm.set_df_property("paid_amount", "hidden", 0); + frm.set_df_property("mode_of_payment", "reqd", 1); + frm.set_df_property("paid_amount", "reqd", 1); + } + else{ + frm.set_df_property("mode_of_payment", "hidden", 1); + frm.set_df_property("paid_amount", "hidden", 1); + frm.set_df_property("mode_of_payment", "reqd", 0); + frm.set_df_property("paid_amount", "reqd", 0); + } + }); }, check_availability: function(frm) { var { practitioner, appointment_date } = frm.doc; @@ -335,6 +349,7 @@ frappe.ui.form.on("Patient Appointment", "practitioner", function(frm) { }, callback: function (data) { frappe.model.set_value(frm.doctype,frm.docname, "department",data.message.department); + frappe.model.set_value(frm.doctype,frm.docname, "paid_amount",data.message.op_consulting_charge); } }); } diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index fc7b69d5e8..4ce4573f02 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -617,7 +617,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "eval:!doc.__islocal", + "depends_on": "", "fieldname": "section_break_1", "fieldtype": "Section Break", "hidden": 0, @@ -674,6 +674,71 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Mode of Payment", + "length": 0, + "no_copy": 0, + "options": "Mode of Payment", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "paid_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Paid Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index a0ffeb1dfd..3e6706f170 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -71,8 +71,8 @@ def invoice_appointment(appointment_doc): item_line.reference_dn = appointment_doc.name payments_line = sales_invoice.append("payments") - payments_line.mode_of_payment = "Cash" - payments_line.amount = practitioner_charge + payments_line.mode_of_payment = appointment_doc.mode_of_payment + payments_line.amount = appointment_doc.paid_amount sales_invoice.set_missing_values(for_validate = True) From a0b3e0268baa3ca3d629462c519475bb79999569 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 6 Aug 2018 16:59:55 +0530 Subject: [PATCH 38/45] Healthcare - Doctype - field index = 1 --- .../healthcare_practitioner.json | 4 ++-- .../healthcare_service_unit.json | 4 ++-- .../inpatient_occupancy/inpatient_occupancy.json | 9 +++++---- .../doctype/lab_prescription/lab_prescription.json | 9 +++++---- .../patient_medical_record.json | 4 ++-- .../procedure_prescription.json | 13 +++++++------ 6 files changed, 23 insertions(+), 20 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json index f8f4cc0eb0..ad68924d5a 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json @@ -201,7 +201,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -927,7 +927,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-07-30 12:00:20.746580", + "modified": "2018-08-06 16:45:37.899084", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Practitioner", diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json index 79af7ae15c..15e41e2e10 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json @@ -246,7 +246,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -461,7 +461,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-01 17:32:53.968441", + "modified": "2018-08-06 16:54:52.727598", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit", diff --git a/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.json b/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.json index 3e10143942..62dc198309 100644 --- a/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.json +++ b/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.json @@ -104,7 +104,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -185,7 +185,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-07-18 18:26:46.009878", + "modified": "2018-08-06 16:46:54.699133", "modified_by": "Administrator", "module": "Healthcare", "name": "Inpatient Occupancy", @@ -199,5 +199,6 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1, - "track_seen": 0 -} + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json b/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json index 2fbe58b7d8..ce6b20618b 100644 --- a/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json +++ b/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json @@ -107,7 +107,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -202,7 +202,7 @@ "remember_last_selected_value": 0, "report_hide": 1, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -218,7 +218,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-07-05 12:05:50.883490", + "modified": "2018-08-06 16:53:02.033406", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Prescription", @@ -233,5 +233,6 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 0, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json index 8c35ccd0cb..c6a6b44ce7 100644 --- a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json +++ b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json @@ -331,7 +331,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -454,4 +454,4 @@ "track_changes": 1, "track_seen": 1, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json b/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json index 205d31dc88..d67da9711d 100644 --- a/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json +++ b/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json @@ -236,7 +236,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -268,7 +268,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -301,7 +301,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -317,7 +317,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-07-17 13:08:15.499491", + "modified": "2018-08-06 16:53:36.440428", "modified_by": "Administrator", "module": "Healthcare", "name": "Procedure Prescription", @@ -331,5 +331,6 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1, - "track_seen": 0 -} + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file From 2e48337cd4b6e1f4eb813cd90402c715aa0ee37d Mon Sep 17 00:00:00 2001 From: Ranjith Date: Sat, 4 Aug 2018 14:28:20 +0530 Subject: [PATCH 39/45] Patient Encounter - update fields patient_name, patient_age --- .../patient_encounter/patient_encounter.json | 112 ++++++++++++------ 1 file changed, 73 insertions(+), 39 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index 84350883ce..6f00e5b465 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -213,6 +213,40 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", + "fetch_from": "patient.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Patient Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -222,7 +256,7 @@ "columns": 0, "fieldname": "patient_age", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -235,9 +269,9 @@ "options": "", "permlevel": 0, "precision": "", - "print_hide": 1, + "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 1, "reqd": 0, @@ -255,7 +289,7 @@ "columns": 0, "fieldname": "patient_sex", "fieldtype": "Select", - "hidden": 1, + "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -268,9 +302,9 @@ "options": "\nMale\nFemale\nOther", "permlevel": 0, "precision": "", - "print_hide": 1, + "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 1, "reqd": 0, @@ -279,39 +313,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "practitioner", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -376,6 +377,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "practitioner", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Healthcare Practitioner", + "length": 0, + "no_copy": 0, + "options": "Healthcare Practitioner", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, From b0ea297cf8beb13d2089df7d8ff0c3f0f4e5791d Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 8 Aug 2018 12:54:31 +0530 Subject: [PATCH 40/45] Patient Appointment - department - reqd = 0 --- .../doctype/patient_appointment/patient_appointment.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index 4ce4573f02..5215c2888c 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -108,7 +108,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 1, "set_only_once": 1, "translatable": 0, From a3b76fa40ed1d474e92bc83d9ba0ae45d5661b81 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 8 Aug 2018 13:07:19 +0530 Subject: [PATCH 41/45] Healthcare - Masters - allow import set to 1 --- .../appointment_type/appointment_type.json | 7 +- .../clinical_procedure_template.json | 76 ++++++++++++------- .../healthcare_service_unit.json | 4 +- .../healthcare_service_unit_type.json | 6 +- 4 files changed, 58 insertions(+), 35 deletions(-) diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.json b/erpnext/healthcare/doctype/appointment_type/appointment_type.json index 4dc40b1cec..ceabce2de1 100644 --- a/erpnext/healthcare/doctype/appointment_type/appointment_type.json +++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.json @@ -1,7 +1,7 @@ { "allow_copy": 0, "allow_guest_to_view": 0, - "allow_import": 0, + "allow_import": 1, "allow_rename": 1, "autoname": "field:appointment_type", "beta": 1, @@ -152,7 +152,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-06-13 00:04:24.597019", + "modified": "2018-08-08 12:57:54.544216", "modified_by": "Administrator", "module": "Healthcare", "name": "Appointment Type", @@ -208,5 +208,6 @@ "sort_order": "DESC", "title_field": "", "track_changes": 0, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json index 35c0ff5f38..df56918b9c 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json @@ -1,7 +1,7 @@ { "allow_copy": 0, "allow_guest_to_view": 0, - "allow_import": 0, + "allow_import": 1, "allow_rename": 1, "autoname": "field:template", "beta": 1, @@ -16,6 +16,7 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -43,11 +44,12 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "translatable": 0, - "unique": 0 + "translatable": 0, + "unique": 1 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -75,11 +77,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -107,11 +110,12 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -139,11 +143,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -169,11 +174,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -200,11 +206,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -232,11 +239,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -263,11 +271,12 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -294,11 +303,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -325,11 +335,12 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -357,11 +368,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -389,11 +401,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -421,11 +434,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -453,16 +467,17 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fetch_from": "sample.sample_uom", + "fetch_from": "sample.sample_uom", "fieldname": "sample_uom", "fieldtype": "Data", "hidden": 0, @@ -486,11 +501,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -517,11 +533,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -547,11 +564,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -578,11 +596,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -609,11 +628,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -640,11 +660,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -672,7 +693,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 } ], @@ -686,7 +707,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-16 22:43:29.674822", + "modified": "2018-08-08 13:00:06.260997", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedure Template", @@ -780,5 +801,6 @@ "sort_order": "DESC", "title_field": "template", "track_changes": 1, - "track_seen": 1 -} + "track_seen": 1, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json index 15e41e2e10..7d6b6c1d17 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json @@ -1,7 +1,7 @@ { "allow_copy": 0, "allow_guest_to_view": 0, - "allow_import": 0, + "allow_import": 1, "allow_rename": 1, "autoname": "field:healthcare_service_unit_name", "beta": 1, @@ -461,7 +461,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-06 16:54:52.727598", + "modified": "2018-08-08 12:57:12.709806", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit", diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json index 431e66e53d..40681e9f77 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json @@ -1,8 +1,8 @@ { "allow_copy": 0, "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, + "allow_import": 1, + "allow_rename": 1, "autoname": "field:service_unit_type", "beta": 0, "creation": "2018-07-11 16:47:51.414675", @@ -547,7 +547,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-06 11:43:03.679223", + "modified": "2018-08-08 13:00:23.751635", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit Type", From 0640b4794eccc8cbf6321b65b118ad44a2977adb Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 8 Aug 2018 13:12:07 +0530 Subject: [PATCH 42/45] Healthcare Practitioner - dashboard - updated --- .../healthcare_practitioner_dashboard.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner_dashboard.py b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner_dashboard.py index 3c01ab02d8..635464edd9 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner_dashboard.py +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner_dashboard.py @@ -9,10 +9,6 @@ def get_data(): { 'label': _('Appointments and Patient Encounters'), 'items': ['Patient Appointment', 'Patient Encounter'] - }, - { - 'label': _('Lab Tests'), - 'items': ['Lab Test'] } ] } From 34e561876af01f68456e43c49155521c421814a3 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Thu, 9 Aug 2018 15:51:37 +0530 Subject: [PATCH 43/45] Test prescription in Sales Invoice - Patient Encounter in draft status --- erpnext/healthcare/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 3acb000da4..1045d495b7 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -91,7 +91,7 @@ def get_healthcare_services_to_invoice(patient): 'service': frappe.db.get_value("Lab Test Template", lab_test_obj.template, "item")}) lab_rxs = frappe.db.sql("""select lp.name from `tabPatient Encounter` et, `tabLab Prescription` lp - where et.patient=%s and lp.parent=et.name and lp.test_created=0 and lp.invoiced=0 and et.docstatus = 1""", (patient.name)) + where et.patient=%s and lp.parent=et.name and lp.test_created=0 and lp.invoiced=0""", (patient.name)) if lab_rxs: for lab_rx in lab_rxs: rx_obj = frappe.get_doc("Lab Prescription", lab_rx[0]) From 8f808cbaf6342c857eb2426a2ba27d1c0a4afabc Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 29 Aug 2018 12:06:15 +0530 Subject: [PATCH 44/45] Patch - redesign healthcare billing work flow - fix --- erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py b/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py index aed6fc236c..08c3497b06 100644 --- a/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py +++ b/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py @@ -13,6 +13,7 @@ sales_invoice_referenced_doc = { def execute(): healthcare_custom_field_in_sales_invoice() + frappe.reload_doc('accounts', 'doctype', 'sales_invoice_item') for si_ref_doc in sales_invoice_referenced_doc: if frappe.db.exists('DocType', si_ref_doc): frappe.reload_doc(get_doctype_module(si_ref_doc), 'doctype', scrub(si_ref_doc)) From ffeff2d3858ce4138d1fe760b91fe633a28df9f1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 6 Sep 2018 12:45:07 +0530 Subject: [PATCH 45/45] fix(tests): reset session user as administrator after running tests --- erpnext/selling/doctype/sales_order/test_sales_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index f8fd1b3483..538ea55737 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -15,7 +15,7 @@ import json class TestSalesOrder(unittest.TestCase): def tearDown(self): - pass + frappe.set_user("Administrator") def test_make_material_request(self): so = make_sales_order(do_not_submit=True)