feat: multi-company billing
sales onvoice - filter get items based on company
utils - company filters in all get item helper methods
utils - refactor appointemnt items
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 6f78db2..7741842 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -919,7 +919,7 @@
if(patient && patient!=selected_patient){
selected_patient = patient;
var method = "erpnext.healthcare.utils.get_healthcare_services_to_invoice";
- var args = {patient: patient};
+ var args = {patient: patient, company: frm.doc.company};
var columns = (["service", "reference_name", "reference_type"]);
get_healthcare_items(frm, true, $results, $placeholder, method, args, columns);
}
@@ -1063,7 +1063,10 @@
description:'Quantity will be calculated only for items which has "Nos" as UoM. You may change as required for each invoice item.',
get_query: function(doc) {
return {
- filters: { patient: dialog.get_value("patient"), docstatus: 1 }
+ filters: { patient: dialog.get_value("patient"),
+ company: frm.doc.company,
+ docstatus: 1
+ }
};
}
},
diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py
index 246242a..e9cc597 100644
--- a/erpnext/healthcare/utils.py
+++ b/erpnext/healthcare/utils.py
@@ -3,82 +3,84 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+import math
+import json
import frappe
from frappe import _
-import math
from frappe.utils import time_diff_in_hours, rounded
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account
from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity
from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple
@frappe.whitelist()
-def get_healthcare_services_to_invoice(patient):
+def get_healthcare_services_to_invoice(patient, company):
patient = frappe.get_doc('Patient', patient)
+ items_to_invoice = []
if patient:
validate_customer_created(patient)
- items_to_invoice = []
- patient_appointments = frappe.get_list(
- 'Patient Appointment',
- fields='*',
- filters={'patient': patient.name, 'invoiced': 0},
- order_by='appointment_date'
- )
- if patient_appointments:
- items_to_invoice = get_fee_validity(patient_appointments)
+ # Customer validated, build a list of billable services
+ items_to_invoice += get_appointments_to_invoice(patient, company)
+ items_to_invoice += get_encounters_to_invoice(patient, company)
+ items_to_invoice += get_lab_tests_to_invoice(patient, company)
+ items_to_invoice += get_clinical_procedures_to_invoice(patient, company)
+ items_to_invoice += get_inpatient_services_to_invoice(patient, company)
- encounters = get_encounters_to_invoice(patient)
- lab_tests = get_lab_tests_to_invoice(patient)
- clinical_procedures = get_clinical_procedures_to_invoice(patient)
- inpatient_services = get_inpatient_services_to_invoice(patient)
-
- items_to_invoice += encounters + lab_tests + clinical_procedures + inpatient_services
return items_to_invoice
+
def validate_customer_created(patient):
if not frappe.db.get_value('Patient', patient.name, 'customer'):
msg = _("Please set a Customer linked to the Patient")
msg += " <b><a href='#Form/Patient/{0}'>{0}</a></b>".format(patient.name)
frappe.throw(msg, title=_('Customer Not Found'))
-def get_fee_validity(patient_appointments):
- if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'):
- return
- items_to_invoice = []
+def get_appointments_to_invoice(patient, company):
+ appointments_to_invoice = []
+ patient_appointments = frappe.get_list(
+ 'Patient Appointment',
+ fields = '*',
+ filters = {'patient': patient.name, 'company': company, 'invoiced': 0},
+ order_by = 'appointment_date'
+ )
+
for appointment in patient_appointments:
+ # Procedure Appointments
if appointment.procedure_template:
if frappe.db.get_value('Clinical Procedure Template', appointment.procedure_template, 'is_billable'):
- items_to_invoice.append({
+ appointments_to_invoice.append({
'reference_type': 'Patient Appointment',
'reference_name': appointment.name,
'service': appointment.procedure_template
})
+ # Consultation Appointments, should check fee validity
else:
- fee_validity = frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name})
- if not fee_validity:
- practitioner_charge = 0
- income_account = None
- service_item = None
- if appointment.practitioner:
- service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment)
- income_account = get_income_account(appointment.practitioner, appointment.company)
- items_to_invoice.append({
- 'reference_type': 'Patient Appointment',
- 'reference_name': appointment.name,
- 'service': service_item,
- 'rate': practitioner_charge,
- 'income_account': income_account
- })
+ if frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups') and \
+ frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name}):
+ continue # Skip invoicing, fee validty present
+ practitioner_charge = 0
+ income_account = None
+ service_item = None
+ if appointment.practitioner:
+ service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment)
+ income_account = get_income_account(appointment.practitioner, appointment.company)
+ appointments_to_invoice.append({
+ 'reference_type': 'Patient Appointment',
+ 'reference_name': appointment.name,
+ 'service': service_item,
+ 'rate': practitioner_charge,
+ 'income_account': income_account
+ })
- return items_to_invoice
+ return appointments_to_invoice
-def get_encounters_to_invoice(patient):
+def get_encounters_to_invoice(patient, company):
encounters_to_invoice = []
encounters = frappe.get_list(
'Patient Encounter',
fields=['*'],
- filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1}
+ filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1}
)
if encounters:
for encounter in encounters:
@@ -101,12 +103,12 @@
return encounters_to_invoice
-def get_lab_tests_to_invoice(patient):
+def get_lab_tests_to_invoice(patient, company):
lab_tests_to_invoice = []
lab_tests = frappe.get_list(
'Lab Test',
fields=['name', 'template'],
- filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1}
+ filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1}
)
for lab_test in lab_tests:
item, is_billable = frappe.get_cached_value('Lab Test Template', lab_test.lab_test_code, ['item', 'is_billable'])
@@ -142,12 +144,12 @@
return lab_tests_to_invoice
-def get_clinical_procedures_to_invoice(patient):
+def get_clinical_procedures_to_invoice(patient, company):
clinical_procedures_to_invoice = []
procedures = frappe.get_list(
'Clinical Procedure',
fields='*',
- filters={'patient': patient.name, 'invoiced': False}
+ filters={'patient': patient.name, 'company': company, 'invoiced': False}
)
for procedure in procedures:
if not procedure.appointment:
@@ -203,7 +205,7 @@
return clinical_procedures_to_invoice
-def get_inpatient_services_to_invoice(patient):
+def get_inpatient_services_to_invoice(patient, company):
services_to_invoice = []
inpatient_services = frappe.db.sql(
'''
@@ -213,10 +215,11 @@
`tabInpatient Record` ip, `tabInpatient Occupancy` io
WHERE
ip.patient=%s
+ and ip.company=%s
and io.parent=ip.name
and io.left=1
and io.invoiced=0
- ''', (patient.name), as_dict=1)
+ ''', (patient.name, company), as_dict=1)
for inpatient_occupancy in inpatient_services:
service_unit_type = frappe.db.get_value('Healthcare Service Unit', inpatient_occupancy.service_unit, 'service_unit_type')
@@ -376,6 +379,7 @@
def manage_fee_validity(appointment):
fee_validity = check_fee_validity(appointment)
+
if fee_validity:
if appointment.status == 'Cancelled' and fee_validity.visited > 0:
fee_validity.visited -= 1