From 47c2317b1a58ea4f375267b7dfe1b7215b0a6c52 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Wed, 16 Jun 2021 19:03:27 +0530 Subject: [PATCH 01/33] feat: details fetched from supplier group in supplier --- erpnext/buying/doctype/supplier/supplier.js | 13 +++++++++++++ erpnext/buying/doctype/supplier/supplier.py | 19 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 4ddc458175b..af6401b3fe6 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -60,10 +60,23 @@ frappe.ui.form.on("Supplier", { erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name); }, __('Create')); + frm.add_custom_button(__('Get Supplier Group Details'), function () { + frm.trigger("get_supplier_group_details"); + }, __('Actions')); + // indicators erpnext.utils.set_party_dashboard_indicators(frm); } }, + get_supplier_group_details: function(frm) { + frappe.call({ + method: "get_supplier_group_details", + doc: frm.doc, + callback: function(r){ + frm.refresh() + } + }); + }, is_internal_supplier: function(frm) { if (frm.doc.is_internal_supplier == 1) { diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index edeb135d951..791f71ed3b0 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -51,6 +51,23 @@ class Supplier(TransactionBase): validate_party_accounts(self) self.validate_internal_supplier() + @frappe.whitelist() + def get_supplier_group_details(self): + doc = frappe.get_doc('Supplier Group', self.supplier_group) + self.payment_terms = "" + self.accounts = [] + + if not self.accounts and doc.accounts: + for account in doc.accounts: + child = self.append('accounts') + child.company = account.company + child.account = account.account + self.save() + + if not self.payment_terms and doc.payment_terms: + self.payment_terms = doc.payment_terms + + def validate_internal_supplier(self): internal_supplier = frappe.db.get_value("Supplier", {"is_internal_supplier": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name") @@ -86,4 +103,4 @@ class Supplier(TransactionBase): create_contact(supplier, 'Supplier', doc.name, args.get('supplier_email_' + str(i))) except frappe.NameError: - pass \ No newline at end of file + pass From 905aebc31061f5d4b3d8869a2c70ff8749b17234 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 17 Jun 2021 15:48:55 +0530 Subject: [PATCH 02/33] feat: details fetched from customer group in customer --- erpnext/selling/doctype/customer/customer.js | 17 ++++++++++++- erpnext/selling/doctype/customer/customer.py | 26 ++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 825b170a901..91944adef3b 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -130,6 +130,10 @@ frappe.ui.form.on("Customer", { erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name); }, __('Create')); + frm.add_custom_button(__('Get Customer Group Details'), function () { + frm.trigger("get_customer_group_details"); + }, __('Actions')); + // indicator erpnext.utils.set_party_dashboard_indicators(frm); @@ -145,4 +149,15 @@ frappe.ui.form.on("Customer", { if(frm.doc.lead_name) frappe.model.clear_doc("Lead", frm.doc.lead_name); }, -}); \ No newline at end of file + get_customer_group_details: function(frm) { + frappe.call({ + method: "get_customer_group_details", + doc: frm.doc, + callback: function(r){ + frm.refresh() + } + }); + + } +}); + diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 818888c0c12..cdeb0896189 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -78,6 +78,32 @@ class Customer(TransactionBase): if sum(member.allocated_percentage or 0 for member in self.sales_team) != 100: frappe.throw(_("Total contribution percentage should be equal to 100")) + @frappe.whitelist() + def get_customer_group_details(self): + doc = frappe.get_doc('Customer Group', self.customer_group) + self.accounts = self.credit_limits = [] + self.payment_terms = self.default_price_list = "" + + if not self.accounts and doc.accounts: + for account in doc.accounts: + child = self.append('accounts') + child.company = account.company + child.account = account.account + self.save() + + if not self.credit_limits and doc.credit_limits: + for credit in doc.credit_limits: + child = self.append('credit_limits') + child.company = credit.company + child.credit_limit = credit.credit_limit + self.save() + + if not self.payment_terms and doc.payment_terms: + self.payment_terms = doc.payment_terms + + if not self.default_price_list and doc.default_price_list: + self.default_price_list = doc.default_price_list + def check_customer_group_change(self): frappe.flags.customer_group_changed = False From d160e73c036b66756cda08b72fef79f64409e219 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Fri, 18 Jun 2021 18:53:28 +0530 Subject: [PATCH 03/33] test: test case for fetching supplier group details --- .../buying/doctype/supplier/test_supplier.py | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py index f9c8d35518d..faa813aa4c4 100644 --- a/erpnext/buying/doctype/supplier/test_supplier.py +++ b/erpnext/buying/doctype/supplier/test_supplier.py @@ -13,6 +13,26 @@ test_records = frappe.get_test_records('Supplier') class TestSupplier(unittest.TestCase): + def test_get_supplier_group_details(self): + doc = frappe.get_doc("Supplier Group", "Local") + doc.payment_terms = "_Test Payment Term Template 3" + doc.accounts = [] + test_account_details = { + "company": "_Test Company", + "account": "Creditors - _TC", + } + doc.append("accounts", test_account_details) + doc.save() + doc = frappe.get_doc("Supplier", "_Test Supplier") + doc.supplier_group = "Local" + doc.payment_terms = "" + doc.accounts = [] + doc.save() + doc.get_supplier_group_details() + self.assertEqual(doc.payment_terms, "_Test Payment Term Template 3") + self.assertEqual(doc.accounts[0].company, "_Test Company") + self.assertEqual(doc.accounts[0].account, "Creditors - _TC") + def test_supplier_default_payment_terms(self): # Payment Term based on Days after invoice date frappe.db.set_value( @@ -136,4 +156,4 @@ def create_supplier(**args): return doc except frappe.DuplicateEntryError: - return frappe.get_doc("Supplier", args.supplier_name) \ No newline at end of file + return frappe.get_doc("Supplier", args.supplier_name) From 872cd1cac8f5ecabd0a32f00dd0457d3fc91275c Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Fri, 18 Jun 2021 19:13:18 +0530 Subject: [PATCH 04/33] test: test cases for fetching customer group details --- .../selling/doctype/customer/test_customer.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 7761aa70fb2..8cb07aaa8ab 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -27,6 +27,38 @@ class TestCustomer(unittest.TestCase): def tearDown(self): set_credit_limit('_Test Customer', '_Test Company', 0) + def test_get_customer_group_details(self): + doc = frappe.get_doc("Customer Group", "Commercial") + doc.payment_terms = "_Test Payment Term Template 3" + doc.accounts = [] + doc.default_price_list = "Standard Buying" + doc.credit_limits = [] + test_account_details = { + "company": "_Test Company", + "account": "Creditors - _TC", + } + test_credit_limits = { + "company": "_Test Company", + "credit_limit": 350000 + } + doc.append("accounts", test_account_details) + doc.append("credit_limits", test_credit_limits) + doc.save() + + doc = frappe.get_doc("Customer", "_Test Customer") + doc.customer_group = "Commercial" + doc.payment_terms = doc.default_price_list = "" + doc.accounts = doc.credit_limits= [] + doc.save() + doc.get_customer_group_details() + self.assertEqual(doc.payment_terms, "_Test Payment Term Template 3") + + self.assertEqual(doc.accounts[0].company, "_Test Company") + self.assertEqual(doc.accounts[0].account, "Creditors - _TC") + + self.assertEqual(doc.credit_limits[0].company, "_Test Company") + self.assertEqual(doc.credit_limits[0].credit_limit, 350000 ) + def test_party_details(self): from erpnext.accounts.party import get_party_details From e60a349432f34c2dd5c72a8ac4147aca95f99fa5 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Fri, 2 Jul 2021 19:35:50 +0530 Subject: [PATCH 05/33] test: updated test cases --- .../buying/doctype/supplier/test_supplier.py | 24 ++++++++------- .../selling/doctype/customer/test_customer.py | 30 +++++++++++-------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py index faa813aa4c4..89804662700 100644 --- a/erpnext/buying/doctype/supplier/test_supplier.py +++ b/erpnext/buying/doctype/supplier/test_supplier.py @@ -14,7 +14,8 @@ test_records = frappe.get_test_records('Supplier') class TestSupplier(unittest.TestCase): def test_get_supplier_group_details(self): - doc = frappe.get_doc("Supplier Group", "Local") + doc = frappe.new_doc("Supplier Group") + doc.supplier_group_name = "_Testing Supplier Group" doc.payment_terms = "_Test Payment Term Template 3" doc.accounts = [] test_account_details = { @@ -23,15 +24,18 @@ class TestSupplier(unittest.TestCase): } doc.append("accounts", test_account_details) doc.save() - doc = frappe.get_doc("Supplier", "_Test Supplier") - doc.supplier_group = "Local" - doc.payment_terms = "" - doc.accounts = [] - doc.save() - doc.get_supplier_group_details() - self.assertEqual(doc.payment_terms, "_Test Payment Term Template 3") - self.assertEqual(doc.accounts[0].company, "_Test Company") - self.assertEqual(doc.accounts[0].account, "Creditors - _TC") + s_doc = frappe.new_doc("Supplier") + s_doc.supplier_name = "Testing Supplier" + s_doc.supplier_group = "_Testing Supplier Group" + s_doc.payment_terms = "" + s_doc.accounts = [] + s_doc.insert() + s_doc.get_supplier_group_details() + self.assertEqual(s_doc.payment_terms, "_Test Payment Term Template 3") + self.assertEqual(s_doc.accounts[0].company, "_Test Company") + self.assertEqual(s_doc.accounts[0].account, "Creditors - _TC") + s_doc.delete() + doc.delete() def test_supplier_default_payment_terms(self): # Payment Term based on Days after invoice date diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 8cb07aaa8ab..b1a5b52f963 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -28,7 +28,8 @@ class TestCustomer(unittest.TestCase): set_credit_limit('_Test Customer', '_Test Company', 0) def test_get_customer_group_details(self): - doc = frappe.get_doc("Customer Group", "Commercial") + doc = frappe.new_doc("Customer Group") + doc.customer_group_name = "_Testing Customer Group" doc.payment_terms = "_Test Payment Term Template 3" doc.accounts = [] doc.default_price_list = "Standard Buying" @@ -43,21 +44,24 @@ class TestCustomer(unittest.TestCase): } doc.append("accounts", test_account_details) doc.append("credit_limits", test_credit_limits) - doc.save() + doc.insert() - doc = frappe.get_doc("Customer", "_Test Customer") - doc.customer_group = "Commercial" - doc.payment_terms = doc.default_price_list = "" - doc.accounts = doc.credit_limits= [] - doc.save() - doc.get_customer_group_details() - self.assertEqual(doc.payment_terms, "_Test Payment Term Template 3") + c_doc = frappe.new_doc("Customer") + c_doc.customer_name = "Testing Customer" + c_doc.customer_group = "_Testing Customer Group" + c_doc.payment_terms = c_doc.default_price_list = "" + c_doc.accounts = c_doc.credit_limits= [] + c_doc.insert() + c_doc.get_customer_group_details() + self.assertEqual(c_doc.payment_terms, "_Test Payment Term Template 3") - self.assertEqual(doc.accounts[0].company, "_Test Company") - self.assertEqual(doc.accounts[0].account, "Creditors - _TC") + self.assertEqual(c_doc.accounts[0].company, "_Test Company") + self.assertEqual(c_doc.accounts[0].account, "Creditors - _TC") - self.assertEqual(doc.credit_limits[0].company, "_Test Company") - self.assertEqual(doc.credit_limits[0].credit_limit, 350000 ) + self.assertEqual(c_doc.credit_limits[0].company, "_Test Company") + self.assertEqual(c_doc.credit_limits[0].credit_limit, 350000) + c_doc.delete() + doc.delete() def test_party_details(self): from erpnext.accounts.party import get_party_details From a1a4e8d6168fd3a9f5c50ef2f5fec6140da8190c Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Fri, 2 Jul 2021 22:02:07 +0530 Subject: [PATCH 06/33] fix: Sider --- erpnext/buying/doctype/supplier/supplier.js | 4 ++-- erpnext/selling/doctype/customer/customer.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index af6401b3fe6..1766c2c80cc 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -72,8 +72,8 @@ frappe.ui.form.on("Supplier", { frappe.call({ method: "get_supplier_group_details", doc: frm.doc, - callback: function(r){ - frm.refresh() + callback: function() { + frm.refresh(); } }); }, diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 91944adef3b..28494662673 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -153,8 +153,8 @@ frappe.ui.form.on("Customer", { frappe.call({ method: "get_customer_group_details", doc: frm.doc, - callback: function(r){ - frm.refresh() + callback: function() { + frm.refresh(); } }); From 449c58d809e2093bd930cba88e36de08d83b7c7c Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Mon, 12 Jul 2021 09:18:19 +0530 Subject: [PATCH 07/33] refactor: suggested changes --- erpnext/buying/doctype/supplier/supplier.py | 6 ++-- erpnext/selling/doctype/customer/customer.py | 29 +++++++++----------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index 791f71ed3b0..fd16b23c220 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -57,16 +57,16 @@ class Supplier(TransactionBase): self.payment_terms = "" self.accounts = [] - if not self.accounts and doc.accounts: + if doc.accounts: for account in doc.accounts: child = self.append('accounts') child.company = account.company child.account = account.account - self.save() - if not self.payment_terms and doc.payment_terms: + if doc.payment_terms: self.payment_terms = doc.payment_terms + self.save() def validate_internal_supplier(self): internal_supplier = frappe.db.get_value("Supplier", diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index cdeb0896189..3b62081e24c 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -84,25 +84,22 @@ class Customer(TransactionBase): self.accounts = self.credit_limits = [] self.payment_terms = self.default_price_list = "" - if not self.accounts and doc.accounts: - for account in doc.accounts: - child = self.append('accounts') - child.company = account.company - child.account = account.account - self.save() + tables = [["accounts", "account"], ["credit_limits", "credit_limit"]] + fields = ["payment_terms", "default_price_list"] - if not self.credit_limits and doc.credit_limits: - for credit in doc.credit_limits: - child = self.append('credit_limits') - child.company = credit.company - child.credit_limit = credit.credit_limit - self.save() + for row in tables: + table, field = row[0], row[1] + if not doc.get(table): continue - if not self.payment_terms and doc.payment_terms: - self.payment_terms = doc.payment_terms + for entry in doc.get(table): + child = self.append(table) + child.update({"company": entry.company, field: entry.get(field)}) - if not self.default_price_list and doc.default_price_list: - self.default_price_list = doc.default_price_list + for field in fields: + if not doc.get(field): continue + self.update({field: doc.get(field)}) + + self.save() def check_customer_group_change(self): frappe.flags.customer_group_changed = False From f08d7410be047be5e70cac7ff0a28018cf9c79ff Mon Sep 17 00:00:00 2001 From: Pruthvi Patel Date: Tue, 24 Aug 2021 12:07:38 +0530 Subject: [PATCH 08/33] refactor: use `read_only_depends_on` instead of code (#27008) (cherry picked from commit 332ac105b5ffec04009539eba94b153a8b93c0c4) # Conflicts: # erpnext/accounts/doctype/pos_invoice/pos_invoice.js # erpnext/accounts/doctype/sales_invoice/sales_invoice.js --- erpnext/accounts/doctype/pos_invoice/pos_invoice.js | 9 +++++---- erpnext/accounts/doctype/pos_invoice/pos_invoice.json | 5 +++-- .../accounts/doctype/sales_invoice/sales_invoice.js | 11 +++++------ .../accounts/doctype/sales_invoice/sales_invoice.json | 5 +++-- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index 04e392a0f27..d39326ff521 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -110,17 +110,18 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend( this.frm.refresh_field("base_paid_amount"); }, +<<<<<<< HEAD write_off_outstanding_amount_automatically: function() { if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { +======= + write_off_outstanding_amount_automatically() { + if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) { +>>>>>>> 332ac105b5 (refactor: use `read_only_depends_on` instead of code (#27008)) frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]); // this will make outstanding amount 0 this.frm.set_value("write_off_amount", flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount")) ); - this.frm.toggle_enable("write_off_amount", false); - - } else { - this.frm.toggle_enable("write_off_amount", true); } this.calculate_outstanding_amount(false); diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index fcccb39b70c..3e22b9e00a8 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -1183,7 +1183,8 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "read_only_depends_on": "eval: doc.write_off_outstanding_amount_automatically" }, { "fieldname": "base_write_off_amount", @@ -1554,7 +1555,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2021-08-17 20:13:44.255437", + "modified": "2021-08-18 16:13:52.080543", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 568e7721a39..7527d02f4ca 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -323,17 +323,18 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte this.frm.refresh_fields(); }, +<<<<<<< HEAD write_off_outstanding_amount_automatically: function() { if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { +======= + write_off_outstanding_amount_automatically() { + if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) { +>>>>>>> 332ac105b5 (refactor: use `read_only_depends_on` instead of code (#27008)) frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]); // this will make outstanding amount 0 this.frm.set_value("write_off_amount", flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount")) ); - this.frm.toggle_enable("write_off_amount", false); - - } else { - this.frm.toggle_enable("write_off_amount", true); } this.calculate_outstanding_amount(false); @@ -787,8 +788,6 @@ frappe.ui.form.on('Sales Invoice', { if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']); else hide_field(['c_form_applicable', 'c_form_no']); - frm.toggle_enable("write_off_amount", !!!cint(doc.write_off_outstanding_amount_automatically)); - frm.refresh_fields(); }, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 4c7a6b51ac7..01ae713cd36 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1444,7 +1444,8 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "read_only_depends_on": "eval:doc.write_off_outstanding_amount_automatically" }, { "fieldname": "base_write_off_amount", @@ -2016,7 +2017,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2021-08-17 20:16:12.737743", + "modified": "2021-08-18 16:07:45.122570", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", From 0a23328151c6bb3ca684af6798e236c0c969a108 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 24 Aug 2021 12:16:46 +0530 Subject: [PATCH 09/33] fix: stock analytics report date range issues and add company filter (#27014) * test: tests for correct get_period_date_ranges * fix: stock analytics report date range issues - Upon selecting second half of month with Monthly filter, data from that period was missing. - Solution: "round down" the date as per expected frequency. * chore: drop py2 and fix misleading docstring * test: fix test to avoid FY clash * feat: add company filter in stock analytics report [skip ci] Co-authored-by: Marica (cherry picked from commit 0dff0beabaa937e397eae2f930bae337cfe8af82) --- .../report/stock_analytics/stock_analytics.js | 16 +++++++- .../report/stock_analytics/stock_analytics.py | 37 ++++++++++++++++--- .../stock_analytics/test_stock_analytics.py | 35 ++++++++++++++++++ 3 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 erpnext/stock/report/stock_analytics/test_stock_analytics.py diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.js b/erpnext/stock/report/stock_analytics/stock_analytics.js index 6b384e28611..78afe6d2642 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.js +++ b/erpnext/stock/report/stock_analytics/stock_analytics.js @@ -36,12 +36,26 @@ frappe.query_reports["Stock Analytics"] = { options:"Brand", default: "", }, + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, { fieldname: "warehouse", label: __("Warehouse"), fieldtype: "Link", - options:"Warehouse", + options: "Warehouse", default: "", + get_query: function() { + const company = frappe.query_report.get_filter_value('company'); + return { + filters: { 'company': company } + } + } }, { fieldname: "from_date", diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py index d62abed91f3..a1e1e7fce7c 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.py +++ b/erpnext/stock/report/stock_analytics/stock_analytics.py @@ -1,14 +1,15 @@ # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +import datetime -from __future__ import unicode_literals import frappe from frappe import _, scrub -from frappe.utils import getdate, flt +from frappe.utils import getdate, get_quarter_start, get_first_day_of_week +from frappe.utils import get_first_day as get_first_day_of_month + from erpnext.stock.report.stock_balance.stock_balance import (get_items, get_stock_ledger_entries, get_item_details) from erpnext.accounts.utils import get_fiscal_year from erpnext.stock.utils import is_reposting_item_valuation_in_progress -from six import iteritems def execute(filters=None): is_reposting_item_valuation_in_progress() @@ -71,7 +72,8 @@ def get_columns(filters): def get_period_date_ranges(filters): from dateutil.relativedelta import relativedelta - from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) + from_date = round_down_to_nearest_frequency(filters.from_date, filters.range) + to_date = getdate(filters.to_date) increment = { "Monthly": 1, @@ -97,6 +99,31 @@ def get_period_date_ranges(filters): return periodic_daterange + +def round_down_to_nearest_frequency(date: str, frequency: str) -> datetime.datetime: + """Rounds down the date to nearest frequency unit. + example: + + >>> round_down_to_nearest_frequency("2021-02-21", "Monthly") + datetime.datetime(2021, 2, 1) + + >>> round_down_to_nearest_frequency("2021-08-21", "Yearly") + datetime.datetime(2021, 1, 1) + """ + + def _get_first_day_of_fiscal_year(date): + fiscal_year = get_fiscal_year(date) + return fiscal_year and fiscal_year[1] or date + + round_down_function = { + "Monthly": get_first_day_of_month, + "Quarterly": get_quarter_start, + "Weekly": get_first_day_of_week, + "Yearly": _get_first_day_of_fiscal_year, + }.get(frequency, getdate) + return round_down_function(date) + + def get_period(posting_date, filters): months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] @@ -177,7 +204,7 @@ def get_data(filters): periodic_data = get_periodic_data(sle, filters) ranges = get_period_date_ranges(filters) - for dummy, item_data in iteritems(item_details): + for dummy, item_data in item_details.items(): row = { "name": item_data.name, "item_name": item_data.item_name, diff --git a/erpnext/stock/report/stock_analytics/test_stock_analytics.py b/erpnext/stock/report/stock_analytics/test_stock_analytics.py new file mode 100644 index 00000000000..00e268b4e0e --- /dev/null +++ b/erpnext/stock/report/stock_analytics/test_stock_analytics.py @@ -0,0 +1,35 @@ +import datetime +import unittest + +from frappe import _dict +from erpnext.accounts.utils import get_fiscal_year + +from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges + + +class TestStockAnalyticsReport(unittest.TestCase): + def test_get_period_date_ranges(self): + + filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06") + + ranges = get_period_date_ranges(filters) + + expected_ranges = [ + [datetime.date(2020, 12, 1), datetime.date(2020, 12, 31)], + [datetime.date(2021, 1, 1), datetime.date(2021, 1, 31)], + [datetime.date(2021, 2, 1), datetime.date(2021, 2, 6)], + ] + + self.assertEqual(ranges, expected_ranges) + + def test_get_period_date_ranges_yearly(self): + + filters = _dict(range="Yearly", from_date="2021-01-28", to_date="2021-02-06") + + ranges = get_period_date_ranges(filters) + first_date = get_fiscal_year("2021-01-28")[1] + expected_ranges = [ + [first_date, datetime.date(2021, 2, 6)], + ] + + self.assertEqual(ranges, expected_ranges) From 1e3a6a8a9806ddde6762d3b8e2b0798d246d2492 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 24 Aug 2021 12:20:51 +0530 Subject: [PATCH 10/33] fix: discard empty rows from update items (#27021) (#27095) (cherry picked from commit 6de7b8ea93e3ffe621976ebf3b613406a379216d) Co-authored-by: Ankush Menat --- erpnext/controllers/accounts_controller.py | 5 +++++ erpnext/public/js/utils.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 5e79eb6fae8..219da37a687 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1841,6 +1841,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil for d in data: new_child_flag = False + + if not d.get("item_code"): + # ignore empty rows + continue + if not d.get("docname"): new_child_flag = True check_doc_permissions(parent, 'create') diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index d16b6d6d19a..58307a0b74d 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -563,7 +563,7 @@ erpnext.utils.update_child_items = function(opts) { }, ], primary_action: function() { - const trans_items = this.get_values()["trans_items"]; + const trans_items = this.get_values()["trans_items"].filter((item) => !!item.item_code); frappe.call({ method: 'erpnext.controllers.accounts_controller.update_child_qty_rate', freeze: true, From cc7ed1573a5e8f665696f3f1c1bb14c3bf6b36d0 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 24 Aug 2021 12:20:34 +0530 Subject: [PATCH 11/33] fix(ux): keep stock entry title & purpose in sync (#27043) Co-authored-by: rohitwaghchaure (cherry picked from commit c09d8a28098ca2963122373ae18f0c8c1954fcf3) --- erpnext/stock/doctype/stock_entry/stock_entry.json | 4 +--- erpnext/stock/doctype/stock_entry/stock_entry.py | 9 +++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index e6ce3c851ff..2f377788961 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -84,8 +84,6 @@ "oldfieldtype": "Section Break" }, { - "allow_on_submit": 1, - "default": "{purpose}", "fieldname": "title", "fieldtype": "Data", "hidden": 1, @@ -630,7 +628,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-08-17 20:16:12.737743", + "modified": "2021-08-20 19:19:31.514846", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 2015aa198be..2b2a80ce109 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -58,6 +58,7 @@ class StockEntry(StockController): self.validate_posting_time() self.validate_purpose() + self.set_title() self.validate_item() self.validate_customer_provided_item() self.validate_qty() @@ -1608,6 +1609,14 @@ class StockEntry(StockController): return sorted(list(set(get_serial_nos(self.pro_doc.serial_no)) - set(used_serial_nos))) + def set_title(self): + if frappe.flags.in_import and self.title: + # Allow updating title during data import/update + return + + self.title = self.purpose + + @frappe.whitelist() def move_sample_to_retention_warehouse(company, items): if isinstance(items, string_types): From ac0800511d48766193d4253387917c8c76973b1f Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 24 Aug 2021 12:59:07 +0530 Subject: [PATCH 12/33] fix: resolved conflicts --- erpnext/accounts/doctype/pos_invoice/pos_invoice.js | 5 ----- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 5 ----- 2 files changed, 10 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index d39326ff521..33f04cbaa63 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -110,13 +110,8 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend( this.frm.refresh_field("base_paid_amount"); }, -<<<<<<< HEAD - write_off_outstanding_amount_automatically: function() { - if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { -======= write_off_outstanding_amount_automatically() { if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) { ->>>>>>> 332ac105b5 (refactor: use `read_only_depends_on` instead of code (#27008)) frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]); // this will make outstanding amount 0 this.frm.set_value("write_off_amount", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 7527d02f4ca..a98a83a0476 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -323,13 +323,8 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte this.frm.refresh_fields(); }, -<<<<<<< HEAD - write_off_outstanding_amount_automatically: function() { - if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { -======= write_off_outstanding_amount_automatically() { if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) { ->>>>>>> 332ac105b5 (refactor: use `read_only_depends_on` instead of code (#27008)) frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]); // this will make outstanding amount 0 this.frm.set_value("write_off_amount", From 0476accf262b02af368da497d138c02c8a9493f8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 24 Aug 2021 16:27:10 +0530 Subject: [PATCH 13/33] fix: Payment Reconciliation link in Accounting Workspace (#27085) --- .../workspace/accounting/accounting.json | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json index 10a4001502f..7e3ecaf3ab6 100644 --- a/erpnext/accounts/workspace/accounting/accounting.json +++ b/erpnext/accounts/workspace/accounting/accounting.json @@ -588,7 +588,7 @@ { "hidden": 0, "is_query_report": 0, - "label": "Bank Statement", + "label": "Banking and Payments", "onboard": 0, "type": "Card Break" }, @@ -642,6 +642,24 @@ "onboard": 0, "type": "Link" }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payment Entry", + "link_to": "Payment Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payment Reconciliation", + "link_to": "Payment Reconciliation", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, { "hidden": 0, "is_query_report": 0, @@ -1064,7 +1082,7 @@ "type": "Link" } ], - "modified": "2021-06-10 03:17:31.427945", + "modified": "2021-08-23 16:06:34.167267", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", From 27fad29ad63367b04ef160b59d231a7a9353e397 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 24 Aug 2021 17:54:28 +0530 Subject: [PATCH 14/33] refactor: Healthcare Redesign Changes (#27100) * chore: reorder workspace cards * fix: Patient Progress Page UI * feat: redesign Patient History * fix: redesign patient history card * fix: filter style * fix: sider * fix: patient history and patient progress links * fix: change percentage/donut charts to bar charts -percentage charts broken in redesign * fix(style): patient progress heatmap * chore: semgrep and translation fixes * fix: patient progress page card views * fix: tests --- .../clinical_procedures.json | 6 +- .../clinical_procedures_status.json | 6 +- .../dashboard_chart/diagnoses/diagnoses.json | 7 +- .../dashboard_chart/lab_tests/lab_tests.json | 6 +- .../dashboard_chart/symptoms/symptoms.json | 6 +- .../healthcare/doctype/lab_test/lab_test.py | 8 +- .../test_patient_appointment.py | 19 +- .../patient_history_settings.py | 6 +- .../test_patient_history_settings.py | 7 +- .../healthcare/healthcare.json | 2 +- .../create_healthcare_practitioner.json | 4 +- .../create_patient/create_patient.json | 6 +- .../create_practitioner_schedule.json | 6 +- .../explore_clinical_procedure_templates.json | 4 +- .../explore_healthcare_settings.json | 4 +- ...troduction_to_healthcare_practitioner.json | 6 +- .../page/patient_history/patient_history.css | 42 +- .../page/patient_history/patient_history.html | 38 +- .../page/patient_history/patient_history.js | 774 ++++++++++-------- .../patient_history_sidebar.html | 21 + .../patient_progress/patient_progress.css | 8 +- .../patient_progress/patient_progress.html | 13 +- .../page/patient_progress/patient_progress.js | 65 +- .../report/lab_test_report/lab_test_report.py | 2 +- erpnext/healthcare/utils.py | 165 ++-- .../workspace/healthcare/healthcare.json | 179 ++-- 26 files changed, 803 insertions(+), 607 deletions(-) create mode 100644 erpnext/healthcare/page/patient_history/patient_history_sidebar.html diff --git a/erpnext/healthcare/dashboard_chart/clinical_procedures/clinical_procedures.json b/erpnext/healthcare/dashboard_chart/clinical_procedures/clinical_procedures.json index a59f149ee56..68035281561 100644 --- a/erpnext/healthcare/dashboard_chart/clinical_procedures/clinical_procedures.json +++ b/erpnext/healthcare/dashboard_chart/clinical_procedures/clinical_procedures.json @@ -12,15 +12,15 @@ "idx": 0, "is_public": 1, "is_standard": 1, - "last_synced_on": "2020-07-22 13:22:47.008622", - "modified": "2020-07-22 13:36:48.114479", + "last_synced_on": "2021-01-30 21:03:30.086891", + "modified": "2021-02-01 13:36:04.469863", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedures", "number_of_groups": 0, "owner": "Administrator", "timeseries": 0, - "type": "Percentage", + "type": "Bar", "use_report_chart": 0, "y_axis": [] } \ No newline at end of file diff --git a/erpnext/healthcare/dashboard_chart/clinical_procedures_status/clinical_procedures_status.json b/erpnext/healthcare/dashboard_chart/clinical_procedures_status/clinical_procedures_status.json index 6d560f74bf1..dae9db19b8d 100644 --- a/erpnext/healthcare/dashboard_chart/clinical_procedures_status/clinical_procedures_status.json +++ b/erpnext/healthcare/dashboard_chart/clinical_procedures_status/clinical_procedures_status.json @@ -12,15 +12,15 @@ "idx": 0, "is_public": 1, "is_standard": 1, - "last_synced_on": "2020-07-22 13:22:46.691764", - "modified": "2020-07-22 13:40:17.215775", + "last_synced_on": "2021-02-01 13:36:38.787783", + "modified": "2021-02-01 13:37:18.718275", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedures Status", "number_of_groups": 0, "owner": "Administrator", "timeseries": 0, - "type": "Pie", + "type": "Bar", "use_report_chart": 0, "y_axis": [] } \ No newline at end of file diff --git a/erpnext/healthcare/dashboard_chart/diagnoses/diagnoses.json b/erpnext/healthcare/dashboard_chart/diagnoses/diagnoses.json index 0195aac8b73..82145d60248 100644 --- a/erpnext/healthcare/dashboard_chart/diagnoses/diagnoses.json +++ b/erpnext/healthcare/dashboard_chart/diagnoses/diagnoses.json @@ -5,21 +5,22 @@ "docstatus": 0, "doctype": "Dashboard Chart", "document_type": "Patient Encounter Diagnosis", + "dynamic_filters_json": "", "filters_json": "[]", "group_by_based_on": "diagnosis", "group_by_type": "Count", "idx": 0, "is_public": 1, "is_standard": 1, - "last_synced_on": "2020-07-22 13:22:47.895521", - "modified": "2020-07-22 13:43:32.369481", + "last_synced_on": "2021-01-30 21:03:33.729487", + "modified": "2021-02-01 13:34:57.385335", "modified_by": "Administrator", "module": "Healthcare", "name": "Diagnoses", "number_of_groups": 0, "owner": "Administrator", "timeseries": 0, - "type": "Percentage", + "type": "Bar", "use_report_chart": 0, "y_axis": [] } \ No newline at end of file diff --git a/erpnext/healthcare/dashboard_chart/lab_tests/lab_tests.json b/erpnext/healthcare/dashboard_chart/lab_tests/lab_tests.json index 052483533e9..70293b158ed 100644 --- a/erpnext/healthcare/dashboard_chart/lab_tests/lab_tests.json +++ b/erpnext/healthcare/dashboard_chart/lab_tests/lab_tests.json @@ -12,15 +12,15 @@ "idx": 0, "is_public": 1, "is_standard": 1, - "last_synced_on": "2020-07-22 13:22:47.344055", - "modified": "2020-07-22 13:37:34.490129", + "last_synced_on": "2021-01-30 21:03:28.272914", + "modified": "2021-02-01 13:36:08.391433", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Tests", "number_of_groups": 0, "owner": "Administrator", "timeseries": 0, - "type": "Percentage", + "type": "Bar", "use_report_chart": 0, "y_axis": [] } \ No newline at end of file diff --git a/erpnext/healthcare/dashboard_chart/symptoms/symptoms.json b/erpnext/healthcare/dashboard_chart/symptoms/symptoms.json index 8fc86a1c592..65e5472aa10 100644 --- a/erpnext/healthcare/dashboard_chart/symptoms/symptoms.json +++ b/erpnext/healthcare/dashboard_chart/symptoms/symptoms.json @@ -12,15 +12,15 @@ "idx": 0, "is_public": 1, "is_standard": 1, - "last_synced_on": "2020-07-22 13:22:47.296748", - "modified": "2020-07-22 13:40:59.655129", + "last_synced_on": "2021-01-30 21:03:32.067473", + "modified": "2021-02-01 13:35:30.953718", "modified_by": "Administrator", "module": "Healthcare", "name": "Symptoms", "number_of_groups": 0, "owner": "Administrator", "timeseries": 0, - "type": "Percentage", + "type": "Bar", "use_report_chart": 0, "y_axis": [] } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 4b57cd073d0..74495a85910 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -34,7 +34,7 @@ class LabTest(Document): frappe.db.set_value('Lab Prescription', self.prescription, 'lab_test_created', 1) if frappe.db.get_value('Lab Prescription', self.prescription, 'invoiced'): self.invoiced = True - if not self.lab_test_name and self.template: + if self.template: self.load_test_from_template() self.reload() @@ -50,7 +50,7 @@ class LabTest(Document): item.secondary_uom_result = float(item.result_value) * float(item.conversion_factor) except: item.secondary_uom_result = '' - frappe.msgprint(_('Row #{0}: Result for Secondary UOM not calculated'.format(item.idx)), title = _('Warning')) + frappe.msgprint(_('Row #{0}: Result for Secondary UOM not calculated').format(item.idx), title = _('Warning')) def validate_result_values(self): if self.normal_test_items: @@ -229,9 +229,9 @@ def create_sample_doc(template, patient, invoice, company = None): sample_collection = frappe.get_doc('Sample Collection', sample_exists[0][0]) quantity = int(sample_collection.sample_qty) + int(template.sample_qty) if template.sample_details: - sample_details = sample_collection.sample_details + '\n-\n' + _('Test: ') + sample_details = sample_collection.sample_details + '\n-\n' + _('Test :') sample_details += (template.get('lab_test_name') or template.get('template')) + '\n' - sample_details += _('Collection Details: ') + '\n\t' + template.sample_details + sample_details += _('Collection Details:') + '\n\t' + template.sample_details frappe.db.set_value('Sample Collection', sample_collection.name, 'sample_details', sample_details) frappe.db.set_value('Sample Collection', sample_collection.name, 'sample_qty', quantity) diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py index bb5abd53ba7..39c7ece285b 100644 --- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py @@ -62,12 +62,13 @@ class TestPatientAppointment(unittest.TestCase): def test_auto_invoicing_based_on_department(self): patient, practitioner = create_healthcare_docs() + medical_department = create_medical_department() frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0) frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) - appointment_type = create_appointment_type() + appointment_type = create_appointment_type({'medical_department': medical_department}) appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), - invoice=1, appointment_type=appointment_type.name, department='_Test Medical Department') + invoice=1, appointment_type=appointment_type.name, department=medical_department) appointment.reload() self.assertEqual(appointment.invoiced, 1) @@ -89,9 +90,9 @@ class TestPatientAppointment(unittest.TestCase): 'op_consulting_charge': 300 }] appointment_type = create_appointment_type(args={ - 'name': 'Generic Appointment Type charge', - 'items': items - }) + 'name': 'Generic Appointment Type charge', + 'items': items + }) appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), invoice=1, appointment_type=appointment_type.name) @@ -360,9 +361,9 @@ def create_appointment_type(args=None): else: item = create_healthcare_service_items() items = [{ - 'medical_department': '_Test Medical Department', - 'op_consulting_charge_item': item, - 'op_consulting_charge': 200 + 'medical_department': args.get('medical_department') or '_Test Medical Department', + 'op_consulting_charge_item': item, + 'op_consulting_charge': 200 }] return frappe.get_doc({ 'doctype': 'Appointment Type', @@ -372,6 +373,8 @@ def create_appointment_type(args=None): 'price_list': args.get('price_list') or frappe.db.get_value("Price List", {"selling": 1}), 'items': args.get('items') or items }).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)}' diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py index 63b00859d71..9e0d3c3e278 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py @@ -18,7 +18,7 @@ class PatientHistorySettings(Document): def validate_submittable_doctypes(self): for entry in self.custom_doctypes: if not cint(frappe.db.get_value('DocType', entry.document_type, 'is_submittable')): - msg = _('Row #{0}: Document Type {1} is not submittable. ').format( + msg = _('Row #{0}: Document Type {1} is not submittable.').format( entry.idx, frappe.bold(entry.document_type)) msg += _('Patient Medical Record can only be created for submittable document types.') frappe.throw(msg) @@ -116,12 +116,12 @@ def set_subject_field(doc): fieldname = entry.get('fieldname') if entry.get('fieldtype') == 'Table' and doc.get(fieldname): formatted_value = get_formatted_value_for_table_field(doc.get(fieldname), meta.get_field(fieldname)) - subject += frappe.bold(_(entry.get('label')) + ': ') + '
' + cstr(formatted_value) + '
' + subject += frappe.bold(_(entry.get('label')) + ':') + '
' + cstr(formatted_value) + '
' else: if doc.get(fieldname): formatted_value = format_value(doc.get(fieldname), meta.get_field(fieldname), doc) - subject += frappe.bold(_(entry.get('label')) + ': ') + cstr(formatted_value) + '
' + subject += frappe.bold(_(entry.get('label')) + ':') + cstr(formatted_value) + '
' return subject diff --git a/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py index f523cd5edea..9169ea642b9 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py +++ b/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe import unittest import json -from frappe.utils import getdate +from frappe.utils import getdate, strip_html from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient class TestPatientHistorySettings(unittest.TestCase): @@ -38,15 +38,14 @@ class TestPatientHistorySettings(unittest.TestCase): # tests for medical record creation of standard doctypes in test_patient_medical_record.py patient = create_patient() doc = create_doc(patient) - # check for medical record medical_rec = frappe.db.exists("Patient Medical Record", {"status": "Open", "reference_name": doc.name}) self.assertTrue(medical_rec) medical_rec = frappe.get_doc("Patient Medical Record", medical_rec) - expected_subject = "Date: {0}
Rating: 3
Feedback: Test Patient History Settings
".format( + expected_subject = "Date:{0}Rating:3Feedback:Test Patient History Settings".format( frappe.utils.format_date(getdate())) - self.assertEqual(medical_rec.subject, expected_subject) + self.assertEqual(strip_html(medical_rec.subject), expected_subject) self.assertEqual(medical_rec.patient, patient) self.assertEqual(medical_rec.communication_date, getdate()) diff --git a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json index 56c3c135599..0aa8f9a027e 100644 --- a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json +++ b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json @@ -10,7 +10,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/healthcare", "idx": 0, "is_complete": 0, - "modified": "2020-07-08 14:06:19.512946", + "modified": "2021-01-30 19:22:20.273766", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", diff --git a/erpnext/healthcare/onboarding_step/create_healthcare_practitioner/create_healthcare_practitioner.json b/erpnext/healthcare/onboarding_step/create_healthcare_practitioner/create_healthcare_practitioner.json index c45a347080c..3f25a9d6760 100644 --- a/erpnext/healthcare/onboarding_step/create_healthcare_practitioner/create_healthcare_practitioner.json +++ b/erpnext/healthcare/onboarding_step/create_healthcare_practitioner/create_healthcare_practitioner.json @@ -5,14 +5,14 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-26 23:16:31.965521", + "modified": "2021-01-30 12:02:22.849260", "modified_by": "Administrator", "name": "Create Healthcare Practitioner", "owner": "Administrator", "reference_document": "Healthcare Practitioner", + "show_form_tour": 0, "show_full_form": 1, "title": "Create Healthcare Practitioner", "validate_action": 1 diff --git a/erpnext/healthcare/onboarding_step/create_patient/create_patient.json b/erpnext/healthcare/onboarding_step/create_patient/create_patient.json index 77bc5bd7adf..b46bb15b48a 100644 --- a/erpnext/healthcare/onboarding_step/create_patient/create_patient.json +++ b/erpnext/healthcare/onboarding_step/create_patient/create_patient.json @@ -5,14 +5,14 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 12:26:24.023418", - "modified_by": "Administrator", + "modified": "2021-01-30 00:09:28.786428", + "modified_by": "ruchamahabal2@gmail.com", "name": "Create Patient", "owner": "Administrator", "reference_document": "Patient", + "show_form_tour": 0, "show_full_form": 1, "title": "Create Patient", "validate_action": 1 diff --git a/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json b/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json index 65980ef6687..7ce122d5c0b 100644 --- a/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json +++ b/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json @@ -5,14 +5,14 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 12:27:09.437825", - "modified_by": "Administrator", + "modified": "2021-01-30 00:09:28.794602", + "modified_by": "ruchamahabal2@gmail.com", "name": "Create Practitioner Schedule", "owner": "Administrator", "reference_document": "Practitioner Schedule", + "show_form_tour": 0, "show_full_form": 1, "title": "Create Practitioner Schedule", "validate_action": 1 diff --git a/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json b/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json index 697b761e528..dfe9f71a76f 100644 --- a/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json +++ b/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json @@ -5,14 +5,14 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-26 23:10:24.504030", + "modified": "2021-01-30 19:22:08.257160", "modified_by": "Administrator", "name": "Explore Clinical Procedure Templates", "owner": "Administrator", "reference_document": "Clinical Procedure Template", + "show_form_tour": 0, "show_full_form": 0, "title": "Explore Clinical Procedure Templates", "validate_action": 1 diff --git a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json index b2d5aef4312..2d952f30938 100644 --- a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json +++ b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json @@ -5,14 +5,14 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 1, "is_single": 1, "is_skipped": 0, - "modified": "2020-05-26 23:10:24.507648", + "modified": "2021-01-30 19:22:07.275735", "modified_by": "Administrator", "name": "Explore Healthcare Settings", "owner": "Administrator", "reference_document": "Healthcare Settings", + "show_form_tour": 0, "show_full_form": 0, "title": "Explore Healthcare Settings", "validate_action": 1 diff --git a/erpnext/healthcare/onboarding_step/introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json b/erpnext/healthcare/onboarding_step/introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json index fa4c9036d7e..baa8358c060 100644 --- a/erpnext/healthcare/onboarding_step/introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json +++ b/erpnext/healthcare/onboarding_step/introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json @@ -6,14 +6,14 @@ "field": "schedule", "idx": 0, "is_complete": 0, - "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-26 22:07:07.482530", - "modified_by": "Administrator", + "modified": "2021-01-30 00:09:28.807129", + "modified_by": "ruchamahabal2@gmail.com", "name": "Introduction to Healthcare Practitioner", "owner": "Administrator", "reference_document": "Healthcare Practitioner", + "show_form_tour": 0, "show_full_form": 0, "title": "Introduction to Healthcare Practitioner", "validate_action": 0 diff --git a/erpnext/healthcare/page/patient_history/patient_history.css b/erpnext/healthcare/page/patient_history/patient_history.css index 1bb589164e6..74b5e7eb918 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.css +++ b/erpnext/healthcare/page/patient_history/patient_history.css @@ -9,6 +9,26 @@ cursor: pointer; } +.patient-image-container { + margin-top: 17px; + } + +.patient-image { + display: inline-block; + width: 100%; + height: 0; + padding: 50% 0px; + background-size: cover; + background-repeat: no-repeat; + background-position: center center; + border-radius: 4px; +} + +.patient-name { + font-size: 20px; + margin-top: 25px; +} + .medical_record-label { max-width: 100px; margin-bottom: -4px; @@ -19,19 +39,19 @@ } .date-indicator { - background:none; - font-size:12px; - vertical-align:middle; - font-weight:bold; - color:#6c7680; + background:none; + font-size:12px; + vertical-align:middle; + font-weight:bold; + color:#6c7680; } .date-indicator::after { - margin:0 -4px 0 12px; - content:''; - display:inline-block; - height:8px; - width:8px; - border-radius:8px; + margin:0 -4px 0 12px; + content:''; + display:inline-block; + height:8px; + width:8px; + border-radius:8px; background: #d1d8dd; } diff --git a/erpnext/healthcare/page/patient_history/patient_history.html b/erpnext/healthcare/page/patient_history/patient_history.html index f1706557f45..d16b38637cd 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.html +++ b/erpnext/healthcare/page/patient_history/patient_history.html @@ -1,26 +1,18 @@ -
-
-

-
+
+
+
+
+
+
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- -
+ +
+
+
+
+
+
+
+
diff --git a/erpnext/healthcare/page/patient_history/patient_history.js b/erpnext/healthcare/page/patient_history/patient_history.js index 54343aae449..bf947cac215 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.js +++ b/erpnext/healthcare/page/patient_history/patient_history.js @@ -1,403 +1,455 @@ frappe.provide('frappe.patient_history'); frappe.pages['patient_history'].on_page_load = function(wrapper) { - let me = this; - let page = frappe.ui.make_app_page({ + frappe.ui.make_app_page({ parent: wrapper, - title: 'Patient History', - single_column: true + title: __('Patient History') }); - frappe.breadcrumbs.add('Healthcare'); - let pid = ''; - page.main.html(frappe.render_template('patient_history', {})); - page.main.find('.header-separator').hide(); - - let patient = frappe.ui.form.make_control({ - parent: page.main.find('.patient'), - df: { - fieldtype: 'Link', - options: 'Patient', - fieldname: 'patient', - placeholder: __('Select Patient'), - only_select: true, - change: function() { - let patient_id = patient.get_value(); - if (pid != patient_id && patient_id) { - me.start = 0; - me.page.main.find('.patient_documents_list').html(''); - setup_filters(patient_id, me); - get_documents(patient_id, me); - show_patient_info(patient_id, me); - show_patient_vital_charts(patient_id, me, 'bp', 'mmHg', 'Blood Pressure'); - } - pid = patient_id; - } - }, - }); - patient.refresh(); - - if (frappe.route_options) { - patient.set_value(frappe.route_options.patient); - } - - this.page.main.on('click', '.btn-show-chart', function() { - let btn_show_id = $(this).attr('data-show-chart-id'), pts = $(this).attr('data-pts'); - let title = $(this).attr('data-title'); - show_patient_vital_charts(patient.get_value(), me, btn_show_id, pts, title); - }); - - this.page.main.on('click', '.btn-more', function() { - let doctype = $(this).attr('data-doctype'), docname = $(this).attr('data-docname'); - if (me.page.main.find('.'+docname).parent().find('.document-html').attr('data-fetched') == '1') { - me.page.main.find('.'+docname).hide(); - me.page.main.find('.'+docname).parent().find('.document-html').show(); - } else { - if (doctype && docname) { - let exclude = ['patient', 'patient_name', 'patient_sex', 'encounter_date']; - frappe.call({ - method: 'erpnext.healthcare.utils.render_doc_as_html', - args:{ - doctype: doctype, - docname: docname, - exclude_fields: exclude - }, - freeze: true, - callback: function(r) { - if (r.message) { - me.page.main.find('.' + docname).hide(); - - me.page.main.find('.' + docname).parent().find('.document-html').html( - `${r.message.html} -
- - -
- `); - - me.page.main.find('.' + docname).parent().find('.document-html').show(); - me.page.main.find('.' + docname).parent().find('.document-html').attr('data-fetched', '1'); - } - } - }); - } - } - }); - - this.page.main.on('click', '.btn-less', function() { - let docname = $(this).attr('data-docname'); - me.page.main.find('.' + docname).parent().find('.document-id').show(); - me.page.main.find('.' + docname).parent().find('.document-html').hide(); - }); - me.start = 0; - me.page.main.on('click', '.btn-get-records', function() { - get_documents(patient.get_value(), me); + let patient_history = new PatientHistory(wrapper); + $(wrapper).bind('show', ()=> { + patient_history.show(); }); }; -let setup_filters = function(patient, me) { - $('.doctype-filter').empty(); - frappe.xcall( - 'erpnext.healthcare.page.patient_history.patient_history.get_patient_history_doctypes' - ).then(document_types => { - let doctype_filter = frappe.ui.form.make_control({ - parent: $('.doctype-filter'), +class PatientHistory { + constructor(wrapper) { + this.wrapper = $(wrapper); + this.page = wrapper.page; + this.sidebar = this.wrapper.find('.layout-side-section'); + this.main_section = this.wrapper.find('.layout-main-section'); + this.start = 0; + } + + show() { + frappe.breadcrumbs.add('Healthcare'); + this.sidebar.empty(); + + let me = this; + let patient = frappe.ui.form.make_control({ + parent: me.sidebar, df: { - fieldtype: 'MultiSelectList', - fieldname: 'document_type', - placeholder: __('Select Document Type'), - input_class: 'input-xs', + fieldtype: 'Link', + options: 'Patient', + fieldname: 'patient', + placeholder: __('Select Patient'), + only_select: true, change: () => { - me.start = 0; - me.page.main.find('.patient_documents_list').html(''); - get_documents(patient, me, doctype_filter.get_value(), date_range_field.get_value()); - }, - get_data: () => { - return document_types.map(document_type => { - return { - description: document_type, - value: document_type - }; - }); - }, + me.patient_id = ''; + if (me.patient_id != patient.get_value() && patient.get_value()) { + me.start = 0; + me.patient_id = patient.get_value(); + me.make_patient_profile(); + } + } } }); - doctype_filter.refresh(); + patient.refresh(); - $('.date-filter').empty(); - let date_range_field = frappe.ui.form.make_control({ - df: { - fieldtype: 'DateRange', - fieldname: 'date_range', - placeholder: __('Date Range'), - input_class: 'input-xs', - change: () => { - let selected_date_range = date_range_field.get_value(); - if (selected_date_range && selected_date_range.length === 2) { + if (frappe.route_options && !this.patient_id) { + patient.set_value(frappe.route_options.patient); + this.patient_id = frappe.route_options.patient; + } + + this.sidebar.find('[data-fieldname="patient"]').append('
'); + } + + make_patient_profile() { + this.page.set_title(__('Patient History')); + this.main_section.empty().append(frappe.render_template('patient_history')); + this.setup_filters(); + this.setup_documents(); + this.show_patient_info(); + this.setup_buttons(); + this.show_patient_vital_charts('bp', 'mmHg', 'Blood Pressure'); + } + + setup_filters() { + $('.doctype-filter').empty(); + let me = this; + + frappe.xcall( + 'erpnext.healthcare.page.patient_history.patient_history.get_patient_history_doctypes' + ).then(document_types => { + let doctype_filter = frappe.ui.form.make_control({ + parent: $('.doctype-filter'), + df: { + fieldtype: 'MultiSelectList', + fieldname: 'document_type', + placeholder: __('Select Document Type'), + change: () => { me.start = 0; me.page.main.find('.patient_documents_list').html(''); - get_documents(patient, me, doctype_filter.get_value(), selected_date_range); - } + this.setup_documents(doctype_filter.get_value(), date_range_field.get_value()); + }, + get_data: () => { + return document_types.map(document_type => { + return { + description: document_type, + value: document_type + }; + }); + }, } - }, - parent: $('.date-filter') + }); + doctype_filter.refresh(); + + $('.date-filter').empty(); + let date_range_field = frappe.ui.form.make_control({ + df: { + fieldtype: 'DateRange', + fieldname: 'date_range', + placeholder: __('Date Range'), + input_class: 'input-xs', + change: () => { + let selected_date_range = date_range_field.get_value(); + if (selected_date_range && selected_date_range.length === 2) { + me.start = 0; + me.page.main.find('.patient_documents_list').html(''); + this.setup_documents(doctype_filter.get_value(), date_range_field.get_value()); + } + } + }, + parent: $('.date-filter') + }); + date_range_field.refresh(); }); - date_range_field.refresh(); - }); -}; + } -let get_documents = function(patient, me, document_types="", selected_date_range="") { - let filters = { - name: patient, - start: me.start, - page_length: 20 - }; - if (document_types) - filters['document_types'] = document_types; - if (selected_date_range) - filters['date_range'] = selected_date_range; + setup_documents(document_types="", selected_date_range="") { + let filters = { + name: this.patient_id, + start: this.start, + page_length: 20 + }; + if (document_types) + filters['document_types'] = document_types; + if (selected_date_range) + filters['date_range'] = selected_date_range; - frappe.call({ - 'method': 'erpnext.healthcare.page.patient_history.patient_history.get_feed', - args: filters, - callback: function(r) { - let data = r.message; - if (data.length) { - add_to_records(me, data); - } else { - me.page.main.find('.patient_documents_list').append(` -
-

${__('No more records..')}

-
`); - me.page.main.find('.btn-get-records').hide(); + let me = this; + frappe.call({ + 'method': 'erpnext.healthcare.page.patient_history.patient_history.get_feed', + args: filters, + callback: function(r) { + let data = r.message; + if (data.length) { + me.add_to_records(data); + } else { + me.page.main.find('.patient_documents_list').append(` +
+

${__('No more records..')}

+
`); + me.page.main.find('.btn-get-records').hide(); + } } - } - }); -}; + }); + } -let add_to_records = function(me, data) { - let details = "
`; + } + } + + this.page.main.find('.patient_documents_list').append(details); + this.start += data.length; + + if (data.length === 20) { + this.page.main.find(".btn-get-records").show(); + } else { + this.page.main.find(".btn-get-records").hide(); + this.page.main.find(".patient_documents_list").append(` +
+

${__('No more records..')}

+
`); } } - details += ''; - me.page.main.find('.patient_documents_list').append(details); - me.start += data.length; + add_date_separator(data) { + let date = frappe.datetime.str_to_obj(data.communication_date); + let pdate = ''; + let diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), + frappe.datetime.obj_to_str(date)); - if (data.length === 20) { - me.page.main.find(".btn-get-records").show(); - } else { - me.page.main.find(".btn-get-records").hide(); - me.page.main.find(".patient_documents_list").append(` -
-

${__('No more records..')}

-
`); - } -}; - -let add_date_separator = function(data) { - let date = frappe.datetime.str_to_obj(data.communication_date); - let pdate = ''; - let diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); - - if (diff < 1) { - pdate = __('Today'); - } else if (diff < 2) { - pdate = __('Yesterday'); - } else { - pdate = __('on ') + frappe.datetime.global_date_format(date); - } - data.date_sep = pdate; - return data; -}; - -let show_patient_info = function(patient, me) { - frappe.call({ - 'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail', - args: { - patient: patient - }, - callback: function(r) { - let data = r.message; - let details = ''; - if (data.image) { - details += `
`; - } - - details += ` ${data.patient_name}
${data.sex}`; - if (data.email) details += `
${data.email}`; - if (data.mobile) details += `
${data.mobile}`; - if (data.occupation) details += `

${__('Occupation')} : ${data.occupation}`; - if (data.blood_group) details += `
${__('Blood Group')} : ${data.blood_group}`; - if (data.allergies) details += `

${__('Allerigies')} : ${data.allergies.replace("\n", ", ")}`; - if (data.medication) details += `
${__('Medication')} : ${data.medication.replace("\n", ", ")}`; - if (data.alcohol_current_use) details += `

${__('Alcohol use')} : ${data.alcohol_current_use}`; - if (data.alcohol_past_use) details += `
${__('Alcohol past use')} : ${data.alcohol_past_use}`; - if (data.tobacco_current_use) details += `
${__('Tobacco use')} : ${data.tobacco_current_use}`; - if (data.tobacco_past_use) details += `
${__('Tobacco past use')} : ${data.tobacco_past_use}`; - if (data.medical_history) details += `

${__('Medical history')} : ${data.medical_history.replace("\n", ", ")}`; - if (data.surgical_history) details += `
${__('Surgical history')} : ${data.surgical_history.replace("\n", ", ")}`; - if (data.surrounding_factors) details += `

${__('Occupational hazards')} : ${data.surrounding_factors.replace("\n", ", ")}`; - if (data.other_risk_factors) details += `
${__('Other risk factors')} : ${data.other_risk_factors.replace("\n", ", ")}`; - if (data.patient_details) details += `

${__('More info')} : ${data.patient_details.replace("\n", ", ")}`; - - if (details) { - details = `
` + details + `
`; - } - me.page.main.find('.patient_details').html(details); + if (diff < 1) { + pdate = __('Today'); + } else if (diff < 2) { + pdate = __('Yesterday'); + } else { + pdate = __('on {0}', [frappe.datetime.global_date_format(date)]); } - }); -}; + data.date_sep = pdate; + return data; + } -let show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) { - frappe.call({ - method: 'erpnext.healthcare.utils.get_patient_vitals', - args:{ - patient: patient - }, - callback: function(r) { - if (r.message) { - let show_chart_btns_html = ` - `; + show_patient_info() { + this.get_patient_info().then(() => { + $('.patient-info').empty().append(frappe.render_template('patient_history_sidebar', { + patient_image: this.patient.image, + patient_name: this.patient.patient_name, + patient_gender: this.patient.sex, + patient_mobile: this.patient.mobile + })); + this.show_patient_details(); + }); + } - me.page.main.find('.show_chart_btns').html(show_chart_btns_html); + show_patient_details() { + let me = this; + frappe.call({ + 'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail', + args: { + patient: me.patient_id + }, + callback: function(r) { let data = r.message; - let labels = [], datasets = []; - let bp_systolic = [], bp_diastolic = [], temperature = []; - let pulse = [], respiratory_rate = [], bmi = [], height = [], weight = []; + let details = ``; - for (let i=0; i
${__('Occupation')} : ${data.occupation}`; + if (data.blood_group) details += `
${__('Blood Group')} : ${data.blood_group}`; + if (data.allergies) details += `

${__('Allerigies')} : ${data.allergies.replace("\n", ", ")}`; + if (data.medication) details += `
${__('Medication')} : ${data.medication.replace("\n", ", ")}`; + if (data.alcohol_current_use) details += `

${__('Alcohol use')} : ${data.alcohol_current_use}`; + if (data.alcohol_past_use) details += `
${__('Alcohol past use')} : ${data.alcohol_past_use}`; + if (data.tobacco_current_use) details += `
${__('Tobacco use')} : ${data.tobacco_current_use}`; + if (data.tobacco_past_use) details += `
${__('Tobacco past use')} : ${data.tobacco_past_use}`; + if (data.medical_history) details += `

${__('Medical history')} : ${data.medical_history.replace("\n", ", ")}`; + if (data.surgical_history) details += `
${__('Surgical history')} : ${data.surgical_history.replace("\n", ", ")}`; + if (data.surrounding_factors) details += `

${__('Occupational hazards')} : ${data.surrounding_factors.replace("\n", ", ")}`; + if (data.other_risk_factors) details += `
${__('Other risk factors')} : ${data.other_risk_factors.replace("\n", ", ")}`; + if (data.patient_details) details += `

${__('More info')} : ${data.patient_details.replace("\n", ", ")}`; - if (btn_show_id === 'bp') { - bp_systolic.push(data[i].bp_systolic); - bp_diastolic.push(data[i].bp_diastolic); - } - if (btn_show_id === 'temperature') { - temperature.push(data[i].temperature); - } - if (btn_show_id === 'pulse_rate') { - pulse.push(data[i].pulse); - respiratory_rate.push(data[i].respiratory_rate); - } - if (btn_show_id === 'bmi') { - bmi.push(data[i].bmi); - height.push(data[i].height); - weight.push(data[i].weight); - } + if (details) { + details = `
` + details + `
`; } - if (btn_show_id === 'temperature') { - datasets.push({name: 'Temperature', values: temperature, chartType: 'line'}); - } - if (btn_show_id === 'bmi') { - datasets.push({name: 'BMI', values: bmi, chartType: 'line'}); - datasets.push({name: 'Height', values: height, chartType: 'line'}); - datasets.push({name: 'Weight', values: weight, chartType: 'line'}); - } - if (btn_show_id === 'bp') { - datasets.push({name: 'BP Systolic', values: bp_systolic, chartType: 'line'}); - datasets.push({name: 'BP Diastolic', values: bp_diastolic, chartType: 'line'}); - } - if (btn_show_id === 'pulse_rate') { - datasets.push({name: 'Heart Rate / Pulse', values: pulse, chartType: 'line'}); - datasets.push({name: 'Respiratory Rate', values: respiratory_rate, chartType: 'line'}); - } - new frappe.Chart('.patient_vital_charts', { - data: { - labels: labels, - datasets: datasets - }, - title: title, - type: 'axis-mixed', - height: 200, - colors: ['purple', '#ffa3ef', 'light-blue'], - - tooltipOptions: { - formatTooltipX: d => (d + '').toUpperCase(), - formatTooltipY: d => d + ' ' + pts, - } - }); - me.page.main.find('.header-separator').show(); - } else { - me.page.main.find('.patient_vital_charts').html(''); - me.page.main.find('.show_chart_btns').html(''); - me.page.main.find('.header-separator').hide(); + me.sidebar.find('.patient-details').html(details); } - } - }); -}; + }); + } + + get_patient_info() { + return frappe.xcall('frappe.client.get', { + doctype: 'Patient', + name: this.patient_id, + }).then((patient) => { + if (patient) { + this.patient = patient; + } + }); + } + + setup_buttons() { + let me = this; + this.page.main.on("click", ".btn-show-chart", function() { + let btn_id = $(this).attr("data-show-chart-id"), scale_unit = $(this).attr("data-pts"); + let title = $(this).attr("data-title"); + me.show_patient_vital_charts(btn_id, scale_unit, title); + }); + + this.page.main.on('click', '.btn-more', function() { + let doctype = $(this).attr('data-doctype'), docname = $(this).attr('data-docname'); + if (me.page.main.find('.'+docname).parent().find('.document-html').attr('data-fetched') == '1') { + me.page.main.find('.'+docname).hide(); + me.page.main.find('.'+docname).parent().find('.document-html').show(); + } else { + if (doctype && docname) { + let exclude = ['patient', 'patient_name', 'patient_sex', 'encounter_date', 'naming_series']; + frappe.call({ + method: 'erpnext.healthcare.utils.render_doc_as_html', + args: { + doctype: doctype, + docname: docname, + exclude_fields: exclude + }, + freeze: true, + callback: function(r) { + if (r.message) { + me.page.main.find('.' + docname).hide(); + + me.page.main.find('.' + docname).parent().find('.document-html').html( + `${r.message.html} +
+
+ + +
+ `); + + me.page.main.find('.' + docname).parent().find('.document-html').attr('hidden', false); + me.page.main.find('.' + docname).parent().find('.document-html').attr('data-fetched', '1'); + } + } + }); + } + } + }); + + this.page.main.on('click', '.btn-less', function() { + let docname = $(this).attr('data-docname'); + me.page.main.find('.' + docname).parent().find('.document-id').show(); + me.page.main.find('.' + docname).parent().find('.document-html').hide(); + }); + + me.page.main.on('click', '.btn-get-records', function() { + this.setup_documents(); + }); + } + + show_patient_vital_charts(btn_id, scale_unit, title) { + let me = this; + + frappe.call({ + method: 'erpnext.healthcare.utils.get_patient_vitals', + args: { + patient: me.patient_id + }, + callback: function(r) { + if (r.message) { + let show_chart_btns_html = ` + `; + + me.page.main.find('.show_chart_btns').html(show_chart_btns_html); + let data = r.message; + let labels = [], datasets = []; + let bp_systolic = [], bp_diastolic = [], temperature = []; + let pulse = [], respiratory_rate = [], bmi = [], height = [], weight = []; + + for (let i=0; i (d + '').toUpperCase(), + formatTooltipY: d => d + ' ' + scale_unit, + } + }); + me.page.main.find('.header-separator').show(); + } else { + me.page.main.find('.patient_vital_charts').html(''); + me.page.main.find('.show_chart_btns').html(''); + me.page.main.find('.header-separator').hide(); + } + } + }); + } +} \ No newline at end of file diff --git a/erpnext/healthcare/page/patient_history/patient_history_sidebar.html b/erpnext/healthcare/page/patient_history/patient_history_sidebar.html new file mode 100644 index 00000000000..4560e7e1254 --- /dev/null +++ b/erpnext/healthcare/page/patient_history/patient_history_sidebar.html @@ -0,0 +1,21 @@ +
+
+ {% if patient_image %} +
+ {% endif %} +
+
+ {% if patient_name %} +

{{patient_name}}

+ {% endif %} + {% if patient_gender %} +

{%=__("Gender: ") %} {{patient_gender}}

+ {% endif %} + {% if patient_mobile %} +

{%=__("Contact: ") %} {{patient_mobile}}

+ {% endif %} +
+
+
+
+ diff --git a/erpnext/healthcare/page/patient_progress/patient_progress.css b/erpnext/healthcare/page/patient_progress/patient_progress.css index 5d85a7487fd..737b2e0ea28 100644 --- a/erpnext/healthcare/page/patient_progress/patient_progress.css +++ b/erpnext/healthcare/page/patient_progress/patient_progress.css @@ -29,6 +29,7 @@ .patient-name { font-size: 20px; + margin-top: 25px; } /* heatmap */ @@ -55,6 +56,7 @@ } .heatmap-container .chart-filter { + z-index: 1; position: relative; top: 5px; margin-right: 10px; @@ -111,10 +113,13 @@ text.title { } .chart-column-container { - border-bottom: 1px solid #d1d8dd; margin: 5px 0; } +.progress-graphs .progress-container { + margin-bottom: var(--margin-xl); +} + .line-chart-container .frappe-chart { margin-top: -20px; } @@ -146,6 +151,7 @@ text.title { } .percentage-chart-container .chart-filter { + z-index: 1; position: relative; top: 12px; margin-right: 10px; diff --git a/erpnext/healthcare/page/patient_progress/patient_progress.html b/erpnext/healthcare/page/patient_progress/patient_progress.html index 30064bd1654..ee60065618f 100644 --- a/erpnext/healthcare/page/patient_progress/patient_progress.html +++ b/erpnext/healthcare/page/patient_progress/patient_progress.html @@ -1,14 +1,15 @@
-