diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index dc201b0acb..e00d17d691 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,52 @@ 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")){ + 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"); + frm.add_custom_button(__('Prescriptions'), function() { + get_drugs_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); + } + } }) frappe.ui.form.on('Sales Invoice Timesheet', { @@ -816,3 +861,270 @@ 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 = ''; + 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; + 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 = ''; + $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, true); + dialog.show(); +}; + +var get_healthcare_items = function(frm, invoice_healthcare_services, $results, $placeholder, method, args, columns) { + var me = this; + $results.empty(); + frappe.call({ + method: method, + args: args, + callback: function(data) { + if(data.message){ + $results.append(make_list_row(columns, invoice_healthcare_services)); + for(let i=0; i + ${ + head ? `${__(frappe.model.unscrub(column))}` + + :(column !== "name" ? `${__(result[column])}` + : ` + ${__(result[column])}`) + } + `; + }) + + let $row = $(`
+
+ +
+ ${contents} +
`); + + $row = list_row_data_items(head, $row, result, invoice_healthcare_services); + return $row; +}; + +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", []); + add_to_item_line(frm, checked_values, invoice_healthcare_services); + dialog.hide(); + } + else{ + if(invoice_healthcare_services){ + frappe.msgprint(__("Please select Healthcare Service")); + } + else{ + frappe.msgprint(__("Please select Drug")); + } + } + }); +}; + +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') != 'undefined'){ + checked_values['rate'] = $(this).attr('data-rate'); + } + else{ + checked_values['rate'] = false; + } + if($(this).attr('data-income-account') != 'undefined'){ + checked_values['income_account'] = $(this).attr('data-income-account'); + } + 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(); +}; + +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/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 53d0fda1fc..b90a555ef0 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([{ @@ -1176,6 +1192,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/domains/healthcare.py b/erpnext/domains/healthcare.py index 5a54cf67d0..ee8dc8161b 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': 'Healthcare 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/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/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 04b96e960a..c755b7fe79 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, @@ -670,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, @@ -745,10 +912,11 @@ "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", "track_changes": 1, "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index 97d8a02080..6d00c25117 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -10,20 +10,29 @@ 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): 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: + 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") 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) @@ -38,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: @@ -52,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 @@ -91,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): @@ -130,6 +146,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 +183,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/clinical_procedure_item/clinical_procedure_item.json b/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json index c0a3247993..a974f2143d 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, @@ -367,7 +410,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-03-28 14:34:03.796229", + "modified": "2018-07-26 17:05:29.402908", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedure Item", @@ -381,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 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/fee_validity/fee_validity.py b/erpnext/healthcare/doctype/fee_validity/fee_validity.py index 53a17417ce..90285459ce 100644 --- a/erpnext/healthcare/doctype/fee_validity/fee_validity.py +++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.py @@ -4,6 +4,33 @@ 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, 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: + 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.ref_invoice = ref_invoice + fee_validity.save(ignore_permissions=True) + return fee_validity + + +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, ref_invoice) + return fee_validity 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 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 e7c575da9c..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 @@ -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, @@ -561,6 +594,102 @@ "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_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": "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 +927,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-07-10 11:18:58.760297", + "modified": "2018-08-06 16:45:37.899084", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Practitioner", @@ -873,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)) 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'] } ] } 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..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, @@ -43,7 +43,7 @@ "search_index": 0, "set_only_once": 0, "translatable": 0, - "unique": 0 + "unique": 1 }, { "allow_bulk_edit": 0, @@ -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", @@ -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 @@ -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-08-08 12:57:12.709806", "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/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/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..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", @@ -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-08-08 13:00:23.751635", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit Type", @@ -545,10 +577,12 @@ "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", "title_field": "service_unit_type", "track_changes": 0, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file 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 diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js index 8e98fee87e..22fbf5019a 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js @@ -23,5 +23,19 @@ 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'); } }); + +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 + } + }; + }); +}; diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json index 0bd8534f95..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, @@ -305,6 +339,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, @@ -797,6 +993,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, @@ -1099,7 +1327,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2018-07-16 14:00:04.171717", + "modified": "2018-08-03 15:18:36.631441", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Settings", @@ -1134,5 +1362,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/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)) diff --git a/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.json b/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.json index 2ac498df11..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 @@ -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-08-06 16:46:54.699133", "modified_by": "Administrator", "module": "Healthcare", "name": "Inpatient Occupancy", @@ -166,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/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.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/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index 07cd9e467e..c107cd7335 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -69,29 +69,75 @@ 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: 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) 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_pending_doc(doc, doc_name_list, pending_invoices) + + if pending_invoices: + frappe.throw(_("Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}").format(", " + .join(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: + 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" @@ -110,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: @@ -118,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 b192064f61..884974853d 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) @@ -22,13 +23,21 @@ 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 - discharge_patient(ip_record) + schedule_discharge(patient=patient) + 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 + 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') @@ -78,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", diff --git a/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json b/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json index 127bebf627..ce6b20618b 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, @@ -103,13 +107,14 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 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, @@ -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, @@ -195,9 +202,9 @@ "remember_last_selected_value": 0, "report_hide": 1, "reqd": 0, - "search_index": 0, + "search_index": 1, "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-08-06 16:53:02.033406", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Prescription", @@ -226,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/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.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/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 767581f656..98ab696e90 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -4,11 +4,9 @@ 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 _ +from frappe.model.document import Document +from frappe.utils import getdate, cstr class LabTest(Document): def on_submit(self): @@ -31,6 +29,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() @@ -60,20 +60,97 @@ 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 +@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) + if item.reference_dt == "Lab Prescription": + lab_test.prescription = item.reference_dn + lab_test.save(ignore_permissions = True) + 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: + 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") - if(invoice): - lab_test.invoice = invoice - if(encounter): - lab_test.practitioner = encounter.practitioner + 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.test_name = template.test_name lab_test.template = template.name lab_test.test_group = template.test_group lab_test.result_date = getdate() @@ -133,7 +210,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 @@ -146,24 +223,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) @@ -211,16 +270,10 @@ 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 -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}) @@ -248,49 +301,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..1f6a12f935 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"){ @@ -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(); +}; 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 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'] } ] } diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index 9799018cea..9338b9f047 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,20 +83,21 @@ 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); + 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; @@ -339,21 +339,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({ @@ -364,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 960648bb69..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, @@ -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, @@ -712,8 +777,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 +787,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 +1055,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.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index ad2a933544..3e6706f170 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -6,12 +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.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account 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): @@ -38,30 +39,112 @@ 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) + 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 = appointment_doc.mode_of_payment + payments_line.amount = appointment_doc.paid_amount + + 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 invoice --> fee_validity update with -1 visit - if appointment.sales_invoice: - validity = frappe.db.exists({"doctype": "Fee Validity", "ref_invoice": appointment.sales_invoice}) - 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)) - ) + # If invoiced --> fee_validity update with -1 visit + if appointment.invoiced: + 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): @@ -197,100 +280,6 @@ def confirm_sms(doc): message = frappe.db.get_value("Healthcare Settings", None, "app_con_msg") 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 +290,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 +348,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.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index d11d1a7c5f..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, @@ -482,8 +516,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 +526,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 +1180,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.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py index 3d8f95298b..4c62e57537 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py @@ -5,9 +5,7 @@ from __future__ import unicode_literals 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 +from frappe.utils import cstr class PatientEncounter(Document): def on_update(self): @@ -23,77 +21,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/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 b4c453280e..d67da9711d 100644 --- a/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json +++ b/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json @@ -236,7 +236,72 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, + "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": 1, + "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": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -252,7 +317,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-07-16 13:08:15.499491", + "modified": "2018-08-06 16:53:36.440428", "modified_by": "Administrator", "module": "Healthcare", "name": "Procedure Prescription", @@ -266,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 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/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/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/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 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..1045d495b7 --- /dev/null +++ b/erpnext/healthcare/utils.py @@ -0,0 +1,426 @@ +# -*- 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 _ +import math +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.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): + 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 + service_item = None + if 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': service_item, '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 + service_item = None + if 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': service_item, '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': 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)) + 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': 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: + 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': 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 + 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': frappe.db.get_value("Clinical Procedure Template", rx_obj.procedure, "item")}) + + 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 + 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: + 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}) + + return item_to_invoice + else: + frappe.throw(_("The Patient {0} do not have customer refrence to invoice").format(patient.name)) + +def service_item_and_practitioner_charge(doc): + is_ip = doc_is_ip(doc) + if is_ip: + 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_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) + + 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_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) + +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: + 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, 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"): + validate_invoiced_on_submit(item) + invoiced = True + + 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" + else: + 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) + + 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): + 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)) + +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': 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) + 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"): + 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 (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 + else: + do_not_update = False + + if not do_not_update: + 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, 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 = 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"): + 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 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) + 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( + dt_from_appointment, + { + "appointment": appointment + } + ) + 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 + +@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 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 +} diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 512e33bdb5..0fe66e3005 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 # 2018-09-03 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..08c3497b06 --- /dev/null +++ b/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py @@ -0,0 +1,60 @@ +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() + 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)) + + 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 + """.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], sales_invoice_referenced_doc[si_ref_doc]) + invoice = frappe.get_doc("Sales Invoice", invoice_id) + if invoice.items: + marked = False + if not marked: + for item_line in invoice.items: + marked = True + 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}` 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(): + frappe.reload_doc('healthcare', 'doctype', 'patient') + frappe.reload_doc('healthcare', 'doctype', 'healthcare_practitioner') + if data['custom_fields']: + create_custom_fields(data['custom_fields']) + + frappe.db.sql(""" + delete from `tabCustom Field` + where fieldname = 'appointment' and options = 'Patient Appointment' + """)