blob: 96282f50a92a458ce0ea516b0031780ce1d50882 [file] [log] [blame]
Jamsheerba119722018-07-06 15:58:13 +05301# -*- coding: utf-8 -*-
2# Copyright (c) 2018, earthians and contributors
3# For license information, please see license.txt
4
5from __future__ import unicode_literals
anoop93d0c782020-04-13 16:42:03 +05306import math
Jamsheerba119722018-07-06 15:58:13 +05307import frappe
Jamsheerba119722018-07-06 15:58:13 +05308from frappe import _
Rucha Mahabal197165f2020-03-26 17:29:50 +05309from frappe.utils import time_diff_in_hours, rounded
Jamsheerba119722018-07-06 15:58:13 +053010from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account
Rucha Mahabal2f2c09b2020-03-17 18:10:39 +053011from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity
Jamsheer0ae100b2018-08-01 14:29:43 +053012from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple
Jamsheerba119722018-07-06 15:58:13 +053013
14@frappe.whitelist()
anoop93d0c782020-04-13 16:42:03 +053015def get_healthcare_services_to_invoice(patient, company):
Rucha Mahabal27512c82020-03-09 17:29:23 +053016 patient = frappe.get_doc('Patient', patient)
anoop93d0c782020-04-13 16:42:03 +053017 items_to_invoice = []
Jamsheerba119722018-07-06 15:58:13 +053018 if patient:
Rucha Mahabal27512c82020-03-09 17:29:23 +053019 validate_customer_created(patient)
anoop93d0c782020-04-13 16:42:03 +053020 # Customer validated, build a list of billable services
21 items_to_invoice += get_appointments_to_invoice(patient, company)
22 items_to_invoice += get_encounters_to_invoice(patient, company)
23 items_to_invoice += get_lab_tests_to_invoice(patient, company)
24 items_to_invoice += get_clinical_procedures_to_invoice(patient, company)
25 items_to_invoice += get_inpatient_services_to_invoice(patient, company)
Rucha Mahabal434791e2020-10-24 14:20:38 +053026 items_to_invoice += get_therapy_plans_to_invoice(patient, company)
Rucha Mahabald7304512020-04-23 00:52:47 +053027 items_to_invoice += get_therapy_sessions_to_invoice(patient, company)
Jamsheerba119722018-07-06 15:58:13 +053028
Rucha Mahabal27512c82020-03-09 17:29:23 +053029 return items_to_invoice
Jamsheerba119722018-07-06 15:58:13 +053030
anoop93d0c782020-04-13 16:42:03 +053031
Rucha Mahabal27512c82020-03-09 17:29:23 +053032def validate_customer_created(patient):
33 if not frappe.db.get_value('Patient', patient.name, 'customer'):
34 msg = _("Please set a Customer linked to the Patient")
35 msg += " <b><a href='#Form/Patient/{0}'>{0}</a></b>".format(patient.name)
36 frappe.throw(msg, title=_('Customer Not Found'))
Jamsheer8da6f4e2018-07-26 21:03:17 +053037
Rucha Mahabal434791e2020-10-24 14:20:38 +053038
anoop93d0c782020-04-13 16:42:03 +053039def get_appointments_to_invoice(patient, company):
40 appointments_to_invoice = []
41 patient_appointments = frappe.get_list(
42 'Patient Appointment',
43 fields = '*',
anoop59030022020-07-28 21:15:54 +053044 filters = {'patient': patient.name, 'company': company, 'invoiced': 0, 'status': ['not in', 'Cancelled']},
anoop93d0c782020-04-13 16:42:03 +053045 order_by = 'appointment_date'
46 )
47
Rucha Mahabal27512c82020-03-09 17:29:23 +053048 for appointment in patient_appointments:
anoop93d0c782020-04-13 16:42:03 +053049 # Procedure Appointments
Rucha Mahabal27512c82020-03-09 17:29:23 +053050 if appointment.procedure_template:
51 if frappe.db.get_value('Clinical Procedure Template', appointment.procedure_template, 'is_billable'):
anoop93d0c782020-04-13 16:42:03 +053052 appointments_to_invoice.append({
Rucha Mahabal27512c82020-03-09 17:29:23 +053053 'reference_type': 'Patient Appointment',
54 'reference_name': appointment.name,
55 'service': appointment.procedure_template
56 })
anoop93d0c782020-04-13 16:42:03 +053057 # Consultation Appointments, should check fee validity
Jamsheerba119722018-07-06 15:58:13 +053058 else:
anoop93d0c782020-04-13 16:42:03 +053059 if frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups') and \
60 frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name}):
61 continue # Skip invoicing, fee validty present
62 practitioner_charge = 0
63 income_account = None
64 service_item = None
65 if appointment.practitioner:
66 service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment)
67 income_account = get_income_account(appointment.practitioner, appointment.company)
68 appointments_to_invoice.append({
69 'reference_type': 'Patient Appointment',
70 'reference_name': appointment.name,
71 'service': service_item,
72 'rate': practitioner_charge,
73 'income_account': income_account
74 })
Rucha Mahabal27512c82020-03-09 17:29:23 +053075
anoop93d0c782020-04-13 16:42:03 +053076 return appointments_to_invoice
Rucha Mahabal27512c82020-03-09 17:29:23 +053077
78
anoop93d0c782020-04-13 16:42:03 +053079def get_encounters_to_invoice(patient, company):
Rucha Mahabal27512c82020-03-09 17:29:23 +053080 encounters_to_invoice = []
81 encounters = frappe.get_list(
82 'Patient Encounter',
83 fields=['*'],
anoop93d0c782020-04-13 16:42:03 +053084 filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1}
Rucha Mahabal27512c82020-03-09 17:29:23 +053085 )
86 if encounters:
87 for encounter in encounters:
88 if not encounter.appointment:
89 practitioner_charge = 0
90 income_account = None
91 service_item = None
92 if encounter.practitioner:
93 service_item, practitioner_charge = get_service_item_and_practitioner_charge(encounter)
94 income_account = get_income_account(encounter.practitioner, encounter.company)
95
96 encounters_to_invoice.append({
97 'reference_type': 'Patient Encounter',
98 'reference_name': encounter.name,
99 'service': service_item,
100 'rate': practitioner_charge,
101 'income_account': income_account
102 })
103
104 return encounters_to_invoice
105
106
anoop93d0c782020-04-13 16:42:03 +0530107def get_lab_tests_to_invoice(patient, company):
Rucha Mahabal27512c82020-03-09 17:29:23 +0530108 lab_tests_to_invoice = []
109 lab_tests = frappe.get_list(
110 'Lab Test',
111 fields=['name', 'template'],
anoop93d0c782020-04-13 16:42:03 +0530112 filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1}
Rucha Mahabal27512c82020-03-09 17:29:23 +0530113 )
114 for lab_test in lab_tests:
Rucha Mahabal131452c2020-04-27 10:52:38 +0530115 item, is_billable = frappe.get_cached_value('Lab Test Template', lab_test.template, ['item', 'is_billable'])
Rucha Mahabalced978e2020-04-02 18:45:53 +0530116 if is_billable:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530117 lab_tests_to_invoice.append({
118 'reference_type': 'Lab Test',
119 'reference_name': lab_test.name,
Rucha Mahabalced978e2020-04-02 18:45:53 +0530120 'service': item
Rucha Mahabal27512c82020-03-09 17:29:23 +0530121 })
122
Rucha Mahabalced978e2020-04-02 18:45:53 +0530123 lab_prescriptions = frappe.db.sql(
124 '''
125 SELECT
126 lp.name, lp.lab_test_code
127 FROM
128 `tabPatient Encounter` et, `tabLab Prescription` lp
129 WHERE
130 et.patient=%s
131 and lp.parent=et.name
132 and lp.lab_test_created=0
133 and lp.invoiced=0
134 ''', (patient.name), as_dict=1)
Rucha Mahabal27512c82020-03-09 17:29:23 +0530135
136 for prescription in lab_prescriptions:
Rucha Mahabalced978e2020-04-02 18:45:53 +0530137 item, is_billable = frappe.get_cached_value('Lab Test Template', prescription.lab_test_code, ['item', 'is_billable'])
138 if prescription.lab_test_code and is_billable:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530139 lab_tests_to_invoice.append({
140 'reference_type': 'Lab Prescription',
141 'reference_name': prescription.name,
Rucha Mahabalced978e2020-04-02 18:45:53 +0530142 'service': item
Rucha Mahabal27512c82020-03-09 17:29:23 +0530143 })
144
145 return lab_tests_to_invoice
146
147
anoop93d0c782020-04-13 16:42:03 +0530148def get_clinical_procedures_to_invoice(patient, company):
Rucha Mahabal27512c82020-03-09 17:29:23 +0530149 clinical_procedures_to_invoice = []
150 procedures = frappe.get_list(
151 'Clinical Procedure',
152 fields='*',
anoop93d0c782020-04-13 16:42:03 +0530153 filters={'patient': patient.name, 'company': company, 'invoiced': False}
Rucha Mahabal27512c82020-03-09 17:29:23 +0530154 )
155 for procedure in procedures:
156 if not procedure.appointment:
Rucha Mahabalced978e2020-04-02 18:45:53 +0530157 item, is_billable = frappe.get_cached_value('Clinical Procedure Template', procedure.procedure_template, ['item', 'is_billable'])
158 if procedure.procedure_template and is_billable:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530159 clinical_procedures_to_invoice.append({
160 'reference_type': 'Clinical Procedure',
161 'reference_name': procedure.name,
Rucha Mahabalced978e2020-04-02 18:45:53 +0530162 'service': item
Rucha Mahabal27512c82020-03-09 17:29:23 +0530163 })
164
165 # consumables
166 if procedure.invoice_separately_as_consumables and procedure.consume_stock \
167 and procedure.status == 'Completed' and not procedure.consumption_invoiced:
168
169 service_item = get_healthcare_service_item('clinical_procedure_consumable_item')
170 if not service_item:
171 msg = _('Please Configure Clinical Procedure Consumable Item in ')
172 msg += '''<b><a href='#Form/Healthcare Settings'>Healthcare Settings</a></b>'''
173 frappe.throw(msg, title=_('Missing Configuration'))
174
175 clinical_procedures_to_invoice.append({
176 'reference_type': 'Clinical Procedure',
177 'reference_name': procedure.name,
178 'service': service_item,
179 'rate': procedure.consumable_total_amount,
180 'description': procedure.consumption_details
181 })
182
Rucha Mahabalced978e2020-04-02 18:45:53 +0530183 procedure_prescriptions = frappe.db.sql(
184 '''
185 SELECT
186 pp.name, pp.procedure
187 FROM
188 `tabPatient Encounter` et, `tabProcedure Prescription` pp
189 WHERE
190 et.patient=%s
191 and pp.parent=et.name
192 and pp.procedure_created=0
193 and pp.invoiced=0
194 and pp.appointment_booked=0
195 ''', (patient.name), as_dict=1)
Rucha Mahabal27512c82020-03-09 17:29:23 +0530196
197 for prescription in procedure_prescriptions:
Rucha Mahabalced978e2020-04-02 18:45:53 +0530198 item, is_billable = frappe.get_cached_value('Clinical Procedure Template', prescription.procedure, ['item', 'is_billable'])
199 if is_billable:
Rucha Mahabal197165f2020-03-26 17:29:50 +0530200 clinical_procedures_to_invoice.append({
Rucha Mahabal27512c82020-03-09 17:29:23 +0530201 'reference_type': 'Procedure Prescription',
202 'reference_name': prescription.name,
Rucha Mahabalced978e2020-04-02 18:45:53 +0530203 'service': item
Rucha Mahabal27512c82020-03-09 17:29:23 +0530204 })
205
206 return clinical_procedures_to_invoice
207
208
anoop93d0c782020-04-13 16:42:03 +0530209def get_inpatient_services_to_invoice(patient, company):
Rucha Mahabal27512c82020-03-09 17:29:23 +0530210 services_to_invoice = []
Rucha Mahabalced978e2020-04-02 18:45:53 +0530211 inpatient_services = frappe.db.sql(
212 '''
213 SELECT
214 io.*
215 FROM
216 `tabInpatient Record` ip, `tabInpatient Occupancy` io
217 WHERE
218 ip.patient=%s
anoop93d0c782020-04-13 16:42:03 +0530219 and ip.company=%s
Rucha Mahabalced978e2020-04-02 18:45:53 +0530220 and io.parent=ip.name
221 and io.left=1
222 and io.invoiced=0
anoop93d0c782020-04-13 16:42:03 +0530223 ''', (patient.name, company), as_dict=1)
Rucha Mahabal27512c82020-03-09 17:29:23 +0530224
225 for inpatient_occupancy in inpatient_services:
226 service_unit_type = frappe.db.get_value('Healthcare Service Unit', inpatient_occupancy.service_unit, 'service_unit_type')
Rucha Mahabalced978e2020-04-02 18:45:53 +0530227 service_unit_type = frappe.get_cached_doc('Healthcare Service Unit Type', service_unit_type)
Rucha Mahabal27512c82020-03-09 17:29:23 +0530228 if service_unit_type and service_unit_type.is_billable:
229 hours_occupied = time_diff_in_hours(inpatient_occupancy.check_out, inpatient_occupancy.check_in)
230 qty = 0.5
231 if hours_occupied > 0:
232 actual_qty = hours_occupied / service_unit_type.no_of_hours
233 floor = math.floor(actual_qty)
234 decimal_part = actual_qty - floor
235 if decimal_part > 0.5:
236 qty = rounded(floor + 1, 1)
237 elif decimal_part < 0.5 and decimal_part > 0:
238 qty = rounded(floor + 0.5, 1)
239 if qty <= 0:
240 qty = 0.5
241 services_to_invoice.append({
242 'reference_type': 'Inpatient Occupancy',
243 'reference_name': inpatient_occupancy.name,
244 'service': service_unit_type.item, 'qty': qty
245 })
246
247 return services_to_invoice
248
Jamsheerba119722018-07-06 15:58:13 +0530249
Rucha Mahabal434791e2020-10-24 14:20:38 +0530250def get_therapy_plans_to_invoice(patient, company):
251 therapy_plans_to_invoice = []
252 therapy_plans = frappe.get_list(
253 'Therapy Plan',
254 fields=['therapy_plan_template', 'name'],
255 filters={
256 'patient': patient.name,
257 'invoiced': 0,
258 'company': company,
259 'therapy_plan_template': ('!=', '')
260 }
261 )
262 for plan in therapy_plans:
263 therapy_plans_to_invoice.append({
264 'reference_type': 'Therapy Plan',
265 'reference_name': plan.name,
266 'service': frappe.db.get_value('Therapy Plan Template', plan.therapy_plan_template, 'linked_item')
267 })
268
269 return therapy_plans_to_invoice
270
271
Rucha Mahabald7304512020-04-23 00:52:47 +0530272def get_therapy_sessions_to_invoice(patient, company):
Rucha Mahabaleaa956b2020-04-22 13:07:12 +0530273 therapy_sessions_to_invoice = []
Rucha Mahabal434791e2020-10-24 14:20:38 +0530274 therapy_plans = frappe.db.get_all('Therapy Plan', {'therapy_plan_template': ('!=', '')})
275 therapy_plans_created_from_template = []
276 for entry in therapy_plans:
277 therapy_plans_created_from_template.append(entry.name)
278
Rucha Mahabaleaa956b2020-04-22 13:07:12 +0530279 therapy_sessions = frappe.get_list(
280 'Therapy Session',
281 fields='*',
Rucha Mahabal434791e2020-10-24 14:20:38 +0530282 filters={
283 'patient': patient.name,
284 'invoiced': 0,
285 'company': company,
286 'therapy_plan': ('not in', therapy_plans_created_from_template)
287 }
Rucha Mahabaleaa956b2020-04-22 13:07:12 +0530288 )
289 for therapy in therapy_sessions:
290 if not therapy.appointment:
291 if therapy.therapy_type and frappe.db.get_value('Therapy Type', therapy.therapy_type, 'is_billable'):
292 therapy_sessions_to_invoice.append({
293 'reference_type': 'Therapy Session',
294 'reference_name': therapy.name,
295 'service': frappe.db.get_value('Therapy Type', therapy.therapy_type, 'item')
296 })
297
298 return therapy_sessions_to_invoice
299
300
Rucha Mahabal24055e12020-02-24 19:09:50 +0530301def get_service_item_and_practitioner_charge(doc):
Rucha Mahabal27512c82020-03-09 17:29:23 +0530302 is_inpatient = doc.inpatient_record
Rucha Mahabal24055e12020-02-24 19:09:50 +0530303 if is_inpatient:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530304 service_item = get_practitioner_service_item(doc.practitioner, 'inpatient_visit_charge_item')
Jamsheeree5f9c72018-07-30 12:42:06 +0530305 if not service_item:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530306 service_item = get_healthcare_service_item('inpatient_visit_charge_item')
Jamsheer8da6f4e2018-07-26 21:03:17 +0530307 else:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530308 service_item = get_practitioner_service_item(doc.practitioner, 'op_consulting_charge_item')
Jamsheeree5f9c72018-07-30 12:42:06 +0530309 if not service_item:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530310 service_item = get_healthcare_service_item('op_consulting_charge_item')
Jamsheer8da6f4e2018-07-26 21:03:17 +0530311 if not service_item:
Rucha Mahabal24055e12020-02-24 19:09:50 +0530312 throw_config_service_item(is_inpatient)
Jamsheer8da6f4e2018-07-26 21:03:17 +0530313
Rucha Mahabal24055e12020-02-24 19:09:50 +0530314 practitioner_charge = get_practitioner_charge(doc.practitioner, is_inpatient)
Jamsheer8da6f4e2018-07-26 21:03:17 +0530315 if not practitioner_charge:
Rucha Mahabal24055e12020-02-24 19:09:50 +0530316 throw_config_practitioner_charge(is_inpatient, doc.practitioner)
Jamsheer8da6f4e2018-07-26 21:03:17 +0530317
318 return service_item, practitioner_charge
319
Jamsheer8da6f4e2018-07-26 21:03:17 +0530320
Rucha Mahabal27512c82020-03-09 17:29:23 +0530321def throw_config_service_item(is_inpatient):
Rucha Mahabalced978e2020-04-02 18:45:53 +0530322 service_item_label = _('Out Patient Consulting Charge Item')
Rucha Mahabal27512c82020-03-09 17:29:23 +0530323 if is_inpatient:
Rucha Mahabalced978e2020-04-02 18:45:53 +0530324 service_item_label = _('Inpatient Visit Charge Item')
Rucha Mahabal27512c82020-03-09 17:29:23 +0530325
Rucha Mahabal4f9a1472020-03-23 10:40:39 +0530326 msg = _(('Please Configure {0} in ').format(service_item_label) \
Rucha Mahabal27512c82020-03-09 17:29:23 +0530327 + '''<b><a href='#Form/Healthcare Settings'>Healthcare Settings</a></b>''')
328 frappe.throw(msg, title=_('Missing Configuration'))
329
Jamsheer8da6f4e2018-07-26 21:03:17 +0530330
Rucha Mahabal24055e12020-02-24 19:09:50 +0530331def throw_config_practitioner_charge(is_inpatient, practitioner):
Rucha Mahabalced978e2020-04-02 18:45:53 +0530332 charge_name = _('OP Consulting Charge')
Rucha Mahabal24055e12020-02-24 19:09:50 +0530333 if is_inpatient:
Rucha Mahabalced978e2020-04-02 18:45:53 +0530334 charge_name = _('Inpatient Visit Charge')
Jamsheer8da6f4e2018-07-26 21:03:17 +0530335
Rucha Mahabal27512c82020-03-09 17:29:23 +0530336 msg = _(('Please Configure {0} for Healthcare Practitioner').format(charge_name) \
337 + ''' <b><a href='#Form/Healthcare Practitioner/{0}'>{0}</a></b>'''.format(practitioner))
338 frappe.throw(msg, title=_('Missing Configuration'))
339
Jamsheer8da6f4e2018-07-26 21:03:17 +0530340
Jamsheeree5f9c72018-07-30 12:42:06 +0530341def get_practitioner_service_item(practitioner, service_item_field):
Rucha Mahabal27512c82020-03-09 17:29:23 +0530342 return frappe.db.get_value('Healthcare Practitioner', practitioner, service_item_field)
343
Jamsheeree5f9c72018-07-30 12:42:06 +0530344
Jamsheer8da6f4e2018-07-26 21:03:17 +0530345def get_healthcare_service_item(service_item_field):
Rucha Mahabal27512c82020-03-09 17:29:23 +0530346 return frappe.db.get_single_value('Healthcare Settings', service_item_field)
Jamsheer8da6f4e2018-07-26 21:03:17 +0530347
Jamsheer8da6f4e2018-07-26 21:03:17 +0530348
Rucha Mahabal24055e12020-02-24 19:09:50 +0530349def get_practitioner_charge(practitioner, is_inpatient):
350 if is_inpatient:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530351 practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, 'inpatient_visit_charge')
Jamsheer8da6f4e2018-07-26 21:03:17 +0530352 else:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530353 practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, 'op_consulting_charge')
Jamsheerba119722018-07-06 15:58:13 +0530354 if practitioner_charge:
355 return practitioner_charge
Jamsheer8da6f4e2018-07-26 21:03:17 +0530356 return False
Jamsheerba119722018-07-06 15:58:13 +0530357
Rucha Mahabal27512c82020-03-09 17:29:23 +0530358
Jamsheerba119722018-07-06 15:58:13 +0530359def manage_invoice_submit_cancel(doc, method):
360 if doc.items:
361 for item in doc.items:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530362 if item.get('reference_dt') and item.get('reference_dn'):
363 if frappe.get_meta(item.reference_dt).has_field('invoiced'):
Jamsheer146683b2018-07-25 11:30:30 +0530364 set_invoiced(item, method, doc.name)
Jamsheerba119722018-07-06 15:58:13 +0530365
Rucha Mahabal27512c82020-03-09 17:29:23 +0530366 if method=='on_submit' and frappe.db.get_single_value('Healthcare Settings', 'create_lab_test_on_si_submit'):
367 create_multiple('Sales Invoice', doc.name)
368
Jamsheer0ae100b2018-08-01 14:29:43 +0530369
Jamsheer146683b2018-07-25 11:30:30 +0530370def set_invoiced(item, method, ref_invoice=None):
Jamsheerba119722018-07-06 15:58:13 +0530371 invoiced = False
Rucha Mahabal27512c82020-03-09 17:29:23 +0530372 if method=='on_submit':
Jamsheerba119722018-07-06 15:58:13 +0530373 validate_invoiced_on_submit(item)
374 invoiced = True
375
Jamsheer8da6f4e2018-07-26 21:03:17 +0530376 if item.reference_dt == 'Clinical Procedure':
377 if get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530378 frappe.db.set_value(item.reference_dt, item.reference_dn, 'consumption_invoiced', invoiced)
Jamsheer8da6f4e2018-07-26 21:03:17 +0530379 else:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530380 frappe.db.set_value(item.reference_dt, item.reference_dn, 'invoiced', invoiced)
Jamsheer8da6f4e2018-07-26 21:03:17 +0530381 else:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530382 frappe.db.set_value(item.reference_dt, item.reference_dn, 'invoiced', invoiced)
Jamsheer8da6f4e2018-07-26 21:03:17 +0530383
Jamsheerba119722018-07-06 15:58:13 +0530384 if item.reference_dt == 'Patient Appointment':
385 if frappe.db.get_value('Patient Appointment', item.reference_dn, 'procedure_template'):
Rucha Mahabal27512c82020-03-09 17:29:23 +0530386 dt_from_appointment = 'Clinical Procedure'
Jamsheerba119722018-07-06 15:58:13 +0530387 else:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530388 dt_from_appointment = 'Patient Encounter'
Rucha Mahabal06d1b042020-03-12 12:16:23 +0530389 manage_doc_for_appointment(dt_from_appointment, item.reference_dn, invoiced)
Jamsheerba119722018-07-06 15:58:13 +0530390
391 elif item.reference_dt == 'Lab Prescription':
Rucha Mahabal27512c82020-03-09 17:29:23 +0530392 manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, 'Lab Test', 'lab_test_created')
Jamsheerba119722018-07-06 15:58:13 +0530393
394 elif item.reference_dt == 'Procedure Prescription':
Rucha Mahabal27512c82020-03-09 17:29:23 +0530395 manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, 'Clinical Procedure', 'procedure_created')
396
Jamsheerba119722018-07-06 15:58:13 +0530397
398def validate_invoiced_on_submit(item):
Jamsheer8da6f4e2018-07-26 21:03:17 +0530399 if item.reference_dt == 'Clinical Procedure' and get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code:
Rucha Mahabal06d1b042020-03-12 12:16:23 +0530400 is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'consumption_invoiced')
Jamsheer8da6f4e2018-07-26 21:03:17 +0530401 else:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530402 is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'invoiced')
403 if is_invoiced:
Rucha Mahabal434791e2020-10-24 14:20:38 +0530404 frappe.throw(_('The item referenced by {0} - {1} is already invoiced').format(
405 item.reference_dt, item.reference_dn))
Jamsheerba119722018-07-06 15:58:13 +0530406
Rucha Mahabal27512c82020-03-09 17:29:23 +0530407
Jamsheerba119722018-07-06 15:58:13 +0530408def manage_prescriptions(invoiced, ref_dt, ref_dn, dt, created_check_field):
409 created = frappe.db.get_value(ref_dt, ref_dn, created_check_field)
Rucha Mahabal27512c82020-03-09 17:29:23 +0530410 if created:
Jamsheerba119722018-07-06 15:58:13 +0530411 # Fetch the doc created for the prescription
Jamsheereafb0462018-07-25 13:15:12 +0530412 doc_created = frappe.db.get_value(dt, {'prescription': ref_dn})
Jamsheerba119722018-07-06 15:58:13 +0530413 frappe.db.set_value(dt, doc_created, 'invoiced', invoiced)
414
Rucha Mahabal27512c82020-03-09 17:29:23 +0530415
Rucha Mahabalcd319962020-03-13 15:39:31 +0530416def check_fee_validity(appointment):
Rucha Mahabalf2574dd2020-03-17 19:28:18 +0530417 if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'):
418 return
419
Rucha Mahabalcd319962020-03-13 15:39:31 +0530420 validity = frappe.db.exists('Fee Validity', {
421 'practitioner': appointment.practitioner,
Rucha Mahabal2f2c09b2020-03-17 18:10:39 +0530422 'patient': appointment.patient,
Rucha Mahabalf2574dd2020-03-17 19:28:18 +0530423 'valid_till': ('>=', appointment.appointment_date)
Rucha Mahabalcd319962020-03-13 15:39:31 +0530424 })
425 if not validity:
426 return
427
Rucha Mahabalf2574dd2020-03-17 19:28:18 +0530428 validity = frappe.get_doc('Fee Validity', validity)
429 return validity
430
Rucha Mahabal27512c82020-03-09 17:29:23 +0530431
Rucha Mahabal2f2c09b2020-03-17 18:10:39 +0530432def manage_fee_validity(appointment):
433 fee_validity = check_fee_validity(appointment)
anoop93d0c782020-04-13 16:42:03 +0530434
Rucha Mahabalcd319962020-03-13 15:39:31 +0530435 if fee_validity:
Rucha Mahabal2f2c09b2020-03-17 18:10:39 +0530436 if appointment.status == 'Cancelled' and fee_validity.visited > 0:
437 fee_validity.visited -= 1
438 frappe.db.delete('Fee Validity Reference', {'appointment': appointment.name})
Rucha Mahabal2cec6bd2020-03-26 14:38:12 +0530439 elif fee_validity.status == 'Completed':
440 return
Rucha Mahabalcd319962020-03-13 15:39:31 +0530441 else:
Rucha Mahabal2f2c09b2020-03-17 18:10:39 +0530442 fee_validity.visited += 1
443 fee_validity.append('ref_appointments', {
444 'appointment': appointment.name
445 })
446 fee_validity.save(ignore_permissions=True)
Jamsheerba119722018-07-06 15:58:13 +0530447 else:
Rucha Mahabal2f2c09b2020-03-17 18:10:39 +0530448 fee_validity = create_fee_validity(appointment)
449 return fee_validity
Rucha Mahabal27512c82020-03-09 17:29:23 +0530450
Jamsheerba119722018-07-06 15:58:13 +0530451
Rucha Mahabal06d1b042020-03-12 12:16:23 +0530452def manage_doc_for_appointment(dt_from_appointment, appointment, invoiced):
Rucha Mahabalc4b2dce2020-03-09 23:57:00 +0530453 dn_from_appointment = frappe.db.get_value(
454 dt_from_appointment,
Rucha Mahabal27512c82020-03-09 17:29:23 +0530455 filters={'appointment': appointment}
Jamsheerba119722018-07-06 15:58:13 +0530456 )
457 if dn_from_appointment:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530458 frappe.db.set_value(dt_from_appointment, dn_from_appointment, 'invoiced', invoiced)
459
Jamsheere82f27a2018-07-30 11:28:37 +0530460
461@frappe.whitelist()
462def get_drugs_to_invoice(encounter):
Rucha Mahabal27512c82020-03-09 17:29:23 +0530463 encounter = frappe.get_doc('Patient Encounter', encounter)
Jamsheere82f27a2018-07-30 11:28:37 +0530464 if encounter:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530465 patient = frappe.get_doc('Patient', encounter.patient)
466 if patient:
467 if patient.customer:
468 items_to_invoice = []
Jamsheere82f27a2018-07-30 11:28:37 +0530469 for drug_line in encounter.drug_prescription:
470 if drug_line.drug_code:
471 qty = 1
Rucha Mahabal27512c82020-03-09 17:29:23 +0530472 if frappe.db.get_value('Item', drug_line.drug_code, 'stock_uom') == 'Nos':
Jamsheere82f27a2018-07-30 11:28:37 +0530473 qty = drug_line.get_quantity()
Rucha Mahabal27512c82020-03-09 17:29:23 +0530474
475 description = ''
476 if drug_line.dosage and drug_line.period:
477 description = _('{0} for {1}').format(drug_line.dosage, drug_line.period)
478
479 items_to_invoice.append({
480 'drug_code': drug_line.drug_code,
481 'quantity': qty,
482 'description': description
483 })
484 return items_to_invoice
485 else:
486 validate_customer_created(patient)
487
Jamsheer4371c7e2018-08-01 18:40:05 +0530488
489@frappe.whitelist()
490def get_children(doctype, parent, company, is_root=False):
Rucha Mahabal05853ef2020-03-12 17:44:46 +0530491 parent_fieldname = "parent_" + doctype.lower().replace(" ", "_")
Jamsheer4371c7e2018-08-01 18:40:05 +0530492 fields = [
Rucha Mahabal05853ef2020-03-12 17:44:46 +0530493 "name as value",
494 "is_group as expandable",
495 "lft",
496 "rgt"
Jamsheer4371c7e2018-08-01 18:40:05 +0530497 ]
Rucha Mahabal05853ef2020-03-12 17:44:46 +0530498 # fields = [ "name", "is_group", "lft", "rgt" ]
499 filters = [["ifnull(`{0}`,'')".format(parent_fieldname), "=", "" if is_root else parent]]
Jamsheer4371c7e2018-08-01 18:40:05 +0530500
501 if is_root:
Rucha Mahabal05853ef2020-03-12 17:44:46 +0530502 fields += ["service_unit_type"] if doctype == "Healthcare Service Unit" else []
503 filters.append(["company", "=", company])
Jamsheer4371c7e2018-08-01 18:40:05 +0530504
505 else:
Rucha Mahabal05853ef2020-03-12 17:44:46 +0530506 fields += ["service_unit_type", "allow_appointments", "inpatient_occupancy", "occupancy_status"] if doctype == "Healthcare Service Unit" else []
507 fields += [parent_fieldname + " as parent"]
Jamsheer4371c7e2018-08-01 18:40:05 +0530508
509 hc_service_units = frappe.get_list(doctype, fields=fields, filters=filters)
510
Rucha Mahabal05853ef2020-03-12 17:44:46 +0530511 if doctype == "Healthcare Service Unit":
Jamsheer4371c7e2018-08-01 18:40:05 +0530512 for each in hc_service_units:
Rucha Mahabal05853ef2020-03-12 17:44:46 +0530513 occupancy_msg = ""
514 if each["expandable"] == 1:
Jamsheer4371c7e2018-08-01 18:40:05 +0530515 occupied = False
516 vacant = False
Rucha Mahabalced978e2020-04-02 18:45:53 +0530517 child_list = frappe.db.sql(
518 '''
519 SELECT
520 name, occupancy_status
521 FROM
522 `tabHealthcare Service Unit`
523 WHERE
524 inpatient_occupancy = 1
525 and lft > %s and rgt < %s
526 ''', (each['lft'], each['rgt']))
527
Jamsheer4371c7e2018-08-01 18:40:05 +0530528 for child in child_list:
Jamsheer4371c7e2018-08-01 18:40:05 +0530529 if not occupied:
530 occupied = 0
Rucha Mahabal05853ef2020-03-12 17:44:46 +0530531 if child[1] == "Occupied":
Jamsheer4371c7e2018-08-01 18:40:05 +0530532 occupied += 1
533 if not vacant:
534 vacant = 0
Rucha Mahabal05853ef2020-03-12 17:44:46 +0530535 if child[1] == "Vacant":
Jamsheer4371c7e2018-08-01 18:40:05 +0530536 vacant += 1
537 if vacant and occupied:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530538 occupancy_total = vacant + occupied
Rucha Mahabal05853ef2020-03-12 17:44:46 +0530539 occupancy_msg = str(occupied) + " Occupied out of " + str(occupancy_total)
540 each["occupied_out_of_vacant"] = occupancy_msg
Jamsheer4371c7e2018-08-01 18:40:05 +0530541 return hc_service_units
Jamsheer5073ac42019-07-12 12:28:34 +0530542
Rucha Mahabal27512c82020-03-09 17:29:23 +0530543
Jamsheer5073ac42019-07-12 12:28:34 +0530544@frappe.whitelist()
545def get_patient_vitals(patient, from_date=None, to_date=None):
546 if not patient: return
Rucha Mahabal27512c82020-03-09 17:29:23 +0530547
Rucha Mahabal4dd6b992020-05-25 18:42:01 +0530548 vitals = frappe.db.get_all('Vital Signs', filters={
Rucha Mahabal27512c82020-03-09 17:29:23 +0530549 'docstatus': 1,
550 'patient': patient
Rucha Mahabal4dd6b992020-05-25 18:42:01 +0530551 }, order_by='signs_date, signs_time', fields=['*'])
Rucha Mahabal27512c82020-03-09 17:29:23 +0530552
553 if len(vitals):
Jamsheer5073ac42019-07-12 12:28:34 +0530554 return vitals
Rucha Mahabal27512c82020-03-09 17:29:23 +0530555 return False
556
Jamsheer5073ac42019-07-12 12:28:34 +0530557
558@frappe.whitelist()
559def render_docs_as_html(docs):
560 # docs key value pair {doctype: docname}
561 docs_html = "<div class='col-md-12 col-sm-12 text-muted'>"
562 for doc in docs:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530563 docs_html += render_doc_as_html(doc['doctype'], doc['docname'])['html'] + '<br/>'
Jamsheer5073ac42019-07-12 12:28:34 +0530564 return {'html': docs_html}
565
Rucha Mahabal27512c82020-03-09 17:29:23 +0530566
Jamsheer5073ac42019-07-12 12:28:34 +0530567@frappe.whitelist()
568def render_doc_as_html(doctype, docname, exclude_fields = []):
569 #render document as html, three column layout will break
570 doc = frappe.get_doc(doctype, docname)
571 meta = frappe.get_meta(doctype)
572 doc_html = "<div class='col-md-12 col-sm-12'>"
Rucha Mahabal27512c82020-03-09 17:29:23 +0530573 section_html = ''
574 section_label = ''
575 html = ''
Jamsheer5073ac42019-07-12 12:28:34 +0530576 sec_on = False
577 col_on = 0
578 has_data = False
579 for df in meta.fields:
580 #on section break append append previous section and html to doc html
581 if df.fieldtype == "Section Break":
582 if has_data and col_on and sec_on:
583 doc_html += section_html + html + "</div>"
584 elif has_data and not col_on and sec_on:
585 doc_html += "<div class='col-md-12 col-sm-12'\
586 ><div class='col-md-12 col-sm-12'>" \
587 + section_html + html +"</div></div>"
588 while col_on:
589 doc_html += "</div>"
590 col_on -= 1
591 sec_on = True
592 has_data= False
593 col_on = 0
Rucha Mahabal27512c82020-03-09 17:29:23 +0530594 section_html = ''
595 html = ''
Jamsheer5073ac42019-07-12 12:28:34 +0530596 if df.label:
597 section_label = df.label
598 continue
599 #on column break append html to section html or doc html
600 if df.fieldtype == "Column Break":
601 if sec_on and has_data:
602 section_html += "<div class='col-md-12 col-sm-12'\
603 ><div class='col-md-6 col\
604 -sm-6'><b>" + section_label + "</b>" + html + "</div><div \
605 class='col-md-6 col-sm-6'>"
606 elif has_data:
607 doc_html += "<div class='col-md-12 col-sm-12'><div class='col-m\
608 d-6 col-sm-6'>" + html + "</div><div class='col-md-6 col-sm-6'>"
609 elif sec_on and not col_on:
610 section_html += "<div class='col-md-6 col-sm-6'>"
Rucha Mahabal27512c82020-03-09 17:29:23 +0530611 html = ''
Jamsheer5073ac42019-07-12 12:28:34 +0530612 col_on += 1
613 if df.label:
614 html += '<br>' + df.label
615 continue
616 #on table iterate in items and create table based on in_list_view, append to section html or doc html
Rucha Mahabal27512c82020-03-09 17:29:23 +0530617 if df.fieldtype == 'Table':
Jamsheer5073ac42019-07-12 12:28:34 +0530618 items = doc.get(df.fieldname)
619 if not items: continue
620 child_meta = frappe.get_meta(df.options)
621 if not has_data : has_data = True
Rucha Mahabal27512c82020-03-09 17:29:23 +0530622 table_head = ''
623 table_row = ''
Jamsheer5073ac42019-07-12 12:28:34 +0530624 create_head = True
625 for item in items:
626 table_row += '<tr>'
627 for cdf in child_meta.fields:
628 if cdf.in_list_view:
629 if create_head:
630 table_head += '<th>' + cdf.label + '</th>'
631 if item.get(cdf.fieldname):
632 table_row += '<td>' + str(item.get(cdf.fieldname)) \
633 + '</td>'
634 else:
635 table_row += '<td></td>'
636 create_head = False
637 table_row += '</tr>'
638 if sec_on:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530639 section_html += "<table class='table table-condensed \
640 bordered'>" + table_head + table_row + '</table>'
Jamsheer5073ac42019-07-12 12:28:34 +0530641 else:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530642 html += "<table class='table table-condensed table-bordered'>" \
643 + table_head + table_row + "</table>"
Jamsheer5073ac42019-07-12 12:28:34 +0530644 continue
645 #on other field types add label and value to html
646 if not df.hidden and not df.print_hide and doc.get(df.fieldname) and df.fieldname not in exclude_fields:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530647 html += '<br>{0} : {1}'.format(df.label or df.fieldname, \
Jamsheer5073ac42019-07-12 12:28:34 +0530648 doc.get(df.fieldname))
649 if not has_data : has_data = True
650 if sec_on and col_on and has_data:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530651 doc_html += section_html + html + '</div></div>'
Jamsheer5073ac42019-07-12 12:28:34 +0530652 elif sec_on and not col_on and has_data:
653 doc_html += "<div class='col-md-12 col-sm-12'\
654 ><div class='col-md-12 col-sm-12'>" \
Rucha Mahabal27512c82020-03-09 17:29:23 +0530655 + section_html + html +'</div></div>'
Jamsheer5073ac42019-07-12 12:28:34 +0530656 if doc_html:
Rucha Mahabal27512c82020-03-09 17:29:23 +0530657 doc_html = "<div class='small'><div class='col-md-12 text-right'><a class='btn btn-default btn-xs' href='#Form/%s/%s'></a></div>" %(doctype, docname) + doc_html + '</div>'
Jamsheer5073ac42019-07-12 12:28:34 +0530658
659 return {'html': doc_html}