Merge branch 'healthcare_v11_wip' of https://github.com/ESS-LLP/erpnext-healthcare into ESS-LLP-healthcare_v11_wip

This commit is contained in:
Nabin Hait 2018-09-05 17:21:47 +05:30
commit aacb915cae
53 changed files with 2521 additions and 649 deletions

View File

@ -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(`<div class="results"
style="border: 1px solid #d1d8dd; border-radius: 3px; height: 300px; overflow: auto;"></div>`);
$results = $wrapper.find('.results');
$placeholder = $(`<div class="multiselect-empty-state">
<span class="text-center" style="margin-top: -40px;">
<i class="fa fa-2x fa-heartbeat text-extra-muted"></i>
<p class="text-extra-muted">No billable Healthcare Services found</p>
</span>
</div>`);
$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<data.message.length; i++){
$results.append(make_list_row(columns, invoice_healthcare_services, data.message[i]));
}
}else {
$results.append($placeholder);
}
}
});
}
var make_list_row= function(columns, invoice_healthcare_services, result={}) {
var me = this;
// Make a head row by default (if result not passed)
let head = Object.keys(result).length === 0;
let contents = ``;
columns.forEach(function(column) {
contents += `<div class="list-item__content ellipsis">
${
head ? `<span class="ellipsis">${__(frappe.model.unscrub(column))}</span>`
:(column !== "name" ? `<span class="ellipsis">${__(result[column])}</span>`
: `<a class="list-id ellipsis">
${__(result[column])}</a>`)
}
</div>`;
})
let $row = $(`<div class="list-item">
<div class="list-item__content" style="flex: 0 0 10px;">
<input type="checkbox" class="list-row-check" ${result.checked ? 'checked' : ''}>
</div>
${contents}
</div>`);
$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(`<div class="results"
style="border: 1px solid #d1d8dd; border-radius: 3px; height: 300px; overflow: auto;"></div>`);
$results = $wrapper.find('.results');
$placeholder = $(`<div class="multiselect-empty-state">
<span class="text-center" style="margin-top: -40px;">
<i class="fa fa-2x fa-heartbeat text-extra-muted"></i>
<p class="text-extra-muted">No Drug Prescription found</p>
</span>
</div>`);
$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 = $(`<div class="list-item-container"
data-dn= "${result.reference_name}" data-dt= "${result.reference_type}" data-item= "${result.service}"
data-rate = ${result.rate}
data-income-account = "${result.income_account}"
data-qty = ${result.qty}
data-description = "${result.description}">
</div>`).append($row);
}
else{
head ? $row.addClass('list-item--head')
: $row = $(`<div class="list-item-container"
data-item= "${result.drug_code}"
data-qty = ${result.quantity}
data-description = "${result.description}">
</div>`).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<checked_values.length; i++){
var si_item = frappe.model.add_child(frm.doc, 'Sales Invoice Item', 'items');
frappe.model.set_value(si_item.doctype, si_item.name, 'item_code', checked_values[i]['item']);
frappe.model.set_value(si_item.doctype, si_item.name, 'qty', 1);
if(checked_values[i]['qty'] > 1){
frappe.model.set_value(si_item.doctype, si_item.name, 'qty', parseFloat(checked_values[i]['qty']));
}
}
frm.refresh_fields();
}
};

View File

@ -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

View File

@ -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'
}

View File

@ -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
}

View File

@ -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");
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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({

View File

@ -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
}

View File

@ -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))

View File

@ -9,10 +9,6 @@ def get_data():
{
'label': _('Appointments and Patient Encounters'),
'items': ['Patient Appointment', 'Patient Encounter']
},
{
'label': _('Lab Tests'),
'items': ['Lab Test']
}
]
}

View File

@ -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
}

View File

@ -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){
$('<span class="balance-area pull-right text-muted small">'
+ " " + node.data.occupied_out_of_vacant
+ '</span>').insertBefore(node.$ul);
}
if (node.data && node.data.inpatient_occupancy!==undefined) {
if (node.data.inpatient_occupancy == 1){
if (node.data.occupancy_status == "Occupied"){
$('<span class="balance-area pull-right small">'
+ " " + node.data.occupancy_status
+ '</span>').insertBefore(node.$ul);
}
if (node.data.occupancy_status == "Vacant"){
$('<span class="balance-area pull-right text-muted small">'
+ " " + node.data.occupancy_status
+ '</span>').insertBefore(node.$ul);
}
}
}
},
};

View File

@ -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
}

View File

@ -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

View File

@ -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
}
};
});
};

View File

@ -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
}

View File

@ -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))

View File

@ -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
}

View File

@ -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"
}
};
};

View File

@ -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
}
}

View File

@ -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()

View File

@ -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",

View File

@ -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
}

View File

@ -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){
<div class="col-xs-1">\
<a data-name="%(name)s" data-lab-test="%(lab_test)s"\
data-encounter="%(encounter)s" data-practitioner="%(practitioner)s"\
data-invoice="%(invoice)s" href="#"><button class="btn btn-default btn-xs">Get Lab Test\
</button></a></div></div>', {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="#"><button class="btn btn-default btn-xs">Get Lab Test\
</button></a></div></div>', {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',
['<a href="#Form/Sales Invoice/'+r.message+'">' + r.message+ '</a>'])); */
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){

View File

@ -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
}
}

View File

@ -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))

View File

@ -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();
};

View File

@ -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

View File

@ -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']
}
]
}

View File

@ -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);
}
});
}

View File

@ -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
}
}

View File

@ -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,

View File

@ -10,10 +10,6 @@ def get_data():
{
'label': _('Consultations'),
'items': ['Patient Encounter', 'Vital Signs', 'Patient Medical Record']
},
{
'label': _('Billing'),
'items': ['Sales Invoice']
}
]
}

View File

@ -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) {

View File

@ -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
}
}

View File

@ -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")

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

File diff suppressed because one or more lines are too long

View File

@ -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"
}
]
}

View File

@ -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)

426
erpnext/healthcare/utils.py Normal file
View File

@ -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") \
+ """<b><a href="#Form/Healthcare Settings">Healthcare Settings</a></b>""")
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) \
+ """<b><a href="#Form/Healthcare Settings">Healthcare Settings</a></b>""")
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) \
+ """ <b><a href="#Form/Healthcare Practitioner/{0}">{0}</a></b>""".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

View File

@ -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
}
]
}
}

View File

@ -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

View File

@ -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'
""")