mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-20 05:29:18 +00:00
Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into payment_entry_validations_and_trigger
This commit is contained in:
@@ -10,3 +10,6 @@
|
|||||||
|
|
||||||
# This commit just changes spaces to tabs for indentation in some files
|
# This commit just changes spaces to tabs for indentation in some files
|
||||||
5f473611bd6ed57703716244a054d3fb5ba9cd23
|
5f473611bd6ed57703716244a054d3fb5ba9cd23
|
||||||
|
|
||||||
|
# Whitespace trimming throughout codebase
|
||||||
|
9bb69e711a5da43aaf8c8ecb5601aeffd89dbe5a
|
||||||
|
|||||||
@@ -450,5 +450,3 @@ def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
|
|||||||
return debit_account
|
return debit_account
|
||||||
else:
|
else:
|
||||||
return credit_account
|
return credit_account
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -113,5 +113,3 @@ def disable_dimension():
|
|||||||
dimension2 = frappe.get_doc("Accounting Dimension", "Location")
|
dimension2 = frappe.get_doc("Accounting Dimension", "Location")
|
||||||
dimension2.disabled = 1
|
dimension2.disabled = 1
|
||||||
dimension2.save()
|
dimension2.save()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -105,4 +105,3 @@ def unclear_reference_payment(doctype, docname):
|
|||||||
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
|
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
|
||||||
|
|
||||||
return doc.payment_entry
|
return doc.payment_entry
|
||||||
|
|
||||||
|
|||||||
@@ -18,5 +18,3 @@ class CashFlowMapping(Document):
|
|||||||
frappe._('You can only select a maximum of one option from the list of check boxes.'),
|
frappe._('You can only select a maximum of one option from the list of check boxes.'),
|
||||||
title='Error'
|
title='Error'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,3 @@ def create_cost_center(**args):
|
|||||||
cc.is_group = args.is_group or 0
|
cc.is_group = args.is_group or 0
|
||||||
cc.parent_cost_center = args.parent_cost_center or "_Test Company - _TC"
|
cc.parent_cost_center = args.parent_cost_center or "_Test Company - _TC"
|
||||||
cc.insert()
|
cc.insert()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,3 @@ class TestCouponCode(unittest.TestCase):
|
|||||||
|
|
||||||
so.submit()
|
so.submit()
|
||||||
self.assertEqual(frappe.db.get_value("Coupon Code", "SAVE30", "used"), 1)
|
self.assertEqual(frappe.db.get_value("Coupon Code", "SAVE30", "used"), 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -39,4 +39,3 @@ class ModeofPayment(Document):
|
|||||||
message = "POS Profile " + frappe.bold(", ".join(pos_profiles)) + " contains \
|
message = "POS Profile " + frappe.bold(", ".join(pos_profiles)) + " contains \
|
||||||
Mode of Payment " + frappe.bold(str(self.name)) + ". Please remove them to disable this mode."
|
Mode of Payment " + frappe.bold(str(self.name)) + ". Please remove them to disable this mode."
|
||||||
frappe.throw(_(message), title="Not Allowed")
|
frappe.throw(_(message), title="Not Allowed")
|
||||||
|
|
||||||
|
|||||||
@@ -240,5 +240,3 @@ def get_temporary_opening_account(company=None):
|
|||||||
frappe.throw(_("Please add a Temporary Opening account in Chart of Accounts"))
|
frappe.throw(_("Please add a Temporary Opening account in Chart of Accounts"))
|
||||||
|
|
||||||
return accounts[0].name
|
return accounts[0].name
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -147,4 +147,3 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
|
|||||||
frappe.set_user("Administrator")
|
frappe.set_user("Administrator")
|
||||||
frappe.db.sql("delete from `tabPOS Profile`")
|
frappe.db.sql("delete from `tabPOS Profile`")
|
||||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||||
|
|
||||||
|
|||||||
@@ -26,4 +26,3 @@ QUnit.test("test pricing rule", function(assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,6 @@
|
|||||||
"depends_on": "eval:doc.rate_or_discount==\"Rate\"",
|
"depends_on": "eval:doc.rate_or_discount==\"Rate\"",
|
||||||
"fieldname": "rate",
|
"fieldname": "rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Rate"
|
"label": "Rate"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -170,7 +169,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-07 11:56:23.424137",
|
"modified": "2021-08-19 15:49:29.598727",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Promotional Scheme Price Discount",
|
"name": "Promotional Scheme Price Discount",
|
||||||
|
|||||||
@@ -72,4 +72,3 @@ QUnit.test("test purchase invoice", function(assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -26,4 +26,3 @@ QUnit.test("test sales taxes and charges template", function(assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -40,4 +40,3 @@ QUnit.test("test sales Invoice", function(assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -33,4 +33,3 @@ QUnit.test("test sales invoice with margin", function(assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -54,4 +54,3 @@ QUnit.test("test sales Invoice with payment", function(assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -49,4 +49,3 @@ QUnit.test("test sales Invoice with payment request", function(assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -42,4 +42,3 @@ QUnit.test("test sales Invoice with serialize item", function(assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -26,4 +26,3 @@ QUnit.test("test sales taxes and charges template", function(assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -34,4 +34,3 @@ QUnit.test("test Shipping Rule", function(assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -34,4 +34,3 @@ QUnit.test("test Shipping Rule", function(assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -630,5 +630,3 @@ class TestSubscription(unittest.TestCase):
|
|||||||
|
|
||||||
subscription.process()
|
subscription.process()
|
||||||
self.assertEqual(len(subscription.invoices), 1)
|
self.assertEqual(len(subscription.invoices), 1)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -286,6 +286,7 @@ def validate_party_gle_currency(party_type, party, company, party_account_curren
|
|||||||
.format(frappe.bold(party_type), frappe.bold(party), frappe.bold(existing_gle_currency), frappe.bold(company)), InvalidAccountCurrency)
|
.format(frappe.bold(party_type), frappe.bold(party), frappe.bold(existing_gle_currency), frappe.bold(company)), InvalidAccountCurrency)
|
||||||
|
|
||||||
def validate_party_accounts(doc):
|
def validate_party_accounts(doc):
|
||||||
|
|
||||||
companies = []
|
companies = []
|
||||||
|
|
||||||
for account in doc.get("accounts"):
|
for account in doc.get("accounts"):
|
||||||
@@ -446,6 +447,10 @@ def get_payment_terms_template(party_name, party_type, company=None):
|
|||||||
return template
|
return template
|
||||||
|
|
||||||
def validate_party_frozen_disabled(party_type, party_name):
|
def validate_party_frozen_disabled(party_type, party_name):
|
||||||
|
|
||||||
|
if frappe.flags.ignore_party_validation:
|
||||||
|
return
|
||||||
|
|
||||||
if party_type and party_name:
|
if party_type and party_name:
|
||||||
if party_type in ("Customer", "Supplier"):
|
if party_type in ("Customer", "Supplier"):
|
||||||
party = frappe.get_cached_value(party_type, party_name, ["is_frozen", "disabled"], as_dict=True)
|
party = frappe.get_cached_value(party_type, party_name, ["is_frozen", "disabled"], as_dict=True)
|
||||||
|
|||||||
@@ -27,4 +27,3 @@
|
|||||||
{{ _("Authorized Signatory") }}
|
{{ _("Authorized Signatory") }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -62,8 +62,3 @@ def make_sales_invoice():
|
|||||||
income_account = 'Sales - _TC2',
|
income_account = 'Sales - _TC2',
|
||||||
expense_account = 'Cost of Goods Sold - _TC2',
|
expense_account = 'Cost of Goods Sold - _TC2',
|
||||||
cost_center = 'Main - _TC2')
|
cost_center = 'Main - _TC2')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -136,4 +136,3 @@ frappe.query_reports["Accounts Payable"] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
erpnext.utils.add_dimensions('Accounts Payable', 9);
|
erpnext.utils.add_dimensions('Accounts Payable', 9);
|
||||||
|
|
||||||
|
|||||||
@@ -105,4 +105,3 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
erpnext.utils.add_dimensions('Accounts Payable Summary', 9);
|
erpnext.utils.add_dimensions('Accounts Payable Summary', 9);
|
||||||
|
|
||||||
|
|||||||
@@ -12,4 +12,3 @@ def execute(filters=None):
|
|||||||
"naming_by": ["Buying Settings", "supp_master_name"],
|
"naming_by": ["Buying Settings", "supp_master_name"],
|
||||||
}
|
}
|
||||||
return AccountsReceivableSummary(filters).run(args)
|
return AccountsReceivableSummary(filters).run(args)
|
||||||
|
|
||||||
|
|||||||
@@ -200,4 +200,3 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
erpnext.utils.add_dimensions('Accounts Receivable', 9);
|
erpnext.utils.add_dimensions('Accounts Receivable', 9);
|
||||||
|
|
||||||
|
|||||||
@@ -93,4 +93,3 @@ def make_credit_note(docname):
|
|||||||
cost_center = 'Main - _TC2',
|
cost_center = 'Main - _TC2',
|
||||||
is_return = 1,
|
is_return = 1,
|
||||||
return_against = docname)
|
return_against = docname)
|
||||||
|
|
||||||
|
|||||||
@@ -92,4 +92,3 @@ frappe.query_reports["Budget Variance Report"] = {
|
|||||||
erpnext.dimension_filters.forEach((dimension) => {
|
erpnext.dimension_filters.forEach((dimension) => {
|
||||||
frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]);
|
frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -399,4 +399,3 @@ def get_chart_data(filters, columns, data):
|
|||||||
},
|
},
|
||||||
'type' : 'bar'
|
'type' : 'bar'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -176,4 +176,3 @@ frappe.query_reports["General Ledger"] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
erpnext.utils.add_dimensions('General Ledger', 15)
|
erpnext.utils.add_dimensions('General Ledger', 15)
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
|||||||
'company': d.company,
|
'company': d.company,
|
||||||
'sales_order': d.sales_order,
|
'sales_order': d.sales_order,
|
||||||
'delivery_note': d.delivery_note,
|
'delivery_note': d.delivery_note,
|
||||||
'income_account': d.unrealized_profit_loss_account or d.income_account,
|
'income_account': d.unrealized_profit_loss_account if d.is_internal_customer == 1 else d.income_account,
|
||||||
'cost_center': d.cost_center,
|
'cost_center': d.cost_center,
|
||||||
'stock_qty': d.stock_qty,
|
'stock_qty': d.stock_qty,
|
||||||
'stock_uom': d.stock_uom
|
'stock_uom': d.stock_uom
|
||||||
@@ -380,6 +380,7 @@ def get_items(filters, additional_query_columns):
|
|||||||
`tabSales Invoice Item`.name, `tabSales Invoice Item`.parent,
|
`tabSales Invoice Item`.name, `tabSales Invoice Item`.parent,
|
||||||
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
|
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
|
||||||
`tabSales Invoice`.unrealized_profit_loss_account,
|
`tabSales Invoice`.unrealized_profit_loss_account,
|
||||||
|
`tabSales Invoice`.is_internal_customer,
|
||||||
`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
|
`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
|
||||||
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
|
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
|
||||||
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
|
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
|
||||||
@@ -625,7 +626,3 @@ def add_sub_total_row(item, total_row_map, group_by_value, tax_columns):
|
|||||||
for tax in tax_columns:
|
for tax in tax_columns:
|
||||||
total_row.setdefault(frappe.scrub(tax + ' Amount'), 0.0)
|
total_row.setdefault(frappe.scrub(tax + ' Amount'), 0.0)
|
||||||
total_row[frappe.scrub(tax + ' Amount')] += flt(item[frappe.scrub(tax + ' Amount')])
|
total_row[frappe.scrub(tax + ' Amount')] += flt(item[frappe.scrub(tax + ' Amount')])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -69,4 +69,3 @@ frappe.query_reports["Sales Register"] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
erpnext.utils.add_dimensions('Sales Register', 7);
|
erpnext.utils.add_dimensions('Sales Register', 7);
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
|
|||||||
# Add amount in unrealized account
|
# Add amount in unrealized account
|
||||||
for account in unrealized_profit_loss_accounts:
|
for account in unrealized_profit_loss_accounts:
|
||||||
row.update({
|
row.update({
|
||||||
frappe.scrub(account): flt(internal_invoice_map.get((inv.name, account)))
|
frappe.scrub(account+"_unrealized"): flt(internal_invoice_map.get((inv.name, account)))
|
||||||
})
|
})
|
||||||
|
|
||||||
# net total
|
# net total
|
||||||
@@ -258,6 +258,7 @@ def get_columns(invoice_list, additional_table_columns):
|
|||||||
|
|
||||||
unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
|
unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
|
||||||
from `tabSales Invoice` where docstatus = 1 and name in (%s)
|
from `tabSales Invoice` where docstatus = 1 and name in (%s)
|
||||||
|
and is_internal_customer = 1
|
||||||
and ifnull(unrealized_profit_loss_account, '') != ''
|
and ifnull(unrealized_profit_loss_account, '') != ''
|
||||||
order by unrealized_profit_loss_account""" %
|
order by unrealized_profit_loss_account""" %
|
||||||
', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list))
|
', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list))
|
||||||
@@ -284,7 +285,7 @@ def get_columns(invoice_list, additional_table_columns):
|
|||||||
for account in unrealized_profit_loss_accounts:
|
for account in unrealized_profit_loss_accounts:
|
||||||
unrealized_profit_loss_account_columns.append({
|
unrealized_profit_loss_account_columns.append({
|
||||||
"label": account,
|
"label": account,
|
||||||
"fieldname": frappe.scrub(account),
|
"fieldname": frappe.scrub(account+"_unrealized"),
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
"width": 120
|
"width": 120
|
||||||
|
|||||||
@@ -110,6 +110,3 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
|
|
||||||
erpnext.utils.add_dimensions('Trial Balance', 6);
|
erpnext.utils.add_dimensions('Trial Balance', 6);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -351,6 +351,7 @@ def reconcile_against_document(args):
|
|||||||
# cancel advance entry
|
# cancel advance entry
|
||||||
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
||||||
|
|
||||||
|
frappe.flags.ignore_party_validation = True
|
||||||
doc.make_gl_entries(cancel=1, adv_adj=1)
|
doc.make_gl_entries(cancel=1, adv_adj=1)
|
||||||
|
|
||||||
# update ref in advance entry
|
# update ref in advance entry
|
||||||
@@ -362,6 +363,7 @@ def reconcile_against_document(args):
|
|||||||
# re-submit advance entry
|
# re-submit advance entry
|
||||||
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
||||||
doc.make_gl_entries(cancel = 0, adv_adj =1)
|
doc.make_gl_entries(cancel = 0, adv_adj =1)
|
||||||
|
frappe.flags.ignore_party_validation = False
|
||||||
|
|
||||||
if d.voucher_type in ('Payment Entry', 'Journal Entry'):
|
if d.voucher_type in ('Payment Entry', 'Journal Entry'):
|
||||||
doc.update_expense_claim()
|
doc.update_expense_claim()
|
||||||
|
|||||||
@@ -36,4 +36,3 @@ QUnit.test("test: Disease", function (assert) {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -10,4 +10,3 @@ frappe.listview_settings['Asset Repair'] = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -93,5 +93,3 @@ var loadAllStandings = function(frm) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -128,4 +128,3 @@ valid_scorecard = [
|
|||||||
"weighting_function":"{total_score} * max( 0, min ( 1 , (12 - {period_number}) / 12) )"
|
"weighting_function":"{total_score} * max( 0, min ( 1 , (12 - {period_number}) / 12) )"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -109,4 +109,3 @@ def make_supplier_scorecard(source_name, target_doc=None):
|
|||||||
}, target_doc, post_process, ignore_permissions=True)
|
}, target_doc, post_process, ignore_permissions=True)
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|||||||
@@ -268,4 +268,3 @@ def get_columns(filters):
|
|||||||
])
|
])
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
|
|||||||
@@ -102,4 +102,3 @@ def get_linked_material_requests(items):
|
|||||||
mr_list.append(material_request)
|
mr_list.append(material_request)
|
||||||
|
|
||||||
return mr_list
|
return mr_list
|
||||||
|
|
||||||
|
|||||||
@@ -1863,7 +1863,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
|||||||
qty_unchanged = prev_qty == new_qty
|
qty_unchanged = prev_qty == new_qty
|
||||||
uom_unchanged = prev_uom == new_uom
|
uom_unchanged = prev_uom == new_uom
|
||||||
conversion_factor_unchanged = prev_con_fac == new_con_fac
|
conversion_factor_unchanged = prev_con_fac == new_con_fac
|
||||||
date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc
|
date_unchanged = prev_date == getdate(new_date) if prev_date and new_date else False # in case of delivery note etc
|
||||||
if rate_unchanged and qty_unchanged and conversion_factor_unchanged and uom_unchanged and date_unchanged:
|
if rate_unchanged and qty_unchanged and conversion_factor_unchanged and uom_unchanged and date_unchanged:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
@@ -344,4 +344,3 @@ def create_variant_doc_for_quick_entry(template, args):
|
|||||||
variant.name = variant.item_code
|
variant.name = variant.item_code
|
||||||
validate_item_variant_attributes(variant, args)
|
validate_item_variant_attributes(variant, args)
|
||||||
return variant.as_dict()
|
return variant.as_dict()
|
||||||
|
|
||||||
|
|||||||
@@ -526,6 +526,9 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters)
|
|||||||
if meta.is_tree:
|
if meta.is_tree:
|
||||||
query_filters.append(['is_group', '=', 0])
|
query_filters.append(['is_group', '=', 0])
|
||||||
|
|
||||||
|
if meta.has_field('disabled'):
|
||||||
|
query_filters.append(['disabled', '!=', 1])
|
||||||
|
|
||||||
if meta.has_field('company'):
|
if meta.has_field('company'):
|
||||||
query_filters.append(['company', '=', filters.get('company')])
|
query_filters.append(['company', '=', filters.get('company')])
|
||||||
|
|
||||||
|
|||||||
@@ -235,4 +235,3 @@ def _get_employee_from_user(user):
|
|||||||
# frappe.db.exists returns a tuple of a tuple
|
# frappe.db.exists returns a tuple of a tuple
|
||||||
return frappe.get_doc('Employee', employee_docname[0][0])
|
return frappe.get_doc('Employee', employee_docname[0][0])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -16,4 +16,3 @@ frappe.query_reports["Campaign Efficiency"] = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,5 +20,3 @@ frappe.query_reports["Lead Conversion Time"] = {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -155,4 +155,3 @@ def get_conditions(filters) :
|
|||||||
conditions.append(" and `tabLead`.status=%(status)s")
|
conditions.append(" and `tabLead`.status=%(status)s")
|
||||||
|
|
||||||
return " ".join(conditions) if conditions else ""
|
return " ".join(conditions) if conditions else ""
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,3 @@ class AssessmentResult(Document):
|
|||||||
"student":self.student, "assessment_plan":self.assessment_plan, "docstatus":("!=", 2)})
|
"student":self.student, "assessment_plan":self.assessment_plan, "docstatus":("!=", 2)})
|
||||||
if assessment_result:
|
if assessment_result:
|
||||||
frappe.throw(_("Assessment Result record {0} already exists.").format(getlink("Assessment Result",assessment_result[0].name)))
|
frappe.throw(_("Assessment Result record {0} already exists.").format(getlink("Assessment Result",assessment_result[0].name)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,4 +16,3 @@ class TestAssessmentResult(unittest.TestCase):
|
|||||||
|
|
||||||
grade = get_grade("_Test Grading Scale", 70)
|
grade = get_grade("_Test Grading Scale", 70)
|
||||||
self.assertEqual("B", grade)
|
self.assertEqual("B", grade)
|
||||||
|
|
||||||
@@ -39,6 +39,3 @@ class TestCourseEnrollment(unittest.TestCase):
|
|||||||
doc = frappe.get_doc("Program Enrollment", entry.name)
|
doc = frappe.get_doc("Program Enrollment", entry.name)
|
||||||
doc.cancel()
|
doc.cancel()
|
||||||
doc.delete()
|
doc.delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -47,4 +47,3 @@ class CourseSchedule(Document):
|
|||||||
|
|
||||||
validate_overlap_for(self, "Assessment Plan", "room")
|
validate_overlap_for(self, "Assessment Plan", "room")
|
||||||
validate_overlap_for(self, "Assessment Plan", "supervisor", self.instructor)
|
validate_overlap_for(self, "Assessment Plan", "supervisor", self.instructor)
|
||||||
|
|
||||||
|
|||||||
@@ -174,4 +174,3 @@ def get_students(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
tuple(students + ["%%%s%%" % txt, start, page_len]
|
tuple(students + ["%%%s%%" % txt, start, page_len]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -128,4 +128,3 @@ def fetch_students(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
order by idx desc, name
|
order by idx desc, name
|
||||||
limit %s, %s""".format(searchfield),
|
limit %s, %s""".format(searchfield),
|
||||||
tuple(["%%%s%%" % txt, "%%%s%%" % txt, start, page_len]))
|
tuple(["%%%s%%" % txt, "%%%s%%" % txt, start, page_len]))
|
||||||
|
|
||||||
|
|||||||
@@ -121,4 +121,3 @@ def get_chart_data(data):
|
|||||||
},
|
},
|
||||||
'type': 'bar'
|
'type': 'bar'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -350,4 +350,3 @@ def is_sync_complete(shopify_settings, order):
|
|||||||
return getdate(shopify_settings.to_date) < getdate(order.get('created_at'))
|
return getdate(shopify_settings.to_date) < getdate(order.get('created_at'))
|
||||||
else:
|
else:
|
||||||
return cstr(order.get('id')) == cstr(shopify_settings.to_order_id)
|
return cstr(order.get('id')) == cstr(shopify_settings.to_order_id)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ test_dependencies = ['Item']
|
|||||||
|
|
||||||
class TestClinicalProcedure(unittest.TestCase):
|
class TestClinicalProcedure(unittest.TestCase):
|
||||||
def test_procedure_template_item(self):
|
def test_procedure_template_item(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
procedure_template = create_clinical_procedure_template()
|
procedure_template = create_clinical_procedure_template()
|
||||||
self.assertTrue(frappe.db.exists('Item', procedure_template.item))
|
self.assertTrue(frappe.db.exists('Item', procedure_template.item))
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ class TestClinicalProcedure(unittest.TestCase):
|
|||||||
self.assertEqual(frappe.db.get_value('Item', procedure_template.item, 'disabled'), 1)
|
self.assertEqual(frappe.db.get_value('Item', procedure_template.item, 'disabled'), 1)
|
||||||
|
|
||||||
def test_consumables(self):
|
def test_consumables(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
procedure_template = create_clinical_procedure_template()
|
procedure_template = create_clinical_procedure_template()
|
||||||
procedure_template.allow_stock_consumption = 1
|
procedure_template.allow_stock_consumption = 1
|
||||||
consumable = create_consumable()
|
consumable = create_consumable()
|
||||||
|
|||||||
@@ -188,4 +188,3 @@ frappe.tour['Clinical Procedure Template'] = [
|
|||||||
description: __('You can also set the Medical Department for the template. After saving the document, an Item will automatically be created for billing this Clinical Procedure. You can then use this template while creating Clinical Procedures for Patients. Templates save you from filling up redundant data every single time. You can also create templates for other operations like Lab Tests, Therapy Sessions, etc.')
|
description: __('You can also set the Medical Department for the template. After saving the document, an Item will automatically be created for billing this Clinical Procedure. You can then use this template while creating Clinical Procedures for Patients. Templates save you from filling up redundant data every single time. You can also create templates for other operations like Lab Tests, Therapy Sessions, etc.')
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -118,4 +118,3 @@ def change_item_code_from_template(item_code, doc):
|
|||||||
rename_doc('Item', doc.item_code, item_code, ignore_permissions=True)
|
rename_doc('Item', doc.item_code, item_code, ignore_permissions=True)
|
||||||
frappe.db.set_value('Clinical Procedure Template', doc.name, 'item_code', item_code)
|
frappe.db.set_value('Clinical Procedure Template', doc.name, 'item_code', item_code)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -12,4 +12,3 @@ class ExerciseType(Document):
|
|||||||
self.name = ' - '.join(filter(None, [self.exercise_name, self.difficulty_level]))
|
self.name = ' - '.join(filter(None, [self.exercise_name, self.difficulty_level]))
|
||||||
else:
|
else:
|
||||||
self.name = self.exercise_name
|
self.name = self.exercise_name
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class TestFeeValidity(unittest.TestCase):
|
|||||||
healthcare_settings.automate_appointment_invoicing = 1
|
healthcare_settings.automate_appointment_invoicing = 1
|
||||||
healthcare_settings.op_consulting_charge_item = item
|
healthcare_settings.op_consulting_charge_item = item
|
||||||
healthcare_settings.save(ignore_permissions=True)
|
healthcare_settings.save(ignore_permissions=True)
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
|
|
||||||
# appointment should not be invoiced. Check Fee Validity created for new patient
|
# appointment should not be invoiced. Check Fee Validity created for new patient
|
||||||
appointment = create_appointment(patient, practitioner, nowdate())
|
appointment = create_appointment(patient, practitioner, nowdate())
|
||||||
|
|||||||
@@ -142,4 +142,3 @@ frappe.tour['Healthcare Practitioner'] = [
|
|||||||
description: __('If this Healthcare Practitioner also works for the In-Patient Department, set the inpatient visit charge for this Practitioner.')
|
description: __('If this Healthcare Practitioner also works for the In-Patient Department, set the inpatient visit charge for this Practitioner.')
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ frappe.ui.form.on('Healthcare Service Unit', {
|
|||||||
|
|
||||||
// get query select healthcare service unit
|
// get query select healthcare service unit
|
||||||
frm.fields_dict['parent_healthcare_service_unit'].get_query = function(doc) {
|
frm.fields_dict['parent_healthcare_service_unit'].get_query = function(doc) {
|
||||||
return{
|
return {
|
||||||
filters:[
|
filters: [
|
||||||
['Healthcare Service Unit', 'is_group', '=', 1],
|
['Healthcare Service Unit', 'is_group', '=', 1],
|
||||||
['Healthcare Service Unit', 'name', '!=', doc.healthcare_service_unit_name]
|
['Healthcare Service Unit', 'name', '!=', doc.healthcare_service_unit_name]
|
||||||
]
|
]
|
||||||
@@ -21,6 +21,14 @@ frappe.ui.form.on('Healthcare Service Unit', {
|
|||||||
frm.add_custom_button(__('Healthcare Service Unit Tree'), function() {
|
frm.add_custom_button(__('Healthcare Service Unit Tree'), function() {
|
||||||
frappe.set_route('Tree', 'Healthcare Service Unit');
|
frappe.set_route('Tree', 'Healthcare Service Unit');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query('warehouse', function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'company': frm.doc.company
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
set_root_readonly: function(frm) {
|
set_root_readonly: function(frm) {
|
||||||
// read-only for root healthcare service unit
|
// read-only for root healthcare service unit
|
||||||
@@ -43,5 +51,10 @@ frappe.ui.form.on('Healthcare Service Unit', {
|
|||||||
else {
|
else {
|
||||||
frm.set_df_property('service_unit_type', 'reqd', 1);
|
frm.set_df_property('service_unit_type', 'reqd', 1);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
overlap_appointments: function(frm) {
|
||||||
|
if (frm.doc.overlap_appointments == 0) {
|
||||||
|
frm.set_value('service_unit_capacity', '');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"service_unit_type",
|
"service_unit_type",
|
||||||
"allow_appointments",
|
"allow_appointments",
|
||||||
"overlap_appointments",
|
"overlap_appointments",
|
||||||
|
"service_unit_capacity",
|
||||||
"inpatient_occupancy",
|
"inpatient_occupancy",
|
||||||
"occupancy_status",
|
"occupancy_status",
|
||||||
"column_break_9",
|
"column_break_9",
|
||||||
@@ -31,6 +32,8 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "healthcare_service_unit_name",
|
"fieldname": "healthcare_service_unit_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Service Unit",
|
"label": "Service Unit",
|
||||||
@@ -41,6 +44,8 @@
|
|||||||
"bold": 1,
|
"bold": 1,
|
||||||
"fieldname": "parent_healthcare_service_unit",
|
"fieldname": "parent_healthcare_service_unit",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Parent Service Unit",
|
"label": "Parent Service Unit",
|
||||||
@@ -52,6 +57,8 @@
|
|||||||
"depends_on": "eval:doc.inpatient_occupancy != 1 && doc.allow_appointments != 1",
|
"depends_on": "eval:doc.inpatient_occupancy != 1 && doc.allow_appointments != 1",
|
||||||
"fieldname": "is_group",
|
"fieldname": "is_group",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Is Group"
|
"label": "Is Group"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -59,6 +66,8 @@
|
|||||||
"depends_on": "eval:doc.is_group != 1",
|
"depends_on": "eval:doc.is_group != 1",
|
||||||
"fieldname": "service_unit_type",
|
"fieldname": "service_unit_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Service Unit Type",
|
"label": "Service Unit Type",
|
||||||
"options": "Healthcare Service Unit Type"
|
"options": "Healthcare Service Unit Type"
|
||||||
},
|
},
|
||||||
@@ -68,6 +77,8 @@
|
|||||||
"fetch_from": "service_unit_type.allow_appointments",
|
"fetch_from": "service_unit_type.allow_appointments",
|
||||||
"fieldname": "allow_appointments",
|
"fieldname": "allow_appointments",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Allow Appointments",
|
"label": "Allow Appointments",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
@@ -79,6 +90,8 @@
|
|||||||
"fetch_from": "service_unit_type.overlap_appointments",
|
"fetch_from": "service_unit_type.overlap_appointments",
|
||||||
"fieldname": "overlap_appointments",
|
"fieldname": "overlap_appointments",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Allow Overlap",
|
"label": "Allow Overlap",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
@@ -90,6 +103,8 @@
|
|||||||
"fetch_from": "service_unit_type.inpatient_occupancy",
|
"fetch_from": "service_unit_type.inpatient_occupancy",
|
||||||
"fieldname": "inpatient_occupancy",
|
"fieldname": "inpatient_occupancy",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Inpatient Occupancy",
|
"label": "Inpatient Occupancy",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
@@ -100,6 +115,8 @@
|
|||||||
"depends_on": "eval:doc.inpatient_occupancy == 1",
|
"depends_on": "eval:doc.inpatient_occupancy == 1",
|
||||||
"fieldname": "occupancy_status",
|
"fieldname": "occupancy_status",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Occupancy Status",
|
"label": "Occupancy Status",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Vacant\nOccupied",
|
"options": "Vacant\nOccupied",
|
||||||
@@ -107,13 +124,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_9",
|
"fieldname": "column_break_9",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bold": 1,
|
"bold": 1,
|
||||||
"depends_on": "eval:doc.is_group != 1",
|
"depends_on": "eval:doc.is_group != 1",
|
||||||
"fieldname": "warehouse",
|
"fieldname": "warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Warehouse",
|
"label": "Warehouse",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Warehouse"
|
"options": "Warehouse"
|
||||||
@@ -121,6 +142,8 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
@@ -134,6 +157,8 @@
|
|||||||
"fieldname": "lft",
|
"fieldname": "lft",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "lft",
|
"label": "lft",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
@@ -143,6 +168,8 @@
|
|||||||
"fieldname": "rgt",
|
"fieldname": "rgt",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "rgt",
|
"label": "rgt",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
@@ -152,6 +179,8 @@
|
|||||||
"fieldname": "old_parent",
|
"fieldname": "old_parent",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
"label": "Old Parent",
|
"label": "Old Parent",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
@@ -163,14 +192,26 @@
|
|||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"fieldname": "tree_details_section",
|
"fieldname": "tree_details_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Tree Details"
|
"label": "Tree Details"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.overlap_appointments == 1",
|
||||||
|
"fieldname": "service_unit_capacity",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Service Unit Capacity",
|
||||||
|
"mandatory_depends_on": "eval:doc.overlap_appointments == 1",
|
||||||
|
"non_negative": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-20 18:26:56.065543",
|
"modified": "2021-08-19 14:09:11.643464",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Healthcare",
|
"module": "Healthcare",
|
||||||
"name": "Healthcare Service Unit",
|
"name": "Healthcare Service Unit",
|
||||||
|
"nsm_parent_field": "parent_healthcare_service_unit",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,14 +5,21 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from frappe.utils.nestedset import NestedSet
|
from frappe.utils.nestedset import NestedSet
|
||||||
|
from frappe.utils import cint, cstr
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
class HealthcareServiceUnit(NestedSet):
|
class HealthcareServiceUnit(NestedSet):
|
||||||
nsm_parent_field = 'parent_healthcare_service_unit'
|
nsm_parent_field = 'parent_healthcare_service_unit'
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
self.set_service_unit_properties()
|
||||||
|
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
if self.company:
|
if self.company:
|
||||||
suffix = " - " + frappe.get_cached_value('Company', self.company, "abbr")
|
suffix = " - " + frappe.get_cached_value('Company', self.company, 'abbr')
|
||||||
if not self.healthcare_service_unit_name.endswith(suffix):
|
if not self.healthcare_service_unit_name.endswith(suffix):
|
||||||
self.name = self.healthcare_service_unit_name + suffix
|
self.name = self.healthcare_service_unit_name + suffix
|
||||||
else:
|
else:
|
||||||
@@ -22,16 +29,86 @@ class HealthcareServiceUnit(NestedSet):
|
|||||||
super(HealthcareServiceUnit, self).on_update()
|
super(HealthcareServiceUnit, self).on_update()
|
||||||
self.validate_one_root()
|
self.validate_one_root()
|
||||||
|
|
||||||
def after_insert(self):
|
def set_service_unit_properties(self):
|
||||||
if self.is_group:
|
if self.is_group:
|
||||||
self.allow_appointments = 0
|
self.allow_appointments = False
|
||||||
self.overlap_appointments = 0
|
self.overlap_appointments = False
|
||||||
self.inpatient_occupancy = 0
|
self.inpatient_occupancy = False
|
||||||
elif self.service_unit_type:
|
self.service_unit_capacity = 0
|
||||||
|
self.occupancy_status = ''
|
||||||
|
self.service_unit_type = ''
|
||||||
|
elif self.service_unit_type != '':
|
||||||
service_unit_type = frappe.get_doc('Healthcare Service Unit Type', self.service_unit_type)
|
service_unit_type = frappe.get_doc('Healthcare Service Unit Type', self.service_unit_type)
|
||||||
self.allow_appointments = service_unit_type.allow_appointments
|
self.allow_appointments = service_unit_type.allow_appointments
|
||||||
self.overlap_appointments = service_unit_type.overlap_appointments
|
|
||||||
self.inpatient_occupancy = service_unit_type.inpatient_occupancy
|
self.inpatient_occupancy = service_unit_type.inpatient_occupancy
|
||||||
if self.inpatient_occupancy:
|
|
||||||
|
if self.inpatient_occupancy and self.occupancy_status != '':
|
||||||
self.occupancy_status = 'Vacant'
|
self.occupancy_status = 'Vacant'
|
||||||
self.overlap_appointments = 0
|
|
||||||
|
if service_unit_type.overlap_appointments:
|
||||||
|
self.overlap_appointments = True
|
||||||
|
else:
|
||||||
|
self.overlap_appointments = False
|
||||||
|
self.service_unit_capacity = 0
|
||||||
|
|
||||||
|
if self.overlap_appointments:
|
||||||
|
if not self.service_unit_capacity:
|
||||||
|
frappe.throw(_('Please set a valid Service Unit Capacity to enable Overlapping Appointments'),
|
||||||
|
title=_('Mandatory'))
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def add_multiple_service_units(parent, data):
|
||||||
|
'''
|
||||||
|
parent - parent service unit under which the service units are to be created
|
||||||
|
data (dict) - company, healthcare_service_unit_name, count, service_unit_type, warehouse, service_unit_capacity
|
||||||
|
'''
|
||||||
|
if not parent or not data:
|
||||||
|
return
|
||||||
|
|
||||||
|
data = json.loads(data)
|
||||||
|
company = data.get('company') or \
|
||||||
|
frappe.defaults.get_defaults().get('company') or \
|
||||||
|
frappe.db.get_single_value('Global Defaults', 'default_company')
|
||||||
|
|
||||||
|
if not data.get('healthcare_service_unit_name') or not company:
|
||||||
|
frappe.throw(_('Service Unit Name and Company are mandatory to create Healthcare Service Units'),
|
||||||
|
title=_('Missing Required Fields'))
|
||||||
|
|
||||||
|
count = cint(data.get('count') or 0)
|
||||||
|
if count <= 0:
|
||||||
|
frappe.throw(_('Number of Service Units to be created should at least be 1'),
|
||||||
|
title=_('Invalid Number of Service Units'))
|
||||||
|
|
||||||
|
capacity = cint(data.get('service_unit_capacity') or 1)
|
||||||
|
|
||||||
|
service_unit = {
|
||||||
|
'doctype': 'Healthcare Service Unit',
|
||||||
|
'parent_healthcare_service_unit': parent,
|
||||||
|
'service_unit_type': data.get('service_unit_type') or None,
|
||||||
|
'service_unit_capacity': capacity if capacity > 0 else 1,
|
||||||
|
'warehouse': data.get('warehouse') or None,
|
||||||
|
'company': company
|
||||||
|
}
|
||||||
|
|
||||||
|
service_unit_name = '{}'.format(data.get('healthcare_service_unit_name').strip(' -'))
|
||||||
|
|
||||||
|
last_suffix = frappe.db.sql("""SELECT
|
||||||
|
IFNULL(MAX(CAST(SUBSTRING(name FROM %(start)s FOR 4) AS UNSIGNED)), 0)
|
||||||
|
FROM `tabHealthcare Service Unit`
|
||||||
|
WHERE name like %(prefix)s AND company=%(company)s""",
|
||||||
|
{'start': len(service_unit_name)+2, 'prefix': '{}-%'.format(service_unit_name), 'company': company},
|
||||||
|
as_list=1)[0][0]
|
||||||
|
start_suffix = cint(last_suffix) + 1
|
||||||
|
|
||||||
|
failed_list = []
|
||||||
|
for i in range(start_suffix, count + start_suffix):
|
||||||
|
# name to be in the form WARD-####
|
||||||
|
service_unit['healthcare_service_unit_name'] = '{}-{}'.format(service_unit_name, cstr('%0*d' % (4, i)))
|
||||||
|
service_unit_doc = frappe.get_doc(service_unit)
|
||||||
|
try:
|
||||||
|
service_unit_doc.insert()
|
||||||
|
except Exception:
|
||||||
|
failed_list.append(service_unit['healthcare_service_unit_name'])
|
||||||
|
|
||||||
|
return failed_list
|
||||||
|
|||||||
@@ -1,35 +1,185 @@
|
|||||||
frappe.treeview_settings["Healthcare Service Unit"] = {
|
frappe.provide("frappe.treeview_settings");
|
||||||
breadcrumbs: "Healthcare Service Unit",
|
|
||||||
title: __("Healthcare Service Unit"),
|
frappe.treeview_settings['Healthcare Service Unit'] = {
|
||||||
|
breadcrumbs: 'Healthcare Service Unit',
|
||||||
|
title: __('Service Unit Tree'),
|
||||||
get_tree_root: false,
|
get_tree_root: false,
|
||||||
filters: [{
|
|
||||||
fieldname: "company",
|
|
||||||
fieldtype: "Select",
|
|
||||||
options: erpnext.utils.get_tree_options("company"),
|
|
||||||
label: __("Company"),
|
|
||||||
default: erpnext.utils.get_tree_default("company")
|
|
||||||
}],
|
|
||||||
get_tree_nodes: 'erpnext.healthcare.utils.get_children',
|
get_tree_nodes: 'erpnext.healthcare.utils.get_children',
|
||||||
ignore_fields:["parent_healthcare_service_unit"],
|
filters: [{
|
||||||
onrender: function(node) {
|
fieldname: 'company',
|
||||||
if (node.data.occupied_out_of_vacant!==undefined) {
|
fieldtype: 'Select',
|
||||||
$('<span class="balance-area pull-right">'
|
options: erpnext.utils.get_tree_options('company'),
|
||||||
+ " " + node.data.occupied_out_of_vacant
|
label: __('Company'),
|
||||||
|
default: erpnext.utils.get_tree_default('company')
|
||||||
|
}],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
fieldtype: 'Data', fieldname: 'healthcare_service_unit_name', label: __('New Service Unit Name'),
|
||||||
|
reqd: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'Check', fieldname: 'is_group', label: __('Is Group'),
|
||||||
|
description: __("Child nodes can be only created under 'Group' type nodes")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'Link', fieldname: 'service_unit_type', label: __('Service Unit Type'),
|
||||||
|
options: 'Healthcare Service Unit Type', description: __('Type of the new Service Unit'),
|
||||||
|
depends_on: 'eval:!doc.is_group', default: '',
|
||||||
|
onchange: () => {
|
||||||
|
if (cur_dialog) {
|
||||||
|
if (cur_dialog.fields_dict.service_unit_type.value) {
|
||||||
|
frappe.db.get_value('Healthcare Service Unit Type',
|
||||||
|
cur_dialog.fields_dict.service_unit_type.value, 'overlap_appointments')
|
||||||
|
.then(r => {
|
||||||
|
if (r.message.overlap_appointments) {
|
||||||
|
cur_dialog.set_df_property('service_unit_capacity', 'hidden', false);
|
||||||
|
cur_dialog.set_df_property('service_unit_capacity', 'reqd', true);
|
||||||
|
} else {
|
||||||
|
cur_dialog.set_df_property('service_unit_capacity', 'hidden', true);
|
||||||
|
cur_dialog.set_df_property('service_unit_capacity', 'reqd', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cur_dialog.set_df_property('service_unit_capacity', 'hidden', true);
|
||||||
|
cur_dialog.set_df_property('service_unit_capacity', 'reqd', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'Int', fieldname: 'service_unit_capacity', label: __('Service Unit Capacity'),
|
||||||
|
description: __('Sets the number of concurrent appointments allowed'), reqd: false,
|
||||||
|
depends_on: "eval:!doc.is_group && doc.service_unit_type != ''", hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'Link', fieldname: 'warehouse', label: __('Warehouse'), options: 'Warehouse',
|
||||||
|
description: __('Optional, if you want to manage stock separately for this Service Unit'),
|
||||||
|
depends_on: 'eval:!doc.is_group'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'Link', fieldname: 'company', label: __('Company'), options: 'Company', reqd: true,
|
||||||
|
default: () => {
|
||||||
|
return cur_page.page.page.fields_dict.company.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
ignore_fields: ['parent_healthcare_service_unit'],
|
||||||
|
onrender: function (node) {
|
||||||
|
if (node.data.occupied_of_available !== undefined) {
|
||||||
|
$("<span class='balance-area pull-right text-muted small'>"
|
||||||
|
+ ' ' + node.data.occupied_of_available
|
||||||
+ '</span>').insertBefore(node.$ul);
|
+ '</span>').insertBefore(node.$ul);
|
||||||
}
|
}
|
||||||
if (node.data && node.data.inpatient_occupancy!==undefined) {
|
if (node.data && node.data.inpatient_occupancy !== undefined) {
|
||||||
if (node.data.inpatient_occupancy == 1) {
|
if (node.data.inpatient_occupancy == 1) {
|
||||||
if (node.data.occupancy_status == "Occupied") {
|
if (node.data.occupancy_status == 'Occupied') {
|
||||||
$('<span class="balance-area pull-right">'
|
$("<span class='balance-area pull-right small'>"
|
||||||
+ " " + node.data.occupancy_status
|
+ ' ' + node.data.occupancy_status
|
||||||
+ '</span>').insertBefore(node.$ul);
|
+ '</span>').insertBefore(node.$ul);
|
||||||
}
|
}
|
||||||
if (node.data.occupancy_status == "Vacant") {
|
if (node.data.occupancy_status == 'Vacant') {
|
||||||
$('<span class="balance-area pull-right">'
|
$("<span class='balance-area pull-right text-muted small'>"
|
||||||
+ " " + node.data.occupancy_status
|
+ ' ' + node.data.occupancy_status
|
||||||
+ '</span>').insertBefore(node.$ul);
|
+ '</span>').insertBefore(node.$ul);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
post_render: function (treeview) {
|
||||||
|
frappe.treeview_settings['Healthcare Service Unit'].treeview = {};
|
||||||
|
$.extend(frappe.treeview_settings['Healthcare Service Unit'].treeview, treeview);
|
||||||
|
},
|
||||||
|
toolbar: [
|
||||||
|
{
|
||||||
|
label: __('Add Multiple'),
|
||||||
|
condition: function (node) {
|
||||||
|
return node.expandable;
|
||||||
|
},
|
||||||
|
click: function (node) {
|
||||||
|
const dialog = new frappe.ui.Dialog({
|
||||||
|
title: __('Add Multiple Service Units'),
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
fieldtype: 'Data', fieldname: 'healthcare_service_unit_name', label: __('Service Unit Name'),
|
||||||
|
reqd: true, description: __("Will be serially suffixed to maintain uniquness. Example: 'Ward' will be named as 'Ward-####'"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'Int', fieldname: 'count', label: __('Number of Service Units'),
|
||||||
|
reqd: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'Link', fieldname: 'service_unit_type', label: __('Service Unit Type'),
|
||||||
|
options: 'Healthcare Service Unit Type', description: __('Type of the new Service Unit'),
|
||||||
|
depends_on: 'eval:!doc.is_group', default: '', reqd: true,
|
||||||
|
onchange: () => {
|
||||||
|
if (cur_dialog) {
|
||||||
|
if (cur_dialog.fields_dict.service_unit_type.value) {
|
||||||
|
frappe.db.get_value('Healthcare Service Unit Type',
|
||||||
|
cur_dialog.fields_dict.service_unit_type.value, 'overlap_appointments')
|
||||||
|
.then(r => {
|
||||||
|
if (r.message.overlap_appointments) {
|
||||||
|
cur_dialog.set_df_property('service_unit_capacity', 'hidden', false);
|
||||||
|
cur_dialog.set_df_property('service_unit_capacity', 'reqd', true);
|
||||||
|
} else {
|
||||||
|
cur_dialog.set_df_property('service_unit_capacity', 'hidden', true);
|
||||||
|
cur_dialog.set_df_property('service_unit_capacity', 'reqd', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cur_dialog.set_df_property('service_unit_capacity', 'hidden', true);
|
||||||
|
cur_dialog.set_df_property('service_unit_capacity', 'reqd', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'Int', fieldname: 'service_unit_capacity', label: __('Service Unit Capacity'),
|
||||||
|
description: __('Sets the number of concurrent appointments allowed'), reqd: false,
|
||||||
|
depends_on: "eval:!doc.is_group && doc.service_unit_type != ''", hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'Link', fieldname: 'warehouse', label: __('Warehouse'), options: 'Warehouse',
|
||||||
|
description: __('Optional, if you want to manage stock separately for this Service Unit'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'Link', fieldname: 'company', label: __('Company'), options: 'Company', reqd: true,
|
||||||
|
default: () => {
|
||||||
|
return cur_page.page.page.fields_dict.company.get_value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
primary_action: () => {
|
||||||
|
dialog.hide();
|
||||||
|
let vals = dialog.get_values();
|
||||||
|
if (!vals) return;
|
||||||
|
|
||||||
|
return frappe.call({
|
||||||
|
method: 'erpnext.healthcare.doctype.healthcare_service_unit.healthcare_service_unit.add_multiple_service_units',
|
||||||
|
args: {
|
||||||
|
parent: node.data.value,
|
||||||
|
data: vals
|
||||||
|
},
|
||||||
|
callback: function (r) {
|
||||||
|
if (!r.exc && r.message) {
|
||||||
|
frappe.treeview_settings['Healthcare Service Unit'].treeview.tree.load_children(node, true);
|
||||||
|
|
||||||
|
frappe.show_alert({
|
||||||
|
message: __('{0} Service Units created', [vals.count - r.message.length]),
|
||||||
|
indicator: 'green'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
frappe.msgprint(__('Could not create Service Units'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
freeze: true,
|
||||||
|
freeze_message: __('Creating {0} Service Units', [vals.count])
|
||||||
|
});
|
||||||
|
},
|
||||||
|
primary_action_label: __('Create')
|
||||||
|
});
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
extend_toolbar: true
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ let change_item_code = function(frm, doc) {
|
|||||||
if (values) {
|
if (values) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
"method": "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.change_item_code",
|
"method": "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.change_item_code",
|
||||||
"args": {item: doc.item, item_code: values['item_code'], doc_name: doc.name},
|
"args": { item: doc.item, item_code: values['item_code'], doc_name: doc.name },
|
||||||
callback: function () {
|
callback: function() {
|
||||||
frm.reload_doc();
|
frm.reload_doc();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "service_unit_type",
|
"fieldname": "service_unit_type",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Service Unit Type",
|
"label": "Service Unit Type",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
@@ -41,6 +43,8 @@
|
|||||||
"depends_on": "eval:doc.inpatient_occupancy != 1",
|
"depends_on": "eval:doc.inpatient_occupancy != 1",
|
||||||
"fieldname": "allow_appointments",
|
"fieldname": "allow_appointments",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Allow Appointments"
|
"label": "Allow Appointments"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -49,6 +53,8 @@
|
|||||||
"depends_on": "eval:doc.allow_appointments == 1 && doc.inpatient_occupany != 1",
|
"depends_on": "eval:doc.allow_appointments == 1 && doc.inpatient_occupany != 1",
|
||||||
"fieldname": "overlap_appointments",
|
"fieldname": "overlap_appointments",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Allow Overlap"
|
"label": "Allow Overlap"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -57,6 +63,8 @@
|
|||||||
"depends_on": "eval:doc.allow_appointments != 1",
|
"depends_on": "eval:doc.allow_appointments != 1",
|
||||||
"fieldname": "inpatient_occupancy",
|
"fieldname": "inpatient_occupancy",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Inpatient Occupancy"
|
"label": "Inpatient Occupancy"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -65,17 +73,23 @@
|
|||||||
"depends_on": "eval:doc.inpatient_occupancy == 1 && doc.allow_appointments != 1",
|
"depends_on": "eval:doc.inpatient_occupancy == 1 && doc.allow_appointments != 1",
|
||||||
"fieldname": "is_billable",
|
"fieldname": "is_billable",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Is Billable"
|
"label": "Is Billable"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "is_billable",
|
"depends_on": "is_billable",
|
||||||
"fieldname": "item_details",
|
"fieldname": "item_details",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Item Details"
|
"label": "Item Details"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "item",
|
"fieldname": "item",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Item",
|
"label": "Item",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Item",
|
"options": "Item",
|
||||||
@@ -84,6 +98,8 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "item_code",
|
"fieldname": "item_code",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Item Code",
|
"label": "Item Code",
|
||||||
"mandatory_depends_on": "eval: doc.is_billable == 1",
|
"mandatory_depends_on": "eval: doc.is_billable == 1",
|
||||||
"no_copy": 1
|
"no_copy": 1
|
||||||
@@ -91,6 +107,8 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "item_group",
|
"fieldname": "item_group",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Item Group",
|
"label": "Item Group",
|
||||||
"mandatory_depends_on": "eval: doc.is_billable == 1",
|
"mandatory_depends_on": "eval: doc.is_billable == 1",
|
||||||
"options": "Item Group"
|
"options": "Item Group"
|
||||||
@@ -98,6 +116,8 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "uom",
|
"fieldname": "uom",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "UOM",
|
"label": "UOM",
|
||||||
"mandatory_depends_on": "eval: doc.is_billable == 1",
|
"mandatory_depends_on": "eval: doc.is_billable == 1",
|
||||||
"options": "UOM"
|
"options": "UOM"
|
||||||
@@ -105,28 +125,38 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "no_of_hours",
|
"fieldname": "no_of_hours",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "UOM Conversion in Hours",
|
"label": "UOM Conversion in Hours",
|
||||||
"mandatory_depends_on": "eval: doc.is_billable == 1"
|
"mandatory_depends_on": "eval: doc.is_billable == 1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_11",
|
"fieldname": "column_break_11",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "rate",
|
"fieldname": "rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Rate / UOM"
|
"label": "Rate / UOM"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "disabled",
|
"fieldname": "disabled",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Disabled",
|
"label": "Disabled",
|
||||||
"no_copy": 1
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Description"
|
"label": "Description"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -134,11 +164,13 @@
|
|||||||
"fieldname": "change_in_item",
|
"fieldname": "change_in_item",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"label": "Change in Item"
|
"label": "Change in Item"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-20 15:31:09.627516",
|
"modified": "2021-08-19 17:52:30.266667",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Healthcare",
|
"module": "Healthcare",
|
||||||
"name": "Healthcare Service Unit Type",
|
"name": "Healthcare Service Unit Type",
|
||||||
|
|||||||
@@ -140,4 +140,3 @@ def create_ipme(filters, update_stock=0):
|
|||||||
ipme = ipme.get_medication_orders()
|
ipme = ipme.get_medication_orders()
|
||||||
|
|
||||||
return ipme
|
return ipme
|
||||||
|
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ def get_healthcare_service_unit(unit_name=None):
|
|||||||
|
|
||||||
if not service_unit:
|
if not service_unit:
|
||||||
service_unit = frappe.new_doc("Healthcare Service Unit")
|
service_unit = frappe.new_doc("Healthcare Service Unit")
|
||||||
service_unit.healthcare_service_unit_name = unit_name or "Test Service Unit Ip Occupancy"
|
service_unit.healthcare_service_unit_name = unit_name or "_Test Service Unit Ip Occupancy"
|
||||||
service_unit.company = "_Test Company"
|
service_unit.company = "_Test Company"
|
||||||
service_unit.service_unit_type = get_service_unit_type()
|
service_unit.service_unit_type = get_service_unit_type()
|
||||||
service_unit.inpatient_occupancy = 1
|
service_unit.inpatient_occupancy = 1
|
||||||
@@ -159,12 +159,12 @@ def get_healthcare_service_unit(unit_name=None):
|
|||||||
service_unit.is_group = 0
|
service_unit.is_group = 0
|
||||||
service_unit_parent_name = frappe.db.exists({
|
service_unit_parent_name = frappe.db.exists({
|
||||||
"doctype": "Healthcare Service Unit",
|
"doctype": "Healthcare Service Unit",
|
||||||
"healthcare_service_unit_name": "All Healthcare Service Units",
|
"healthcare_service_unit_name": "_Test All Healthcare Service Units",
|
||||||
"is_group": 1
|
"is_group": 1
|
||||||
})
|
})
|
||||||
if not service_unit_parent_name:
|
if not service_unit_parent_name:
|
||||||
parent_service_unit = frappe.new_doc("Healthcare Service Unit")
|
parent_service_unit = frappe.new_doc("Healthcare Service Unit")
|
||||||
parent_service_unit.healthcare_service_unit_name = "All Healthcare Service Units"
|
parent_service_unit.healthcare_service_unit_name = "_Test All Healthcare Service Units"
|
||||||
parent_service_unit.is_group = 1
|
parent_service_unit.is_group = 1
|
||||||
parent_service_unit.save(ignore_permissions = True)
|
parent_service_unit.save(ignore_permissions = True)
|
||||||
service_unit.parent_healthcare_service_unit = parent_service_unit.name
|
service_unit.parent_healthcare_service_unit = parent_service_unit.name
|
||||||
@@ -180,7 +180,7 @@ def get_service_unit_type():
|
|||||||
|
|
||||||
if not service_unit_type:
|
if not service_unit_type:
|
||||||
service_unit_type = frappe.new_doc("Healthcare Service Unit Type")
|
service_unit_type = frappe.new_doc("Healthcare Service Unit Type")
|
||||||
service_unit_type.service_unit_type = "Test Service Unit Type Ip Occupancy"
|
service_unit_type.service_unit_type = "_Test Service Unit Type Ip Occupancy"
|
||||||
service_unit_type.inpatient_occupancy = 1
|
service_unit_type.inpatient_occupancy = 1
|
||||||
service_unit_type.save(ignore_permissions = True)
|
service_unit_type.save(ignore_permissions = True)
|
||||||
return service_unit_type.name
|
return service_unit_type.name
|
||||||
|
|||||||
@@ -26,31 +26,39 @@ frappe.ui.form.on('Patient', {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.patient_name && frappe.user.has_role('Physician')) {
|
if (frm.doc.patient_name && frappe.user.has_role('Physician')) {
|
||||||
|
frm.add_custom_button(__('Patient Progress'), function() {
|
||||||
|
frappe.route_options = {'patient': frm.doc.name};
|
||||||
|
frappe.set_route('patient-progress');
|
||||||
|
}, __('View'));
|
||||||
|
|
||||||
frm.add_custom_button(__('Patient History'), function() {
|
frm.add_custom_button(__('Patient History'), function() {
|
||||||
frappe.route_options = {'patient': frm.doc.name};
|
frappe.route_options = {'patient': frm.doc.name};
|
||||||
frappe.set_route('patient_history');
|
frappe.set_route('patient_history');
|
||||||
},'View');
|
}, __('View'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!frm.doc.__islocal && (frappe.user.has_role('Nursing User') || frappe.user.has_role('Physician'))) {
|
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Patient'};
|
||||||
frm.add_custom_button(__('Vital Signs'), function () {
|
frm.toggle_display(['address_html', 'contact_html'], !frm.is_new());
|
||||||
create_vital_signs(frm);
|
|
||||||
}, 'Create');
|
if (!frm.is_new()) {
|
||||||
frm.add_custom_button(__('Medical Record'), function () {
|
if ((frappe.user.has_role('Nursing User') || frappe.user.has_role('Physician'))) {
|
||||||
create_medical_record(frm);
|
frm.add_custom_button(__('Medical Record'), function () {
|
||||||
}, 'Create');
|
create_medical_record(frm);
|
||||||
frm.add_custom_button(__('Patient Encounter'), function () {
|
}, 'Create');
|
||||||
create_encounter(frm);
|
frm.toggle_enable(['customer'], 0);
|
||||||
}, 'Create');
|
}
|
||||||
frm.toggle_enable(['customer'], 0); // ToDo, allow change only if no transactions booked or better, add merge option
|
frappe.contacts.render_address_and_contact(frm);
|
||||||
|
erpnext.utils.set_party_dashboard_indicators(frm);
|
||||||
|
} else {
|
||||||
|
frappe.contacts.clear_address_and_contact(frm);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: function (frm) {
|
onload: function (frm) {
|
||||||
if (!frm.doc.dob) {
|
|
||||||
$(frm.fields_dict['age_html'].wrapper).html('');
|
|
||||||
}
|
|
||||||
if (frm.doc.dob) {
|
if (frm.doc.dob) {
|
||||||
$(frm.fields_dict['age_html'].wrapper).html(`${__('AGE')} : ${get_age(frm.doc.dob)}`);
|
$(frm.fields_dict['age_html'].wrapper).html(`${__('AGE')} : ${get_age(frm.doc.dob)}`);
|
||||||
|
} else {
|
||||||
|
$(frm.fields_dict['age_html'].wrapper).html('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -59,16 +67,14 @@ frappe.ui.form.on('Patient', 'dob', function(frm) {
|
|||||||
if (frm.doc.dob) {
|
if (frm.doc.dob) {
|
||||||
let today = new Date();
|
let today = new Date();
|
||||||
let birthDate = new Date(frm.doc.dob);
|
let birthDate = new Date(frm.doc.dob);
|
||||||
if (today < birthDate){
|
if (today < birthDate) {
|
||||||
frappe.msgprint(__('Please select a valid Date'));
|
frappe.msgprint(__('Please select a valid Date'));
|
||||||
frappe.model.set_value(frm.doctype,frm.docname, 'dob', '');
|
frappe.model.set_value(frm.doctype,frm.docname, 'dob', '');
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
let age_str = get_age(frm.doc.dob);
|
let age_str = get_age(frm.doc.dob);
|
||||||
$(frm.fields_dict['age_html'].wrapper).html(`${__('AGE')} : ${age_str}`);
|
$(frm.fields_dict['age_html'].wrapper).html(`${__('AGE')} : ${age_str}`);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$(frm.fields_dict['age_html'].wrapper).html('');
|
$(frm.fields_dict['age_html'].wrapper).html('');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
"allow_copy": 1,
|
"allow_events_in_timeline": 1,
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
@@ -24,12 +24,19 @@
|
|||||||
"image",
|
"image",
|
||||||
"column_break_14",
|
"column_break_14",
|
||||||
"status",
|
"status",
|
||||||
|
"uid",
|
||||||
"inpatient_record",
|
"inpatient_record",
|
||||||
"inpatient_status",
|
"inpatient_status",
|
||||||
"report_preference",
|
"report_preference",
|
||||||
"mobile",
|
"mobile",
|
||||||
"email",
|
|
||||||
"phone",
|
"phone",
|
||||||
|
"email",
|
||||||
|
"invite_user",
|
||||||
|
"user_id",
|
||||||
|
"address_contacts",
|
||||||
|
"address_html",
|
||||||
|
"column_break_22",
|
||||||
|
"contact_html",
|
||||||
"customer_details_section",
|
"customer_details_section",
|
||||||
"customer",
|
"customer",
|
||||||
"customer_group",
|
"customer_group",
|
||||||
@@ -74,6 +81,7 @@
|
|||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_preview": 1,
|
"in_preview": 1,
|
||||||
"label": "Inpatient Status",
|
"label": "Inpatient Status",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "\nAdmission Scheduled\nAdmitted\nDischarge Scheduled",
|
"options": "\nAdmission Scheduled\nAdmitted\nDischarge Scheduled",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@@ -81,6 +89,7 @@
|
|||||||
"fieldname": "inpatient_record",
|
"fieldname": "inpatient_record",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Inpatient Record",
|
"label": "Inpatient Record",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "Inpatient Record",
|
"options": "Inpatient Record",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@@ -101,6 +110,7 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Full Name",
|
"label": "Full Name",
|
||||||
|
"no_copy": 1,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
@@ -118,6 +128,7 @@
|
|||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_preview": 1,
|
"in_preview": 1,
|
||||||
"label": "Blood Group",
|
"label": "Blood Group",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative"
|
"options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -125,7 +136,8 @@
|
|||||||
"fieldname": "dob",
|
"fieldname": "dob",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"in_preview": 1,
|
"in_preview": 1,
|
||||||
"label": "Date of birth"
|
"label": "Date of birth",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "age_html",
|
"fieldname": "age_html",
|
||||||
@@ -167,6 +179,7 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
"label": "Customer",
|
"label": "Customer",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "Customer",
|
"options": "Customer",
|
||||||
"set_only_once": 1
|
"set_only_once": 1
|
||||||
},
|
},
|
||||||
@@ -183,6 +196,7 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Mobile",
|
"label": "Mobile",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "Phone"
|
"options": "Phone"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -192,6 +206,7 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Email",
|
"label": "Email",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "Email"
|
"options": "Email"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -199,6 +214,7 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"label": "Phone",
|
"label": "Phone",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "Phone"
|
"options": "Phone"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -230,7 +246,8 @@
|
|||||||
"fieldname": "medication",
|
"fieldname": "medication",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"ignore_xss_filter": 1,
|
"ignore_xss_filter": 1,
|
||||||
"label": "Medication"
|
"label": "Medication",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_20",
|
"fieldname": "column_break_20",
|
||||||
@@ -240,13 +257,15 @@
|
|||||||
"fieldname": "medical_history",
|
"fieldname": "medical_history",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"ignore_xss_filter": 1,
|
"ignore_xss_filter": 1,
|
||||||
"label": "Medical History"
|
"label": "Medical History",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "surgical_history",
|
"fieldname": "surgical_history",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"ignore_xss_filter": 1,
|
"ignore_xss_filter": 1,
|
||||||
"label": "Surgical History"
|
"label": "Surgical History",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
@@ -258,8 +277,8 @@
|
|||||||
"fieldname": "occupation",
|
"fieldname": "occupation",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"ignore_xss_filter": 1,
|
"ignore_xss_filter": 1,
|
||||||
"in_standard_filter": 1,
|
"label": "Occupation",
|
||||||
"label": "Occupation"
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_25",
|
"fieldname": "column_break_25",
|
||||||
@@ -269,6 +288,7 @@
|
|||||||
"fieldname": "marital_status",
|
"fieldname": "marital_status",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Marital Status",
|
"label": "Marital Status",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "\nSingle\nMarried\nDivorced\nWidow"
|
"options": "\nSingle\nMarried\nDivorced\nWidow"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -281,25 +301,29 @@
|
|||||||
"fieldname": "tobacco_past_use",
|
"fieldname": "tobacco_past_use",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"ignore_xss_filter": 1,
|
"ignore_xss_filter": 1,
|
||||||
"label": "Tobacco Consumption (Past)"
|
"label": "Tobacco Consumption (Past)",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "tobacco_current_use",
|
"fieldname": "tobacco_current_use",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"ignore_xss_filter": 1,
|
"ignore_xss_filter": 1,
|
||||||
"label": "Tobacco Consumption (Present)"
|
"label": "Tobacco Consumption (Present)",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "alcohol_past_use",
|
"fieldname": "alcohol_past_use",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"ignore_xss_filter": 1,
|
"ignore_xss_filter": 1,
|
||||||
"label": "Alcohol Consumption (Past)"
|
"label": "Alcohol Consumption (Past)",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "alcohol_current_use",
|
"fieldname": "alcohol_current_use",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
"label": "Alcohol Consumption (Present)"
|
"label": "Alcohol Consumption (Present)",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_32",
|
"fieldname": "column_break_32",
|
||||||
@@ -309,13 +333,15 @@
|
|||||||
"fieldname": "surrounding_factors",
|
"fieldname": "surrounding_factors",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"ignore_xss_filter": 1,
|
"ignore_xss_filter": 1,
|
||||||
"label": "Occupational Hazards and Environmental Factors"
|
"label": "Occupational Hazards and Environmental Factors",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "other_risk_factors",
|
"fieldname": "other_risk_factors",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"ignore_xss_filter": 1,
|
"ignore_xss_filter": 1,
|
||||||
"label": "Other Risk Factors"
|
"label": "Other Risk Factors",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
@@ -331,7 +357,8 @@
|
|||||||
"fieldname": "patient_details",
|
"fieldname": "patient_details",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Text",
|
||||||
"ignore_xss_filter": 1,
|
"ignore_xss_filter": 1,
|
||||||
"label": "Patient Details"
|
"label": "Patient Details",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "default_currency",
|
"fieldname": "default_currency",
|
||||||
@@ -342,19 +369,22 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "last_name",
|
"fieldname": "last_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Last Name"
|
"label": "Last Name",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "first_name",
|
"fieldname": "first_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "First Name",
|
"label": "First Name",
|
||||||
|
"no_copy": 1,
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "middle_name",
|
"fieldname": "middle_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Middle Name (optional)"
|
"label": "Middle Name (optional)",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
@@ -389,13 +419,63 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Print Language",
|
"label": "Print Language",
|
||||||
"options": "Language"
|
"options": "Language"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:!doc.__islocal",
|
||||||
|
"fieldname": "address_contacts",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Address and Contact",
|
||||||
|
"options": "fa fa-map-marker"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "address_html",
|
||||||
|
"fieldtype": "HTML",
|
||||||
|
"label": "Address HTML",
|
||||||
|
"no_copy": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_22",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "contact_html",
|
||||||
|
"fieldtype": "HTML",
|
||||||
|
"label": "Contact HTML",
|
||||||
|
"no_copy": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_in_quick_entry": 1,
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "invite_user",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Invite as User",
|
||||||
|
"no_copy": 1,
|
||||||
|
"read_only_depends_on": "eval: doc.user_id"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "user_id",
|
||||||
|
"fieldtype": "Read Only",
|
||||||
|
"label": "User ID",
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_in_quick_entry": 1,
|
||||||
|
"bold": 1,
|
||||||
|
"fieldname": "uid",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_standard_filter": 1,
|
||||||
|
"label": "Identification Number (UID)",
|
||||||
|
"unique": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-user",
|
"icon": "fa fa-user",
|
||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"links": [],
|
"links": [],
|
||||||
"max_attachments": 50,
|
"max_attachments": 50,
|
||||||
"modified": "2020-04-25 17:24:32.146415",
|
"modified": "2021-03-14 13:21:09.759906",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Healthcare",
|
"module": "Healthcare",
|
||||||
"name": "Patient",
|
"name": "Patient",
|
||||||
@@ -453,7 +533,7 @@
|
|||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"restrict_to_domain": "Healthcare",
|
"restrict_to_domain": "Healthcare",
|
||||||
"search_fields": "patient_name,mobile,email,phone",
|
"search_fields": "patient_name,mobile,email,phone,uid",
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "ASC",
|
"sort_order": "ASC",
|
||||||
|
|||||||
@@ -8,24 +8,27 @@ from frappe import _
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import cint, cstr, getdate
|
from frappe.utils import cint, cstr, getdate
|
||||||
import dateutil
|
import dateutil
|
||||||
|
from frappe.contacts.address_and_contact import load_address_and_contact
|
||||||
|
from frappe.contacts.doctype.contact.contact import get_default_contact
|
||||||
from frappe.model.naming import set_name_by_naming_series
|
from frappe.model.naming import set_name_by_naming_series
|
||||||
from frappe.utils.nestedset import get_root_of
|
from frappe.utils.nestedset import get_root_of
|
||||||
from erpnext import get_default_currency
|
from erpnext import get_default_currency
|
||||||
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account, send_registration_sms
|
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account, send_registration_sms
|
||||||
|
from erpnext.accounts.party import get_dashboard_info
|
||||||
|
|
||||||
class Patient(Document):
|
class Patient(Document):
|
||||||
|
def onload(self):
|
||||||
|
'''Load address and contacts in `__onload`'''
|
||||||
|
load_address_and_contact(self)
|
||||||
|
self.load_dashboard_info()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.set_full_name()
|
self.set_full_name()
|
||||||
self.add_as_website_user()
|
|
||||||
|
|
||||||
def before_insert(self):
|
def before_insert(self):
|
||||||
self.set_missing_customer_details()
|
self.set_missing_customer_details()
|
||||||
|
|
||||||
def after_insert(self):
|
def after_insert(self):
|
||||||
self.add_as_website_user()
|
|
||||||
self.reload()
|
|
||||||
if frappe.db.get_single_value('Healthcare Settings', 'link_customer_to_patient') and not self.customer:
|
|
||||||
create_customer(self)
|
|
||||||
if frappe.db.get_single_value('Healthcare Settings', 'collect_registration_fee'):
|
if frappe.db.get_single_value('Healthcare Settings', 'collect_registration_fee'):
|
||||||
frappe.db.set_value('Patient', self.name, 'status', 'Disabled')
|
frappe.db.set_value('Patient', self.name, 'status', 'Disabled')
|
||||||
else:
|
else:
|
||||||
@@ -50,6 +53,16 @@ class Patient(Document):
|
|||||||
else:
|
else:
|
||||||
create_customer(self)
|
create_customer(self)
|
||||||
|
|
||||||
|
self.set_contact() # add or update contact
|
||||||
|
|
||||||
|
if not self.user_id and self.email and self.invite_user:
|
||||||
|
self.create_website_user()
|
||||||
|
|
||||||
|
def load_dashboard_info(self):
|
||||||
|
if self.customer:
|
||||||
|
info = get_dashboard_info('Customer', self.customer, None)
|
||||||
|
self.set_onload('dashboard_info', info)
|
||||||
|
|
||||||
def set_full_name(self):
|
def set_full_name(self):
|
||||||
if self.last_name:
|
if self.last_name:
|
||||||
self.patient_name = ' '.join(filter(None, [self.first_name, self.last_name]))
|
self.patient_name = ' '.join(filter(None, [self.first_name, self.last_name]))
|
||||||
@@ -72,18 +85,24 @@ class Patient(Document):
|
|||||||
if not self.language:
|
if not self.language:
|
||||||
self.language = frappe.db.get_single_value('System Settings', 'language')
|
self.language = frappe.db.get_single_value('System Settings', 'language')
|
||||||
|
|
||||||
def add_as_website_user(self):
|
def create_website_user(self):
|
||||||
if self.email:
|
if self.email and not frappe.db.exists('User', self.email):
|
||||||
if not frappe.db.exists ('User', self.email):
|
user = frappe.get_doc({
|
||||||
user = frappe.get_doc({
|
'doctype': 'User',
|
||||||
'doctype': 'User',
|
'first_name': self.first_name,
|
||||||
'first_name': self.first_name,
|
'last_name': self.last_name,
|
||||||
'last_name': self.last_name,
|
'email': self.email,
|
||||||
'email': self.email,
|
'user_type': 'Website User',
|
||||||
'user_type': 'Website User'
|
'gender': self.sex,
|
||||||
})
|
'phone': self.phone,
|
||||||
user.flags.ignore_permissions = True
|
'mobile_no': self.mobile,
|
||||||
user.add_roles('Patient')
|
'birth_date': self.dob
|
||||||
|
})
|
||||||
|
user.flags.ignore_permissions = True
|
||||||
|
user.enabled = True
|
||||||
|
user.send_welcome_email = True
|
||||||
|
user.add_roles('Patient')
|
||||||
|
frappe.db.set_value(self.doctype, self.name, 'user_id', user.name)
|
||||||
|
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
patient_name_by = frappe.db.get_single_value('Healthcare Settings', 'patient_name_by')
|
patient_name_by = frappe.db.get_single_value('Healthcare Settings', 'patient_name_by')
|
||||||
@@ -108,7 +127,8 @@ class Patient(Document):
|
|||||||
if self.dob:
|
if self.dob:
|
||||||
dob = getdate(self.dob)
|
dob = getdate(self.dob)
|
||||||
age = dateutil.relativedelta.relativedelta(getdate(), dob)
|
age = dateutil.relativedelta.relativedelta(getdate(), dob)
|
||||||
age_str = str(age.years) + ' ' + _("Years(s)") + ' ' + str(age.months) + ' ' + _("Month(s)") + ' ' + str(age.days) + ' ' + _("Day(s)")
|
age_str = f'{str(age.years)} {_("Years(s)")} {str(age.months)} {_("Month(s)")} {str(age.days)} {_("Day(s)")}'
|
||||||
|
|
||||||
return age_str
|
return age_str
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -125,6 +145,58 @@ class Patient(Document):
|
|||||||
|
|
||||||
return {'invoice': sales_invoice.name}
|
return {'invoice': sales_invoice.name}
|
||||||
|
|
||||||
|
def set_contact(self):
|
||||||
|
if frappe.db.exists('Dynamic Link', {'parenttype':'Contact', 'link_doctype':'Patient', 'link_name':self.name}):
|
||||||
|
old_doc = self.get_doc_before_save()
|
||||||
|
if old_doc.email != self.email or old_doc.mobile != self.mobile or old_doc.phone != self.phone:
|
||||||
|
self.update_contact()
|
||||||
|
else:
|
||||||
|
self.reload()
|
||||||
|
if self.email or self.mobile or self.phone:
|
||||||
|
contact = frappe.get_doc({
|
||||||
|
'doctype': 'Contact',
|
||||||
|
'first_name': self.first_name,
|
||||||
|
'middle_name': self.middle_name,
|
||||||
|
'last_name': self.last_name,
|
||||||
|
'gender': self.sex,
|
||||||
|
'is_primary_contact': 1
|
||||||
|
})
|
||||||
|
contact.append('links', dict(link_doctype='Patient', link_name=self.name))
|
||||||
|
if self.customer:
|
||||||
|
contact.append('links', dict(link_doctype='Customer', link_name=self.customer))
|
||||||
|
|
||||||
|
contact.insert(ignore_permissions=True)
|
||||||
|
self.update_contact(contact) # update email, mobile and phone
|
||||||
|
|
||||||
|
def update_contact(self, contact=None):
|
||||||
|
if not contact:
|
||||||
|
contact_name = get_default_contact(self.doctype, self.name)
|
||||||
|
if contact_name:
|
||||||
|
contact = frappe.get_doc('Contact', contact_name)
|
||||||
|
|
||||||
|
if contact:
|
||||||
|
if self.email and self.email != contact.email_id:
|
||||||
|
for email in contact.email_ids:
|
||||||
|
email.is_primary = True if email.email_id == self.email else False
|
||||||
|
contact.add_email(self.email, is_primary=True)
|
||||||
|
contact.set_primary_email()
|
||||||
|
|
||||||
|
if self.mobile and self.mobile != contact.mobile_no:
|
||||||
|
for mobile in contact.phone_nos:
|
||||||
|
mobile.is_primary_mobile_no = True if mobile.phone == self.mobile else False
|
||||||
|
contact.add_phone(self.mobile, is_primary_mobile_no=True)
|
||||||
|
contact.set_primary('mobile_no')
|
||||||
|
|
||||||
|
if self.phone and self.phone != contact.phone:
|
||||||
|
for phone in contact.phone_nos:
|
||||||
|
phone.is_primary_phone = True if phone.phone == self.phone else False
|
||||||
|
contact.add_phone(self.phone, is_primary_phone=True)
|
||||||
|
contact.set_primary('phone')
|
||||||
|
|
||||||
|
contact.flags.ignore_validate = True # disable hook TODO: safe?
|
||||||
|
contact.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
def create_customer(doc):
|
def create_customer(doc):
|
||||||
customer = frappe.get_doc({
|
customer = frappe.get_doc({
|
||||||
'doctype': 'Customer',
|
'doctype': 'Customer',
|
||||||
@@ -150,8 +222,8 @@ def make_invoice(patient, company):
|
|||||||
sales_invoice.debit_to = get_receivable_account(company)
|
sales_invoice.debit_to = get_receivable_account(company)
|
||||||
|
|
||||||
item_line = sales_invoice.append('items')
|
item_line = sales_invoice.append('items')
|
||||||
item_line.item_name = 'Registeration Fee'
|
item_line.item_name = 'Registration Fee'
|
||||||
item_line.description = 'Registeration Fee'
|
item_line.description = 'Registration Fee'
|
||||||
item_line.qty = 1
|
item_line.qty = 1
|
||||||
item_line.uom = uom
|
item_line.uom = uom
|
||||||
item_line.conversion_factor = 1
|
item_line.conversion_factor = 1
|
||||||
@@ -175,8 +247,11 @@ def get_patient_detail(patient):
|
|||||||
return details
|
return details
|
||||||
|
|
||||||
def get_timeline_data(doctype, name):
|
def get_timeline_data(doctype, name):
|
||||||
"""Return timeline data from medical records"""
|
'''
|
||||||
return dict(frappe.db.sql('''
|
Return Patient's timeline data from medical records
|
||||||
|
Also include the associated Customer timeline data
|
||||||
|
'''
|
||||||
|
patient_timeline_data = dict(frappe.db.sql('''
|
||||||
SELECT
|
SELECT
|
||||||
unix_timestamp(communication_date), count(*)
|
unix_timestamp(communication_date), count(*)
|
||||||
FROM
|
FROM
|
||||||
@@ -185,3 +260,11 @@ def get_timeline_data(doctype, name):
|
|||||||
patient=%s
|
patient=%s
|
||||||
and `communication_date` > date_sub(curdate(), interval 1 year)
|
and `communication_date` > date_sub(curdate(), interval 1 year)
|
||||||
GROUP BY communication_date''', name))
|
GROUP BY communication_date''', name))
|
||||||
|
|
||||||
|
customer = frappe.db.get_value(doctype, name, 'customer')
|
||||||
|
if customer:
|
||||||
|
from erpnext.accounts.party import get_timeline_data
|
||||||
|
customer_timeline_data = get_timeline_data('Customer', customer)
|
||||||
|
patient_timeline_data.update(customer_timeline_data)
|
||||||
|
|
||||||
|
return patient_timeline_data
|
||||||
|
|||||||
@@ -6,22 +6,33 @@ def get_data():
|
|||||||
'heatmap': True,
|
'heatmap': True,
|
||||||
'heatmap_message': _('This is based on transactions against this Patient. See timeline below for details'),
|
'heatmap_message': _('This is based on transactions against this Patient. See timeline below for details'),
|
||||||
'fieldname': 'patient',
|
'fieldname': 'patient',
|
||||||
|
'non_standard_fieldnames': {
|
||||||
|
'Payment Entry': 'party'
|
||||||
|
},
|
||||||
'transactions': [
|
'transactions': [
|
||||||
{
|
{
|
||||||
'label': _('Appointments and Patient Encounters'),
|
'label': _('Appointments and Encounters'),
|
||||||
'items': ['Patient Appointment', 'Patient Encounter']
|
'items': ['Patient Appointment', 'Vital Signs', 'Patient Encounter']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Lab Tests and Vital Signs'),
|
'label': _('Lab Tests and Vital Signs'),
|
||||||
'items': ['Lab Test', 'Sample Collection', 'Vital Signs']
|
'items': ['Lab Test', 'Sample Collection']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Billing'),
|
'label': _('Rehab and Physiotherapy'),
|
||||||
'items': ['Sales Invoice']
|
'items': ['Patient Assessment', 'Therapy Session', 'Therapy Plan']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Orders'),
|
'label': _('Surgery'),
|
||||||
'items': ['Inpatient Medication Order']
|
'items': ['Clinical Procedure']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _('Admissions'),
|
||||||
|
'items': ['Inpatient Record', 'Inpatient Medication Order']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _('Billing and Payments'),
|
||||||
|
'items': ['Sales Invoice', 'Payment Entry']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ frappe.ui.form.on('Patient Appointment', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frm.set_query('patient', function () {
|
frm.set_query('patient', function() {
|
||||||
return {
|
return {
|
||||||
filters: {'status': 'Active'}
|
filters: { 'status': 'Active' }
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ frappe.ui.form.on('Patient Appointment', {
|
|||||||
} else {
|
} else {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.check_payment_fields_reqd',
|
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.check_payment_fields_reqd',
|
||||||
args: {'patient': frm.doc.patient},
|
args: { 'patient': frm.doc.patient },
|
||||||
callback: function(data) {
|
callback: function(data) {
|
||||||
if (data.message == true) {
|
if (data.message == true) {
|
||||||
if (frm.doc.mode_of_payment && frm.doc.paid_amount) {
|
if (frm.doc.mode_of_payment && frm.doc.paid_amount) {
|
||||||
@@ -97,7 +97,7 @@ frappe.ui.form.on('Patient Appointment', {
|
|||||||
|
|
||||||
if (frm.doc.patient) {
|
if (frm.doc.patient) {
|
||||||
frm.add_custom_button(__('Patient History'), function() {
|
frm.add_custom_button(__('Patient History'), function() {
|
||||||
frappe.route_options = {'patient': frm.doc.patient};
|
frappe.route_options = { 'patient': frm.doc.patient };
|
||||||
frappe.set_route('patient_history');
|
frappe.set_route('patient_history');
|
||||||
}, __('View'));
|
}, __('View'));
|
||||||
}
|
}
|
||||||
@@ -111,14 +111,14 @@ frappe.ui.form.on('Patient Appointment', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (frm.doc.procedure_template) {
|
if (frm.doc.procedure_template) {
|
||||||
frm.add_custom_button(__('Clinical Procedure'), function(){
|
frm.add_custom_button(__('Clinical Procedure'), function() {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: 'erpnext.healthcare.doctype.clinical_procedure.clinical_procedure.make_procedure',
|
method: 'erpnext.healthcare.doctype.clinical_procedure.clinical_procedure.make_procedure',
|
||||||
frm: frm,
|
frm: frm,
|
||||||
});
|
});
|
||||||
}, __('Create'));
|
}, __('Create'));
|
||||||
} else if (frm.doc.therapy_type) {
|
} else if (frm.doc.therapy_type) {
|
||||||
frm.add_custom_button(__('Therapy Session'),function(){
|
frm.add_custom_button(__('Therapy Session'), function() {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: 'erpnext.healthcare.doctype.therapy_session.therapy_session.create_therapy_session',
|
method: 'erpnext.healthcare.doctype.therapy_session.therapy_session.create_therapy_session',
|
||||||
frm: frm,
|
frm: frm,
|
||||||
@@ -148,7 +148,7 @@ frappe.ui.form.on('Patient Appointment', {
|
|||||||
doctype: 'Patient',
|
doctype: 'Patient',
|
||||||
name: frm.doc.patient
|
name: frm.doc.patient
|
||||||
},
|
},
|
||||||
callback: function (data) {
|
callback: function(data) {
|
||||||
let age = null;
|
let age = null;
|
||||||
if (data.message.dob) {
|
if (data.message.dob) {
|
||||||
age = calculate_age(data.message.dob);
|
age = calculate_age(data.message.dob);
|
||||||
@@ -165,7 +165,7 @@ frappe.ui.form.on('Patient Appointment', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
practitioner: function(frm) {
|
practitioner: function(frm) {
|
||||||
if (frm.doc.practitioner ) {
|
if (frm.doc.practitioner) {
|
||||||
frm.events.set_payment_details(frm);
|
frm.events.set_payment_details(frm);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -230,7 +230,7 @@ frappe.ui.form.on('Patient Appointment', {
|
|||||||
toggle_payment_fields: function(frm) {
|
toggle_payment_fields: function(frm) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.check_payment_fields_reqd',
|
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.check_payment_fields_reqd',
|
||||||
args: {'patient': frm.doc.patient},
|
args: { 'patient': frm.doc.patient },
|
||||||
callback: function(data) {
|
callback: function(data) {
|
||||||
if (data.message.fee_validity) {
|
if (data.message.fee_validity) {
|
||||||
// if fee validity exists and automated appointment invoicing is enabled,
|
// if fee validity exists and automated appointment invoicing is enabled,
|
||||||
@@ -247,7 +247,7 @@ frappe.ui.form.on('Patient Appointment', {
|
|||||||
frm.toggle_display('paid_amount', data.message ? 1 : 0);
|
frm.toggle_display('paid_amount', data.message ? 1 : 0);
|
||||||
frm.toggle_display('billing_item', data.message ? 1 : 0);
|
frm.toggle_display('billing_item', data.message ? 1 : 0);
|
||||||
frm.toggle_reqd('mode_of_payment', data.message ? 1 : 0);
|
frm.toggle_reqd('mode_of_payment', data.message ? 1 : 0);
|
||||||
frm.toggle_reqd('paid_amount', data.message ? 1 :0);
|
frm.toggle_reqd('paid_amount', data.message ? 1 : 0);
|
||||||
frm.toggle_reqd('billing_item', data.message ? 1 : 0);
|
frm.toggle_reqd('billing_item', data.message ? 1 : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,7 +258,7 @@ frappe.ui.form.on('Patient Appointment', {
|
|||||||
if (frm.doc.patient) {
|
if (frm.doc.patient) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_prescribed_therapies",
|
method: "erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_prescribed_therapies",
|
||||||
args: {patient: frm.doc.patient},
|
args: { patient: frm.doc.patient },
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if (r.message) {
|
if (r.message) {
|
||||||
show_therapy_types(frm, r.message);
|
show_therapy_types(frm, r.message);
|
||||||
@@ -295,13 +295,13 @@ let check_and_set_availability = function(frm) {
|
|||||||
let d = new frappe.ui.Dialog({
|
let d = new frappe.ui.Dialog({
|
||||||
title: __('Available slots'),
|
title: __('Available slots'),
|
||||||
fields: [
|
fields: [
|
||||||
{ fieldtype: 'Link', options: 'Medical Department', reqd: 1, fieldname: 'department', label: 'Medical Department'},
|
{ fieldtype: 'Link', options: 'Medical Department', reqd: 1, fieldname: 'department', label: 'Medical Department' },
|
||||||
{ fieldtype: 'Column Break'},
|
{ fieldtype: 'Column Break' },
|
||||||
{ fieldtype: 'Link', options: 'Healthcare Practitioner', reqd: 1, fieldname: 'practitioner', label: 'Healthcare Practitioner'},
|
{ fieldtype: 'Link', options: 'Healthcare Practitioner', reqd: 1, fieldname: 'practitioner', label: 'Healthcare Practitioner' },
|
||||||
{ fieldtype: 'Column Break'},
|
{ fieldtype: 'Column Break' },
|
||||||
{ fieldtype: 'Date', reqd: 1, fieldname: 'appointment_date', label: 'Date'},
|
{ fieldtype: 'Date', reqd: 1, fieldname: 'appointment_date', label: 'Date' },
|
||||||
{ fieldtype: 'Section Break'},
|
{ fieldtype: 'Section Break' },
|
||||||
{ fieldtype: 'HTML', fieldname: 'available_slots'}
|
{ fieldtype: 'HTML', fieldname: 'available_slots' }
|
||||||
|
|
||||||
],
|
],
|
||||||
primary_action_label: __('Book'),
|
primary_action_label: __('Book'),
|
||||||
@@ -379,59 +379,22 @@ let check_and_set_availability = function(frm) {
|
|||||||
let $wrapper = d.fields_dict.available_slots.$wrapper;
|
let $wrapper = d.fields_dict.available_slots.$wrapper;
|
||||||
|
|
||||||
// make buttons for each slot
|
// make buttons for each slot
|
||||||
let slot_details = data.slot_details;
|
let slot_html = get_slots(data.slot_details);
|
||||||
let slot_html = '';
|
|
||||||
for (let i = 0; i < slot_details.length; i++) {
|
|
||||||
slot_html = slot_html + `<label>${slot_details[i].slot_name}</label>`;
|
|
||||||
slot_html = slot_html + `<br/>` + slot_details[i].avail_slot.map(slot => {
|
|
||||||
let disabled = '';
|
|
||||||
let start_str = slot.from_time;
|
|
||||||
let slot_start_time = moment(slot.from_time, 'HH:mm:ss');
|
|
||||||
let slot_to_time = moment(slot.to_time, 'HH:mm:ss');
|
|
||||||
let interval = (slot_to_time - slot_start_time)/60000 | 0;
|
|
||||||
// iterate in all booked appointments, update the start time and duration
|
|
||||||
slot_details[i].appointments.forEach(function(booked) {
|
|
||||||
let booked_moment = moment(booked.appointment_time, 'HH:mm:ss');
|
|
||||||
let end_time = booked_moment.clone().add(booked.duration, 'minutes');
|
|
||||||
// Deal with 0 duration appointments
|
|
||||||
if (booked_moment.isSame(slot_start_time) || booked_moment.isBetween(slot_start_time, slot_to_time)) {
|
|
||||||
if(booked.duration == 0){
|
|
||||||
disabled = 'disabled="disabled"';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check for overlaps considering appointment duration
|
|
||||||
if (slot_start_time.isBefore(end_time) && slot_to_time.isAfter(booked_moment)) {
|
|
||||||
// There is an overlap
|
|
||||||
disabled = 'disabled="disabled"';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return `<button class="btn btn-default"
|
|
||||||
data-name=${start_str}
|
|
||||||
data-duration=${interval}
|
|
||||||
data-service-unit="${slot_details[i].service_unit || ''}"
|
|
||||||
style="margin: 0 10px 10px 0; width: 72px;" ${disabled}>
|
|
||||||
${start_str.substring(0, start_str.length - 3)}
|
|
||||||
</button>`;
|
|
||||||
}).join("");
|
|
||||||
slot_html = slot_html + `<br/>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
$wrapper
|
$wrapper
|
||||||
.css('margin-bottom', 0)
|
.css('margin-bottom', 0)
|
||||||
.addClass('text-center')
|
.addClass('text-center')
|
||||||
.html(slot_html);
|
.html(slot_html);
|
||||||
|
|
||||||
// blue button when clicked
|
// highlight button when clicked
|
||||||
$wrapper.on('click', 'button', function() {
|
$wrapper.on('click', 'button', function() {
|
||||||
let $btn = $(this);
|
let $btn = $(this);
|
||||||
$wrapper.find('button').removeClass('btn-primary');
|
$wrapper.find('button').removeClass('btn-outline-primary');
|
||||||
$btn.addClass('btn-primary');
|
$btn.addClass('btn-outline-primary');
|
||||||
selected_slot = $btn.attr('data-name');
|
selected_slot = $btn.attr('data-name');
|
||||||
service_unit = $btn.attr('data-service-unit');
|
service_unit = $btn.attr('data-service-unit');
|
||||||
duration = $btn.attr('data-duration');
|
duration = $btn.attr('data-duration');
|
||||||
// enable dialog action
|
// enable primary action 'Book'
|
||||||
d.get_primary_btn().attr('disabled', null);
|
d.get_primary_btn().attr('disabled', null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -441,19 +404,102 @@ let check_and_set_availability = function(frm) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
freeze: true,
|
freeze: true,
|
||||||
freeze_message: __('Fetching records......')
|
freeze_message: __('Fetching Schedule...')
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
fd.available_slots.html(__('Appointment date and Healthcare Practitioner are Mandatory').bold());
|
fd.available_slots.html(__('Appointment date and Healthcare Practitioner are Mandatory').bold());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function get_slots(slot_details) {
|
||||||
|
let slot_html = '';
|
||||||
|
let appointment_count = 0;
|
||||||
|
let disabled = false;
|
||||||
|
let start_str, slot_start_time, slot_end_time, interval, count, count_class, tool_tip, available_slots;
|
||||||
|
|
||||||
|
slot_details.forEach((slot_info) => {
|
||||||
|
slot_html += `<div class="slot-info">
|
||||||
|
<span> <b> ${__('Practitioner Schedule:')} </b> ${slot_info.slot_name} </span><br>
|
||||||
|
<span> <b> ${__('Service Unit:')} </b> ${slot_info.service_unit} </span>`;
|
||||||
|
|
||||||
|
if (slot_info.service_unit_capacity) {
|
||||||
|
slot_html += `<br><span> <b> ${__('Maximum Capacity:')} </b> ${slot_info.service_unit_capacity} </span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
slot_html += '</div><br><br>';
|
||||||
|
|
||||||
|
slot_html += slot_info.avail_slot.map(slot => {
|
||||||
|
appointment_count = 0;
|
||||||
|
disabled = false;
|
||||||
|
start_str = slot.from_time;
|
||||||
|
slot_start_time = moment(slot.from_time, 'HH:mm:ss');
|
||||||
|
slot_end_time = moment(slot.to_time, 'HH:mm:ss');
|
||||||
|
interval = (slot_end_time - slot_start_time) / 60000 | 0;
|
||||||
|
|
||||||
|
// iterate in all booked appointments, update the start time and duration
|
||||||
|
slot_info.appointments.forEach((booked) => {
|
||||||
|
let booked_moment = moment(booked.appointment_time, 'HH:mm:ss');
|
||||||
|
let end_time = booked_moment.clone().add(booked.duration, 'minutes');
|
||||||
|
|
||||||
|
// Deal with 0 duration appointments
|
||||||
|
if (booked_moment.isSame(slot_start_time) || booked_moment.isBetween(slot_start_time, slot_end_time)) {
|
||||||
|
if (booked.duration == 0) {
|
||||||
|
disabled = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for overlaps considering appointment duration
|
||||||
|
if (slot_info.allow_overlap != 1) {
|
||||||
|
if (slot_start_time.isBefore(end_time) && slot_end_time.isAfter(booked_moment)) {
|
||||||
|
// There is an overlap
|
||||||
|
disabled = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (slot_start_time.isBefore(end_time) && slot_end_time.isAfter(booked_moment)) {
|
||||||
|
appointment_count++;
|
||||||
|
}
|
||||||
|
if (appointment_count >= slot_info.service_unit_capacity) {
|
||||||
|
// There is an overlap
|
||||||
|
disabled = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (slot_info.allow_overlap == 1 && slot_info.service_unit_capacity > 1) {
|
||||||
|
available_slots = slot_info.service_unit_capacity - appointment_count;
|
||||||
|
count = `${(available_slots > 0 ? available_slots : __('Full'))}`;
|
||||||
|
count_class = `${(available_slots > 0 ? 'badge-success' : 'badge-danger')}`;
|
||||||
|
tool_tip =`${available_slots} ${__('slots available for booking')}`;
|
||||||
|
}
|
||||||
|
return `
|
||||||
|
<button class="btn btn-secondary" data-name=${start_str}
|
||||||
|
data-duration=${interval}
|
||||||
|
data-service-unit="${slot_info.service_unit || ''}"
|
||||||
|
style="margin: 0 10px 10px 0; width: auto;" ${disabled ? 'disabled="disabled"' : ""}
|
||||||
|
data-toggle="tooltip" title="${tool_tip}">
|
||||||
|
${start_str.substring(0, start_str.length - 3)}<br>
|
||||||
|
<span class='badge ${count_class}'> ${count} </span>
|
||||||
|
</button>`;
|
||||||
|
}).join("");
|
||||||
|
|
||||||
|
if (slot_info.service_unit_capacity) {
|
||||||
|
slot_html += `<br/><small>${__('Each slot indicates the capacity currently available for booking')}</small>`;
|
||||||
|
}
|
||||||
|
slot_html += `<br/><br/>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
return slot_html;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let get_prescribed_procedure = function(frm) {
|
let get_prescribed_procedure = function(frm) {
|
||||||
if (frm.doc.patient) {
|
if (frm.doc.patient) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_procedure_prescribed',
|
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_procedure_prescribed',
|
||||||
args: {patient: frm.doc.patient},
|
args: { patient: frm.doc.patient },
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if (r.message && r.message.length) {
|
if (r.message && r.message.length) {
|
||||||
show_procedure_templates(frm, r.message);
|
show_procedure_templates(frm, r.message);
|
||||||
@@ -473,7 +519,7 @@ let get_prescribed_procedure = function(frm) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let show_procedure_templates = function(frm, result){
|
let show_procedure_templates = function(frm, result) {
|
||||||
let d = new frappe.ui.Dialog({
|
let d = new frappe.ui.Dialog({
|
||||||
title: __('Prescribed Procedures'),
|
title: __('Prescribed Procedures'),
|
||||||
fields: [
|
fields: [
|
||||||
@@ -493,9 +539,11 @@ let show_procedure_templates = function(frm, result){
|
|||||||
data-encounter="%(encounter)s" data-practitioner="%(practitioner)s"\
|
data-encounter="%(encounter)s" data-practitioner="%(practitioner)s"\
|
||||||
data-date="%(date)s" data-department="%(department)s">\
|
data-date="%(date)s" data-department="%(department)s">\
|
||||||
<button class="btn btn-default btn-xs">Add\
|
<button class="btn btn-default btn-xs">Add\
|
||||||
</button></a></div></div><div class="col-xs-12"><hr/><div/>', {name:y[0], procedure_template: y[1],
|
</button></a></div></div><div class="col-xs-12"><hr/><div/>', {
|
||||||
encounter:y[2], consulting_practitioner:y[3], encounter_date:y[4],
|
name: y[0], procedure_template: y[1],
|
||||||
practitioner:y[5]? y[5]:'', date: y[6]? y[6]:'', department: y[7]? y[7]:''})).appendTo(html_field);
|
encounter: y[2], consulting_practitioner: y[3], encounter_date: y[4],
|
||||||
|
practitioner: y[5] ? y[5] : '', date: y[6] ? y[6] : '', department: y[7] ? y[7] : ''
|
||||||
|
})).appendTo(html_field);
|
||||||
row.find("a").click(function() {
|
row.find("a").click(function() {
|
||||||
frm.doc.procedure_template = $(this).attr('data-procedure-template');
|
frm.doc.procedure_template = $(this).attr('data-procedure-template');
|
||||||
frm.doc.procedure_prescription = $(this).attr('data-name');
|
frm.doc.procedure_prescription = $(this).attr('data-name');
|
||||||
@@ -513,7 +561,7 @@ let show_procedure_templates = function(frm, result){
|
|||||||
});
|
});
|
||||||
if (!result) {
|
if (!result) {
|
||||||
let msg = __('There are no procedure prescribed for ') + frm.doc.patient;
|
let msg = __('There are no procedure prescribed for ') + frm.doc.patient;
|
||||||
$(repl('<div class="col-xs-12" style="padding-top:20px;" >%(msg)s</div></div>', {msg: msg})).appendTo(html_field);
|
$(repl('<div class="col-xs-12" style="padding-top:20px;" >%(msg)s</div></div>', { msg: msg })).appendTo(html_field);
|
||||||
}
|
}
|
||||||
d.show();
|
d.show();
|
||||||
};
|
};
|
||||||
@@ -528,7 +576,7 @@ let show_therapy_types = function(frm, result) {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
var html_field = d.fields_dict.therapy_type.$wrapper;
|
var html_field = d.fields_dict.therapy_type.$wrapper;
|
||||||
$.each(result, function(x, y){
|
$.each(result, function(x, y) {
|
||||||
var row = $(repl('<div class="col-xs-12" style="padding-top:12px; text-align:center;" >\
|
var row = $(repl('<div class="col-xs-12" style="padding-top:12px; text-align:center;" >\
|
||||||
<div class="col-xs-5"> %(encounter)s <br> %(practitioner)s <br> %(date)s </div>\
|
<div class="col-xs-5"> %(encounter)s <br> %(practitioner)s <br> %(date)s </div>\
|
||||||
<div class="col-xs-5"> %(therapy)s </div>\
|
<div class="col-xs-5"> %(therapy)s </div>\
|
||||||
@@ -537,9 +585,11 @@ let show_therapy_types = function(frm, result) {
|
|||||||
data-encounter="%(encounter)s" data-practitioner="%(practitioner)s"\
|
data-encounter="%(encounter)s" data-practitioner="%(practitioner)s"\
|
||||||
data-date="%(date)s" data-department="%(department)s">\
|
data-date="%(date)s" data-department="%(department)s">\
|
||||||
<button class="btn btn-default btn-xs">Add\
|
<button class="btn btn-default btn-xs">Add\
|
||||||
</button></a></div></div><div class="col-xs-12"><hr/><div/>', {therapy:y[0],
|
</button></a></div></div><div class="col-xs-12"><hr/><div/>', {
|
||||||
name: y[1], encounter:y[2], practitioner:y[3], date:y[4],
|
therapy: y[0],
|
||||||
department:y[6]? y[6]:'', therapy_plan:y[5]})).appendTo(html_field);
|
name: y[1], encounter: y[2], practitioner: y[3], date: y[4],
|
||||||
|
department: y[6] ? y[6] : '', therapy_plan: y[5]
|
||||||
|
})).appendTo(html_field);
|
||||||
|
|
||||||
row.find("a").click(function() {
|
row.find("a").click(function() {
|
||||||
frm.doc.therapy_type = $(this).attr("data-therapy");
|
frm.doc.therapy_type = $(this).attr("data-therapy");
|
||||||
@@ -574,13 +624,13 @@ let create_vital_signs = function(frm) {
|
|||||||
frappe.new_doc('Vital Signs');
|
frappe.new_doc('Vital Signs');
|
||||||
};
|
};
|
||||||
|
|
||||||
let update_status = function(frm, status){
|
let update_status = function(frm, status) {
|
||||||
let doc = frm.doc;
|
let doc = frm.doc;
|
||||||
frappe.confirm(__('Are you sure you want to cancel this appointment?'),
|
frappe.confirm(__('Are you sure you want to cancel this appointment?'),
|
||||||
function() {
|
function() {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_status',
|
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_status',
|
||||||
args: {appointment_id: doc.name, status:status},
|
args: { appointment_id: doc.name, status: status },
|
||||||
callback: function(data) {
|
callback: function(data) {
|
||||||
if (!data.exc) {
|
if (!data.exc) {
|
||||||
frm.reload_doc();
|
frm.reload_doc();
|
||||||
|
|||||||
@@ -131,7 +131,7 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Service Unit",
|
"label": "Service Unit",
|
||||||
"options": "Healthcare Service Unit",
|
"options": "Healthcare Service Unit",
|
||||||
"set_only_once": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "section_break_12",
|
"fieldname": "section_break_12",
|
||||||
@@ -349,7 +349,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-02-08 13:13:15.116833",
|
"modified": "2021-08-19 17:28:41.329387",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Healthcare",
|
"module": "Healthcare",
|
||||||
"name": "Patient Appointment",
|
"name": "Patient Appointment",
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ 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.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account
|
||||||
from erpnext.healthcare.utils import check_fee_validity, get_service_item_and_practitioner_charge, manage_fee_validity
|
from erpnext.healthcare.utils import check_fee_validity, get_service_item_and_practitioner_charge, manage_fee_validity
|
||||||
|
|
||||||
|
class MaximumCapacityError(frappe.ValidationError):
|
||||||
|
pass
|
||||||
|
class OverlapError(frappe.ValidationError):
|
||||||
|
pass
|
||||||
|
|
||||||
class PatientAppointment(Document):
|
class PatientAppointment(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_overlaps()
|
self.validate_overlaps()
|
||||||
@@ -49,26 +54,49 @@ class PatientAppointment(Document):
|
|||||||
end_time = datetime.datetime.combine(getdate(self.appointment_date), get_time(self.appointment_time)) \
|
end_time = datetime.datetime.combine(getdate(self.appointment_date), get_time(self.appointment_time)) \
|
||||||
+ datetime.timedelta(minutes=flt(self.duration))
|
+ datetime.timedelta(minutes=flt(self.duration))
|
||||||
|
|
||||||
overlaps = frappe.db.sql("""
|
# all appointments for both patient and practitioner overlapping the duration of this appointment
|
||||||
select
|
overlapping_appointments = frappe.db.sql("""
|
||||||
name, practitioner, patient, appointment_time, duration
|
SELECT
|
||||||
from
|
name, practitioner, patient, appointment_time, duration, service_unit
|
||||||
`tabPatient Appointment`
|
FROM
|
||||||
where
|
`tabPatient Appointment`
|
||||||
appointment_date=%s and name!=%s and status NOT IN ("Closed", "Cancelled")
|
WHERE
|
||||||
and (practitioner=%s or patient=%s) and
|
appointment_date=%(appointment_date)s AND name!=%(name)s AND status NOT IN ("Closed", "Cancelled") AND
|
||||||
((appointment_time<%s and appointment_time + INTERVAL duration MINUTE>%s) or
|
(practitioner=%(practitioner)s OR patient=%(patient)s) AND
|
||||||
(appointment_time>%s and appointment_time<%s) or
|
((appointment_time<%(appointment_time)s AND appointment_time + INTERVAL duration MINUTE>%(appointment_time)s) OR
|
||||||
(appointment_time=%s))
|
(appointment_time>%(appointment_time)s AND appointment_time<%(end_time)s) OR
|
||||||
""", (self.appointment_date, self.name, self.practitioner, self.patient,
|
(appointment_time=%(appointment_time)s))
|
||||||
self.appointment_time, end_time.time(), self.appointment_time, end_time.time(), self.appointment_time))
|
""",
|
||||||
|
{
|
||||||
|
'appointment_date': self.appointment_date,
|
||||||
|
'name': self.name,
|
||||||
|
'practitioner': self.practitioner,
|
||||||
|
'patient': self.patient,
|
||||||
|
'appointment_time': self.appointment_time,
|
||||||
|
'end_time':end_time.time()
|
||||||
|
},
|
||||||
|
as_dict = True
|
||||||
|
)
|
||||||
|
|
||||||
|
if not overlapping_appointments:
|
||||||
|
return # No overlaps, nothing to validate!
|
||||||
|
|
||||||
|
if self.service_unit: # validate service unit capacity if overlap enabled
|
||||||
|
allow_overlap, service_unit_capacity = frappe.get_value('Healthcare Service Unit', self.service_unit,
|
||||||
|
['overlap_appointments', 'service_unit_capacity'])
|
||||||
|
if allow_overlap:
|
||||||
|
service_unit_appointments = list(filter(lambda appointment: appointment['service_unit'] == self.service_unit and
|
||||||
|
appointment['patient'] != self.patient, overlapping_appointments)) # if same patient already booked, it should be an overlap
|
||||||
|
if len(service_unit_appointments) >= (service_unit_capacity or 1):
|
||||||
|
frappe.throw(_("Not allowed, {} cannot exceed maximum capacity {}")
|
||||||
|
.format(frappe.bold(self.service_unit), frappe.bold(service_unit_capacity or 1)), MaximumCapacityError)
|
||||||
|
else: # service_unit_appointments within capacity, remove from overlapping_appointments
|
||||||
|
overlapping_appointments = [appointment for appointment in overlapping_appointments if appointment not in service_unit_appointments]
|
||||||
|
|
||||||
|
if overlapping_appointments:
|
||||||
|
frappe.throw(_("Not allowed, cannot overlap appointment {}")
|
||||||
|
.format(frappe.bold(', '.join([appointment['name'] for appointment in overlapping_appointments]))), OverlapError)
|
||||||
|
|
||||||
if overlaps:
|
|
||||||
overlapping_details = _('Appointment overlaps with ')
|
|
||||||
overlapping_details += "<b><a href='/app/Form/Patient Appointment/{0}'>{0}</a></b><br>".format(overlaps[0][0])
|
|
||||||
overlapping_details += _('{0} has appointment scheduled with {1} at {2} having {3} minute(s) duration.').format(
|
|
||||||
overlaps[0][1], overlaps[0][2], overlaps[0][3], overlaps[0][4])
|
|
||||||
frappe.throw(overlapping_details, title=_('Appointments Overlapping'))
|
|
||||||
|
|
||||||
def validate_service_unit(self):
|
def validate_service_unit(self):
|
||||||
if self.inpatient_record and self.service_unit:
|
if self.inpatient_record and self.service_unit:
|
||||||
@@ -327,6 +355,8 @@ def get_available_slots(practitioner_doc, date):
|
|||||||
|
|
||||||
if available_slots:
|
if available_slots:
|
||||||
appointments = []
|
appointments = []
|
||||||
|
allow_overlap = 0
|
||||||
|
service_unit_capacity = 0
|
||||||
# fetch all appointments to practitioner by service unit
|
# fetch all appointments to practitioner by service unit
|
||||||
filters = {
|
filters = {
|
||||||
'practitioner': practitioner,
|
'practitioner': practitioner,
|
||||||
@@ -336,8 +366,8 @@ def get_available_slots(practitioner_doc, date):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if schedule_entry.service_unit:
|
if schedule_entry.service_unit:
|
||||||
slot_name = schedule_entry.schedule + ' - ' + schedule_entry.service_unit
|
slot_name = f'{schedule_entry.schedule}'
|
||||||
allow_overlap = frappe.get_value('Healthcare Service Unit', schedule_entry.service_unit, 'overlap_appointments')
|
allow_overlap, service_unit_capacity = frappe.get_value('Healthcare Service Unit', schedule_entry.service_unit, ['overlap_appointments', 'service_unit_capacity'])
|
||||||
if not allow_overlap:
|
if not allow_overlap:
|
||||||
# fetch all appointments to service unit
|
# fetch all appointments to service unit
|
||||||
filters.pop('practitioner')
|
filters.pop('practitioner')
|
||||||
@@ -352,8 +382,8 @@ def get_available_slots(practitioner_doc, date):
|
|||||||
filters=filters,
|
filters=filters,
|
||||||
fields=['name', 'appointment_time', 'duration', 'status'])
|
fields=['name', 'appointment_time', 'duration', 'status'])
|
||||||
|
|
||||||
slot_details.append({'slot_name':slot_name, 'service_unit':schedule_entry.service_unit,
|
slot_details.append({'slot_name': slot_name, 'service_unit': schedule_entry.service_unit, 'avail_slot': available_slots,
|
||||||
'avail_slot':available_slots, 'appointments': appointments})
|
'appointments': appointments, 'allow_overlap': allow_overlap, 'service_unit_capacity': service_unit_capacity})
|
||||||
|
|
||||||
return slot_details
|
return slot_details
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ class TestPatientAppointment(unittest.TestCase):
|
|||||||
frappe.db.sql("""delete from `tabFee Validity`""")
|
frappe.db.sql("""delete from `tabFee Validity`""")
|
||||||
frappe.db.sql("""delete from `tabPatient Encounter`""")
|
frappe.db.sql("""delete from `tabPatient Encounter`""")
|
||||||
make_pos_profile()
|
make_pos_profile()
|
||||||
|
frappe.db.sql("""delete from `tabHealthcare Service Unit` where name like '_Test %'""")
|
||||||
|
frappe.db.sql("""delete from `tabHealthcare Service Unit` where name like '_Test Service Unit Type%'""")
|
||||||
|
|
||||||
def test_status(self):
|
def test_status(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0)
|
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0)
|
||||||
appointment = create_appointment(patient, practitioner, nowdate())
|
appointment = create_appointment(patient, practitioner, nowdate())
|
||||||
self.assertEqual(appointment.status, 'Open')
|
self.assertEqual(appointment.status, 'Open')
|
||||||
@@ -29,7 +31,7 @@ class TestPatientAppointment(unittest.TestCase):
|
|||||||
self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open')
|
self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open')
|
||||||
|
|
||||||
def test_start_encounter(self):
|
def test_start_encounter(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
|
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
|
||||||
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4), invoice = 1)
|
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4), invoice = 1)
|
||||||
appointment.reload()
|
appointment.reload()
|
||||||
@@ -43,7 +45,7 @@ class TestPatientAppointment(unittest.TestCase):
|
|||||||
self.assertEqual(encounter.invoiced, frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'))
|
self.assertEqual(encounter.invoiced, frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'))
|
||||||
|
|
||||||
def test_auto_invoicing(self):
|
def test_auto_invoicing(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
|
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
|
||||||
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0)
|
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0)
|
||||||
appointment = create_appointment(patient, practitioner, nowdate())
|
appointment = create_appointment(patient, practitioner, nowdate())
|
||||||
@@ -59,7 +61,7 @@ class TestPatientAppointment(unittest.TestCase):
|
|||||||
self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount)
|
self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount)
|
||||||
|
|
||||||
def test_auto_invoicing_based_on_department(self):
|
def test_auto_invoicing_based_on_department(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
|
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
|
||||||
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
|
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
|
||||||
appointment_type = create_appointment_type()
|
appointment_type = create_appointment_type()
|
||||||
@@ -77,7 +79,7 @@ class TestPatientAppointment(unittest.TestCase):
|
|||||||
self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount)
|
self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount)
|
||||||
|
|
||||||
def test_auto_invoicing_according_to_appointment_type_charge(self):
|
def test_auto_invoicing_according_to_appointment_type_charge(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
|
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
|
||||||
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
|
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
|
||||||
|
|
||||||
@@ -103,7 +105,7 @@ class TestPatientAppointment(unittest.TestCase):
|
|||||||
self.assertTrue(sales_invoice_name)
|
self.assertTrue(sales_invoice_name)
|
||||||
|
|
||||||
def test_appointment_cancel(self):
|
def test_appointment_cancel(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 1)
|
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 1)
|
||||||
appointment = create_appointment(patient, practitioner, nowdate())
|
appointment = create_appointment(patient, practitioner, nowdate())
|
||||||
fee_validity = frappe.db.get_value('Fee Validity Reference', {'appointment': appointment.name}, 'parent')
|
fee_validity = frappe.db.get_value('Fee Validity Reference', {'appointment': appointment.name}, 'parent')
|
||||||
@@ -129,7 +131,7 @@ class TestPatientAppointment(unittest.TestCase):
|
|||||||
create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
|
create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
|
||||||
|
|
||||||
frappe.db.sql("""delete from `tabInpatient Record`""")
|
frappe.db.sql("""delete from `tabInpatient Record`""")
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
patient = create_patient()
|
patient = create_patient()
|
||||||
# Schedule Admission
|
# Schedule Admission
|
||||||
ip_record = create_inpatient(patient)
|
ip_record = create_inpatient(patient)
|
||||||
@@ -137,7 +139,7 @@ class TestPatientAppointment(unittest.TestCase):
|
|||||||
ip_record.save(ignore_permissions = True)
|
ip_record.save(ignore_permissions = True)
|
||||||
|
|
||||||
# Admit
|
# Admit
|
||||||
service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy')
|
service_unit = get_healthcare_service_unit('_Test Service Unit Ip Occupancy')
|
||||||
admit_patient(ip_record, service_unit, now_datetime())
|
admit_patient(ip_record, service_unit, now_datetime())
|
||||||
|
|
||||||
appointment = create_appointment(patient, practitioner, nowdate(), service_unit=service_unit)
|
appointment = create_appointment(patient, practitioner, nowdate(), service_unit=service_unit)
|
||||||
@@ -155,7 +157,7 @@ class TestPatientAppointment(unittest.TestCase):
|
|||||||
create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
|
create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
|
||||||
|
|
||||||
frappe.db.sql("""delete from `tabInpatient Record`""")
|
frappe.db.sql("""delete from `tabInpatient Record`""")
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
patient = create_patient()
|
patient = create_patient()
|
||||||
# Schedule Admission
|
# Schedule Admission
|
||||||
ip_record = create_inpatient(patient)
|
ip_record = create_inpatient(patient)
|
||||||
@@ -163,10 +165,10 @@ class TestPatientAppointment(unittest.TestCase):
|
|||||||
ip_record.save(ignore_permissions = True)
|
ip_record.save(ignore_permissions = True)
|
||||||
|
|
||||||
# Admit
|
# Admit
|
||||||
service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy')
|
service_unit = get_healthcare_service_unit('_Test Service Unit Ip Occupancy')
|
||||||
admit_patient(ip_record, service_unit, now_datetime())
|
admit_patient(ip_record, service_unit, now_datetime())
|
||||||
|
|
||||||
appointment_service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy for Appointment')
|
appointment_service_unit = get_healthcare_service_unit('_Test Service Unit Ip Occupancy for Appointment')
|
||||||
appointment = create_appointment(patient, practitioner, nowdate(), service_unit=appointment_service_unit, save=0)
|
appointment = create_appointment(patient, practitioner, nowdate(), service_unit=appointment_service_unit, save=0)
|
||||||
self.assertRaises(frappe.exceptions.ValidationError, appointment.save)
|
self.assertRaises(frappe.exceptions.ValidationError, appointment.save)
|
||||||
|
|
||||||
@@ -176,39 +178,102 @@ class TestPatientAppointment(unittest.TestCase):
|
|||||||
mark_invoiced_inpatient_occupancy(ip_record1)
|
mark_invoiced_inpatient_occupancy(ip_record1)
|
||||||
discharge_patient(ip_record1)
|
discharge_patient(ip_record1)
|
||||||
|
|
||||||
|
def test_overlap_appointment(self):
|
||||||
|
from erpnext.healthcare.doctype.patient_appointment.patient_appointment import OverlapError
|
||||||
|
patient, practitioner = create_healthcare_docs(id=1)
|
||||||
|
patient_1, practitioner_1 = create_healthcare_docs(id=2)
|
||||||
|
service_unit = create_service_unit(id=0)
|
||||||
|
service_unit_1 = create_service_unit(id=1)
|
||||||
|
appointment = create_appointment(patient, practitioner, nowdate(), service_unit=service_unit) # valid
|
||||||
|
|
||||||
def create_healthcare_docs():
|
# patient and practitioner cannot have overlapping appointments
|
||||||
patient = create_patient()
|
appointment = create_appointment(patient, practitioner, nowdate(), service_unit=service_unit, save=0)
|
||||||
practitioner = frappe.db.exists('Healthcare Practitioner', '_Test Healthcare Practitioner')
|
self.assertRaises(OverlapError, appointment.save)
|
||||||
medical_department = frappe.db.exists('Medical Department', '_Test Medical Department')
|
appointment = create_appointment(patient, practitioner, nowdate(), service_unit=service_unit_1, save=0) # diff service unit
|
||||||
|
self.assertRaises(OverlapError, appointment.save)
|
||||||
|
appointment = create_appointment(patient, practitioner, nowdate(), save=0) # with no service unit link
|
||||||
|
self.assertRaises(OverlapError, appointment.save)
|
||||||
|
|
||||||
if not medical_department:
|
# patient cannot have overlapping appointments with other practitioners
|
||||||
medical_department = frappe.new_doc('Medical Department')
|
appointment = create_appointment(patient, practitioner_1, nowdate(), service_unit=service_unit, save=0)
|
||||||
medical_department.department = '_Test Medical Department'
|
self.assertRaises(OverlapError, appointment.save)
|
||||||
medical_department.save(ignore_permissions=True)
|
appointment = create_appointment(patient, practitioner_1, nowdate(), service_unit=service_unit_1, save=0)
|
||||||
medical_department = medical_department.name
|
self.assertRaises(OverlapError, appointment.save)
|
||||||
|
appointment = create_appointment(patient, practitioner_1, nowdate(), save=0)
|
||||||
|
self.assertRaises(OverlapError, appointment.save)
|
||||||
|
|
||||||
if not practitioner:
|
# practitioner cannot have overlapping appointments with other patients
|
||||||
practitioner = frappe.new_doc('Healthcare Practitioner')
|
appointment = create_appointment(patient_1, practitioner, nowdate(), service_unit=service_unit, save=0)
|
||||||
practitioner.first_name = '_Test Healthcare Practitioner'
|
self.assertRaises(OverlapError, appointment.save)
|
||||||
practitioner.gender = 'Female'
|
appointment = create_appointment(patient_1, practitioner, nowdate(), service_unit=service_unit_1, save=0)
|
||||||
practitioner.department = medical_department
|
self.assertRaises(OverlapError, appointment.save)
|
||||||
practitioner.op_consulting_charge = 500
|
appointment = create_appointment(patient_1, practitioner, nowdate(), save=0)
|
||||||
practitioner.inpatient_visit_charge = 500
|
self.assertRaises(OverlapError, appointment.save)
|
||||||
practitioner.save(ignore_permissions=True)
|
|
||||||
practitioner = practitioner.name
|
|
||||||
|
|
||||||
return patient, medical_department, practitioner
|
def test_service_unit_capacity(self):
|
||||||
|
from erpnext.healthcare.doctype.patient_appointment.patient_appointment import MaximumCapacityError, OverlapError
|
||||||
|
practitioner = create_practitioner()
|
||||||
|
capacity = 3
|
||||||
|
overlap_service_unit_type = create_service_unit_type(id=10, allow_appointments=1, overlap_appointments=1)
|
||||||
|
overlap_service_unit = create_service_unit(id=100, service_unit_type=overlap_service_unit_type, service_unit_capacity=capacity)
|
||||||
|
|
||||||
|
for i in range(0, capacity):
|
||||||
|
patient = create_patient(id=i)
|
||||||
|
create_appointment(patient, practitioner, nowdate(), service_unit=overlap_service_unit) # valid
|
||||||
|
appointment = create_appointment(patient, practitioner, nowdate(), service_unit=overlap_service_unit, save=0) # overlap
|
||||||
|
self.assertRaises(OverlapError, appointment.save)
|
||||||
|
|
||||||
|
patient = create_patient(id=capacity)
|
||||||
|
appointment = create_appointment(patient, practitioner, nowdate(), service_unit=overlap_service_unit, save=0)
|
||||||
|
self.assertRaises(MaximumCapacityError, appointment.save)
|
||||||
|
|
||||||
|
|
||||||
|
def create_healthcare_docs(id=0):
|
||||||
|
patient = create_patient(id)
|
||||||
|
practitioner = create_practitioner(id)
|
||||||
|
|
||||||
|
return patient, practitioner
|
||||||
|
|
||||||
|
|
||||||
|
def create_patient(id=0):
|
||||||
|
if frappe.db.exists('Patient', {'firstname':f'_Test Patient {str(id)}'}):
|
||||||
|
patient = frappe.db.get_value('Patient', {'first_name': f'_Test Patient {str(id)}'}, ['name'])
|
||||||
|
return patient
|
||||||
|
|
||||||
|
patient = frappe.new_doc('Patient')
|
||||||
|
patient.first_name = f'_Test Patient {str(id)}'
|
||||||
|
patient.sex = 'Female'
|
||||||
|
patient.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
return patient.name
|
||||||
|
|
||||||
|
|
||||||
|
def create_medical_department(id=0):
|
||||||
|
if frappe.db.exists('Medical Department', f'_Test Medical Department {str(id)}'):
|
||||||
|
return f'_Test Medical Department {str(id)}'
|
||||||
|
|
||||||
|
medical_department = frappe.new_doc('Medical Department')
|
||||||
|
medical_department.department = f'_Test Medical Department {str(id)}'
|
||||||
|
medical_department.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
return medical_department.name
|
||||||
|
|
||||||
|
|
||||||
|
def create_practitioner(id=0, medical_department=None):
|
||||||
|
if frappe.db.exists('Healthcare Practitioner', {'firstname':f'_Test Healthcare Practitioner {str(id)}'}):
|
||||||
|
practitioner = frappe.db.get_value('Healthcare Practitioner', {'firstname':f'_Test Healthcare Practitioner {str(id)}'}, ['name'])
|
||||||
|
return practitioner
|
||||||
|
|
||||||
|
practitioner = frappe.new_doc('Healthcare Practitioner')
|
||||||
|
practitioner.first_name = f'_Test Healthcare Practitioner {str(id)}'
|
||||||
|
practitioner.gender = 'Female'
|
||||||
|
practitioner.department = medical_department or create_medical_department(id)
|
||||||
|
practitioner.op_consulting_charge = 500
|
||||||
|
practitioner.inpatient_visit_charge = 500
|
||||||
|
practitioner.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
return practitioner.name
|
||||||
|
|
||||||
def create_patient():
|
|
||||||
patient = frappe.db.exists('Patient', '_Test Patient')
|
|
||||||
if not patient:
|
|
||||||
patient = frappe.new_doc('Patient')
|
|
||||||
patient.first_name = '_Test Patient'
|
|
||||||
patient.sex = 'Female'
|
|
||||||
patient.save(ignore_permissions=True)
|
|
||||||
patient = patient.name
|
|
||||||
return patient
|
|
||||||
|
|
||||||
def create_encounter(appointment):
|
def create_encounter(appointment):
|
||||||
if appointment:
|
if appointment:
|
||||||
@@ -221,8 +286,10 @@ def create_encounter(appointment):
|
|||||||
encounter.company = appointment.company
|
encounter.company = appointment.company
|
||||||
encounter.save()
|
encounter.save()
|
||||||
encounter.submit()
|
encounter.submit()
|
||||||
|
|
||||||
return encounter
|
return encounter
|
||||||
|
|
||||||
|
|
||||||
def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0,
|
def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0,
|
||||||
service_unit=None, appointment_type=None, save=1, department=None):
|
service_unit=None, appointment_type=None, save=1, department=None):
|
||||||
item = create_healthcare_service_items()
|
item = create_healthcare_service_items()
|
||||||
@@ -235,6 +302,7 @@ def create_appointment(patient, practitioner, appointment_date, invoice=0, proce
|
|||||||
appointment.appointment_date = appointment_date
|
appointment.appointment_date = appointment_date
|
||||||
appointment.company = '_Test Company'
|
appointment.company = '_Test Company'
|
||||||
appointment.duration = 15
|
appointment.duration = 15
|
||||||
|
|
||||||
if service_unit:
|
if service_unit:
|
||||||
appointment.service_unit = service_unit
|
appointment.service_unit = service_unit
|
||||||
if invoice:
|
if invoice:
|
||||||
@@ -245,11 +313,14 @@ def create_appointment(patient, practitioner, appointment_date, invoice=0, proce
|
|||||||
appointment.procedure_template = create_clinical_procedure_template().get('name')
|
appointment.procedure_template = create_clinical_procedure_template().get('name')
|
||||||
if save:
|
if save:
|
||||||
appointment.save(ignore_permissions=True)
|
appointment.save(ignore_permissions=True)
|
||||||
|
|
||||||
return appointment
|
return appointment
|
||||||
|
|
||||||
|
|
||||||
def create_healthcare_service_items():
|
def create_healthcare_service_items():
|
||||||
if frappe.db.exists('Item', 'HLC-SI-001'):
|
if frappe.db.exists('Item', 'HLC-SI-001'):
|
||||||
return 'HLC-SI-001'
|
return 'HLC-SI-001'
|
||||||
|
|
||||||
item = frappe.new_doc('Item')
|
item = frappe.new_doc('Item')
|
||||||
item.item_code = 'HLC-SI-001'
|
item.item_code = 'HLC-SI-001'
|
||||||
item.item_name = 'Consulting Charges'
|
item.item_name = 'Consulting Charges'
|
||||||
@@ -257,11 +328,14 @@ def create_healthcare_service_items():
|
|||||||
item.is_stock_item = 0
|
item.is_stock_item = 0
|
||||||
item.stock_uom = 'Nos'
|
item.stock_uom = 'Nos'
|
||||||
item.save()
|
item.save()
|
||||||
|
|
||||||
return item.name
|
return item.name
|
||||||
|
|
||||||
|
|
||||||
def create_clinical_procedure_template():
|
def create_clinical_procedure_template():
|
||||||
if frappe.db.exists('Clinical Procedure Template', 'Knee Surgery and Rehab'):
|
if frappe.db.exists('Clinical Procedure Template', 'Knee Surgery and Rehab'):
|
||||||
return frappe.get_doc('Clinical Procedure Template', 'Knee Surgery and Rehab')
|
return frappe.get_doc('Clinical Procedure Template', 'Knee Surgery and Rehab')
|
||||||
|
|
||||||
template = frappe.new_doc('Clinical Procedure Template')
|
template = frappe.new_doc('Clinical Procedure Template')
|
||||||
template.template = 'Knee Surgery and Rehab'
|
template.template = 'Knee Surgery and Rehab'
|
||||||
template.item_code = 'Knee Surgery and Rehab'
|
template.item_code = 'Knee Surgery and Rehab'
|
||||||
@@ -270,8 +344,10 @@ def create_clinical_procedure_template():
|
|||||||
template.description = 'Knee Surgery and Rehab'
|
template.description = 'Knee Surgery and Rehab'
|
||||||
template.rate = 50000
|
template.rate = 50000
|
||||||
template.save()
|
template.save()
|
||||||
|
|
||||||
return template
|
return template
|
||||||
|
|
||||||
|
|
||||||
def create_appointment_type(args=None):
|
def create_appointment_type(args=None):
|
||||||
if not args:
|
if not args:
|
||||||
args = frappe.local.form_dict
|
args = frappe.local.form_dict
|
||||||
@@ -296,3 +372,28 @@ def create_appointment_type(args=None):
|
|||||||
'price_list': args.get('price_list') or frappe.db.get_value("Price List", {"selling": 1}),
|
'price_list': args.get('price_list') or frappe.db.get_value("Price List", {"selling": 1}),
|
||||||
'items': args.get('items') or items
|
'items': args.get('items') or items
|
||||||
}).insert()
|
}).insert()
|
||||||
|
def create_service_unit_type(id=0, allow_appointments=1, overlap_appointments=0):
|
||||||
|
if frappe.db.exists('Healthcare Service Unit Type', f'_Test Service Unit Type {str(id)}'):
|
||||||
|
return f'_Test Service Unit Type {str(id)}'
|
||||||
|
|
||||||
|
service_unit_type = frappe.new_doc('Healthcare Service Unit Type')
|
||||||
|
service_unit_type.service_unit_type = f'_Test Service Unit Type {str(id)}'
|
||||||
|
service_unit_type.allow_appointments = allow_appointments
|
||||||
|
service_unit_type.overlap_appointments = overlap_appointments
|
||||||
|
service_unit_type.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
return service_unit_type.name
|
||||||
|
|
||||||
|
|
||||||
|
def create_service_unit(id=0, service_unit_type=None, service_unit_capacity=0):
|
||||||
|
if frappe.db.exists('Healthcare Service Unit', f'_Test Service Unit {str(id)}'):
|
||||||
|
return f'_Test service_unit {str(id)}'
|
||||||
|
|
||||||
|
service_unit = frappe.new_doc('Healthcare Service Unit')
|
||||||
|
service_unit.is_group = 0
|
||||||
|
service_unit.healthcare_service_unit_name= f'_Test Service Unit {str(id)}'
|
||||||
|
service_unit.service_unit_type = service_unit_type or create_service_unit_type(id)
|
||||||
|
service_unit.service_unit_capacity = service_unit_capacity
|
||||||
|
service_unit.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
return service_unit.name
|
||||||
|
|||||||
@@ -31,6 +31,3 @@ def create_patient_assessment(source_name, target_doc=None):
|
|||||||
}, target_doc)
|
}, target_doc)
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import unittest
|
import unittest
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import nowdate
|
from frappe.utils import nowdate
|
||||||
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_encounter, create_healthcare_docs, create_appointment
|
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_encounter, create_healthcare_docs, create_appointment, create_medical_department
|
||||||
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
||||||
|
|
||||||
class TestPatientMedicalRecord(unittest.TestCase):
|
class TestPatientMedicalRecord(unittest.TestCase):
|
||||||
@@ -15,7 +15,8 @@ class TestPatientMedicalRecord(unittest.TestCase):
|
|||||||
make_pos_profile()
|
make_pos_profile()
|
||||||
|
|
||||||
def test_medical_record(self):
|
def test_medical_record(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
|
medical_department = create_medical_department()
|
||||||
appointment = create_appointment(patient, practitioner, nowdate(), invoice=1)
|
appointment = create_appointment(patient, practitioner, nowdate(), invoice=1)
|
||||||
encounter = create_encounter(appointment)
|
encounter = create_encounter(appointment)
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ import unittest
|
|||||||
from frappe.utils import getdate, flt, nowdate
|
from frappe.utils import getdate, flt, nowdate
|
||||||
from erpnext.healthcare.doctype.therapy_type.test_therapy_type import create_therapy_type
|
from erpnext.healthcare.doctype.therapy_type.test_therapy_type import create_therapy_type
|
||||||
from erpnext.healthcare.doctype.therapy_plan.therapy_plan import make_therapy_session, make_sales_invoice
|
from erpnext.healthcare.doctype.therapy_plan.therapy_plan import make_therapy_session, make_sales_invoice
|
||||||
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_patient, create_appointment
|
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import \
|
||||||
|
create_healthcare_docs, create_patient, create_appointment, create_medical_department
|
||||||
|
|
||||||
class TestTherapyPlan(unittest.TestCase):
|
class TestTherapyPlan(unittest.TestCase):
|
||||||
def test_creation_on_encounter_submission(self):
|
def test_creation_on_encounter_submission(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
|
medical_department = create_medical_department()
|
||||||
encounter = create_encounter(patient, medical_department, practitioner)
|
encounter = create_encounter(patient, medical_department, practitioner)
|
||||||
self.assertTrue(frappe.db.exists('Therapy Plan', encounter.therapy_plan))
|
self.assertTrue(frappe.db.exists('Therapy Plan', encounter.therapy_plan))
|
||||||
|
|
||||||
@@ -28,8 +30,9 @@ class TestTherapyPlan(unittest.TestCase):
|
|||||||
frappe.get_doc(session).submit()
|
frappe.get_doc(session).submit()
|
||||||
self.assertEqual(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'Completed')
|
self.assertEqual(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'Completed')
|
||||||
|
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
appointment = create_appointment(patient, practitioner, nowdate())
|
appointment = create_appointment(patient, practitioner, nowdate())
|
||||||
|
|
||||||
session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company', appointment.name)
|
session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company', appointment.name)
|
||||||
session = frappe.get_doc(session)
|
session = frappe.get_doc(session)
|
||||||
session.submit()
|
session.submit()
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ def create_therapy_type():
|
|||||||
})
|
})
|
||||||
therapy_type.save()
|
therapy_type.save()
|
||||||
else:
|
else:
|
||||||
therapy_type = frappe.get_doc('Therapy Type', 'Basic Rehab')
|
therapy_type = frappe.get_doc('Therapy Type', therapy_type)
|
||||||
|
|
||||||
return therapy_type
|
return therapy_type
|
||||||
|
|
||||||
def create_exercise_type():
|
def create_exercise_type():
|
||||||
@@ -47,4 +48,7 @@ def create_exercise_type():
|
|||||||
'description': 'Squat and Rise'
|
'description': 'Squat and Rise'
|
||||||
})
|
})
|
||||||
exercise_type.save()
|
exercise_type.save()
|
||||||
|
else:
|
||||||
|
exercise_type = frappe.get_doc('Exercise Type', exercise_type)
|
||||||
|
|
||||||
return exercise_type
|
return exercise_type
|
||||||
@@ -15,4 +15,3 @@ class VitalSigns(Document):
|
|||||||
def set_title(self):
|
def set_title(self):
|
||||||
self.title = _('{0} on {1}').format(self.patient_name or self.patient,
|
self.title = _('{0} on {1}').format(self.patient_name or self.patient,
|
||||||
frappe.utils.format_date(self.signs_date))[:100]
|
frappe.utils.format_date(self.signs_date))[:100]
|
||||||
|
|
||||||
|
|||||||
@@ -194,4 +194,3 @@ def get_date_range(time_span):
|
|||||||
return time_span
|
return time_span
|
||||||
except json.decoder.JSONDecodeError:
|
except json.decoder.JSONDecodeError:
|
||||||
return get_timespan_date_range(time_span.lower())
|
return get_timespan_date_range(time_span.lower())
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class TestInpatientMedicationOrders(unittest.TestCase):
|
|||||||
'from_date': getdate(),
|
'from_date': getdate(),
|
||||||
'to_date': getdate(),
|
'to_date': getdate(),
|
||||||
'patient': '_Test IPD Patient',
|
'patient': '_Test IPD Patient',
|
||||||
'service_unit': 'Test Service Unit Ip Occupancy - _TC'
|
'service_unit': '_Test Service Unit Ip Occupancy - _TC'
|
||||||
}
|
}
|
||||||
|
|
||||||
report = execute(filters)
|
report = execute(filters)
|
||||||
@@ -42,7 +42,7 @@ class TestInpatientMedicationOrders(unittest.TestCase):
|
|||||||
'date': getdate(),
|
'date': getdate(),
|
||||||
'time': datetime.timedelta(seconds=32400),
|
'time': datetime.timedelta(seconds=32400),
|
||||||
'is_completed': 0,
|
'is_completed': 0,
|
||||||
'healthcare_service_unit': 'Test Service Unit Ip Occupancy - _TC'
|
'healthcare_service_unit': '_Test Service Unit Ip Occupancy - _TC'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'patient': '_Test IPD Patient',
|
'patient': '_Test IPD Patient',
|
||||||
@@ -55,7 +55,7 @@ class TestInpatientMedicationOrders(unittest.TestCase):
|
|||||||
'date': getdate(),
|
'date': getdate(),
|
||||||
'time': datetime.timedelta(seconds=50400),
|
'time': datetime.timedelta(seconds=50400),
|
||||||
'is_completed': 0,
|
'is_completed': 0,
|
||||||
'healthcare_service_unit': 'Test Service Unit Ip Occupancy - _TC'
|
'healthcare_service_unit': '_Test Service Unit Ip Occupancy - _TC'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'patient': '_Test IPD Patient',
|
'patient': '_Test IPD Patient',
|
||||||
@@ -68,7 +68,7 @@ class TestInpatientMedicationOrders(unittest.TestCase):
|
|||||||
'date': getdate(),
|
'date': getdate(),
|
||||||
'time': datetime.timedelta(seconds=75600),
|
'time': datetime.timedelta(seconds=75600),
|
||||||
'is_completed': 0,
|
'is_completed': 0,
|
||||||
'healthcare_service_unit': 'Test Service Unit Ip Occupancy - _TC'
|
'healthcare_service_unit': '_Test Service Unit Ip Occupancy - _TC'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ class TestInpatientMedicationOrders(unittest.TestCase):
|
|||||||
'from_date': getdate(),
|
'from_date': getdate(),
|
||||||
'to_date': getdate(),
|
'to_date': getdate(),
|
||||||
'patient': '_Test IPD Patient',
|
'patient': '_Test IPD Patient',
|
||||||
'service_unit': 'Test Service Unit Ip Occupancy - _TC',
|
'service_unit': '_Test Service Unit Ip Occupancy - _TC',
|
||||||
'show_completed_orders': 0
|
'show_completed_orders': 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ def create_records(patient):
|
|||||||
ip_record.expected_length_of_stay = 0
|
ip_record.expected_length_of_stay = 0
|
||||||
ip_record.save()
|
ip_record.save()
|
||||||
ip_record.reload()
|
ip_record.reload()
|
||||||
service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy')
|
service_unit = get_healthcare_service_unit('_Test Service Unit Ip Occupancy')
|
||||||
admit_patient(ip_record, service_unit, now_datetime())
|
admit_patient(ip_record, service_unit, now_datetime())
|
||||||
|
|
||||||
ipmo = create_ipmo(patient)
|
ipmo = create_ipmo(patient)
|
||||||
|
|||||||
@@ -543,58 +543,43 @@ def get_drugs_to_invoice(encounter):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_children(doctype, parent, company, is_root=False):
|
def get_children(doctype, parent=None, company=None, is_root=False):
|
||||||
parent_fieldname = "parent_" + doctype.lower().replace(" ", "_")
|
parent_fieldname = 'parent_' + doctype.lower().replace(' ', '_')
|
||||||
fields = [
|
fields = [
|
||||||
"name as value",
|
'name as value',
|
||||||
"is_group as expandable",
|
'is_group as expandable',
|
||||||
"lft",
|
'lft',
|
||||||
"rgt"
|
'rgt'
|
||||||
]
|
]
|
||||||
# fields = [ "name", "is_group", "lft", "rgt" ]
|
|
||||||
filters = [["ifnull(`{0}`,'')".format(parent_fieldname), "=", "" if is_root else parent]]
|
filters = [["ifnull(`{0}`,'')".format(parent_fieldname),
|
||||||
|
'=', '' if is_root else parent]]
|
||||||
|
|
||||||
if is_root:
|
if is_root:
|
||||||
fields += ["service_unit_type"] if doctype == "Healthcare Service Unit" else []
|
fields += ['service_unit_type'] if doctype == 'Healthcare Service Unit' else []
|
||||||
filters.append(["company", "=", company])
|
filters.append(['company', '=', company])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
fields += ["service_unit_type", "allow_appointments", "inpatient_occupancy", "occupancy_status"] if doctype == "Healthcare Service Unit" else []
|
fields += ['service_unit_type', 'allow_appointments', 'inpatient_occupancy',
|
||||||
fields += [parent_fieldname + " as parent"]
|
'occupancy_status'] if doctype == 'Healthcare Service Unit' else []
|
||||||
|
fields += [parent_fieldname + ' as parent']
|
||||||
|
|
||||||
hc_service_units = frappe.get_list(doctype, fields=fields, filters=filters)
|
service_units = frappe.get_list(doctype, fields=fields, filters=filters)
|
||||||
|
for each in service_units:
|
||||||
|
if each['expandable'] == 1: # group node
|
||||||
|
available_count = frappe.db.count('Healthcare Service Unit', filters={
|
||||||
|
'parent_healthcare_service_unit': each['value'],
|
||||||
|
'inpatient_occupancy': 1})
|
||||||
|
|
||||||
if doctype == "Healthcare Service Unit":
|
if available_count > 0:
|
||||||
for each in hc_service_units:
|
occupied_count = frappe.db.count('Healthcare Service Unit', {
|
||||||
occupancy_msg = ""
|
'parent_healthcare_service_unit': each['value'],
|
||||||
if each["expandable"] == 1:
|
'inpatient_occupancy': 1,
|
||||||
occupied = False
|
'occupancy_status': 'Occupied'})
|
||||||
vacant = False
|
# set occupancy status of group node
|
||||||
child_list = frappe.db.sql(
|
each['occupied_of_available'] = str(
|
||||||
'''
|
occupied_count) + ' Occupied of ' + str(available_count)
|
||||||
SELECT
|
|
||||||
name, occupancy_status
|
|
||||||
FROM
|
|
||||||
`tabHealthcare Service Unit`
|
|
||||||
WHERE
|
|
||||||
inpatient_occupancy = 1
|
|
||||||
and lft > %s and rgt < %s
|
|
||||||
''', (each['lft'], each['rgt']))
|
|
||||||
|
|
||||||
for child in child_list:
|
return service_units
|
||||||
if not occupied:
|
|
||||||
occupied = 0
|
|
||||||
if child[1] == "Occupied":
|
|
||||||
occupied += 1
|
|
||||||
if not vacant:
|
|
||||||
vacant = 0
|
|
||||||
if child[1] == "Vacant":
|
|
||||||
vacant += 1
|
|
||||||
if vacant and occupied:
|
|
||||||
occupancy_total = vacant + occupied
|
|
||||||
occupancy_msg = str(occupied) + " Occupied out of " + str(occupancy_total)
|
|
||||||
each["occupied_out_of_vacant"] = occupancy_msg
|
|
||||||
return hc_service_units
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -717,3 +702,40 @@ def render_doc_as_html(doctype, docname, exclude_fields = []):
|
|||||||
doc_html = "<div class='small'><div class='col-md-12 text-right'><a class='btn btn-default btn-xs' href='/app/Form/%s/%s'></a></div>" %(doctype, docname) + doc_html + '</div>'
|
doc_html = "<div class='small'><div class='col-md-12 text-right'><a class='btn btn-default btn-xs' href='/app/Form/%s/%s'></a></div>" %(doctype, docname) + doc_html + '</div>'
|
||||||
|
|
||||||
return {'html': doc_html}
|
return {'html': doc_html}
|
||||||
|
|
||||||
|
|
||||||
|
def update_address_links(address, method):
|
||||||
|
'''
|
||||||
|
Hook validate Address
|
||||||
|
If Patient is linked in Address, also link the associated Customer
|
||||||
|
'''
|
||||||
|
if 'Healthcare' not in frappe.get_active_domains():
|
||||||
|
return
|
||||||
|
|
||||||
|
patient_links = list(filter(lambda link: link.get('link_doctype') == 'Patient', address.links))
|
||||||
|
|
||||||
|
for link in patient_links:
|
||||||
|
customer = frappe.db.get_value('Patient', link.get('link_name'), 'customer')
|
||||||
|
if customer and not address.has_link('Customer', customer):
|
||||||
|
address.append('links', dict(link_doctype = 'Customer', link_name = customer))
|
||||||
|
|
||||||
|
|
||||||
|
def update_patient_email_and_phone_numbers(contact, method):
|
||||||
|
'''
|
||||||
|
Hook validate Contact
|
||||||
|
Update linked Patients' primary mobile and phone numbers
|
||||||
|
'''
|
||||||
|
if 'Healthcare' not in frappe.get_active_domains():
|
||||||
|
return
|
||||||
|
|
||||||
|
if contact.is_primary_contact and (contact.email_id or contact.mobile_no or contact.phone):
|
||||||
|
patient_links = list(filter(lambda link: link.get('link_doctype') == 'Patient', contact.links))
|
||||||
|
|
||||||
|
for link in patient_links:
|
||||||
|
contact_details = frappe.db.get_value('Patient', link.get('link_name'), ['email', 'mobile', 'phone'], as_dict=1)
|
||||||
|
if contact.email_id and contact.email_id != contact_details.get('email'):
|
||||||
|
frappe.db.set_value('Patient', link.get('link_name'), 'email', contact.email_id)
|
||||||
|
if contact.mobile_no and contact.mobile_no != contact_details.get('mobile'):
|
||||||
|
frappe.db.set_value('Patient', link.get('link_name'), 'mobile', contact.mobile_no)
|
||||||
|
if contact.phone and contact.phone != contact_details.get('phone'):
|
||||||
|
frappe.db.set_value('Patient', link.get('link_name'), 'phone', contact.phone)
|
||||||
|
|||||||
@@ -282,7 +282,12 @@ doc_events = {
|
|||||||
"on_trash": "erpnext.regional.check_deletion_permission"
|
"on_trash": "erpnext.regional.check_deletion_permission"
|
||||||
},
|
},
|
||||||
'Address': {
|
'Address': {
|
||||||
'validate': ['erpnext.regional.india.utils.validate_gstin_for_india', 'erpnext.regional.italy.utils.set_state_code', 'erpnext.regional.india.utils.update_gst_category']
|
'validate': [
|
||||||
|
'erpnext.regional.india.utils.validate_gstin_for_india',
|
||||||
|
'erpnext.regional.italy.utils.set_state_code',
|
||||||
|
'erpnext.regional.india.utils.update_gst_category',
|
||||||
|
'erpnext.healthcare.utils.update_address_links'
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'Supplier': {
|
'Supplier': {
|
||||||
'validate': 'erpnext.regional.india.utils.validate_pan_for_india'
|
'validate': 'erpnext.regional.india.utils.validate_pan_for_india'
|
||||||
@@ -293,13 +298,16 @@ doc_events = {
|
|||||||
"Contact": {
|
"Contact": {
|
||||||
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
|
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
|
||||||
"after_insert": "erpnext.telephony.doctype.call_log.call_log.link_existing_conversations",
|
"after_insert": "erpnext.telephony.doctype.call_log.call_log.link_existing_conversations",
|
||||||
"validate": "erpnext.crm.utils.update_lead_phone_numbers"
|
"validate": ["erpnext.crm.utils.update_lead_phone_numbers", "erpnext.healthcare.utils.update_patient_email_and_phone_numbers"]
|
||||||
},
|
},
|
||||||
"Email Unsubscribe": {
|
"Email Unsubscribe": {
|
||||||
"after_insert": "erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient"
|
"after_insert": "erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient"
|
||||||
},
|
},
|
||||||
('Quotation', 'Sales Order', 'Sales Invoice'): {
|
('Quotation', 'Sales Order', 'Sales Invoice'): {
|
||||||
'validate': ["erpnext.erpnext_integrations.taxjar_integration.set_sales_tax"]
|
'validate': ["erpnext.erpnext_integrations.taxjar_integration.set_sales_tax"]
|
||||||
|
},
|
||||||
|
"Company": {
|
||||||
|
"on_trash": "erpnext.regional.india.utils.delete_gst_settings_for_company"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,4 +55,3 @@ QUnit.test("Test: Expense Claim [HR]", function (assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -27,4 +27,3 @@ QUnit.test("Test: Appraisal Template [HR]", function (assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -267,5 +267,3 @@ erpnext.EmployeeSelector = Class.extend({
|
|||||||
mark_employee_toolbar.appendTo($(this.wrapper));
|
mark_employee_toolbar.appendTo($(this.wrapper));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -176,4 +176,3 @@ def time_diff_in_hours(start, end):
|
|||||||
|
|
||||||
def find_index_in_dict(dict_list, key, value):
|
def find_index_in_dict(dict_list, key, value):
|
||||||
return next((index for (index, d) in enumerate(dict_list) if d[key] == value), None)
|
return next((index for (index, d) in enumerate(dict_list) if d[key] == value), None)
|
||||||
|
|
||||||
|
|||||||
@@ -12,4 +12,3 @@ class EmployeeGrievance(Document):
|
|||||||
bold("Invalid"),
|
bold("Invalid"),
|
||||||
bold("Resolved"))
|
bold("Resolved"))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -48,4 +48,3 @@ def create_grievance_type():
|
|||||||
grievance_type.save()
|
grievance_type.save()
|
||||||
|
|
||||||
return grievance_type.name
|
return grievance_type.name
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user