diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.json b/erpnext/healthcare/doctype/fee_validity/fee_validity.json index 802f04a359c..a65f46fc79a 100644 --- a/erpnext/healthcare/doctype/fee_validity/fee_validity.json +++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.json @@ -1,259 +1,120 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "beta": 1, - "creation": "2017-01-05 10:56:29.564806", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_copy": 1, + "allow_import": 1, + "beta": 1, + "creation": "2017-01-05 10:56:29.564806", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "practitioner", + "patient", + "column_break_3", + "status", + "section_break_3", + "max_visits", + "visited", + "valid_till", + "column_break_6", + "ref_invoice", + "start_date" + ], "fields": [ { - "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": 0, - "in_standard_filter": 0, - "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": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "practitioner", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient", - "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": "Patient", - "length": 0, - "no_copy": 0, - "options": "Patient", - "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 - }, + "fieldname": "patient", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Patient", + "options": "Patient", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "max_visit", - "fieldtype": "Int", - "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": "Max number of visit", - "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 - }, + "fieldname": "visited", + "fieldtype": "Int", + "label": "Visited yet" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "visited", - "fieldtype": "Int", - "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": "Visited yet", - "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 - }, + "fieldname": "valid_till", + "fieldtype": "Date", + "label": "Valid till" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "valid_till", - "fieldtype": "Date", - "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": "Valid till", - "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 - }, + "fieldname": "ref_invoice", + "fieldtype": "Link", + "label": "Reference Invoice", + "options": "Sales Invoice" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ref_invoice", - "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": "Reference Inv", - "length": 0, - "no_copy": 0, - "options": "Sales Invoice", - "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 + "fieldname": "section_break_3", + "fieldtype": "Section Break", + "label": "Validity" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "max_visits", + "fieldtype": "Int", + "label": "Max number of visit" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Ongoing\nCompleted\nExpired", + "read_only": 1 + }, + { + "fetch_from": "ref_invoice.posting_date", + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-16 12:43:45.635230", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Fee Validity", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-03-09 23:14:08.581821", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Fee Validity", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "practitioner, patient", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "practitioner", - "track_changes": 0, - "track_seen": 0 + ], + "quick_entry": 1, + "restrict_to_domain": "Healthcare", + "search_fields": "practitioner, patient", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "practitioner" } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.py b/erpnext/healthcare/doctype/fee_validity/fee_validity.py index 90285459ce5..b7e76b0ed40 100644 --- a/erpnext/healthcare/doctype/fee_validity/fee_validity.py +++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.py @@ -9,18 +9,31 @@ from frappe.utils import getdate import datetime class FeeValidity(Document): - pass + def validate(self): + self.update_status() + + def update_status(self): + valid_till = getdate(self.valid_till) + today = getdate() + if self.visited >= self.max_visits: + self.status = 'Completed' + elif self.visited < self.max_visits: + if valid_till >= today: + self.status = 'Ongoing' + elif valid_till < today: + self.status = 'Expired' + 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") + max_visits = frappe.db.get_single_value("Healthcare Settings", "max_visits") + valid_days = frappe.db.get_single_value("Healthcare Settings", "valid_days") if not valid_days: valid_days = 1 - if not max_visit: - max_visit = 1 + if not max_visits: + max_visits = 1 date = getdate(date) valid_till = date + datetime.timedelta(days=int(valid_days)) - fee_validity.max_visit = max_visit + fee_validity.max_visits = max_visits fee_validity.visited = 1 fee_validity.valid_till = valid_till fee_validity.ref_invoice = ref_invoice @@ -34,3 +47,9 @@ def create_fee_validity(practitioner, patient, date, ref_invoice=None): fee_validity.patient = patient fee_validity = update_fee_validity(fee_validity, date, ref_invoice) return fee_validity + + +def update_validity_status(): + docs = frappe.get_all('Fee Validity', filters={'status': ['not in', ['Completed', 'Expired']]}) + for doc in docs: + frappe.get_doc("Task", doc.name).update_status() diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity_list.js b/erpnext/healthcare/doctype/fee_validity/fee_validity_list.js new file mode 100644 index 00000000000..e6a2e28903f --- /dev/null +++ b/erpnext/healthcare/doctype/fee_validity/fee_validity_list.js @@ -0,0 +1,10 @@ +frappe.listview_settings['Fee Validity'] = { + get_indicator: function(doc) { + var colors = { + 'Ongoing': 'orange', + 'Completed': 'green', + 'Expired': 'grey' + }; + return [__(doc.status), colors[doc.status], 'status,=,' + doc.status]; + } +}; diff --git a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py index 26b14504630..76014cc9155 100644 --- a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py +++ b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py @@ -40,7 +40,7 @@ class TestFeeValidity(unittest.TestCase): - frappe.db.set_value("Healthcare Settings", None, "max_visit", 2) + frappe.db.set_value("Healthcare Settings", None, "max_visits", 2) frappe.db.set_value("Healthcare Settings", None, "valid_days", 7) appointment = create_appointment(patient, practitioner, nowdate(), department) diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json index fafec22ed95..2d03c88fa73 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json @@ -14,7 +14,7 @@ "collect_registration_fee", "registration_fee", "automate_appointment_invoicing", - "max_visit", + "max_visits", "valid_days", "healthcare_service_items", "inpatient_visit_charge_item", @@ -77,12 +77,6 @@ "mandatory_depends_on": "eval:doc.collect_registration_fee == 1", "options": "Currency" }, - { - "description": "The number of free follow ups (Patient Encounters in valid days) allowed", - "fieldname": "max_visit", - "fieldtype": "Int", - "label": "Patient Encounters in valid days" - }, { "description": "Time period (Valid number of days) for free consultations", "fieldname": "valid_days", @@ -291,11 +285,17 @@ "fieldname": "remind_before", "fieldtype": "Time", "label": "Remind Before" + }, + { + "description": "The number of free follow ups (Patient Encounters in valid days) allowed", + "fieldname": "max_visits", + "fieldtype": "Int", + "label": "Number of Patient Encounters in valid days" } ], "issingle": 1, "links": [], - "modified": "2020-02-24 10:51:23.015896", + "modified": "2020-03-09 17:43:23.251559", "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 d0f5eff1f8e..d329e5db316 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -85,13 +85,13 @@ class PatientAppointment(Document): def check_fee_validity(self): # Check fee validity exists - validity_exists = check_validity_exists(self.practitioner, self.patient) - if validity_exists: - fee_validity = frappe.get_doc('Fee Validity', validity_exists.name) + validity = check_validity_exists(self.practitioner, self.patient) + if validity: + fee_validity = frappe.get_doc('Fee Validity', validity) # Check if the validity is valid appointment_date = getdate(self.appointment_date) - if (fee_validity.valid_till >= appointment_date) and (fee_validity.visited < fee_validity.max_visit): + if (fee_validity.valid_till >= appointment_date) and (fee_validity.visited < fee_validity.max_visits): visited = fee_validity.visited + 1 frappe.db.set_value('Fee Validity', fee_validity.name, 'visited', visited) if fee_validity.ref_invoice: @@ -172,7 +172,7 @@ def cancel_appointment(appointment_id): def validate_appointment_in_fee_validity(appointment, valid_end_date, ref_invoice): valid_days = frappe.db.get_single_value('Healthcare Settings', 'valid_days') - max_visit = frappe.db.get_single_value('Healthcare Settings', 'max_visit') + max_visits = frappe.db.get_single_value('Healthcare Settings', 'max_visits') valid_start_date = add_days(getdate(valid_end_date), -int(valid_days)) # Appointments which have same fee validity range with the appointment @@ -182,7 +182,7 @@ def validate_appointment_in_fee_validity(appointment, valid_end_date, ref_invoic 'appointment_date': ('<=', getdate(valid_end_date)), 'appointment_date':('>=', getdate(valid_start_date)), 'practitioner': appointment.practitioner - }, order_by='appointment_date desc', limit=int(max_visit)) + }, order_by='appointment_date desc', limit=int(max_visits)) if appointments and len(appointments) > 0: appointment_obj = appointments[len(appointments)-1] diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index f1f9564a476..40db1b7d78c 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -45,7 +45,7 @@ def get_fee_validity(patient_appointments): fee_validity_details = [] items_to_invoice = [] valid_days = frappe.db.get_single_value('Healthcare Settings', 'valid_days') - max_visit = frappe.db.get_single_value('Healthcare Settings', 'max_visit') + max_visits = frappe.db.get_single_value('Healthcare Settings', 'max_visits') for appointment in patient_appointments: if appointment.procedure_template: if frappe.db.get_value('Clinical Procedure Template', appointment.procedure_template, 'is_billable'): @@ -63,7 +63,7 @@ def get_fee_validity(patient_appointments): practitioner_exist_in_list = True if validity['valid_till'] >= appointment.appointment_date: validity['visits'] = validity['visits'] + 1 - if int(max_visit) > validity['visits']: + if int(max_visits) > validity['visits']: skip_invoice = True if not skip_invoice: validity['visits'] = 1 @@ -72,9 +72,9 @@ def get_fee_validity(patient_appointments): if not practitioner_exist_in_list: valid_till = appointment.appointment_date + datetime.timedelta(days=int(valid_days)) visits = 0 - validity_exist = check_validity_exists(appointment.practitioner, appointment.patient) - if validity_exist: - fee_validity = frappe.get_doc('Fee Validity', validity_exist[0][0]) + validity = check_validity_exists(appointment.practitioner, appointment.patient) + if validity: + fee_validity = frappe.get_doc('Fee Validity', validity) valid_till = fee_validity.valid_till visits = fee_validity.visited fee_validity_details.append({'practitioner': appointment.practitioner, @@ -348,19 +348,16 @@ def manage_prescriptions(invoiced, ref_dt, ref_dn, dt, created_check_field): def check_validity_exists(practitioner, patient): - return frappe.db.exists({ - 'doctype': 'Fee Validity', - 'practitioner': practitioner, - 'patient': patient}) + return frappe.db.get_value('Fee Validity', {'practitioner': practitioner, 'patient': patient}, 'name') def manage_fee_validity(appointment_name, method, ref_invoice=None): appointment_doc = frappe.get_doc('Patient Appointment', appointment_name) - validity_exists = check_validity_exists(appointment_doc.practitioner, appointment_doc.patient) + validity = check_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_exists.name) + if validity: + fee_validity = frappe.get_doc('Fee Validity', validity) # Check if the validity is valid if fee_validity.valid_till >= appointment_doc.appointment_date: if method == 'on_cancel' and appointment_doc.status != 'Closed': @@ -370,7 +367,7 @@ def manage_fee_validity(appointment_name, method, ref_invoice=None): 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: + elif method == 'on_submit' and fee_validity.visited < fee_validity.max_visits: visited = fee_validity.visited + 1 frappe.db.set_value('Fee Validity', fee_validity.name, 'visited', visited) do_not_update = True @@ -383,12 +380,7 @@ 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 - mark_appointments_as_invoiced(fee_validity, ref_invoice, method) - - if method == 'on_cancel': - invoiced = True - else: - invoiced = False + mark_appointments_as_invoiced(fee_validity, ref_invoice, method, appointment_doc) if method == 'on_cancel': ref_invoice_in_fee_validity = frappe.db.get_value('Fee Validity', fee_validity.name, 'ref_invoice') @@ -396,7 +388,12 @@ def manage_fee_validity(appointment_name, method, ref_invoice=None): frappe.delete_doc('Fee Validity', fee_validity.name) -def mark_appointments_as_invoiced(fee_validity, ref_invoice, method): +def mark_appointments_as_invoiced(fee_validity, ref_invoice, method, appointment_doc): + 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 @@ -409,7 +406,7 @@ def mark_appointments_as_invoiced(fee_validity, ref_invoice, method): 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: + elif method == 'on_submit' and int(fee_validity.max_visits) > visit: if ref_invoice == fee_validity.ref_invoice: visited += 1 frappe.db.set_value('Fee Validity', fee_validity.name, 'visited', visited) @@ -421,9 +418,9 @@ def mark_appointments_as_invoiced(fee_validity, ref_invoice, method): def appointments_valid_in_fee_validity(appointment, invoiced): valid_days = frappe.db.get_single_value('Healthcare Settings', 'valid_days') - max_visit = frappe.db.get_single_value('Healthcare Settings', 'max_visit') - if int(max_visit) < 1: - max_visit = 1 + max_visits = frappe.db.get_single_value('Healthcare Settings', 'max_visits') + if int(max_visits) < 1: + max_visits = 1 valid_days_date = add_days(getdate(appointment.appointment_date), int(valid_days)) return frappe.get_list('Patient Appointment',{ @@ -432,12 +429,12 @@ def appointments_valid_in_fee_validity(appointment, invoiced): 'appointment_date':('<=', valid_days_date), 'appointment_date':('>=', getdate(appointment.appointment_date)), 'practitioner': appointment.practitioner - }, order_by='appointment_date', limit=int(max_visit)-1) + }, order_by='appointment_date', limit=int(max_visits)-1) def manage_doc_for_appoitnment(dt_from_appointment, appointment, invoiced): - dn_from_appointment = frappe.db.exists( - doctype=dt_from_appointment, + dn_from_appointment = frappe.db.get_value( + dt_from_appointment, filters={'appointment': appointment} ) if dn_from_appointment: diff --git a/erpnext/hooks.py b/erpnext/hooks.py index a67414447b9..7a938b68192 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -310,7 +310,8 @@ scheduler_events = { "erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts", "erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status", "erpnext.selling.doctype.quotation.quotation.set_expired_status", - "erpnext.healthcare_healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status" + "erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status", + "erpnext.healthcare.doctype.fee_validity.fee_validity.update_validity_status" ], "daily_long": [ "erpnext.setup.doctype.email_digest.email_digest.send",