feat: Fee Validity status (Ongoing, Expired, Completed)
diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.json b/erpnext/healthcare/doctype/fee_validity/fee_validity.json
index 802f04a..a65f46f 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 9028545..b7e76b0 100644
--- a/erpnext/healthcare/doctype/fee_validity/fee_validity.py
+++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.py
@@ -9,18 +9,31 @@
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 @@
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 0000000..e6a2e28
--- /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 26b1450..76014cc 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 @@
- 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 fafec22..2d03c88 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",
@@ -78,12 +78,6 @@
"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",
"fieldtype": "Int",
@@ -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 d0f5eff..d329e5d 100755
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
@@ -85,13 +85,13 @@
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 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 @@
'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 f1f9564..40db1b7 100644
--- a/erpnext/healthcare/utils.py
+++ b/erpnext/healthcare/utils.py
@@ -45,7 +45,7 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
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 @@
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 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 @@
'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 a674144..7a938b6 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -310,7 +310,8 @@
"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",