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

No billable Healthcare Services found

+
+
`); + $results.on('click', '.list-item--head :checkbox', (e) => { + $results.find('.list-item-container .list-row-check') + .prop("checked", ($(e.target).is(':checked'))); + }); + set_primary_action(frm, dialog, $results); + dialog.show(); +}; + +var get_services= function(frm, patient_id, $results, $placeholder) { + var me = this; + $results.empty(); + frappe.call({ + method:"erpnext.healthcare.utils.get_healthcare_services_to_invoice", + args: { + patient: patient_id + }, + callback: function(data) { + if(data.message){ + $results.append(make_list_row()); + for(let i=0; i + ${ + head ? `${__(frappe.model.unscrub(column))}` + + :(column !== "name" ? `${__(result[column])}` + : ` + ${__(result[column])}`) + } + `; + }) + + let $row = $(`
+
+ +
+ ${contents} +
`); + + head ? $row.addClass('list-item--head') + : $row = $(`
+
`).append($row); + return $row; +}; + +var set_primary_action= function(frm, dialog, $results) { + var me = this; + dialog.set_primary_action(__('Add'), function() { + // TODO: Get checked items + let checked_values = get_checked_values($results); + if(checked_values.length > 0){ + frm.set_value("patient", dialog.fields_dict.patient.input.value); + frm.set_value("items", []); + for(let i=0; i 1){ + frappe.model.set_value(si_item.doctype, si_item.name, 'qty', checked_values[i]['qty']); + } + } + frm.refresh_fields(); + dialog.hide(); + } + else{ + frappe.msgprint(__("Please select Healthcare Service")); + } + }); +}; + +var get_checked_values= function($results) { + return $results.find('.list-item-container').map(function() { + let checked_values = {}; + if ($(this).find('.list-row-check:checkbox:checked').length > 0 ) { + checked_values['dn'] = $(this).attr('data-dn'); + checked_values['dt'] = $(this).attr('data-dt'); + checked_values['item'] = $(this).attr('data-item'); + if($(this).attr('data-rate')){ + checked_values['rate'] = $(this).attr('data-rate'); + } + if($(this).attr('data-income-account')){ + checked_values['income_account'] = $(this).attr('data-income-account'); + } + if($(this).attr('data-qty')){ + checked_values['qty'] = $(this).attr('data-qty'); + } + return checked_values; + } + }).get(); +}; diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index a45b953cdd..7aeb86d7af 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -24,6 +24,8 @@ from erpnext.accounts.general_ledger import get_round_off_account_and_cost_cente from erpnext.accounts.doctype.loyalty_program.loyalty_program import \ get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points +from erpnext.healthcare.utils import manage_invoice_submit_cancel + from six import iteritems form_grid_templates = { @@ -179,6 +181,13 @@ class SalesInvoice(SellingController): if self.redeem_loyalty_points and self.loyalty_points: self.apply_loyalty_points() + # Healthcare Service Invoice. + domain_settings = frappe.get_doc('Domain Settings') + active_domains = [d.domain for d in domain_settings.active_domains] + + if "Healthcare" in active_domains: + manage_invoice_submit_cancel(self, "on_submit") + def validate_pos_paid_amount(self): if len(self.payments) == 0 and self.is_pos: frappe.throw(_("At least one mode of payment is required for POS invoice.")) @@ -227,6 +236,13 @@ class SalesInvoice(SellingController): unlink_inter_company_invoice(self.doctype, self.name, self.inter_company_invoice_reference) + # Healthcare Service Invoice. + domain_settings = frappe.get_doc('Domain Settings') + active_domains = [d.domain for d in domain_settings.active_domains] + + if "Healthcare" in active_domains: + manage_invoice_submit_cancel(self, "on_cancel") + def update_status_updater_args(self): if cint(self.update_stock): self.status_updater.extend([{ diff --git a/erpnext/domains/healthcare.py b/erpnext/domains/healthcare.py index 5a54cf67d0..57f30f6301 100644 --- a/erpnext/domains/healthcare.py +++ b/erpnext/domains/healthcare.py @@ -21,9 +21,30 @@ data = { 'Patient' ], 'custom_fields': { - 'Sales Invoice': dict(fieldname='appointment', label='Patient Appointment', - fieldtype='Link', options='Patient Appointment', - insert_after='customer') + 'Sales Invoice': [ + { + 'fieldname': 'patient', 'label': 'Patient', 'fieldtype': 'Link', 'options': 'Patient', + 'insert_after': 'naming_series' + }, + { + 'fieldname': 'patient_name', 'label': 'Patient Name', 'fieldtype': 'Data', 'fetch_from': 'patient.patient_name', + 'insert_after': 'patient', 'read_only': True + }, + { + 'fieldname': 'ref_practitioner', 'label': 'Referring Practitioner', 'fieldtype': 'Link', 'options': 'Practitioner', + 'insert_after': 'customer' + } + ], + 'Sales Invoice Item': [ + { + 'fieldname': 'reference_dt', 'label': 'Reference DocType', 'fieldtype': 'Link', 'options': 'DocType', + 'insert_after': 'edit_references' + }, + { + 'fieldname': 'reference_dn', 'label': 'Reference Name', 'fieldtype': 'Dynamic Link', 'options': 'reference_dt', + 'insert_after': 'reference_dt' + } + ] }, 'on_setup': 'erpnext.healthcare.setup.setup_healthcare' } diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index 97d8a02080..d51d062408 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -24,6 +24,8 @@ class ClinicalProcedure(Document): self.set_actual_qty(); def after_insert(self): + if self.prescription: + frappe.db.set_value("Procedure Prescription", self.prescription, "procedure_created", 1) if self.appointment: frappe.db.set_value("Patient Appointment", self.appointment, "status", "Closed") template = frappe.get_doc("Clinical Procedure Template", self.procedure_template) @@ -130,6 +132,8 @@ def set_stock_items(doc, stock_detail_parent, parenttype): se_child.conversion_factor = flt(d["conversion_factor"]) if d["batch_no"]: se_child.batch_no = d["batch_no"] + if parenttype == "Clinical Procedure Template": + se_child.invoice_separately_as_consumables = d["invoice_separately_as_consumables"] return doc def get_item_dict(table, parent, parenttype): @@ -165,6 +169,8 @@ def create_procedure(appointment): procedure.patient_age = appointment.patient_age procedure.patient_sex = appointment.patient_sex procedure.procedure_template = appointment.procedure_template + procedure.procedure_prescription = appointment.procedure_prescription + procedure.invoiced = appointment.invoiced procedure.medical_department = appointment.department procedure.start_date = appointment.appointment_date procedure.start_time = appointment.appointment_time diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.py b/erpnext/healthcare/doctype/fee_validity/fee_validity.py index 53a17417ce..a707236c86 100644 --- a/erpnext/healthcare/doctype/fee_validity/fee_validity.py +++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.py @@ -4,6 +4,32 @@ from __future__ import unicode_literals from frappe.model.document import Document +import frappe +from frappe.utils import getdate +import datetime class FeeValidity(Document): pass + +def update_fee_validity(fee_validity, date): + max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") + valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") + if not valid_days: + valid_days = 1 + if not max_visit: + max_visit = 1 + date = getdate(date) + valid_till = date + datetime.timedelta(days=int(valid_days)) + fee_validity.max_visit = max_visit + fee_validity.visited = 1 + fee_validity.valid_till = valid_till + fee_validity.save(ignore_permissions=True) + return fee_validity + + +def create_fee_validity(practitioner, patient, date): + fee_validity = frappe.new_doc("Fee Validity") + fee_validity.practitioner = practitioner + fee_validity.patient = patient + fee_validity = update_fee_validity(fee_validity, date) + return fee_validity diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js index c3b069d049..06637bcdcc 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test.js @@ -24,11 +24,6 @@ frappe.ui.form.on('Lab Test', { refresh : function(frm){ refresh_field('normal_test_items'); refresh_field('special_test_items'); - if(!frm.doc.__islocal && !frm.doc.invoice && frappe.user.has_role("Accounts User")){ - frm.add_custom_button(__('Make Invoice'), function() { - make_invoice(frm); - }); - } if(frm.doc.__islocal){ frm.add_custom_button(__('Get from Patient Encounter'), function () { get_lab_test_prescribed(frm); @@ -166,8 +161,8 @@ var show_lab_tests = function(frm, result){ ', {name:y[0], lab_test: y[1], encounter:y[2], invoice:y[3], practitioner:y[4], date:y[5]})).appendTo(html_field); + data-invoiced="%(invoiced)s" href="#">', {name:y[0], lab_test: y[1], encounter:y[2], invoiced:y[3], practitioner:y[4], date:y[5]})).appendTo(html_field); row.find("a").click(function() { frm.doc.template = $(this).attr("data-lab-test"); frm.doc.prescription = $(this).attr("data-name"); @@ -175,14 +170,11 @@ var show_lab_tests = function(frm, result){ frm.set_df_property("template", "read_only", 1); frm.set_df_property("patient", "read_only", 1); frm.set_df_property("practitioner", "read_only", 1); - if($(this).attr("data-invoice") != 'null'){ - frm.doc.invoice = $(this).attr("data-invoice"); - refresh_field("invoice"); - }else { - frm.doc.invoice = ""; - refresh_field("invoice"); + frm.doc.invoiced = 0; + if($(this).attr("data-invoiced") == 1){ + frm.doc.invoiced = 1; } - + refresh_field("invoiced"); refresh_field("template"); d.hide(); return false; @@ -195,24 +187,6 @@ var show_lab_tests = function(frm, result){ d.show(); }; -var make_invoice = function(frm){ - var doc = frm.doc; - frappe.call({ - method: "erpnext.healthcare.doctype.lab_test.lab_test.create_invoice", - args: {company:doc.company, patient:doc.patient, lab_tests: [doc.name], prescriptions:[]}, - callback: function(r){ - if(!r.exc){ - if(r.message){ - /* frappe.show_alert(__('Sales Invoice {0} created', - ['' + r.message+ ''])); */ - frappe.set_route("Form", "Sales Invoice", r.message); - } - cur_frm.reload_doc(); - } - } - }); -}; - cur_frm.cscript.custom_before_submit = function(doc) { if(doc.normal_test_items){ for(let result in doc.normal_test_items){ diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 767581f656..9cba34cd58 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -5,10 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -import json from frappe.utils import getdate, cstr -from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account -from frappe import _ class LabTest(Document): def on_submit(self): @@ -31,6 +28,8 @@ class LabTest(Document): def after_insert(self): if(self.prescription): frappe.db.set_value("Lab Prescription", self.prescription, "test_created", 1) + if frappe.db.get_value("Lab Prescription", self.prescription, 'invoiced') == 1: + self.invoiced = True if not self.test_name and self.template: self.load_test_from_template() self.reload() @@ -64,7 +63,7 @@ def create_lab_test_doc(invoice, encounter, patient, template): #create Test Result for template, copy vals from Invoice lab_test = frappe.new_doc("Lab Test") if(invoice): - lab_test.invoice = invoice + lab_test.invoiced = True if(encounter): lab_test.practitioner = encounter.practitioner lab_test.patient = patient.name @@ -133,7 +132,7 @@ def create_sample_doc(template, patient, invoice): #create Sample Collection for template, copy vals from Invoice sample_collection = frappe.new_doc("Sample Collection") if(invoice): - sample_collection.invoice = invoice + sample_collection.invoiced = True sample_collection.patient = patient.name sample_collection.patient_age = patient.get_age() sample_collection.patient_sex = patient.sex @@ -211,7 +210,7 @@ def load_result_format(lab_test, template, prescription, invoice): if(prescription): lab_test.prescription = prescription if(invoice): - frappe.db.set_value("Lab Prescription", prescription, "invoice", invoice) + frappe.db.set_value("Lab Prescription", prescription, "invoiced", True) lab_test.save(ignore_permissions=True) # insert the result return lab_test @@ -248,49 +247,7 @@ def delete_lab_test_from_medical_record(self): if medical_record_id and medical_record_id[0][0]: frappe.delete_doc("Patient Medical Record", medical_record_id[0][0]) -def create_item_line(test_code, sales_invoice): - if test_code: - item = frappe.get_doc("Item", test_code) - if item: - if not item.disabled: - sales_invoice_line = sales_invoice.append("items") - sales_invoice_line.item_code = item.item_code - sales_invoice_line.item_name = item.item_name - sales_invoice_line.qty = 1.0 - sales_invoice_line.description = item.description - -@frappe.whitelist() -def create_invoice(company, patient, lab_tests, prescriptions): - test_ids = json.loads(lab_tests) - line_ids = json.loads(prescriptions) - if not test_ids and not line_ids: - return - sales_invoice = frappe.new_doc("Sales Invoice") - sales_invoice.customer = frappe.get_value("Patient", patient, "customer") - sales_invoice.due_date = getdate() - sales_invoice.is_pos = '0' - sales_invoice.debit_to = get_receivable_account(company) - for line in line_ids: - test_code = frappe.get_value("Lab Prescription", line, "test_code") - create_item_line(test_code, sales_invoice) - for test in test_ids: - template = frappe.get_value("Lab Test", test, "template") - test_code = frappe.get_value("Lab Test Template", template, "item") - create_item_line(test_code, sales_invoice) - sales_invoice.set_missing_values() - sales_invoice.save() - #set invoice in lab test - for test in test_ids: - frappe.db.set_value("Lab Test", test, "invoice", sales_invoice.name) - prescription = frappe.db.get_value("Lab Test", test, "prescription") - if prescription: - frappe.db.set_value("Lab Prescription", prescription, "invoice", sales_invoice.name) - #set invoice in prescription - for line in line_ids: - frappe.db.set_value("Lab Prescription", line, "invoice", sales_invoice.name) - return sales_invoice.name - @frappe.whitelist() def get_lab_test_prescribed(patient): - return frappe.db.sql("""select cp.name, cp.test_code, cp.parent, cp.invoice, ct.practitioner, ct.encounter_date from `tabPatient Encounter` ct, + return frappe.db.sql("""select cp.name, cp.test_code, cp.parent, cp.invoiced, ct.practitioner, ct.encounter_date from `tabPatient Encounter` ct, `tabLab Prescription` cp where ct.patient=%s and cp.parent=ct.name and cp.test_created=0""", (patient)) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test_list.js b/erpnext/healthcare/doctype/lab_test/lab_test_list.js index c36c115f99..1d65e5be66 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test_list.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test_list.js @@ -2,7 +2,7 @@ (c) ESS 2015-16 */ frappe.listview_settings['Lab Test'] = { - add_fields: ["name", "status", "invoice"], + add_fields: ["name", "status", "invoiced"], filters:[["docstatus","=","0"]], get_indicator: function(doc) { if(doc.status=="Approved"){ diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index 9799018cea..c0e5cd0979 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -4,7 +4,6 @@ frappe.provide("erpnext.queries"); frappe.ui.form.on('Patient Appointment', { setup: function(frm) { frm.custom_make_buttons = { - 'Sales Invoice': 'Invoice', 'Vital Signs': 'Vital Signs', 'Patient Encounter': 'Patient Encounter' }; @@ -84,19 +83,6 @@ frappe.ui.form.on('Patient Appointment', { btn_update_status(frm, "Cancelled"); }); } - - if(!frm.doc.__islocal){ - if(frm.doc.sales_invoice && frappe.user.has_role("Accounts User")){ - frm.add_custom_button(__('Invoice'), function() { - frappe.set_route("Form", "Sales Invoice", frm.doc.sales_invoice); - },__("View") ); - } - else if(frm.doc.status != "Cancelled" && frappe.user.has_role("Accounts User")){ - frm.add_custom_button(__('Invoice'), function() { - btn_invoice_encounter(frm); - },__("Create")); - } - } frm.set_df_property("get_procedure_from_encounter", "read_only", frm.doc.__islocal ? 0 : 1); }, check_availability: function(frm) { @@ -339,21 +325,6 @@ var btn_update_status = function(frm, status){ ); }; -var btn_invoice_encounter = function(frm){ - frappe.call({ - doc: frm.doc, - method:"create_invoice", - callback: function(data){ - if(!data.exc){ - if(data.message){ - frappe.set_route("Form", "Sales Invoice", data.message); - } - cur_frm.reload_doc(); - } - } - }); -}; - frappe.ui.form.on("Patient Appointment", "practitioner", function(frm) { if(frm.doc.practitioner){ frappe.call({ diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index ad2a933544..7d0dade5e0 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -10,7 +10,6 @@ from frappe.utils import getdate from frappe import _ import datetime from frappe.core.doctype.sms_settings.sms_settings import send_sms -from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account from erpnext.hr.doctype.employee.employee import is_holiday class PatientAppointment(Document): @@ -38,26 +37,23 @@ class PatientAppointment(Document): visited = fee_validity.visited + 1 frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) if fee_validity.ref_invoice: - frappe.db.set_value("Patient Appointment", appointment.name, "sales_invoice", fee_validity.ref_invoice) + frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", True) frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till)) confirm_sms(self) - def create_invoice(self): - return invoice_appointment(self) - def appointment_cancel(appointment_id): appointment = frappe.get_doc("Patient Appointment", appointment_id) - # If invoice --> fee_validity update with -1 visit - if appointment.sales_invoice: - validity = frappe.db.exists({"doctype": "Fee Validity", "ref_invoice": appointment.sales_invoice}) + # If invoiced --> fee_validity update with -1 visit + if appointment.invoiced: + validity = validity_exists(appointment.practitioner, appointment.patient) if validity: fee_validity = frappe.get_doc("Fee Validity", validity[0][0]) visited = fee_validity.visited - 1 frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) if visited <= 0: frappe.msgprint( - _("Appointment cancelled, Please review and cancel the invoice {0}".format(appointment.sales_invoice)) + _("Appointment cancelled, Please review and cancel the invoice {0}".format(fee_validity.ref_invoice)) ) else: frappe.msgprint(_("Appointment cancelled")) @@ -198,99 +194,12 @@ def confirm_sms(doc): send_message(doc, message) -@frappe.whitelist() -def invoice_appointment(appointment_doc): - if not appointment_doc.name: - return False - sales_invoice = frappe.new_doc("Sales Invoice") - sales_invoice.customer = frappe.get_value("Patient", appointment_doc.patient, "customer") - sales_invoice.appointment = appointment_doc.name - sales_invoice.due_date = getdate() - sales_invoice.is_pos = '0' - sales_invoice.company = appointment_doc.company - sales_invoice.debit_to = get_receivable_account(appointment_doc.company) - - fee_validity = get_fee_validity(appointment_doc.practitioner, appointment_doc.patient, appointment_doc.appointment_date) - procedure_template = False - if appointment_doc.procedure_template: - procedure_template = appointment_doc.procedure_template - create_invoice_items(appointment_doc.practitioner, appointment_doc.company, sales_invoice, procedure_template) - - sales_invoice.save(ignore_permissions=True) - frappe.db.sql("""update `tabPatient Appointment` set sales_invoice=%s where name=%s""", (sales_invoice.name, appointment_doc.name)) - frappe.db.set_value("Fee Validity", fee_validity.name, "ref_invoice", sales_invoice.name) - encounter = frappe.db.exists({ - "doctype": "Patient Encounter", - "appointment": appointment_doc.name}) - if encounter: - frappe.db.set_value("Patient Encounter", encounter[0][0], "invoice", sales_invoice.name) - return sales_invoice.name - - -def get_fee_validity(practitioner, patient, date): - validity_exist = validity_exists(practitioner, patient) - if validity_exist: - fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0]) - fee_validity = update_fee_validity(fee_validity, date) - else: - fee_validity = create_fee_validity(practitioner, patient, date) - return fee_validity - - def validity_exists(practitioner, patient): return frappe.db.exists({ "doctype": "Fee Validity", "practitioner": practitioner, "patient": patient}) - -def update_fee_validity(fee_validity, date): - max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") - valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") - if not valid_days: - valid_days = 1 - if not max_visit: - max_visit = 1 - date = getdate(date) - valid_till = date + datetime.timedelta(days=int(valid_days)) - fee_validity.max_visit = max_visit - fee_validity.visited = 1 - fee_validity.valid_till = valid_till - fee_validity.save(ignore_permissions=True) - return fee_validity - - -def create_fee_validity(practitioner, patient, date): - fee_validity = frappe.new_doc("Fee Validity") - fee_validity.practitioner = practitioner - fee_validity.patient = patient - fee_validity = update_fee_validity(fee_validity, date) - return fee_validity - - -def create_invoice_items(practitioner, company, invoice, procedure_template): - item_line = invoice.append("items") - if procedure_template: - procedure_template_obj = frappe.get_doc("Clinical Procedure Template", procedure_template) - item_line.item_code = procedure_template_obj.item_code - item_line.item_name = procedure_template_obj.template - item_line.description = procedure_template_obj.description - else: - item_line.item_name = "Consulting Charges" - item_line.description = "Consulting Charges: " + practitioner - item_line.uom = "Nos" - item_line.conversion_factor = 1 - item_line.income_account = get_income_account(practitioner, company) - op_consulting_charge = frappe.db.get_value("Healthcare Practitioner", practitioner, "op_consulting_charge") - if op_consulting_charge: - item_line.rate = op_consulting_charge - item_line.amount = op_consulting_charge - item_line.qty = 1 - - - return invoice - - @frappe.whitelist() def create_encounter(appointment): appointment = frappe.get_doc("Patient Appointment", appointment) @@ -301,8 +210,8 @@ def create_encounter(appointment): encounter.visit_department = appointment.department encounter.patient_sex = appointment.patient_sex encounter.encounter_date = appointment.appointment_date - if appointment.sales_invoice: - encounter.invoice = appointment.sales_invoice + if appointment.invoiced: + encounter.invoiced = True return encounter.as_dict() @@ -359,6 +268,7 @@ def get_events(start, end, filters=None): item.appointment_datetime = item.appointment_date + datetime.timedelta(minutes = item.duration) return data + @frappe.whitelist() def get_procedure_prescribed(patient): return frappe.db.sql("""select pp.name, pp.procedure, pp.parent, ct.practitioner, diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py index f9ef1cbe77..a030f19e95 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py @@ -10,10 +10,6 @@ def get_data(): { 'label': _('Consultations'), 'items': ['Patient Encounter', 'Vital Signs', 'Patient Medical Record'] - }, - { - 'label': _('Billing'), - 'items': ['Sales Invoice'] } ] } diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 47c9cada6b..2dd3512bc0 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -94,11 +94,6 @@ frappe.ui.form.on('Patient Encounter', { } }; }); - if(!frm.doc.__islocal && !frm.doc.invoice && (frappe.user.has_role("Accounts User"))){ - frm.add_custom_button(__('Invoice'), function() { - btn_invoice_encounter(frm); - },__("Create")); - } frm.set_df_property("appointment", "read_only", frm.doc.__islocal ? 0:1); frm.set_df_property("patient", "read_only", frm.doc.__islocal ? 0:1); frm.set_df_property("patient_age", "read_only", frm.doc.__islocal ? 0:1); @@ -139,23 +134,6 @@ var schedule_discharge = function(frm) { }); }; -var btn_invoice_encounter = function(frm){ - var doc = frm.doc; - frappe.call({ - method: - "erpnext.healthcare.doctype.encounter.encounter.create_invoice", - args: {company: doc.company, patient: doc.patient, practitioner: doc.practitioner, encounter_id: doc.name }, - callback: function(data){ - if(!data.exc){ - if(data.message){ - frappe.set_route("Form", "Sales Invoice", data.message); - } - cur_frm.reload_doc(); - } - } - }); -}; - var create_medical_record = function (frm) { if(!frm.doc.patient){ frappe.throw(__("Please select patient")); @@ -203,10 +181,16 @@ frappe.ui.form.on("Patient Encounter", "appointment", function(frm){ frappe.model.set_value(frm.doctype,frm.docname, "patient", data.message.patient); frappe.model.set_value(frm.doctype,frm.docname, "type", data.message.appointment_type); frappe.model.set_value(frm.doctype,frm.docname, "practitioner", data.message.practitioner); - frappe.model.set_value(frm.doctype,frm.docname, "invoice", data.message.sales_invoice); + frappe.model.set_value(frm.doctype,frm.docname, "invoiced", data.message.invoiced); } }); } + else{ + frappe.model.set_value(frm.doctype,frm.docname, "patient", ""); + frappe.model.set_value(frm.doctype,frm.docname, "type", ""); + frappe.model.set_value(frm.doctype,frm.docname, "practitioner", ""); + frappe.model.set_value(frm.doctype,frm.docname, "invoiced", 0); + } }); frappe.ui.form.on("Patient Encounter", "practitioner", function(frm) { diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py index 3d8f95298b..5aa7867167 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py @@ -7,7 +7,6 @@ import frappe from frappe.model.document import Document from frappe.utils import getdate, cstr import json -from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account class PatientEncounter(Document): def on_update(self): @@ -23,77 +22,6 @@ class PatientEncounter(Document): frappe.db.set_value("Patient Appointment", self.appointment, "status", "Open") delete_medical_record(self) -def set_sales_invoice_fields(company, patient): - sales_invoice = frappe.new_doc("Sales Invoice") - sales_invoice.customer = frappe.get_value("Patient", patient, "customer") - # patient is custom field in sales inv. - sales_invoice.due_date = getdate() - sales_invoice.is_pos = '0' - sales_invoice.debit_to = get_receivable_account(company) - - return sales_invoice - -def create_sales_invoice_item_lines(item, sales_invoice): - sales_invoice_line = sales_invoice.append("items") - sales_invoice_line.item_code = item.item_code - sales_invoice_line.item_name = item.item_name - sales_invoice_line.qty = 1.0 - sales_invoice_line.description = item.description - return sales_invoice_line - -@frappe.whitelist() -def create_drug_invoice(company, patient, prescriptions): - list_ids = json.loads(prescriptions) - if not (company or patient or prescriptions): - return False - - sales_invoice = set_sales_invoice_fields(company, patient) - sales_invoice.update_stock = 1 - - for line_id in list_ids: - line_obj = frappe.get_doc("Drug Prescription", line_id) - if line_obj: - if(line_obj.drug_code): - item = frappe.get_doc("Item", line_obj.drug_code) - sales_invoice_line = create_sales_invoice_item_lines(item, sales_invoice) - sales_invoice_line.qty = line_obj.get_quantity() - #income_account and cost_center in itemlines - by set_missing_values() - sales_invoice.set_missing_values() - return sales_invoice.as_dict() - -@frappe.whitelist() -def create_invoice(company, patient, practitioner, encounter_id): - if not encounter_id: - return False - sales_invoice = frappe.new_doc("Sales Invoice") - sales_invoice.customer = frappe.get_value("Patient", patient, "customer") - sales_invoice.due_date = getdate() - sales_invoice.is_pos = '0' - sales_invoice.debit_to = get_receivable_account(company) - - create_invoice_items(practitioner, sales_invoice, company) - - sales_invoice.save(ignore_permissions=True) - frappe.db.sql("""update `tabPatient Encounter` set invoice=%s where name=%s""", (sales_invoice.name, encounter_id)) - appointment = frappe.db.get_value("Patient Encounter", encounter_id, "appointment") - if appointment: - frappe.db.set_value("Patient Appointment", appointment, "sales_invoice", sales_invoice.name) - return sales_invoice.name - -def create_invoice_items(practitioner, invoice, company): - item_line = invoice.append("items") - item_line.item_name = "Consulting Charges" - item_line.description = "Consulting Charges: " + practitioner - item_line.qty = 1 - item_line.uom = "Nos" - item_line.conversion_factor = 1 - item_line.income_account = get_income_account(practitioner, company) - op_consulting_charge = frappe.get_value("Healthcare Practitioner", practitioner, "op_consulting_charge") - if op_consulting_charge: - item_line.rate = op_consulting_charge - item_line.amount = op_consulting_charge - return invoice - def insert_encounter_to_medical_record(doc): subject = set_subject_field(doc) medical_record = frappe.new_doc("Patient Medical Record") diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.py b/erpnext/healthcare/report/lab_test_report/lab_test_report.py index e4771c56bb..b9a26dfcad 100644 --- a/erpnext/healthcare/report/lab_test_report/lab_test_report.py +++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.py @@ -17,7 +17,7 @@ def execute(filters=None): data = [] for lab_test in lab_test_list: - row = [ lab_test.test_name, lab_test.patient, lab_test.practitioner, lab_test.invoice, lab_test.status, lab_test.result_date, lab_test.department] + row = [ lab_test.test_name, lab_test.patient, lab_test.practitioner, lab_test.invoiced, lab_test.status, lab_test.result_date, lab_test.department] data.append(row) return columns, data @@ -28,7 +28,7 @@ def get_columns(): _("Test") + ":Data:120", _("Patient") + ":Link/Patient:180", _("Healthcare Practitioner") + ":Link/Healthcare Practitioner:120", - _("Invoice") + ":Link/Sales Invoice:120", + _("Invoiced") + ":Check:100", _("Status") + ":Data:120", _("Result Date") + ":Date:120", _("Department") + ":Data:120", @@ -52,7 +52,7 @@ def get_conditions(filters): def get_lab_test(filters): conditions = get_conditions(filters) - return frappe.db.sql("""select name, patient, test_name, patient_name, status, result_date, practitioner, invoice, department + return frappe.db.sql("""select name, patient, test_name, patient_name, status, result_date, practitioner, invoiced, department from `tabLab Test` where docstatus<2 %s order by submitted_date desc, name desc""" % conditions, filters, as_dict=1) diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py new file mode 100644 index 0000000000..5a7c7e3680 --- /dev/null +++ b/erpnext/healthcare/utils.py @@ -0,0 +1,232 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, earthians and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import datetime +from frappe import _ +from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account +from erpnext.healthcare.doctype.patient_appointment.patient_appointment import validity_exists +from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity, update_fee_validity + +@frappe.whitelist() +def get_healthcare_services_to_invoice(patient): + patient = frappe.get_doc("Patient", patient) + if patient: + if patient.customer: + item_to_invoice = [] + patient_appointments = frappe.get_list("Patient Appointment",{'patient': patient.name, 'invoiced': False}, + order_by="appointment_date") + if patient_appointments: + fee_validity_details = [] + valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") + max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") + for patient_appointment in patient_appointments: + patient_appointment_obj = frappe.get_doc("Patient Appointment", patient_appointment['name']) + + if patient_appointment_obj.procedure_template: + if frappe.db.get_value("Clinical Procedure Template", patient_appointment_obj.procedure_template, "is_billable") == 1: + item_to_invoice.append({'reference_type': 'Patient Appointment', 'reference_name': patient_appointment_obj.name, 'service': patient_appointment_obj.procedure_template}) + else: + practitioner_exist_in_list = False + skip_invoice = False + if fee_validity_details: + for validity in fee_validity_details: + if validity['practitioner'] == patient_appointment_obj.practitioner: + practitioner_exist_in_list = True + if validity['valid_till'] >= patient_appointment_obj.appointment_date: + validity['visits'] = validity['visits']+1 + if int(max_visit) > validity['visits']: + skip_invoice = True + if not skip_invoice: + validity['visits'] = 1 + validity['valid_till'] = patient_appointment_obj.appointment_date + datetime.timedelta(days=int(valid_days)) + if not practitioner_exist_in_list: + valid_till = patient_appointment_obj.appointment_date + datetime.timedelta(days=int(valid_days)) + visits = 0 + validity_exist = validity_exists(patient_appointment_obj.practitioner, patient_appointment_obj.patient) + if validity_exist: + fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0]) + valid_till = fee_validity.valid_till + visits = fee_validity.visited + fee_validity_details.append({'practitioner': patient_appointment_obj.practitioner, + 'valid_till': valid_till, 'visits': visits}) + + if not skip_invoice: + practitioner_charge = 0 + income_account = None + if patient_appointment_obj.practitioner: + practitioner_charge = get_practitioner_charge(patient_appointment_obj.practitioner) + income_account = get_income_account(patient_appointment_obj.practitioner, patient_appointment_obj.company) + item_to_invoice.append({'reference_type': 'Patient Appointment', 'reference_name': patient_appointment_obj.name, + 'service': 'Consulting Charges', 'rate': practitioner_charge, + 'income_account': income_account}) + + encounters = frappe.get_list("Patient Encounter", {'patient': patient.name, 'invoiced': False, 'docstatus': 1}) + if encounters: + for encounter in encounters: + encounter_obj = frappe.get_doc("Patient Encounter", encounter['name']) + if not encounter_obj.appointment: + practitioner_charge = 0 + income_account = None + if encounter_obj.practitioner: + practitioner_charge = get_practitioner_charge(encounter_obj.practitioner) + income_account = get_income_account(encounter_obj.practitioner, encounter_obj.company) + item_to_invoice.append({'reference_type': 'Patient Encounter', 'reference_name': encounter_obj.name, + 'service': 'Consulting Charges', 'rate': practitioner_charge, + 'income_account': income_account}) + + lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False}) + if lab_tests: + for lab_test in lab_tests: + lab_test_obj = frappe.get_doc("Lab Test", lab_test['name']) + if frappe.db.get_value("Lab Test Template", lab_test_obj.template, "is_billable") == 1: + item_to_invoice.append({'reference_type': 'Lab Test', 'reference_name': lab_test_obj.name, 'service': lab_test_obj.template}) + + lab_rxs = frappe.db.sql("""select lp.name from `tabPatient Encounter` et, + `tabLab Prescription` lp where et.patient=%s and lp.parent=et.name and lp.test_created=0 and lp.invoiced=0""", (patient.name)) + if lab_rxs: + for lab_rx in lab_rxs: + rx_obj = frappe.get_doc("Lab Prescription", lab_rx[0]) + if rx_obj.test_code and (frappe.db.get_value("Lab Test Template", rx_obj.test_code, "is_billable") == 1): + item_to_invoice.append({'reference_type': 'Lab Prescription', 'reference_name': rx_obj.name, 'service': rx_obj.test_code}) + + procedures = frappe.get_list("Clinical Procedure", {'patient': patient.name, 'invoiced': False}) + if procedures: + for procedure in procedures: + procedure_obj = frappe.get_doc("Clinical Procedure", procedure['name']) + if not procedure_obj.appointment: + if procedure_obj.procedure_template and (frappe.db.get_value("Clinical Procedure Template", procedure_obj.procedure_template, "is_billable") == 1): + item_to_invoice.append({'reference_type': 'Clinical Procedure', 'reference_name': procedure_obj.name, 'service': procedure_obj.procedure_template}) + + procedure_rxs = frappe.db.sql("""select pp.name from `tabPatient Encounter` et, + `tabProcedure Prescription` pp where et.patient=%s and pp.parent=et.name and + pp.procedure_created=0 and pp.invoiced=0 and pp.appointment_booked=0""", (patient.name)) + if procedure_rxs: + for procedure_rx in procedure_rxs: + rx_obj = frappe.get_doc("Procedure Prescription", procedure_rx[0]) + if frappe.db.get_value("Clinical Procedure Template", rx_obj.procedure, "is_billable") == 1: + item_to_invoice.append({'reference_type': 'Procedure Prescription', 'reference_name': rx_obj.name, 'service': rx_obj.procedure}) + + procedure_consumables = frappe.db.sql("""select pc.name from `tabClinical Procedure` cp, + `tabClinical Procedure Item` pc where cp.patient=%s and pc.parent=cp.name and + pc.invoice_separately_as_consumables=1 and pc.invoiced=0""", (patient.name)) + if procedure_consumables: + for procedure_consumable in procedure_consumables: + procedure_consumable_obj = frappe.get_doc("Clinical Procedure Item", procedure_consumable[0]) + item_to_invoice.append({'reference_type': 'Clinical Procedure Item', 'reference_name': procedure_consumable_obj.name, + 'service': procedure_consumable_obj.item_code, 'qty': procedure_consumable_obj.qty}) + + return item_to_invoice + else: + frappe.throw(_("The Patient {0} do not have customer refrence to invoice").format(patient.name)) + +def get_practitioner_charge(practitioner): + practitioner_charge = frappe.db.get_value("Healthcare Practitioner", practitioner, "op_consulting_charge") + if practitioner_charge: + return practitioner_charge + +def manage_invoice_submit_cancel(doc, method): + if doc.items: + for item in doc.items: + if item.reference_dt and item.reference_dn: + if frappe.get_meta(item.reference_dt).has_field("invoiced"): + set_invoiced(item, method) + +def set_invoiced(item, method): + invoiced = False + if(method=="on_submit"): + validate_invoiced_on_submit(item) + invoiced = True + + frappe.db.set_value(item.reference_dt, item.reference_dn, "invoiced", invoiced) + if item.reference_dt == 'Patient Appointment': + if frappe.db.get_value('Patient Appointment', item.reference_dn, 'procedure_template'): + dt_from_appointment = "Clinical Procedure" + else: + manage_fee_validity(item.reference_dn, method) + dt_from_appointment = "Patient Encounter" + manage_doc_for_appoitnment(dt_from_appointment, item.reference_dn, invoiced) + + elif item.reference_dt == 'Lab Prescription': + manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, "Lab Test", "test_created") + + elif item.reference_dt == 'Procedure Prescription': + manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, "Clinical Procedure", "procedure_created") + +def validate_invoiced_on_submit(item): + is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, "invoiced") + if is_invoiced == 1: + frappe.throw(_("The item referenced by {0} - {1} is already invoiced"\ + ).format(item.reference_dt, item.reference_dn)) + +def manage_prescriptions(invoiced, ref_dt, ref_dn, dt, created_check_field): + created = frappe.db.get_value(ref_dt, ref_dn, created_check_field) + if created == 1: + # Fetch the doc created for the prescription + doc_created = frappe.db.get_value(dt, {'prescription': item.reference_dn}) + frappe.db.set_value(dt, doc_created, 'invoiced', invoiced) + +def manage_fee_validity(appointment_name, method): + appointment_doc = frappe.get_doc("Patient Appointment", appointment_name) + validity_exist = validity_exists(appointment_doc.practitioner, appointment_doc.patient) + do_not_update = False + visited = 0 + if validity_exist: + fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0]) + # Check if the validity is valid + if (fee_validity.valid_till >= appointment_doc.appointment_date): + if (method == "on_cancel" and appointment_doc.status != "Closed"): + visited = fee_validity.visited - 1 + if visited < 0: + visited = 0 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + do_not_update = True + elif (fee_validity.visited < fee_validity.max_visit): + visited = fee_validity.visited + 1 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + do_not_update = True + else: + do_not_update = False + + if not do_not_update: + fee_validity = update_fee_validity(fee_validity, appointment_doc.appointment_date) + visited = fee_validity.visited + else: + fee_validity = create_fee_validity(appointment_doc.practitioner, appointment_doc.patient, appointment_doc.appointment_date) + visited = fee_validity.visited + + # Mark All Patient Appointment invoiced = True in the validity range do not cross the max visit + if (method == "on_cancel"): + invoiced = True + else: + invoiced = False + patient_appointments = frappe.get_list("Patient Appointment",{'patient': fee_validity.patient, 'invoiced': invoiced, + 'appointment_date':("<=", fee_validity.valid_till), 'practitioner': fee_validity.practitioner}, order_by="appointment_date") + if patient_appointments and fee_validity: + visit = visited + for appointment in patient_appointments: + if (method == "on_cancel" and appointment.status != "Closed"): + visited = visited - 1 + if visited < 0: + visited = 0 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", False) + manage_doc_for_appoitnment("Patient Encounter", appointment.name, False) + elif int(fee_validity.max_visit) > visit: + visited = visited + 1 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", True) + manage_doc_for_appoitnment("Patient Encounter", appointment.name, True) + visit = visit + 1 + +def manage_doc_for_appoitnment(dt_from_appointment, appointment, invoiced): + dn_from_appointment = frappe.db.exists( + dt_from_appointment, + { + "appointment": appointment + } + ) + if dn_from_appointment: + frappe.db.set_value(dt_from_appointment, dn_from_appointment, "invoiced", invoiced)