diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json index cbe49c1ac2..24c3cd9033 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json @@ -241,6 +241,40 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "description": "Manage Appointment Invoice submit and cancel automatically for Patient Encounter", + "fieldname": "manage_appointment_invoice_automatically", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Manage Appointment Invoice Automatically", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -1293,7 +1327,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2018-08-01 13:30:04.448397", + "modified": "2018-08-03 15:18:36.631441", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Settings", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 7d0dade5e0..a0ffeb1dfd 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -6,11 +6,13 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document import json -from frappe.utils import getdate +from frappe.utils import getdate, add_days from frappe import _ import datetime from frappe.core.doctype.sms_settings.sms_settings import send_sms from erpnext.hr.doctype.employee.employee import is_holiday +from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account +from erpnext.healthcare.utils import validity_exists, service_item_and_practitioner_charge class PatientAppointment(Document): def on_update(self): @@ -41,23 +43,108 @@ class PatientAppointment(Document): frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till)) confirm_sms(self) + if frappe.db.get_value("Healthcare Settings", None, "manage_appointment_invoice_automatically") == '1' and \ + frappe.db.get_value("Patient Appointment", self.name, "invoiced") != 1: + invoice_appointment(self) + +@frappe.whitelist() +def invoice_appointment(appointment_doc): + if not appointment_doc.name: + return False + sales_invoice = frappe.new_doc("Sales Invoice") + sales_invoice.customer = frappe.get_value("Patient", appointment_doc.patient, "customer") + sales_invoice.appointment = appointment_doc.name + sales_invoice.due_date = getdate() + sales_invoice.is_pos = True + sales_invoice.company = appointment_doc.company + sales_invoice.debit_to = get_receivable_account(appointment_doc.company) + + item_line = sales_invoice.append("items") + service_item, practitioner_charge = service_item_and_practitioner_charge(appointment_doc) + item_line.item_code = service_item + item_line.description = "Consulting Charges: " + appointment_doc.practitioner + item_line.income_account = get_income_account(appointment_doc.practitioner, appointment_doc.company) + item_line.rate = practitioner_charge + item_line.amount = practitioner_charge + item_line.qty = 1 + item_line.reference_dt = "Patient Appointment" + item_line.reference_dn = appointment_doc.name + + payments_line = sales_invoice.append("payments") + payments_line.mode_of_payment = "Cash" + payments_line.amount = practitioner_charge + + sales_invoice.set_missing_values(for_validate = True) + + sales_invoice.save(ignore_permissions=True) + sales_invoice.submit() + frappe.msgprint(_("Sales Invoice {0} created as paid".format(sales_invoice.name)), alert=True) + def appointment_cancel(appointment_id): appointment = frappe.get_doc("Patient Appointment", appointment_id) - # If invoiced --> fee_validity update with -1 visit if appointment.invoiced: - validity = validity_exists(appointment.practitioner, appointment.patient) - if validity: - fee_validity = frappe.get_doc("Fee Validity", validity[0][0]) - visited = fee_validity.visited - 1 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) - if visited <= 0: - frappe.msgprint( - _("Appointment cancelled, Please review and cancel the invoice {0}".format(fee_validity.ref_invoice)) - ) + sales_invoice = exists_sales_invoice(appointment) + if sales_invoice and cancel_sales_invoice(sales_invoice): + frappe.msgprint( + _("Appointment {0} and Sales Invoice {1} cancelled".format(appointment.name, sales_invoice.name)) + ) + else: + validity = validity_exists(appointment.practitioner, appointment.patient) + if validity: + fee_validity = frappe.get_doc("Fee Validity", validity[0][0]) + if appointment_valid_in_fee_validity(appointment, fee_validity.valid_till, True, fee_validity.ref_invoice): + visited = fee_validity.visited - 1 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + frappe.msgprint( + _("Appointment cancelled, Please review and cancel the invoice {0}".format(fee_validity.ref_invoice)) + ) + else: + frappe.msgprint(_("Appointment cancelled")) else: frappe.msgprint(_("Appointment cancelled")) + else: + frappe.msgprint(_("Appointment cancelled")) +def appointment_valid_in_fee_validity(appointment, valid_end_date, invoiced, ref_invoice): + valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") + max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") + valid_start_date = add_days(getdate(valid_end_date), -int(valid_days)) + + # Appointments which has same fee validity range with the appointment + appointments = frappe.get_list("Patient Appointment",{'patient': appointment.patient, 'invoiced': invoiced, + 'appointment_date':("<=", getdate(valid_end_date)), 'appointment_date':(">=", getdate(valid_start_date)), + 'practitioner': appointment.practitioner}, order_by="appointment_date desc", limit=int(max_visit)) + + if appointments and len(appointments) > 0: + appointment_obj = appointments[len(appointments)-1] + sales_invoice = exists_sales_invoice(appointment_obj) + if sales_invoice.name == ref_invoice: + return True + return False + +def cancel_sales_invoice(sales_invoice): + if frappe.db.get_value("Healthcare Settings", None, "manage_appointment_invoice_automatically") == '1': + if len(sales_invoice.items) == 1: + sales_invoice.cancel() + return True + return False + +def exists_sales_invoice_item(appointment): + return frappe.db.exists( + "Sales Invoice Item", + { + "reference_dt": "Patient Appointment", + "reference_dn": appointment.name + } + ) + +def exists_sales_invoice(appointment): + sales_item_exist = exists_sales_invoice_item(appointment) + if sales_item_exist: + sales_invoice = frappe.get_doc("Sales Invoice", frappe.db.get_value("Sales Invoice Item", sales_item_exist, "parent")) + return sales_invoice + return False @frappe.whitelist() def get_availability_data(date, practitioner): @@ -193,13 +280,6 @@ def confirm_sms(doc): message = frappe.db.get_value("Healthcare Settings", None, "app_con_msg") send_message(doc, message) - -def validity_exists(practitioner, patient): - return frappe.db.exists({ - "doctype": "Fee Validity", - "practitioner": practitioner, - "patient": patient}) - @frappe.whitelist() def create_encounter(appointment): appointment = frappe.get_doc("Patient Appointment", appointment) diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 3372e1d81e..3acb000da4 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -7,9 +7,8 @@ import frappe import datetime from frappe import _ import math -from frappe.utils import time_diff_in_hours, rounded +from frappe.utils import time_diff_in_hours, rounded, getdate, add_days from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account -from erpnext.healthcare.doctype.patient_appointment.patient_appointment import validity_exists from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity, update_fee_validity from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple @@ -272,6 +271,12 @@ def manage_prescriptions(invoiced, ref_dt, ref_dn, dt, created_check_field): doc_created = frappe.db.get_value(dt, {'prescription': ref_dn}) frappe.db.set_value(dt, doc_created, 'invoiced', invoiced) +def validity_exists(practitioner, patient): + return frappe.db.exists({ + "doctype": "Fee Validity", + "practitioner": practitioner, + "patient": patient}) + def manage_fee_validity(appointment_name, method, ref_invoice=None): appointment_doc = frappe.get_doc("Patient Appointment", appointment_name) validity_exist = validity_exists(appointment_doc.practitioner, appointment_doc.patient) @@ -282,12 +287,13 @@ def manage_fee_validity(appointment_name, method, ref_invoice=None): # Check if the validity is valid if (fee_validity.valid_till >= appointment_doc.appointment_date): if (method == "on_cancel" and appointment_doc.status != "Closed"): - visited = fee_validity.visited - 1 - if visited < 0: - visited = 0 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + if ref_invoice == fee_validity.ref_invoice: + visited = fee_validity.visited - 1 + if visited < 0: + visited = 0 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) do_not_update = True - elif (fee_validity.visited < fee_validity.max_visit): + elif (method == "on_submit" and fee_validity.visited < fee_validity.max_visit): visited = fee_validity.visited + 1 frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) do_not_update = True @@ -301,29 +307,48 @@ def manage_fee_validity(appointment_name, method, ref_invoice=None): fee_validity = create_fee_validity(appointment_doc.practitioner, appointment_doc.patient, appointment_doc.appointment_date, ref_invoice) visited = fee_validity.visited + print "do_not_update: ", do_not_update + print "visited: ", visited + # Mark All Patient Appointment invoiced = True in the validity range do not cross the max visit if (method == "on_cancel"): invoiced = True else: invoiced = False - patient_appointments = frappe.get_list("Patient Appointment",{'patient': fee_validity.patient, 'invoiced': invoiced, - 'appointment_date':("<=", fee_validity.valid_till), 'practitioner': fee_validity.practitioner}, order_by="appointment_date") + + patient_appointments = appointments_valid_in_fee_validity(appointment_doc, invoiced) if patient_appointments and fee_validity: visit = visited for appointment in patient_appointments: if (method == "on_cancel" and appointment.status != "Closed"): - visited = visited - 1 - if visited < 0: - visited = 0 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + if ref_invoice == fee_validity.ref_invoice: + visited = visited - 1 + if visited < 0: + visited = 0 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", False) manage_doc_for_appoitnment("Patient Encounter", appointment.name, False) - elif int(fee_validity.max_visit) > visit: - visited = visited + 1 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + elif method == "on_submit" and int(fee_validity.max_visit) > visit: + if ref_invoice == fee_validity.ref_invoice: + visited = visited + 1 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", True) manage_doc_for_appoitnment("Patient Encounter", appointment.name, True) - visit = visit + 1 + if ref_invoice == fee_validity.ref_invoice: + visit = visit + 1 + + if method == "on_cancel": + ref_invoice_in_fee_validity = frappe.db.get_value("Fee Validity", fee_validity.name, 'ref_invoice') + if ref_invoice_in_fee_validity == ref_invoice: + frappe.delete_doc("Fee Validity", fee_validity.name) + +def appointments_valid_in_fee_validity(appointment, invoiced): + valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") + max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") + valid_days_date = add_days(getdate(appointment.appointment_date), int(valid_days)) + return frappe.get_list("Patient Appointment",{'patient': appointment.patient, 'invoiced': invoiced, + 'appointment_date':("<=", valid_days_date), 'appointment_date':(">=", getdate(appointment.appointment_date)), + 'practitioner': appointment.practitioner}, order_by="appointment_date", limit=int(max_visit)-1) def manage_doc_for_appoitnment(dt_from_appointment, appointment, invoiced): dn_from_appointment = frappe.db.exists(