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 = $(`
+
+
+
+
+
`);
+ $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 = $(``);
+
+ $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 = $(`
+
+
+
+
+
`);
+ $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="#">Get Lab Test\
+ ', {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 Order No. \n
\n
\n : {{doc.invoice}}\n
\n
\n {%- endif -%}\n\n
\n
\n Patient \n
\n {% if doc.patient %}\n
\n : {{doc.patient}}\n
\n {% else %}\n
\n : Patient Name \n
\n {%- endif -%}\n
\n\n
\n
\n Age \n
\n
\n : {{doc.patient_age}}\n
\n
\n\n
\n
\n Gender \n
\n
\n : {{doc.patient_sex}}\n
\n
\n\n
\n\n
\n\n
\n
\n Healthcare Practitioner \n
\n {% if doc.practitioner %}\n
\n : {{doc.practitioner}}\n
\n {%- endif -%}\n
\n\n {% if doc.sample_date %}\n
\n
\n Sample Date \n
\n
\n : {{doc.sample_date}}\n
\n
\n {%- endif -%}\n\n {% if doc.result_date %}\n
\n
\n Result Date \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 Name of Test \n Result \n Normal Range \n \n\n {%- if doc.normal_test_items|length > 1 %}\n {{ doc.test_name }} \n {%- endif -%}\n\n {%- for row in doc.normal_test_items -%}\n \n \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\n \n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%} \n {%- if row.test_uom -%}{{ row.test_uom }}{%- endif -%}\n \n\n \n \n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n
\n \n \n\n {%- endfor -%}\n {%- endif -%}\n \n
\n\n
\n \n {%- if doc.special_test_items -%}\n \n Name of Test \n Result \n \n {{ doc.test_name }} \n {%- for row in doc.special_test_items -%}\n \n {{ row.test_particulars }} \n \n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%}\n \n \n\n {%- endfor -%}\n {%- endif -%}\n\n {%- if doc.sensitivity_test_items -%}\n \n Antibiotic \n Sensitivity \n \n {%- for row in doc.sensitivity_test_items -%}\n \n {{ row.antibiotic }} \n {{ row.antibiotic_sensitivity }} \n \n\n {%- endfor -%}\n {%- endif -%}\n\n \n
\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 Patient \n
\n {% if doc.patient %}\n
\n : {{doc.patient}}\n
\n {% else %}\n
\n : Patient Name \n
\n {%- endif -%}\n
\n\n
\n
\n Age \n
\n
\n : {{doc.patient_age}}\n
\n
\n\n
\n
\n Gender \n
\n
\n : {{doc.patient_sex}}\n
\n
\n\n
\n\n
\n\n
\n
\n Practitioner \n
\n {% if doc.practitioner %}\n
\n : {{doc.practitioner}}\n
\n {%- endif -%}\n
\n\n {% if doc.sample_date %}\n
\n
\n Sample Date \n
\n
\n : {{doc.sample_date}}\n
\n
\n {%- endif -%}\n\n {% if doc.result_date %}\n
\n
\n Result Date \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 Name of Test \n Result \n Normal Range \n \n\n {%- if doc.normal_test_items|length > 1 %}\n {{ doc.test_name }} \n {%- endif -%}\n\n {%- for row in doc.normal_test_items -%}\n \n \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\n \n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%} \n {%- if row.test_uom -%}{{ row.test_uom }}{%- endif -%}\n \n\n \n \n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n
\n \n \n\n {%- endfor -%}\n {%- endif -%}\n \n
\n\n
\n \n {%- if doc.special_test_items -%}\n \n Name of Test \n Result \n \n {{ doc.test_name }} \n {%- for row in doc.special_test_items -%}\n \n {{ row.test_particulars }} \n \n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%}\n \n \n\n {%- endfor -%}\n {%- endif -%}\n\n {%- if doc.sensitivity_test_items -%}\n \n Antibiotic \n Sensitivity \n \n {%- for row in doc.sensitivity_test_items -%}\n \n {{ row.antibiotic }} \n {{ row.antibiotic_sensitivity }} \n \n\n {%- endfor -%}\n {%- endif -%}\n\n \n
\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'
+ """)