diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000000..5e1113d34f9 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,19 @@ +# Each line is a file pattern followed by one or more owners. + +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, + +* @nabinhait +manufacturing/ @rohitwaghchaure +accounts/ @deepeshgarg007 @nextchamp-saqib +loan_management/ @deepeshgarg007 +pos* @nextchamp-saqib +assets/ @nextchamp-saqib +stock/ @marination @rohitwaghchaure +buying/ @marination @rohitwaghchaure +hr/ @Anurag810 +projects/ @hrwX +support/ @hrwX +healthcare/ @ruchamahabal +erpnext_integrations/ @Mangesh-Khairnar +requirements.txt @gavindsouza diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index d0b747d83f0..8a9a7c58d2c 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -1,83 +1,96 @@ { "cards": [ { - "links": "[\n {\n \"description\": \"Company (not Customer or Supplier) master.\",\n \"label\": \"Company\",\n \"name\": \"Company\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of financial accounts.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Chart of Accounts\",\n \"name\": \"Account\",\n \"onboard\": 1,\n \"route\": \"#Tree/Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounts Settings\",\n \"name\": \"Accounts Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Financial / accounting year.\",\n \"label\": \"Fiscal Year\",\n \"name\": \"Fiscal Year\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Dimension\",\n \"name\": \"Accounting Dimension\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Finance Book\",\n \"name\": \"Finance Book\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Period\",\n \"name\": \"Accounting Period\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Terms based on conditions\",\n \"label\": \"Payment Term\",\n \"name\": \"Payment Term\",\n \"type\": \"doctype\"\n }\n]", - "title": "Accounting Masters" + "hidden": 0, + "label": "Accounting Masters", + "links": "[\n {\n \"description\": \"Company (not Customer or Supplier) master.\",\n \"label\": \"Company\",\n \"name\": \"Company\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of financial accounts.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Chart of Accounts\",\n \"name\": \"Account\",\n \"onboard\": 1,\n \"route\": \"#Tree/Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounts Settings\",\n \"name\": \"Accounts Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Financial / accounting year.\",\n \"label\": \"Fiscal Year\",\n \"name\": \"Fiscal Year\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Dimension\",\n \"name\": \"Accounting Dimension\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Finance Book\",\n \"name\": \"Finance Book\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Period\",\n \"name\": \"Accounting Period\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Terms based on conditions\",\n \"label\": \"Payment Term\",\n \"name\": \"Payment Term\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Accounting journal entries.\",\n \"label\": \"Journal Entry\",\n \"name\": \"Journal Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"General Ledger\",\n \"name\": \"General Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Customer Ledger Summary\",\n \"name\": \"Customer Ledger Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Supplier Ledger Summary\",\n \"name\": \"Supplier Ledger Summary\",\n \"type\": \"report\"\n }\n]", - "title": "General Ledger" + "hidden": 0, + "label": "General Ledger", + "links": "[\n {\n \"description\": \"Accounting journal entries.\",\n \"label\": \"Journal Entry\",\n \"name\": \"Journal Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"General Ledger\",\n \"name\": \"General Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Customer Ledger Summary\",\n \"name\": \"Customer Ledger Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Supplier Ledger Summary\",\n \"name\": \"Supplier Ledger Summary\",\n \"type\": \"report\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Billed\",\n \"name\": \"Ordered Items To Be Billed\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]", - "title": "Accounts Receivable" + "hidden": 0, + "label": "Accounts Receivable", + "links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Billed\",\n \"name\": \"Ordered Items To Be Billed\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Bills raised by Suppliers.\",\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable\",\n \"name\": \"Accounts Payable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable Summary\",\n \"name\": \"Accounts Payable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Register\",\n \"name\": \"Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase Register\",\n \"name\": \"Item-wise Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Items To Be Billed\",\n \"name\": \"Purchase Order Items To Be Billed\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Received Items To Be Billed\",\n \"name\": \"Received Items To Be Billed\",\n \"type\": \"report\"\n }\n]", - "title": "Accounts Payable" + "hidden": 0, + "label": "Accounts Payable", + "links": "[\n {\n \"description\": \"Bills raised by Suppliers.\",\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable\",\n \"name\": \"Accounts Payable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable Summary\",\n \"name\": \"Accounts Payable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Register\",\n \"name\": \"Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase Register\",\n \"name\": \"Item-wise Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Items To Be Billed\",\n \"name\": \"Purchase Order Items To Be Billed\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Received Items To Be Billed\",\n \"name\": \"Received Items To Be Billed\",\n \"type\": \"report\"\n }\n]" }, { - "icon": "fa fa-table", - "links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance for Party\",\n \"name\": \"Trial Balance for Party\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Payment Period Based On Invoice Date\",\n \"name\": \"Payment Period Based On Invoice Date\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Payment Summary\",\n \"name\": \"Sales Payment Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Address And Contacts\",\n \"name\": \"Address And Contacts\",\n \"type\": \"report\"\n }\n]", - "title": "Reports" + "hidden": 0, + "label": "Reports", + "links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance for Party\",\n \"name\": \"Trial Balance for Party\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Payment Period Based On Invoice Date\",\n \"name\": \"Payment Period Based On Invoice Date\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Payment Summary\",\n \"name\": \"Sales Payment Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Address And Contacts\",\n \"name\": \"Address And Contacts\",\n \"type\": \"report\"\n }\n]" }, { - "links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance\",\n \"name\": \"Trial Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Profit and Loss Statement\",\n \"name\": \"Profit and Loss Statement\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Balance Sheet\",\n \"name\": \"Balance Sheet\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Cash Flow\",\n \"name\": \"Cash Flow\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Consolidated Financial Statement\",\n \"name\": \"Consolidated Financial Statement\",\n \"type\": \"report\"\n }\n]", - "title": "Financial Statements" + "hidden": 0, + "label": "Financial Statements", + "links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance\",\n \"name\": \"Trial Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Profit and Loss Statement\",\n \"name\": \"Profit and Loss Statement\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Balance Sheet\",\n \"name\": \"Balance Sheet\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Cash Flow\",\n \"name\": \"Cash Flow\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Consolidated Financial Statement\",\n \"name\": \"Consolidated Financial Statement\",\n \"type\": \"report\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Enable / disable currencies.\",\n \"label\": \"Currency\",\n \"name\": \"Currency\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Currency exchange rate master.\",\n \"label\": \"Currency Exchange\",\n \"name\": \"Currency Exchange\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Exchange Rate Revaluation master.\",\n \"label\": \"Exchange Rate Revaluation\",\n \"name\": \"Exchange Rate Revaluation\",\n \"type\": \"doctype\"\n }\n]", - "title": "Multi Currency" + "hidden": 0, + "label": "Multi Currency", + "links": "[\n {\n \"description\": \"Enable / disable currencies.\",\n \"label\": \"Currency\",\n \"name\": \"Currency\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Currency exchange rate master.\",\n \"label\": \"Currency Exchange\",\n \"name\": \"Currency Exchange\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Exchange Rate Revaluation master.\",\n \"label\": \"Exchange Rate Revaluation\",\n \"name\": \"Exchange Rate Revaluation\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-cog", - "links": "[\n {\n \"description\": \"Setup Gateway accounts.\",\n \"label\": \"Payment Gateway Account\",\n \"name\": \"Payment Gateway Account\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"e.g. Bank, Cash, Credit Card\",\n \"label\": \"Mode of Payment\",\n \"name\": \"Mode of Payment\",\n \"type\": \"doctype\"\n }\n]", - "title": "Settings" + "hidden": 0, + "label": "Settings", + "links": "[\n {\n \"description\": \"Setup Gateway accounts.\",\n \"label\": \"Payment Gateway Account\",\n \"name\": \"Payment Gateway Account\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"e.g. Bank, Cash, Credit Card\",\n \"label\": \"Mode of Payment\",\n \"name\": \"Mode of Payment\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]", - "title": "Bank Statement" + "hidden": 0, + "label": "Bank Statement", + "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]" }, { + "hidden": 0, "links": "[\n {\n \"description\": \"Match non-linked Invoices and Payments.\",\n \"label\": \"Match Payments with Invoices\",\n \"name\": \"Payment Reconciliation\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Update bank payment dates with journals.\",\n \"label\": \"Update Bank Clearance Dates\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Invoice Discounting\",\n \"name\": \"Invoice Discounting\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Clearance Summary\",\n \"name\": \"Bank Clearance Summary\",\n \"type\": \"report\"\n },\n {\n \"label\": \"Bank Guarantee\",\n \"name\": \"Bank Guarantee\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup cheque dimensions for printing\",\n \"label\": \"Cheque Print Template\",\n \"name\": \"Cheque Print Template\",\n \"type\": \"doctype\"\n }\n]", "title": "Banking and Payments" }, { - "links": "[\n {\n \"label\": \"Subscription Plan\",\n \"name\": \"Subscription Plan\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Subscription\",\n \"name\": \"Subscription\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Subscription Settings\",\n \"name\": \"Subscription Settings\",\n \"type\": \"doctype\"\n }\n]", - "title": "Subscription Management" + "hidden": 0, + "label": "Subscription Management", + "links": "[\n {\n \"label\": \"Subscription Plan\",\n \"name\": \"Subscription Plan\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Subscription\",\n \"name\": \"Subscription\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Subscription Settings\",\n \"name\": \"Subscription Settings\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"GST Settings\",\n \"name\": \"GST Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"GST HSN Code\",\n \"name\": \"GST HSN Code\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GSTR-1\",\n \"name\": \"GSTR-1\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GSTR-2\",\n \"name\": \"GSTR-2\",\n \"type\": \"report\"\n },\n {\n \"label\": \"GSTR 3B Report\",\n \"name\": \"GSTR 3B Report\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Sales Register\",\n \"name\": \"GST Sales Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Purchase Register\",\n \"name\": \"GST Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Itemised Sales Register\",\n \"name\": \"GST Itemised Sales Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Itemised Purchase Register\",\n \"name\": \"GST Itemised Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"country\": \"India\",\n \"description\": \"C-Form records\",\n \"label\": \"C-Form\",\n \"name\": \"C-Form\",\n \"type\": \"doctype\"\n }\n]", - "title": "Goods and Services Tax (GST India)" + "hidden": 0, + "label": "Goods and Services Tax (GST India)", + "links": "[\n {\n \"label\": \"GST Settings\",\n \"name\": \"GST Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"GST HSN Code\",\n \"name\": \"GST HSN Code\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GSTR-1\",\n \"name\": \"GSTR-1\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GSTR-2\",\n \"name\": \"GSTR-2\",\n \"type\": \"report\"\n },\n {\n \"label\": \"GSTR 3B Report\",\n \"name\": \"GSTR 3B Report\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Sales Register\",\n \"name\": \"GST Sales Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Purchase Register\",\n \"name\": \"GST Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Itemised Sales Register\",\n \"name\": \"GST Itemised Sales Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Itemised Purchase Register\",\n \"name\": \"GST Itemised Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"country\": \"India\",\n \"description\": \"C-Form records\",\n \"label\": \"C-Form\",\n \"name\": \"C-Form\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-microchip ", - "links": "[\n {\n \"description\": \"List of available Shareholders with folio numbers\",\n \"label\": \"Shareholder\",\n \"name\": \"Shareholder\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"List of all share transactions\",\n \"label\": \"Share Transfer\",\n \"name\": \"Share Transfer\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Share Transfer\"\n ],\n \"doctype\": \"Share Transfer\",\n \"is_query_report\": true,\n \"label\": \"Share Ledger\",\n \"name\": \"Share Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Share Transfer\"\n ],\n \"doctype\": \"Share Transfer\",\n \"is_query_report\": true,\n \"label\": \"Share Balance\",\n \"name\": \"Share Balance\",\n \"type\": \"report\"\n }\n]", - "title": "Share Management" + "hidden": 0, + "label": "Share Management", + "links": "[\n {\n \"description\": \"List of available Shareholders with folio numbers\",\n \"label\": \"Shareholder\",\n \"name\": \"Shareholder\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"List of all share transactions\",\n \"label\": \"Share Transfer\",\n \"name\": \"Share Transfer\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Share Transfer\"\n ],\n \"doctype\": \"Share Transfer\",\n \"is_query_report\": true,\n \"label\": \"Share Ledger\",\n \"name\": \"Share Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Share Transfer\"\n ],\n \"doctype\": \"Share Transfer\",\n \"is_query_report\": true,\n \"label\": \"Share Balance\",\n \"name\": \"Share Balance\",\n \"type\": \"report\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Tree of financial Cost Centers.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Chart of Cost Centers\",\n \"name\": \"Cost Center\",\n \"route\": \"#Tree/Cost Center\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define budget for a financial year.\",\n \"label\": \"Budget\",\n \"name\": \"Budget\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Dimension\",\n \"name\": \"Accounting Dimension\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Cost Center\"\n ],\n \"doctype\": \"Cost Center\",\n \"is_query_report\": true,\n \"label\": \"Budget Variance Report\",\n \"name\": \"Budget Variance Report\",\n \"type\": \"report\"\n },\n {\n \"description\": \"Seasonality for setting budgets, targets etc.\",\n \"label\": \"Monthly Distribution\",\n \"name\": \"Monthly Distribution\",\n \"type\": \"doctype\"\n }\n]", - "title": "Cost Center and Budgeting" + "hidden": 0, + "label": "Cost Center and Budgeting", + "links": "[\n {\n \"description\": \"Tree of financial Cost Centers.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Chart of Cost Centers\",\n \"name\": \"Cost Center\",\n \"route\": \"#Tree/Cost Center\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define budget for a financial year.\",\n \"label\": \"Budget\",\n \"name\": \"Budget\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Dimension\",\n \"name\": \"Accounting Dimension\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Cost Center\"\n ],\n \"doctype\": \"Cost Center\",\n \"is_query_report\": true,\n \"label\": \"Budget Variance Report\",\n \"name\": \"Budget Variance Report\",\n \"type\": \"report\"\n },\n {\n \"description\": \"Seasonality for setting budgets, targets etc.\",\n \"label\": \"Monthly Distribution\",\n \"name\": \"Monthly Distribution\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Opening Invoice Creation Tool\",\n \"name\": \"Opening Invoice Creation Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Chart of Accounts Importer\",\n \"name\": \"Chart of Accounts Importer\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Close Balance Sheet and book Profit or Loss.\",\n \"label\": \"Period Closing Voucher\",\n \"name\": \"Period Closing Voucher\",\n \"type\": \"doctype\"\n }\n]", - "title": "Opening and Closing" + "hidden": 0, + "label": "Opening and Closing", + "links": "[\n {\n \"label\": \"Opening Invoice Creation Tool\",\n \"name\": \"Opening Invoice Creation Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Chart of Accounts Importer\",\n \"name\": \"Chart of Accounts Importer\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Close Balance Sheet and book Profit or Loss.\",\n \"label\": \"Period Closing Voucher\",\n \"name\": \"Period Closing Voucher\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for buying transactions.\",\n \"label\": \"Purchase Taxes and Charges Template\",\n \"name\": \"Purchase Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for item tax rates.\",\n \"label\": \"Item Tax Template\",\n \"name\": \"Item Tax Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Category for overriding tax rates.\",\n \"label\": \"Tax Category\",\n \"name\": \"Tax Category\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Rule for transactions.\",\n \"label\": \"Tax Rule\",\n \"name\": \"Tax Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Withholding rates to be applied on transactions.\",\n \"label\": \"Tax Withholding Category\",\n \"name\": \"Tax Withholding Category\",\n \"type\": \"doctype\"\n }\n]", - "title": "Taxes" + "hidden": 0, + "label": "Taxes", + "links": "[\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for buying transactions.\",\n \"label\": \"Purchase Taxes and Charges Template\",\n \"name\": \"Purchase Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for item tax rates.\",\n \"label\": \"Item Tax Template\",\n \"name\": \"Item Tax Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Category for overriding tax rates.\",\n \"label\": \"Tax Category\",\n \"name\": \"Tax Category\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Rule for transactions.\",\n \"label\": \"Tax Rule\",\n \"name\": \"Tax Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Withholding rates to be applied on transactions.\",\n \"label\": \"Tax Withholding Category\",\n \"name\": \"Tax Withholding Category\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Gross Profit\",\n \"name\": \"Gross Profit\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Profitability Analysis\",\n \"name\": \"Profitability Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"type\": \"report\"\n }\n]", - "title": "Profitability" + "hidden": 0, + "label": "Profitability", + "links": "[\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Gross Profit\",\n \"name\": \"Gross Profit\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Profitability Analysis\",\n \"name\": \"Profitability Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", "charts": [ { "chart_name": "Bank Balance", - "label": "Bank Balance", - "size": "Full" + "label": "Bank Balance" } ], "creation": "2020-03-02 15:41:59.515192", @@ -90,7 +103,7 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-03-12 16:30:35.580450", + "modified": "2020-04-01 11:28:50.925719", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", @@ -99,37 +112,37 @@ "pin_to_top": 0, "shortcuts": [ { - "is_query_report": 0, + "label": "Account", "link_to": "Account", "type": "DocType" }, { - "is_query_report": 0, + "label": "Journal Entry", "link_to": "Journal Entry", "type": "DocType" }, { - "is_query_report": 0, + "label": "Payment Entry", "link_to": "Payment Entry", "type": "DocType" }, { - "is_query_report": 1, + "label": "Accounts Receivable", "link_to": "Accounts Receivable", "type": "Report" }, { - "is_query_report": 0, + "label": "General Ledger", "link_to": "General Ledger", "type": "Report" }, { - "is_query_report": 0, + "label": "Profit and Loss Statement", "link_to": "Profit and Loss Statement", "type": "Report" }, { - "is_query_report": 0, + "label": "Trial Balance", "link_to": "Trial Balance", "type": "Report" } diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 0a72d4fa4e6..c6de6410ebc 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -89,7 +89,7 @@ class Account(NestedSet): throw(_("Root cannot be edited."), RootNotEditable) if not self.parent_account and not self.is_group: - frappe.throw(_("Root Account must be a group")) + frappe.throw(_("The root account {0} must be a group").format(frappe.bold(self.name))) def validate_root_company_and_sync_account_to_children(self): # ignore validation while creating new compnay or while syncing to child companies diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index efac1af551e..f62d07668de 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -1,7 +1,7 @@ frappe.provide("frappe.treeview_settings") frappe.treeview_settings["Account"] = { - breadcrumbs: "Accounts", + breadcrumb: "Accounts", title: __("Chart Of Accounts"), get_tree_root: false, filters: [ diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index dc23b2b2d05..89bb0184af8 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -69,6 +69,7 @@ class TestAccount(unittest.TestCase): acc.account_name = "Accumulated Depreciation" acc.parent_account = "Fixed Assets - _TC" acc.company = "_Test Company" + acc.account_type = "Accumulated Depreciation" acc.insert() doc = frappe.get_doc("Account", "Securities and Deposits - _TC") @@ -149,7 +150,7 @@ def _make_test_records(verbose): # fixed asset depreciation ["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None], - ["_Test Accumulated Depreciations", "Current Assets", 0, None, None], + ["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None], ["_Test Depreciations", "Expenses", 0, None, None], ["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None], diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 462d967d015..14fdffc0a78 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -193,7 +193,7 @@ def get_dimension_with_children(doctype, dimension): all_dimensions = [] lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"]) - children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}) + children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft") all_dimensions += [c.name for c in children] return all_dimensions diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index b6f5396ccb1..e1b331be2b3 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -152,10 +152,9 @@ def build_forest(data): return [parent_account] elif account_name == child: parent_account_list = return_parent(data, parent_account) - if not parent_account_list: + if not parent_account_list and parent_account: frappe.throw(_("The parent account {0} does not exists in the uploaded template").format( frappe.bold(parent_account))) - return [child] + parent_account_list charts_map, paths = {}, [] @@ -164,7 +163,7 @@ def build_forest(data): error_messages = [] for i in data: - account_name, _, account_number, is_group, account_type, root_type = i + account_name, dummy, account_number, is_group, account_type, root_type = i if not account_name: error_messages.append("Row {0}: Please enter Account Name".format(line_no)) diff --git a/erpnext/accounts/doctype/cost_center/cost_center_tree.js b/erpnext/accounts/doctype/cost_center/cost_center_tree.js index 16d97344320..fde41233c4d 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center_tree.js +++ b/erpnext/accounts/doctype/cost_center/cost_center_tree.js @@ -1,5 +1,5 @@ frappe.treeview_settings["Cost Center"] = { - breadcrumbs: "Accounts", + breadcrumb: "Accounts", get_tree_root: false, filters: [{ fieldname: "company", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index d2080874533..b7c97a776e9 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -156,8 +156,11 @@ frappe.ui.form.on('Payment Entry', { frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency); - frm.toggle_display("base_received_amount", (frm.doc.paid_to_account_currency != company_currency && - frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)); + frm.toggle_display("base_received_amount", ( + frm.doc.paid_to_account_currency != company_currency + && frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency + && frm.doc.base_paid_amount != frm.doc.base_received_amount + )); frm.toggle_display("received_amount", (frm.doc.payment_type=="Internal Transfer" || frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)) @@ -501,6 +504,7 @@ frappe.ui.form.on('Payment Entry', { paid_amount: function(frm) { frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate)); frm.trigger("reset_received_amount"); + frm.events.hide_unhide_fields(frm); }, received_amount: function(frm) { @@ -524,6 +528,7 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_unallocated_amount(frm); frm.set_paid_amount_based_on_received_amount = false; + frm.events.hide_unhide_fields(frm); }, reset_received_amount: function(frm) { diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 55d275831ec..a453e95b2bc 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -81,7 +81,12 @@ class PaymentEntry(AccountsController): self.update_advance_paid() self.update_expense_claim() self.delink_advance_entry_references() + self.set_payment_req_status() self.set_status() + + def set_payment_req_status(self): + from erpnext.accounts.doctype.payment_request.payment_request import update_payment_req_status + update_payment_req_status(self, None) def update_outstanding_amounts(self): self.set_missing_ref_details(force=True) diff --git a/erpnext/accounts/doctype/payment_order/payment_order.js b/erpnext/accounts/doctype/payment_order/payment_order.js index d6d494668a4..d12e474c5b1 100644 --- a/erpnext/accounts/doctype/payment_order/payment_order.js +++ b/erpnext/accounts/doctype/payment_order/payment_order.js @@ -15,11 +15,11 @@ frappe.ui.form.on('Payment Order', { if (frm.doc.docstatus == 0) { frm.add_custom_button(__('Payment Request'), function() { frm.trigger("get_from_payment_request"); - }, __("Get from")); + }, __("Get Payments from")); frm.add_custom_button(__('Payment Entry'), function() { frm.trigger("get_from_payment_entry"); - }, __("Get from")); + }, __("Get Payments from")); frm.trigger('remove_button'); } diff --git a/erpnext/accounts/doctype/payment_order/payment_order.json b/erpnext/accounts/doctype/payment_order/payment_order.json index 2e12ad35236..2ed0a4a22fd 100644 --- a/erpnext/accounts/doctype/payment_order/payment_order.json +++ b/erpnext/accounts/doctype/payment_order/payment_order.json @@ -59,7 +59,6 @@ "fieldtype": "Section Break" }, { - "allow_bulk_edit": 1, "fieldname": "references", "fieldtype": "Table", "label": "Payment Order Reference", @@ -108,7 +107,7 @@ } ], "is_submittable": 1, - "modified": "2019-05-14 17:12:24.912666", + "modified": "2020-04-06 18:00:56.022642", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Order", diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index c1559a74f21..97ae5ffde36 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -1,1560 +1,387 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2015-12-15 22:23:24.745065", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 0, - "engine": "InnoDB", + "autoname": "naming_series:", + "creation": "2015-12-15 22:23:24.745065", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "payment_request_type", + "transaction_date", + "column_break_2", + "naming_series", + "mode_of_payment", + "party_details", + "party_type", + "party", + "column_break_4", + "reference_doctype", + "reference_name", + "transaction_details", + "grand_total", + "is_a_subscription", + "column_break_18", + "currency", + "subscription_section", + "subscription_plans", + "bank_account_details", + "bank_account", + "bank", + "bank_account_no", + "account", + "column_break_11", + "iban", + "branch_code", + "swift_number", + "recipient_and_message", + "print_format", + "email_to", + "subject", + "column_break_9", + "payment_gateway_account", + "status", + "make_sales_invoice", + "section_break_10", + "message", + "message_examples", + "mute_email", + "payment_url", + "section_break_7", + "payment_gateway", + "payment_account", + "payment_order", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Inward", - "fieldname": "payment_request_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Request Type", - "length": 0, - "no_copy": 0, - "options": "Outward\nInward", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Inward", + "fieldname": "payment_request_type", + "fieldtype": "Select", + "label": "Payment Request Type", + "options": "Outward\nInward", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transaction_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transaction Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "transaction_date", + "fieldtype": "Date", + "label": "Transaction Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 1, - "options": "ACC-PRQ-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Series", + "no_copy": 1, + "options": "ACC-PRQ-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mode_of_payment", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Mode of Payment", - "length": 0, - "no_copy": 0, - "options": "Mode of Payment", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "options": "Mode of Payment" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "party_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Party Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "party_details", + "fieldtype": "Section Break", + "label": "Party Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "party_type", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Party Type", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "party_type", + "fieldtype": "Link", + "label": "Party Type", + "options": "DocType" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "party", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Party", - "length": 0, - "no_copy": 0, - "options": "party_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "party", + "fieldtype": "Dynamic Link", + "label": "Party", + "options": "party_type" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_doctype", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Reference Doctype", - "length": 0, - "no_copy": 1, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Reference Doctype", + "no_copy": 1, + "options": "DocType", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_name", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Reference Name", - "length": 0, - "no_copy": 1, - "options": "reference_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Reference Name", + "no_copy": 1, + "options": "reference_doctype", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transaction_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transaction Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "transaction_details", + "fieldtype": "Section Break", + "label": "Transaction Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Amount in customer's currency", - "fieldname": "grand_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Amount in customer's currency", + "fieldname": "grand_total", + "fieldtype": "Currency", + "label": "Amount", + "options": "currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_a_subscription", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is a Subscription", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "is_a_subscription", + "fieldtype": "Check", + "label": "Is a Subscription" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_18", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "currency", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transaction Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "currency", + "fieldtype": "Link", + "label": "Transaction Currency", + "options": "Currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "eval:doc.is_a_subscription", - "fieldname": "subscription_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subscription Section", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.is_a_subscription", + "fieldname": "subscription_section", + "fieldtype": "Section Break", + "label": "Subscription Section" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subscription_plans", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subscription Plans", - "length": 0, - "no_copy": 0, - "options": "Subscription Plan Detail", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "subscription_plans", + "fieldtype": "Table", + "label": "Subscription Plans", + "options": "Subscription Plan Detail" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "bank_account_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Account Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "bank_account_details", + "fieldtype": "Section Break", + "label": "Bank Account Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Account", - "length": 0, - "no_copy": 0, - "options": "Bank Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bank_account", + "fieldtype": "Link", + "label": "Bank Account", + "options": "Bank Account" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "bank_account.bank", - "fieldname": "bank", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "bank_account.bank", + "fieldname": "bank", + "fieldtype": "Read Only", + "label": "Bank" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "bank_account.bank_account_no", - "fieldname": "bank_account_no", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Account No", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "bank_account.bank_account_no", + "fieldname": "bank_account_no", + "fieldtype": "Read Only", + "label": "Bank Account No" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "bank_account.account", - "fieldname": "account", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Account", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "bank_account.account", + "fieldname": "account", + "fieldtype": "Read Only", + "label": "Account" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "bank_account.iban", - "fieldname": "iban", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "IBAN", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "bank_account.iban", + "fieldname": "iban", + "fieldtype": "Read Only", + "label": "IBAN" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "bank.branch_code", - "fieldname": "branch_code", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Branch Code", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "bank_account.branch_code", + "fieldname": "branch_code", + "fieldtype": "Read Only", + "label": "Branch Code" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "bank.swift_number", - "fieldname": "swift_number", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "SWIFT Number", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "bank_account.swift_number", + "fieldname": "swift_number", + "fieldtype": "Read Only", + "label": "SWIFT Number" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.payment_request_type == 'Inward'", - "fieldname": "recipient_and_message", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Recipient Message And Payment Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval: doc.payment_request_type == 'Inward'", + "fieldname": "recipient_and_message", + "fieldtype": "Section Break", + "label": "Recipient Message And Payment Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "print_format", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Format", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "print_format", + "fieldtype": "Select", + "label": "Print Format" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "email_to", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "To", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "email_to", + "fieldtype": "Data", + "in_global_search": 1, + "label": "To" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subject", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subject", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "subject", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Subject" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.payment_request_type == 'Inward'", - "fieldname": "payment_gateway_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Gateway Account", - "length": 0, - "no_copy": 0, - "options": "Payment Gateway Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval: doc.payment_request_type == 'Inward'", + "fieldname": "payment_gateway_account", + "fieldtype": "Link", + "label": "Payment Gateway Account", + "options": "Payment Gateway Account" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Draft", - "fieldname": "status", - "fieldtype": "Select", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "\nDraft\nInitiated\nPayment Ordered\nPaid\nFailed\nCancelled", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "hidden": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "\nDraft\nRequested\nInitiated\nPartially Paid\nPayment Ordered\nPaid\nFailed\nCancelled", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.reference_doctype==\"Sales Order\"", - "fieldname": "make_sales_invoice", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Make Sales Invoice", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:doc.reference_doctype==\"Sales Order\"", + "fieldname": "make_sales_invoice", + "fieldtype": "Check", + "hidden": 1, + "label": "Make Sales Invoice", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.payment_request_type == 'Inward'", - "fieldname": "section_break_10", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval: doc.payment_request_type == 'Inward'", + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "message", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Message", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "message", + "fieldtype": "Text", + "label": "Message" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "message_examples", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Message Examples", - "length": 0, - "no_copy": 0, - "options": "
Message Example
\n\n<p>Dear {{ doc.contact_person }},</p>\n\n<p>Requesting payment for {{ doc.doctype }}, {{ doc.name }} for {{ doc.grand_total }}.</p>\n\n<a href=\"{{ payment_url }}\"> click here to pay </a>\n\n
\n", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "message_examples", + "fieldtype": "HTML", + "label": "Message Examples", + "options": "
Message Example
\n\n<p>Dear {{ doc.contact_person }},</p>\n\n<p>Requesting payment for {{ doc.doctype }}, {{ doc.name }} for {{ doc.grand_total }}.</p>\n\n<a href=\"{{ payment_url }}\"> click here to pay </a>\n\n
\n", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mute_email", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Mute Email", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "mute_email", + "fieldtype": "Check", + "hidden": 1, + "label": "Mute Email", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_url", - "fieldtype": "Small Text", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "payment_url", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "payment_url", + "fieldtype": "Small Text", + "hidden": 1, + "label": "payment_url", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "doc.payment_gateway_account", - "columns": 0, - "depends_on": "eval: doc.payment_request_type == 'Inward'", - "fieldname": "section_break_7", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Gateway Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "doc.payment_gateway_account", + "depends_on": "eval: doc.payment_request_type == 'Inward'", + "fieldname": "section_break_7", + "fieldtype": "Section Break", + "label": "Payment Gateway Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "payment_gateway_account.payment_gateway", - "fieldname": "payment_gateway", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Gateway", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "payment_gateway_account.payment_gateway", + "fieldname": "payment_gateway", + "fieldtype": "Read Only", + "label": "Payment Gateway" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "payment_gateway_account.payment_account", - "fieldname": "payment_account", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Account", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "payment_gateway_account.payment_account", + "fieldname": "payment_account", + "fieldtype": "Read Only", + "label": "Payment Account", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_order", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Order", - "length": 0, - "no_copy": 0, - "options": "Payment Order", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "payment_order", + "fieldtype": "Link", + "label": "Payment Order", + "options": "Payment Order", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Payment Request", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Payment Request", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-02-18 18:52:34.203239", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Payment Request", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "modified": "2020-03-28 16:07:31.960798", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Payment Request", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 7e9211af9f4..53ff2225d31 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -66,6 +66,8 @@ class PaymentRequest(Document): if self.payment_request_type == 'Outward': self.db_set('status', 'Initiated') return + elif self.payment_request_type == 'Inward': + self.db_set('status', 'Requested') send_mail = self.payment_gateway_validation() ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) @@ -88,6 +90,7 @@ class PaymentRequest(Document): if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart"): from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice si = make_sales_invoice(self.reference_name, ignore_permissions=True) + si.allocate_advances_automatically = True si = si.insert(ignore_permissions=True) si.submit() @@ -415,17 +418,31 @@ def make_payment_entry(docname): doc = frappe.get_doc("Payment Request", docname) return doc.create_payment_entry(submit=False).as_dict() -def make_status_as_paid(doc, method): +def update_payment_req_status(doc, method): + from erpnext.accounts.doctype.payment_entry.payment_entry import get_reference_details + for ref in doc.references: payment_request_name = frappe.db.get_value("Payment Request", {"reference_doctype": ref.reference_doctype, "reference_name": ref.reference_name, "docstatus": 1}) if payment_request_name: - doc = frappe.get_doc("Payment Request", payment_request_name) - if doc.status != "Paid": - doc.db_set('status', 'Paid') - frappe.db.commit() + ref_details = get_reference_details(ref.reference_doctype, ref.reference_name, doc.party_account_currency) + pay_req_doc = frappe.get_doc('Payment Request', payment_request_name) + status = pay_req_doc.status + + if status != "Paid" and not ref_details.outstanding_amount: + status = 'Paid' + elif status != "Partially Paid" and ref_details.outstanding_amount != ref_details.total_amount: + status = 'Partially Paid' + elif ref_details.outstanding_amount == ref_details.total_amount: + if pay_req_doc.payment_request_type == 'Outward': + status = 'Initiated' + elif pay_req_doc.payment_request_type == 'Inward': + status = 'Requested' + + pay_req_doc.db_set('status', status) + frappe.db.commit() def get_dummy_message(doc): return frappe.render_template("""{% if doc.contact_person -%} diff --git a/erpnext/accounts/doctype/payment_request/payment_request_list.js b/erpnext/accounts/doctype/payment_request/payment_request_list.js index 0caf1c2f7ff..72833d235f8 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request_list.js +++ b/erpnext/accounts/doctype/payment_request/payment_request_list.js @@ -4,14 +4,20 @@ frappe.listview_settings['Payment Request'] = { if(doc.status == "Draft") { return [__("Draft"), "darkgrey", "status,=,Draft"]; } + if(doc.status == "Requested") { + return [__("Requested"), "green", "status,=,Requested"]; + } else if(doc.status == "Initiated") { return [__("Initiated"), "green", "status,=,Initiated"]; } + else if(doc.status == "Partially Paid") { + return [__("Partially Paid"), "orange", "status,=,Partially Paid"]; + } else if(doc.status == "Paid") { return [__("Paid"), "blue", "status,=,Paid"]; } else if(doc.status == "Cancelled") { - return [__("Cancelled"), "orange", "status,=,Cancelled"]; + return [__("Cancelled"), "red", "status,=,Cancelled"]; } } } diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 188ab0aecf3..8a10e2cbd95 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -101,6 +101,23 @@ class TestPaymentRequest(unittest.TestCase): self.assertEqual(expected_gle[gle.account][2], gle.credit) self.assertEqual(expected_gle[gle.account][3], gle.against_voucher) + def test_status(self): + si_usd = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", + currency="USD", conversion_rate=50) + + pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com", + mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1, return_doc=1) + + pe = pr.create_payment_entry() + pr.load_from_db() + + self.assertEqual(pr.status, 'Paid') + + pe.cancel() + pr.load_from_db() + + self.assertEqual(pr.status, 'Requested') + def test_multiple_payment_entries_against_sales_order(self): # Make Sales Order, grand_total = 1000 so = make_sales_order() diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 2ffa27f6d99..100bb1d3e38 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -178,7 +178,8 @@ def filter_pricing_rules(args, pricing_rules, doc=None): if pricing_rules[0].mixed_conditions and doc: stock_qty, amount, items = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args) - pricing_rules[0].apply_rule_on_other_items = items + for pricing_rule_args in pricing_rules: + pricing_rule_args.apply_rule_on_other_items = items elif pricing_rules[0].is_cumulative: items = [args.get(frappe.scrub(pr_doc.get('apply_on')))] @@ -329,9 +330,9 @@ def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args): if pr_doc.mixed_conditions: amt = args.get('qty') * args.get("price_list_rate") if args.get("item_code") != row.get("item_code"): - amt = row.get('qty') * row.get("price_list_rate") + amt = row.get('qty') * (row.get("price_list_rate") or args.get("rate")) - sum_qty += row.get("stock_qty") or args.get("stock_qty") + sum_qty += row.get("stock_qty") or args.get("stock_qty") or args.get("qty") sum_amt += amt if pr_doc.is_cumulative: diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index c5c54837a7b..9292b633fc3 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -174,7 +174,8 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ read_only: 0, fieldtype:'Date', label: __('Release Date'), - default: me.frm.doc.release_date + default: me.frm.doc.release_date, + reqd: 1 }, { fieldname: 'hold_comment', diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 3cd988ccd23..3af236cbf9e 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -73,9 +73,9 @@ "base_total", "base_net_total", "column_break_28", + "total_net_weight", "total", "net_total", - "total_net_weight", "taxes_section", "tax_category", "column_break_49", @@ -1298,7 +1298,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2019-12-30 19:13:49.610538", + "modified": "2020-04-17 13:05:25.199832", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 9c974260a25..db3f72ada08 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -754,8 +754,7 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" }, { "depends_on": "is_fixed_asset", @@ -777,7 +776,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-04-01 14:20:17.297284", + "modified": "2020-04-07 18:34:35.104178", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index db6ac55a119..6f78db2ccc3 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -794,7 +794,7 @@ frappe.ui.form.on('Sales Invoice', { }, refresh: function(frm) { - if (frappe.boot.active_domains.includes("Healthcare")){ + if (frappe.boot.active_domains.includes("Healthcare")) { frm.set_df_property("patient", "hidden", 0); frm.set_df_property("patient_name", "hidden", 0); frm.set_df_property("ref_practitioner", "hidden", 0); @@ -807,7 +807,7 @@ frappe.ui.form.on('Sales Invoice', { },"Get items from"); } } - else{ + else { frm.set_df_property("patient", "hidden", 1); frm.set_df_property("patient_name", "hidden", 1); frm.set_df_property("ref_practitioner", "hidden", 1); diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index e239f9143dc..918fa140b2d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-24 19:29:05", @@ -74,9 +75,9 @@ "base_total", "base_net_total", "column_break_32", + "total_net_weight", "total", "net_total", - "total_net_weight", "taxes_section", "taxes_and_charges", "column_break_38", @@ -1577,7 +1578,8 @@ "icon": "fa fa-file-text", "idx": 181, "is_submittable": 1, - "modified": "2020-02-10 04:57:11.221180", + "links": [], + "modified": "2020-04-17 12:38:41.435728", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 82aedb62479..3c40112ae6f 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -440,11 +440,12 @@ class SalesInvoice(SellingController): if pos.get("company_address"): self.company_address = pos.get("company_address") - customer_price_list, customer_group = frappe.get_value("Customer", self.customer, ['default_price_list', 'customer_group']) - - customer_group_price_list = frappe.get_value("Customer Group", customer_group, 'default_price_list') - - selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list') + if self.customer: + customer_price_list, customer_group = frappe.get_value("Customer", self.customer, ['default_price_list', 'customer_group']) + customer_group_price_list = frappe.get_value("Customer Group", customer_group, 'default_price_list') + selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list') + else: + selling_price_list = pos.get('selling_price_list') if selling_price_list: self.set('selling_price_list', selling_price_list) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 3d5ce8ac40f..a2819af5086 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1948,7 +1948,7 @@ def create_sales_invoice(**args): "gst_hsn_code": "999800", "warehouse": args.warehouse or "_Test Warehouse - _TC", "qty": args.qty or 1, - "rate": args.rate or 100, + "rate": args.rate if args.get("rate") is not None else 100, "income_account": args.income_account or "Sales - _TC", "expense_account": args.expense_account or "Cost of Goods Sold - _TC", "cost_center": args.cost_center or "_Test Cost Center - _TC", diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py index 1fd340c7316..b2638c78279 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py @@ -82,7 +82,7 @@ class ShippingRule(Document): if not shipping_country: frappe.throw(_('Shipping Address does not have country, which is required for this Shipping Rule')) if shipping_country not in [d.country for d in self.countries]: - frappe.throw(_('Shipping rule not applicable for country {0}').format(shipping_country)) + frappe.throw(_('Shipping rule not applicable for country {0} in Shipping Address').format(shipping_country)) def add_shipping_rule_to_tax_table(self, doc, shipping_amount): shipping_charge = { diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 4cfeb251d60..47dfa09c610 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -162,8 +162,9 @@ def get_default_price_list(party): def set_price_list(party_details, party, party_type, given_price_list, pos=None): # price list price_list = get_permitted_documents('Price List') - - if price_list: + + # if there is only one permitted document based on user permissions, set it + if price_list and len(price_list) == 1: price_list = price_list[0] elif pos and party_type == 'Customer': customer_price_list = frappe.get_value('Customer', party.name, 'default_price_list') @@ -635,4 +636,4 @@ def get_default_contact(doctype, name): except: return None else: - return None \ No newline at end of file + return None diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index cb1fdc1a79a..a858c1998f5 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -9,6 +9,7 @@ from erpnext.accounts.report.financial_statements import (get_period_list, get_c def execute(filters=None): period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, + filters.period_start_date, filters.period_end_date, filters.filter_based_on, filters.periodicity, company=filters.company) currency = filters.presentation_currency or frappe.get_cached_value('Company', filters.company, "default_currency") diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index 904874f6459..e5d0c899181 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -9,7 +9,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { // filter. It won't be used in cash flow for now so we pop it. Please take // of this if you are working here. - frappe.query_reports["Cash Flow"]["filters"].splice(5, 1); + frappe.query_reports["Cash Flow"]["filters"].splice(8, 1); frappe.query_reports["Cash Flow"]["filters"].push( { diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index be6e93cae0b..cf0946beaba 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -17,7 +17,8 @@ def execute(filters=None): return execute_custom(filters=filters) period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, - filters.periodicity, filters.accumulated_values, filters.company) + filters.period_start_date, filters.period_end_date, filters.filter_based_on, + filters.periodicity, company=filters.company) cash_flow_accounts = get_cash_flow_accounts() diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index 92c5ee9124c..38fd5fa2787 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -12,6 +12,39 @@ frappe.query_reports["Consolidated Financial Statement"] = { "default": frappe.defaults.get_user_default("Company"), "reqd": 1 }, + { + "fieldname":"filter_based_on", + "label": __("Filter Based On"), + "fieldtype": "Select", + "options": ["Fiscal Year", "Date Range"], + "default": ["Fiscal Year"], + "reqd": 1, + on_change: function() { + let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); + frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year'); + frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year'); + + frappe.query_report.refresh(); + } + }, + { + "fieldname":"period_start_date", + "label": __("Start Date"), + "fieldtype": "Date", + "default": frappe.datetime.nowdate(), + "hidden": 1, + "reqd": 1 + }, + { + "fieldname":"period_end_date", + "label": __("End Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), + "hidden": 1, + "reqd": 1 + }, { "fieldname":"from_fiscal_year", "label": __("Start Year"), diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 461291b4531..b62238b59bb 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _ -from frappe.utils import flt, cint +from frappe.utils import flt, cint, getdate from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency from erpnext.accounts.report.financial_statements import get_fiscal_year_data, sort_accounts from erpnext.accounts.report.balance_sheet.balance_sheet import (get_provisional_profit_loss, @@ -208,17 +208,24 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i company_currency = get_company_currency(filters) + if filters.filter_based_on == 'Fiscal Year': + start_date = fiscal_year.year_start_date + end_date = fiscal_year.year_end_date + else: + start_date = filters.period_start_date + end_date = filters.period_end_date + gl_entries_by_account = {} for root in frappe.db.sql("""select lft, rgt from tabAccount where root_type=%s and ifnull(parent_account, '') = ''""", root_type, as_dict=1): - set_gl_entries_by_account(fiscal_year.year_start_date, - fiscal_year.year_end_date, root.lft, root.rgt, filters, + set_gl_entries_by_account(start_date, + end_date, root.lft, root.rgt, filters, gl_entries_by_account, accounts_by_name, ignore_closing_entries=False) - calculate_values(accounts_by_name, gl_entries_by_account, companies, fiscal_year, filters) + calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters) accumulate_values_into_parents(accounts, accounts_by_name, companies) - out = prepare_data(accounts, fiscal_year, balance_must_be, companies, company_currency) + out = prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency) if out: add_total_row(out, root_type, balance_must_be, companies, company_currency) @@ -229,7 +236,7 @@ def get_company_currency(filters=None): return (filters.get('presentation_currency') or frappe.get_cached_value('Company', filters.company, "default_currency")) -def calculate_values(accounts_by_name, gl_entries_by_account, companies, fiscal_year, filters): +def calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters): for entries in gl_entries_by_account.values(): for entry in entries: key = entry.account_number or entry.account_name @@ -241,7 +248,7 @@ def calculate_values(accounts_by_name, gl_entries_by_account, companies, fiscal_ and entry.company in companies.get(company)): d[company] = d.get(company, 0.0) + flt(entry.debit) - flt(entry.credit) - if entry.posting_date < fiscal_year.year_start_date: + if entry.posting_date < getdate(start_date): d["opening_balance"] = d.get("opening_balance", 0.0) + flt(entry.debit) - flt(entry.credit) def accumulate_values_into_parents(accounts, accounts_by_name, companies): @@ -295,10 +302,8 @@ def get_accounts(root_type, filters): `tabAccount` where company = %s and root_type = %s """ , (filters.get('company'), root_type), as_dict=1) -def prepare_data(accounts, fiscal_year, balance_must_be, companies, company_currency): +def prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency): data = [] - year_start_date = fiscal_year.year_start_date - year_end_date = fiscal_year.year_end_date for d in accounts: # add to output @@ -309,8 +314,8 @@ def prepare_data(accounts, fiscal_year, balance_must_be, companies, company_curr "account": _(d.account_name), "parent_account": _(d.parent_account), "indent": flt(d.indent), - "year_start_date": year_start_date, - "year_end_date": year_end_date, + "year_start_date": start_date, + "year_end_date": end_date, "currency": company_currency, "opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be == "Debit" else -1) }) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index e760b790963..7fb598bd689 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -18,17 +18,20 @@ from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, for from six import itervalues from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children -def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_values=False, +def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_end_date, filter_based_on, periodicity, accumulated_values=False, company=None, reset_period_on_fy_change=True): """Get a list of dict {"from_date": from_date, "to_date": to_date, "key": key, "label": label} Periodicity can be (Yearly, Quarterly, Monthly)""" - fiscal_year = get_fiscal_year_data(from_fiscal_year, to_fiscal_year) - validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year) - - # start with first day, so as to avoid year to_dates like 2-April if ever they occur] - year_start_date = getdate(fiscal_year.year_start_date) - year_end_date = getdate(fiscal_year.year_end_date) + if filter_based_on == 'Fiscal Year': + fiscal_year = get_fiscal_year_data(from_fiscal_year, to_fiscal_year) + validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year) + year_start_date = getdate(fiscal_year.year_start_date) + year_end_date = getdate(fiscal_year.year_end_date) + else: + validate_dates(period_start_date, period_end_date) + year_start_date = getdate(period_start_date) + year_end_date = getdate(period_end_date) months_to_add = { "Yearly": 12, @@ -42,6 +45,9 @@ def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_v start_date = year_start_date months = get_months(year_start_date, year_end_date) + if (months // months_to_add) != (months / months_to_add): + months += months_to_add + for i in range(months // months_to_add): period = frappe._dict({ "from_date": start_date @@ -103,9 +109,18 @@ def get_fiscal_year_data(from_fiscal_year, to_fiscal_year): def validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year): - if not fiscal_year.get('year_start_date') and not fiscal_year.get('year_end_date'): + if not fiscal_year.get('year_start_date') or not fiscal_year.get('year_end_date'): + frappe.throw(_("Start Year and End Year are mandatory")) + + if getdate(fiscal_year.get('year_end_date')) < getdate(fiscal_year.get('year_start_date')): frappe.throw(_("End Year cannot be before Start Year")) +def validate_dates(from_date, to_date): + if not from_date or not to_date: + frappe.throw("From Date and To Date are mandatory") + + if to_date < from_date: + frappe.throw("To Date cannot be less than From Date") def get_months(start_date, end_date): diff = (12 * end_date.year + end_date.month) - (12 * start_date.year + start_date.month) @@ -419,7 +434,9 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, filters.get(dimension.fieldname)) - additional_conditions.append("{0} in %({0})s".format(dimension.fieldname)) + additional_conditions.append("{0} in %({0})s".format(dimension.fieldname)) + else: + additional_conditions.append("{0} in (%({0})s)".format(dimension.fieldname)) return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else "" diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index 9a2205a5791..378fa3791c1 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -2,7 +2,7 @@

{% if (filters.party_name) { %} {%= filters.party_name %} - {% } else if (filters.party) { %} + {% } else if (filters.party && filters.party.length) { %} {%= filters.party %} {% } else if (filters.account) { %} {%= filters.account %} diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 898ac13e0e4..f776d933014 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -202,7 +202,9 @@ def get_conditions(filters): if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, filters.get(dimension.fieldname)) - conditions.append("{0} in %({0})s".format(dimension.fieldname)) + conditions.append("{0} in %({0})s".format(dimension.fieldname)) + else: + conditions.append("{0} in (%({0})s)".format(dimension.fieldname)) return "and {}".format(" and ".join(conditions)) if conditions else "" @@ -363,6 +365,7 @@ def get_columns(filters): columns = [ { + "label": _("GL Entry"), "fieldname": "gl_entry", "fieldtype": "Link", "options": "GL Entry", diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index 81fb1e012e2..7caa7648b12 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -9,7 +9,8 @@ from erpnext.accounts.report.financial_statements import (get_period_list, get_c def execute(filters=None): period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, - filters.periodicity, filters.accumulated_values, filters.company) + filters.period_start_date, filters.period_end_date, filters.filter_based_on, filters.periodicity, + company=filters.company) income = get_data(filters.company, "Income", "Credit", period_list, filters = filters, accumulated_values=filters.accumulated_values, diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js index 42b35c2a997..b2b95b2b81b 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.js +++ b/erpnext/accounts/report/purchase_register/purchase_register.js @@ -34,6 +34,33 @@ frappe.query_reports["Purchase Register"] = { "label": __("Mode of Payment"), "fieldtype": "Link", "options": "Mode of Payment" + }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center" + }, + { + "fieldname":"warehouse", + "label": __("Warehouse"), + "fieldtype": "Link", + "options": "Warehouse" + }, + { + "fieldname":"item_group", + "label": __("Item Group"), + "fieldtype": "Link", + "options": "Item Group" } ] } + +erpnext.dimension_filters.forEach((dimension) => { + frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); +}); \ No newline at end of file diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index 3f8abb76e26..9399e707390 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import flt from frappe import msgprint, _ +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children def execute(filters=None): return _execute(filters) @@ -66,7 +67,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum total_tax += tax_amount row.append(tax_amount) - # total tax, grand total, rounded total & outstanding amount + # total tax, grand total, rounded total & outstanding amount row += [total_tax, inv.base_grand_total, flt(inv.base_grand_total, 0), inv.outstanding_amount] data.append(row) @@ -134,6 +135,38 @@ def get_conditions(filters): if filters.get("mode_of_payment"): conditions += " and ifnull(mode_of_payment, '') = %(mode_of_payment)s" + if filters.get("cost_center"): + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.cost_center, '') = %(cost_center)s)""" + + if filters.get("warehouse"): + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.warehouse, '') = %(warehouse)s)""" + + if filters.get("item_group"): + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.item_group, '') = %(item_group)s)""" + + accounting_dimensions = get_accounting_dimensions(as_list=False) + + if accounting_dimensions: + common_condition = """ + and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + """ + for dimension in accounting_dimensions: + if filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + filters.get(dimension.fieldname)) + + conditions += common_condition + "and ifnull(`tabPurchase Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname) + else: + conditions += common_condition + "and ifnull(`tabPurchase Invoice Item`.{0}, '') in (%({0})s))".format(dimension.fieldname) + return conditions def get_invoices(filters, additional_query_columns): diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index 9864e40c1a9..b6e61b13069 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -344,16 +344,19 @@ def get_conditions(filters): accounting_dimensions = get_accounting_dimensions(as_list=False) if accounting_dimensions: + common_condition = """ + and exists(select name from `tabSales Invoice Item` + where parent=`tabSales Invoice`.name + """ for dimension in accounting_dimensions: if filters.get(dimension.fieldname): if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, filters.get(dimension.fieldname)) - conditions += """ and exists(select name from `tabSales Invoice Item` - where parent=`tabSales Invoice`.name - and ifnull(`tabSales Invoice Item`.{0}, '') in %({0})s)""".format(dimension.fieldname) - + conditions += common_condition + "and ifnull(`tabSales Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname) + else: + conditions += common_condition + "and ifnull(`tabSales Invoice Item`.{0}, '') in (%({0})s))".format(dimension.fieldname) return conditions diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 5fe6b416fda..d78324157a9 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -126,7 +126,9 @@ def get_rootwise_opening_balances(filters, report_type): if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, filters.get(dimension.fieldname)) - additional_conditions += "and {0} in %({0})s".format(dimension.fieldname) + additional_conditions += "and {0} in %({0})s".format(dimension.fieldname) + else: + additional_conditions += "and {0} in (%({0})s)".format(dimension.fieldname) query_filters.update({ dimension.fieldname: filters.get(dimension.fieldname) diff --git a/erpnext/agriculture/desk_page/agriculture/agriculture.json b/erpnext/agriculture/desk_page/agriculture/agriculture.json index 6020d628a2b..e0d2c9ca25a 100644 --- a/erpnext/agriculture/desk_page/agriculture/agriculture.json +++ b/erpnext/agriculture/desk_page/agriculture/agriculture.json @@ -1,16 +1,19 @@ { "cards": [ { - "links": "[\n {\n \"label\": \"Crop\",\n \"name\": \"Crop\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Crop Cycle\",\n \"name\": \"Crop Cycle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Location\",\n \"name\": \"Location\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]", - "title": "Crops & Lands" + "hidden": 0, + "label": "Crops & Lands", + "links": "[\n {\n \"label\": \"Crop\",\n \"name\": \"Crop\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Crop Cycle\",\n \"name\": \"Crop Cycle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Location\",\n \"name\": \"Location\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Plant Analysis\",\n \"name\": \"Plant Analysis\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Soil Analysis\",\n \"name\": \"Soil Analysis\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Water Analysis\",\n \"name\": \"Water Analysis\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Soil Texture\",\n \"name\": \"Soil Texture\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Weather\",\n \"name\": \"Weather\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Agriculture Analysis Criteria\",\n \"name\": \"Agriculture Analysis Criteria\",\n \"type\": \"doctype\"\n }\n]", - "title": "Analytics" + "hidden": 0, + "label": "Analytics", + "links": "[\n {\n \"label\": \"Plant Analysis\",\n \"name\": \"Plant Analysis\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Soil Analysis\",\n \"name\": \"Soil Analysis\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Water Analysis\",\n \"name\": \"Water Analysis\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Soil Texture\",\n \"name\": \"Soil Texture\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Weather\",\n \"name\": \"Weather\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Agriculture Analysis Criteria\",\n \"name\": \"Agriculture Analysis Criteria\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Disease\",\n \"name\": \"Disease\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fertilizer\",\n \"name\": \"Fertilizer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]", - "title": "Diseases & Fertilizers" + "hidden": 0, + "label": "Diseases & Fertilizers", + "links": "[\n {\n \"label\": \"Disease\",\n \"name\": \"Disease\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fertilizer\",\n \"name\": \"Fertilizer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" } ], "category": "Domains", @@ -24,7 +27,7 @@ "idx": 0, "is_standard": 1, "label": "Agriculture", - "modified": "2020-03-12 16:30:37.565413", + "modified": "2020-04-01 11:28:51.032822", "modified_by": "Administrator", "module": "Agriculture", "name": "Agriculture", diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index f9c65cb9606..03094160e5f 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -1,17 +1,19 @@ { "cards": [ { - "links": "[\n {\n \"label\": \"Asset\",\n \"name\": \"Asset\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Location\",\n \"name\": \"Location\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Asset Category\",\n \"name\": \"Asset Category\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Transfer an asset from one warehouse to another\",\n \"label\": \"Asset Movement\",\n \"name\": \"Asset Movement\",\n \"type\": \"doctype\"\n }\n]", - "title": "Assets" + "hidden": 0, + "label": "Assets", + "links": "[\n {\n \"label\": \"Asset\",\n \"name\": \"Asset\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Location\",\n \"name\": \"Location\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Asset Category\",\n \"name\": \"Asset Category\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Transfer an asset from one warehouse to another\",\n \"label\": \"Asset Movement\",\n \"name\": \"Asset Movement\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Asset Maintenance Team\",\n \"name\": \"Asset Maintenance Team\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Asset Maintenance Team\"\n ],\n \"label\": \"Asset Maintenance\",\n \"name\": \"Asset Maintenance\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Asset Maintenance\"\n ],\n \"label\": \"Asset Maintenance Log\",\n \"name\": \"Asset Maintenance Log\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Asset\"\n ],\n \"label\": \"Asset Value Adjustment\",\n \"name\": \"Asset Value Adjustment\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Asset\"\n ],\n \"label\": \"Asset Repair\",\n \"name\": \"Asset Repair\",\n \"type\": \"doctype\"\n }\n]", - "title": "Maintenance" + "hidden": 0, + "label": "Maintenance", + "links": "[\n {\n \"label\": \"Asset Maintenance Team\",\n \"name\": \"Asset Maintenance Team\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Asset Maintenance Team\"\n ],\n \"label\": \"Asset Maintenance\",\n \"name\": \"Asset Maintenance\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Asset Maintenance\"\n ],\n \"label\": \"Asset Maintenance Log\",\n \"name\": \"Asset Maintenance Log\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Asset\"\n ],\n \"label\": \"Asset Value Adjustment\",\n \"name\": \"Asset Value Adjustment\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Asset\"\n ],\n \"label\": \"Asset Repair\",\n \"name\": \"Asset Repair\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-table", - "links": "[\n {\n \"dependencies\": [\n \"Asset\"\n ],\n \"doctype\": \"Asset\",\n \"is_query_report\": true,\n \"label\": \"Asset Depreciation Ledger\",\n \"name\": \"Asset Depreciation Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Asset\"\n ],\n \"doctype\": \"Asset\",\n \"is_query_report\": true,\n \"label\": \"Asset Depreciations and Balances\",\n \"name\": \"Asset Depreciations and Balances\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Asset Maintenance\"\n ],\n \"doctype\": \"Asset Maintenance\",\n \"label\": \"Asset Maintenance\",\n \"name\": \"Asset Maintenance\",\n \"type\": \"report\"\n }\n]", - "title": "Reports" + "hidden": 0, + "label": "Reports", + "links": "[\n {\n \"dependencies\": [\n \"Asset\"\n ],\n \"doctype\": \"Asset\",\n \"is_query_report\": true,\n \"label\": \"Asset Depreciation Ledger\",\n \"name\": \"Asset Depreciation Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Asset\"\n ],\n \"doctype\": \"Asset\",\n \"is_query_report\": true,\n \"label\": \"Asset Depreciations and Balances\",\n \"name\": \"Asset Depreciations and Balances\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Asset Maintenance\"\n ],\n \"doctype\": \"Asset Maintenance\",\n \"label\": \"Asset Maintenance\",\n \"name\": \"Asset Maintenance\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", @@ -26,7 +28,7 @@ "idx": 0, "is_standard": 1, "label": "Assets", - "modified": "2020-03-12 16:30:38.651019", + "modified": "2020-04-01 11:28:51.072198", "modified_by": "Administrator", "module": "Assets", "name": "Assets", @@ -35,17 +37,17 @@ "pin_to_top": 0, "shortcuts": [ { - "is_query_report": 0, + "label": "Asset", "link_to": "Asset", "type": "DocType" }, { - "is_query_report": 0, + "label": "Asset Movement", "link_to": "Asset Movement", "type": "DocType" }, { - "is_query_report": 0, + "label": "Fixed Asset Register", "link_to": "Fixed Asset Register", "type": "Report" } diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py index fc08841be99..9bf4df423c2 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.py +++ b/erpnext/assets/doctype/asset_category/asset_category.py @@ -11,12 +11,34 @@ from frappe.model.document import Document class AssetCategory(Document): def validate(self): self.validate_finance_books() + self.validate_accounts() def validate_finance_books(self): for d in self.finance_books: for field in ("Total Number of Depreciations", "Frequency of Depreciation"): if cint(d.get(frappe.scrub(field)))<1: frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError) + + def validate_accounts(self): + account_type_map = { + 'fixed_asset_account': { 'account_type': 'Fixed Asset' }, + 'accumulated_depreciation_account': { 'account_type': 'Accumulated Depreciation' }, + 'depreciation_expense_account': { 'root_type': 'Expense' }, + 'capital_work_in_progress_account': { 'account_type': 'Capital Work in Progress' } + } + for d in self.accounts: + for fieldname in account_type_map.keys(): + if d.get(fieldname): + selected_account = d.get(fieldname) + key_to_match = next(iter(account_type_map.get(fieldname))) # acount_type or root_type + selected_key_type = frappe.db.get_value('Account', selected_account, key_to_match) + expected_key_type = account_type_map[fieldname][key_to_match] + + if selected_key_type != expected_key_type: + frappe.throw(_("Row #{}: {} of {} should be {}. Please modify the account or select a different account.") + .format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_type)), + title=_("Invalid Account")) + @frappe.whitelist() def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None): diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index ae8a605b851..5e764cf8bbe 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -1,44 +1,46 @@ { "cards": [ { - "links": "[\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier Group master.\",\n \"label\": \"Supplier Group\",\n \"name\": \"Supplier Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n }\n]", - "title": "Supplier" + "hidden": 0, + "label": "Supplier", + "links": "[\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier Group master.\",\n \"label\": \"Supplier Group\",\n \"name\": \"Supplier Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-star", - "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Purchase Orders given to Suppliers.\",\n \"label\": \"Purchase Order\",\n \"name\": \"Purchase Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Request for purchase.\",\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Request for quotation.\",\n \"label\": \"Request for Quotation\",\n \"name\": \"Request for Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Quotations received from Suppliers.\",\n \"label\": \"Supplier Quotation\",\n \"name\": \"Supplier Quotation\",\n \"type\": \"doctype\"\n }\n]", - "title": "Purchasing" + "hidden": 0, + "label": "Purchasing", + "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Purchase Orders given to Suppliers.\",\n \"label\": \"Purchase Order\",\n \"name\": \"Purchase Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Request for purchase.\",\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Request for quotation.\",\n \"label\": \"Request for Quotation\",\n \"name\": \"Request for Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Quotations received from Suppliers.\",\n \"label\": \"Supplier Quotation\",\n \"name\": \"Supplier Quotation\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n }\n]", - "title": "Items and Pricing" + "hidden": 0, + "label": "Items and Pricing", + "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-cog", - "links": "[\n {\n \"description\": \"Default settings for buying transactions.\",\n \"label\": \"Buying Settings\",\n \"name\": \"Buying Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for buying transactions.\",\n \"label\": \"Purchase Taxes and Charges Template\",\n \"name\": \"Purchase Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"type\": \"doctype\"\n }\n]", - "title": "Settings" + "hidden": 0, + "label": "Settings", + "links": "[\n {\n \"description\": \"Default settings for buying transactions.\",\n \"label\": \"Buying Settings\",\n \"name\": \"Buying Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for buying transactions.\",\n \"label\": \"Purchase Taxes and Charges Template\",\n \"name\": \"Purchase Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"All Supplier scorecards.\",\n \"label\": \"Supplier Scorecard\",\n \"name\": \"Supplier Scorecard\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Templates of supplier scorecard variables.\",\n \"label\": \"Supplier Scorecard Variable\",\n \"name\": \"Supplier Scorecard Variable\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Templates of supplier scorecard criteria.\",\n \"label\": \"Supplier Scorecard Criteria\",\n \"name\": \"Supplier Scorecard Criteria\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Templates of supplier standings.\",\n \"label\": \"Supplier Scorecard Standing\",\n \"name\": \"Supplier Scorecard Standing\",\n \"type\": \"doctype\"\n }\n]", - "title": "Supplier Scorecard" + "hidden": 0, + "label": "Supplier Scorecard", + "links": "[\n {\n \"description\": \"All Supplier scorecards.\",\n \"label\": \"Supplier Scorecard\",\n \"name\": \"Supplier Scorecard\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Templates of supplier scorecard variables.\",\n \"label\": \"Supplier Scorecard Variable\",\n \"name\": \"Supplier Scorecard Variable\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Templates of supplier scorecard criteria.\",\n \"label\": \"Supplier Scorecard Criteria\",\n \"name\": \"Supplier Scorecard Criteria\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Templates of supplier standings.\",\n \"label\": \"Supplier Scorecard Standing\",\n \"name\": \"Supplier Scorecard Standing\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-table", - "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Analytics\",\n \"name\": \"Purchase Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier-Wise Sales Analytics\",\n \"name\": \"Supplier-Wise Sales Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Stock Ledger Entry\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Trends\",\n \"name\": \"Purchase Order Trends\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Procurement Tracker\",\n \"name\": \"Procurement Tracker\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Requested Items To Be Ordered\",\n \"name\": \"Requested Items To Be Ordered\",\n \"onboard\": 1,\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n }\n]", - "title": "Key Reports" + "hidden": 0, + "label": "Key Reports", + "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Analytics\",\n \"name\": \"Purchase Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier-Wise Sales Analytics\",\n \"name\": \"Supplier-Wise Sales Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Stock Ledger Entry\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Trends\",\n \"name\": \"Purchase Order Trends\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Procurement Tracker\",\n \"name\": \"Procurement Tracker\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Requested Items To Be Ordered\",\n \"name\": \"Requested Items To Be Ordered\",\n \"onboard\": 1,\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n }\n]" }, { - "icon": "fa fa-list", - "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]", - "title": "Other Reports" + "hidden": 0, + "label": "Other Reports", + "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", "charts": [ { "chart_name": "Expenses", - "label": "Expenses", - "size": "Full" + "label": "Expenses" } ], "creation": "2020-01-28 11:50:26.195467", @@ -51,7 +53,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-03-12 16:30:40.779078", + "modified": "2020-04-01 11:28:51.192097", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -61,30 +63,30 @@ "shortcuts": [ { "format": "{} Unpaid", - "is_query_report": 0, + "label": "Purchase Invoice", "link_to": "Purchase Invoice", "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"Unpaid\"\n}", "type": "DocType" }, { "format": "{} to receive", - "is_query_report": 0, + "label": "Purchase Order", "link_to": "Purchase Order", "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Receive\"\n}", "type": "DocType" }, { - "is_query_report": 0, + "label": "Supplier Quotation", "link_to": "Supplier Quotation", "type": "DocType" }, { - "is_query_report": 0, + "label": "Accounts Payable", "link_to": "Accounts Payable", "type": "Report" }, { - "is_query_report": 0, + "label": "Purchase Register", "link_to": "Purchase Register", "type": "Report" } diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 4d836903911..578858ca520 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:39", @@ -63,9 +64,9 @@ "base_total", "base_net_total", "column_break_26", + "total_net_weight", "total", "net_total", - "total_net_weight", "taxes_section", "tax_category", "column_break_50", @@ -170,8 +171,8 @@ "search_index": 1 }, { - "description": "Fetch items based on Default Supplier.", "depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))", + "description": "Fetch items based on Default Supplier.", "fieldname": "get_items_from_open_material_requests", "fieldtype": "Button", "label": "Get Items from Open Material Requests" @@ -1054,7 +1055,8 @@ "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, - "modified": "2020-01-14 18:54:39.694448", + "links": [], + "modified": "2020-04-17 13:04:28.185197", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 6768dfabfea..e37e1dd99d8 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -702,8 +702,7 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" }, { "default": "0", @@ -723,7 +722,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2019-12-06 13:17:12.142799", + "modified": "2020-04-07 18:35:17.558928", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json index 7d7d6f4d3db..b50e834ec73 100644 --- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json +++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-05-22 12:43:10", "doctype": "DocType", @@ -522,8 +523,7 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" }, { "fieldname": "column_break_15", @@ -532,7 +532,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-06-02 05:32:46.019237", + "links": [], + "modified": "2020-04-07 18:35:51.175947", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation Item", diff --git a/erpnext/config/healthcare.py b/erpnext/config/healthcare.py index 756d22e416b..2b461273ad4 100644 --- a/erpnext/config/healthcare.py +++ b/erpnext/config/healthcare.py @@ -2,131 +2,68 @@ from __future__ import unicode_literals from frappe import _ def get_data(): - return [ - { - "label": _("Consultation"), - "icon": "icon-star", - "items": [ - { - "type": "doctype", - "name": "Patient Appointment", - "label": _("Patient Appointment"), - }, - { - "type": "doctype", - "name": "Patient Encounter", - "label": _("Patient Encounter"), - }, - { - "type": "doctype", - "name": "Vital Signs", - "label": _("Vital Signs"), - "description": _("Record Patient Vitals"), - }, - { - "type": "page", - "name": "patient_history", - "label": _("Patient History"), - }, - { - "type": "page", - "name": "appointment-analytic", - "label": _("Appointment Analytics"), - }, - { - "type": "doctype", - "name": "Clinical Procedure", - "label": _("Clinical Procedure"), - }, - { - "type": "doctype", - "name": "Inpatient Record", - "label": _("Inpatient Record"), - } - ] - }, - { - "label": _("Laboratory"), - "icon": "icon-list", - "items": [ - { - "type": "doctype", - "name": "Lab Test", - "label": _("Lab Test"), - }, - { - "type": "doctype", - "name": "Sample Collection", - "label": _("Sample Collection"), - }, - { - "type": "report", - "name": "Lab Test Report", - "is_query_report": True, - "label": _("Lab Test Report"), - } - ] - }, { "label": _("Masters"), - "icon": "icon-list", "items": [ { "type": "doctype", "name": "Patient", "label": _("Patient"), - "onboard": 1, + "onboard": 1 }, { "type": "doctype", "name": "Healthcare Practitioner", "label": _("Healthcare Practitioner"), - "onboard": 1, + "onboard": 1 }, { "type": "doctype", "name": "Practitioner Schedule", "label": _("Practitioner Schedule"), - }, - { - "type": "doctype", - "name": "Medical Code Standard", - "label": _("Medical Code Standard"), - }, - { - "type": "doctype", - "name": "Medical Code", - "label": _("Medical Code"), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Healthcare Service Unit", - "label": _("Healthcare Service Unit") - } - ] - }, - { - "label": _("Settings"), - "icon": "icon-cog", - "items": [ - { - "type": "doctype", - "name": "Healthcare Settings", - "label": _("Healthcare Settings"), - "onboard": 1, + "onboard": 1 }, { "type": "doctype", "name": "Medical Department", "label": _("Medical Department"), }, + { + "type": "doctype", + "name": "Healthcare Service Unit Type", + "label": _("Healthcare Service Unit Type") + }, + { + "type": "doctype", + "name": "Healthcare Service Unit", + "label": _("Healthcare Service Unit") + }, + { + "type": "doctype", + "name": "Medical Code Standard", + "label": _("Medical Code Standard") + }, + { + "type": "doctype", + "name": "Medical Code", + "label": _("Medical Code") + } + ] + }, + { + "label": _("Consultation Setup"), + "items": [ { "type": "doctype", "name": "Appointment Type", "label": _("Appointment Type"), }, + { + "type": "doctype", + "name": "Clinical Procedure Template", + "label": _("Clinical Procedure Template") + }, { "type": "doctype", "name": "Prescription Dosage", @@ -137,6 +74,36 @@ def get_data(): "name": "Prescription Duration", "label": _("Prescription Duration") }, + { + "type": "doctype", + "name": "Antibiotic", + "label": _("Antibiotic") + } + ] + }, + { + "label": _("Consultation"), + "items": [ + { + "type": "doctype", + "name": "Patient Appointment", + "label": _("Patient Appointment") + }, + { + "type": "doctype", + "name": "Clinical Procedure", + "label": _("Clinical Procedure") + }, + { + "type": "doctype", + "name": "Patient Encounter", + "label": _("Patient Encounter") + }, + { + "type": "doctype", + "name": "Vital Signs", + "label": _("Vital Signs") + }, { "type": "doctype", "name": "Complaint", @@ -147,40 +114,104 @@ def get_data(): "name": "Diagnosis", "label": _("Diagnosis") }, + { + "type": "doctype", + "name": "Fee Validity", + "label": _("Fee Validity") + } + ] + }, + { + "label": _("Settings"), + "items": [ + { + "type": "doctype", + "name": "Healthcare Settings", + "label": _("Healthcare Settings"), + "onboard": 1 + } + ] + }, + { + "label": _("Laboratory Setup"), + "items": [ + { + "type": "doctype", + "name": "Lab Test Template", + "label": _("Lab Test Template") + }, { "type": "doctype", "name": "Lab Test Sample", - "label": _("Lab Test Sample"), + "label": _("Lab Test Sample") }, { "type": "doctype", "name": "Lab Test UOM", "label": _("Lab Test UOM") }, - { - "type": "doctype", - "name": "Antibiotic", - "label": _("Antibiotic") - }, { "type": "doctype", "name": "Sensitivity", "label": _("Sensitivity") + } + ] + }, + { + "label": _("Laboratory"), + "items": [ + { + "type": "doctype", + "name": "Lab Test", + "label": _("Lab Test") }, { "type": "doctype", - "name": "Lab Test Template", - "label": _("Lab Test Template") + "name": "Sample Collection", + "label": _("Sample Collection") }, { "type": "doctype", - "name": "Clinical Procedure Template", - "label": _("Clinical Procedure Template"), + "name": "Dosage Form", + "label": _("Dosage Form") + } + ] + }, + { + "label": _("Records and History"), + "items": [ + { + "type": "page", + "name": "patient_history", + "label": _("Patient History"), }, { "type": "doctype", - "name": "Healthcare Service Unit Type", - "label": _("Healthcare Service Unit Type") + "name": "Patient Medical Record", + "label": _("Patient Medical Record") + }, + { + "type": "doctype", + "name": "Inpatient Record", + "label": _("Inpatient Record") + } + ] + }, + { + "label": _("Reports"), + "items": [ + { + "type": "report", + "is_query_report": True, + "name": "Patient Appointment Analytics", + "doctype": "Patient Appointment" + }, + { + "type": "report", + "is_query_report": True, + "name": "Lab Test Report", + "doctype": "Lab Test", + "label": _("Lab Test Report") } ] } diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 76eb56f523c..4045250c330 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -439,7 +439,7 @@ class AccountsController(TransactionBase): if account_currency not in valid_currency: frappe.throw(_("Account {0} is invalid. Account Currency must be {1}") - .format(account, _(" or ").join(valid_currency))) + .format(account, (' ' + _("or") + ' ').join(valid_currency))) def clear_unallocated_advances(self, childtype, parentfield): self.set(parentfield, self.get(parentfield, {"allocated_amount": ["not in", [0, None, ""]]})) @@ -834,7 +834,7 @@ class AccountsController(TransactionBase): for d in self.get("payment_schedule"): if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date): - frappe.throw(_("Row {0}: Due Date cannot be before posting date").format(d.idx)) + frappe.throw(_("Row {0}: Due Date in the Payment Terms table cannot be before Posting Date").format(d.idx)) elif d.due_date in dates: li.append(_("{0} in row {1}").format(d.due_date, d.idx)) dates.append(d.due_date) @@ -1123,36 +1123,39 @@ def get_supplier_block_status(party_name): } return info -def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_code): +def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item): """ Returns a Sales Order Item child item containing the default values """ p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname) - item = frappe.get_doc("Item", item_code) + item = frappe.get_doc("Item", trans_item.get('item_code')) child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description - child_item.reqd_by_date = p_doc.delivery_date + child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 + child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) + if not child_item.warehouse: + frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") + .format(frappe.bold("default warehouse"), frappe.bold(item.item_code))) return child_item -def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_code): +def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item): """ Returns a Purchase Order Item child item containing the default values """ p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname) - item = frappe.get_doc("Item", item_code) + item = frappe.get_doc("Item", trans_item.get('item_code')) child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description - child_item.schedule_date = p_doc.schedule_date + child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 + child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation return child_item @@ -1196,9 +1199,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if not d.get("docname"): new_child_flag = True if parent_doctype == "Sales Order": - child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code")) + child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) if parent_doctype == "Purchase Order": - child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code")) + child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) else: child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")): @@ -1243,6 +1246,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil child_item.flags.ignore_validate_update_after_submit = True if new_child_flag: + parent.load_from_db() child_item.idx = len(parent.items) + 1 child_item.insert() else: diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index fcc9098ba10..0e72ec2853c 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -45,6 +45,7 @@ class BuyingController(StockController): self.validate_warehouse() self.validate_from_warehouse() self.set_supplier_address() + self.validate_asset_return() if self.doctype=="Purchase Invoice": self.validate_purchase_receipt_if_update_stock() @@ -100,6 +101,19 @@ class BuyingController(StockController): for d in tax_for_valuation: d.category = 'Total' msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items')) + + def validate_asset_return(self): + if self.doctype not in ['Purchase Receipt', 'Purchase Invoice'] or not self.is_return: + return + + purchase_doc_field = 'purchase_receipt' if self.doctype == 'Purchase Receipt' else 'purchase_invoice' + not_cancelled_asset = [d.name for d in frappe.db.get_all("Asset", { + purchase_doc_field: self.return_against, + "docstatus": 1 + })] + if self.is_return and len(not_cancelled_asset): + frappe.throw(_("{} has submitted assets linked to it. You need to cancel the assets to create purchase return.".format(self.return_against)), + title=_("Not Allowed")) def get_asset_items(self): if self.doctype not in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']: diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 4d0520abc81..de76e45cd11 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -69,16 +69,16 @@ status_map = { ["Cancelled", "eval:self.docstatus==2"], ["Closed", "eval:self.status=='Closed'"], ], - "Purchase Invoice": [ - ["Draft", None], - ["Submitted", "eval:self.docstatus==1"], - ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"], - ["Return", "eval:self.is_return==1 and self.docstatus==1"], - ["Debit Note Issued", - "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], - ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], - ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], - ["Cancelled", "eval:self.docstatus==2"], + "Purchase Invoice": [ + ["Draft", None], + ["Submitted", "eval:self.docstatus==1"], + ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"], + ["Return", "eval:self.is_return==1 and self.docstatus==1"], + ["Debit Note Issued", + "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], + ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], + ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], + ["Cancelled", "eval:self.docstatus==2"], ], "Material Request": [ ["Draft", None], diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index f6908c06bd2..55a2c435a12 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -21,6 +21,7 @@ class StockController(AccountsController): super(StockController, self).validate() self.validate_inspection() self.validate_serialized_batch() + self.validate_customer_provided_item() def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False): if self.docstatus == 2: @@ -377,6 +378,12 @@ class StockController(AccountsController): for blanket_order in blanket_orders: frappe.get_doc("Blanket Order", blanket_order).update_ordered_qty() + def validate_customer_provided_item(self): + for d in self.get('items'): + # Customer Provided parts will have zero valuation rate + if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): + d.allow_zero_valuation_rate = 1 + def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, warehouse_account=None, company=None): def _delete_gl_entries(voucher_type, voucher_no): diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 95e661aa221..4e568e2795a 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -667,8 +667,7 @@ def get_itemised_tax_breakup_html(doc): itemised_tax=itemised_tax, itemised_taxable_amount=itemised_taxable_amount, tax_accounts=tax_accounts, - conversion_rate=doc.conversion_rate, - currency=doc.currency + doc=doc ) ) diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index 9ebbe271218..4a599fe478d 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -1,24 +1,24 @@ { "cards": [ { - "icon": "fa fa-star", - "links": "[\n {\n \"description\": \"Database of potential customers.\",\n \"label\": \"Lead\",\n \"name\": \"Lead\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Potential opportunities for selling.\",\n \"label\": \"Opportunity\",\n \"name\": \"Opportunity\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Record of all communications of type email, phone, chat, visit, etc.\",\n \"label\": \"Communication\",\n \"name\": \"Communication\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Track Leads by Lead Source.\",\n \"label\": \"Lead Source\",\n \"name\": \"Lead Source\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Helps you keep tracks of Contracts based on Supplier, Customer and Employee\",\n \"label\": \"Contract\",\n \"name\": \"Contract\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Helps you manage appointments with your leads\",\n \"label\": \"Appointment\",\n \"name\": \"Appointment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Newsletter\",\n \"name\": \"Newsletter\",\n \"type\": \"doctype\"\n }\n]", - "title": "Sales Pipeline" + "hidden": 0, + "label": "Sales Pipeline", + "links": "[\n {\n \"description\": \"Database of potential customers.\",\n \"label\": \"Lead\",\n \"name\": \"Lead\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Potential opportunities for selling.\",\n \"label\": \"Opportunity\",\n \"name\": \"Opportunity\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Record of all communications of type email, phone, chat, visit, etc.\",\n \"label\": \"Communication\",\n \"name\": \"Communication\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Track Leads by Lead Source.\",\n \"label\": \"Lead Source\",\n \"name\": \"Lead Source\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Helps you keep tracks of Contracts based on Supplier, Customer and Employee\",\n \"label\": \"Contract\",\n \"name\": \"Contract\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Helps you manage appointments with your leads\",\n \"label\": \"Appointment\",\n \"name\": \"Appointment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Newsletter\",\n \"name\": \"Newsletter\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-list", - "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Prospects Engaged But Not Converted\",\n \"name\": \"Prospects Engaged But Not Converted\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Opportunity\"\n ],\n \"doctype\": \"Opportunity\",\n \"is_query_report\": true,\n \"label\": \"Minutes to First Response for Opportunity\",\n \"name\": \"Minutes to First Response for Opportunity\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Campaign Efficiency\",\n \"name\": \"Campaign Efficiency\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Owner Efficiency\",\n \"name\": \"Lead Owner Efficiency\",\n \"type\": \"report\"\n }\n]", - "title": "Reports" + "hidden": 0, + "label": "Reports", + "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Prospects Engaged But Not Converted\",\n \"name\": \"Prospects Engaged But Not Converted\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Opportunity\"\n ],\n \"doctype\": \"Opportunity\",\n \"is_query_report\": true,\n \"label\": \"Minutes to First Response for Opportunity\",\n \"name\": \"Minutes to First Response for Opportunity\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Campaign Efficiency\",\n \"name\": \"Campaign Efficiency\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Owner Efficiency\",\n \"name\": \"Lead Owner Efficiency\",\n \"type\": \"report\"\n }\n]" }, { - "icon": "fa fa-cog", - "links": "[\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sends Mails to lead or contact based on a Campaign schedule\",\n \"label\": \"Email Campaign\",\n \"name\": \"Email Campaign\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Send mass SMS to your contacts\",\n \"label\": \"SMS Center\",\n \"name\": \"SMS Center\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Logs for maintaining sms delivery status\",\n \"label\": \"SMS Log\",\n \"name\": \"SMS Log\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup SMS gateway settings\",\n \"label\": \"SMS Settings\",\n \"name\": \"SMS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Email Group\",\n \"name\": \"Email Group\",\n \"type\": \"doctype\"\n }\n]", - "title": "Settings" + "hidden": 0, + "label": "Settings", + "links": "[\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sends Mails to lead or contact based on a Campaign schedule\",\n \"label\": \"Email Campaign\",\n \"name\": \"Email Campaign\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Send mass SMS to your contacts\",\n \"label\": \"SMS Center\",\n \"name\": \"SMS Center\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Logs for maintaining sms delivery status\",\n \"label\": \"SMS Log\",\n \"name\": \"SMS Log\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup SMS gateway settings\",\n \"label\": \"SMS Settings\",\n \"name\": \"SMS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Email Group\",\n \"name\": \"Email Group\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-star", - "links": "[\n {\n \"description\": \"Plan for maintenance visits.\",\n \"label\": \"Maintenance Schedule\",\n \"name\": \"Maintenance Schedule\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Visit report for maintenance call.\",\n \"label\": \"Maintenance Visit\",\n \"name\": \"Maintenance Visit\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Warranty Claim against Serial No.\",\n \"label\": \"Warranty Claim\",\n \"name\": \"Warranty Claim\",\n \"type\": \"doctype\"\n }\n]", - "title": "Maintenance" + "hidden": 0, + "label": "Maintenance", + "links": "[\n {\n \"description\": \"Plan for maintenance visits.\",\n \"label\": \"Maintenance Schedule\",\n \"name\": \"Maintenance Schedule\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Visit report for maintenance call.\",\n \"label\": \"Maintenance Visit\",\n \"name\": \"Maintenance Visit\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Warranty Claim against Serial No.\",\n \"label\": \"Warranty Claim\",\n \"name\": \"Warranty Claim\",\n \"type\": \"doctype\"\n }\n]" } ], "category": "Modules", @@ -33,7 +33,7 @@ "idx": 0, "is_standard": 1, "label": "CRM", - "modified": "2020-03-12 16:30:41.142037", + "modified": "2020-04-01 11:28:51.219999", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -43,25 +43,25 @@ "shortcuts": [ { "format": "Open", - "is_query_report": 0, + "label": "Lead", "link_to": "Lead", "stats_filter": "{\"status\":\"Open\"}", "type": "DocType" }, { "format": "{} Assigned", - "is_query_report": 0, + "label": "Opportunity", "link_to": "Opportunity", "stats_filter": "{\"_assign\": [\"like\", '%' + frappe.session.user + '%']}", "type": "DocType" }, { - "is_query_report": 0, + "label": "Customer", "link_to": "Customer", "type": "DocType" }, { - "is_query_report": 0, + "label": "Sales Analytics", "link_to": "Sales Analytics", "type": "Report" } diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index b3197ae60c0..20ab51d44bc 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -1,12 +1,10 @@ { - "actions": [], "allow_events_in_timeline": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2013-04-10 11:45:37", "doctype": "DocType", "document_type": "Document", - "email_append_to": 1, "engine": "InnoDB", "field_order": [ "organization_lead", @@ -34,6 +32,7 @@ "notes", "address_info", "address_html", + "address_type", "address_title", "address_line1", "address_line2", @@ -285,8 +284,7 @@ "depends_on": "eval: doc.__islocal", "fieldname": "pincode", "fieldtype": "Data", - "label": "Postal Code", - "options": "Country" + "label": "Postal Code" }, { "fieldname": "column_break2", @@ -304,7 +302,8 @@ "fieldtype": "Data", "label": "Phone", "oldfieldname": "contact_no", - "oldfieldtype": "Data" + "oldfieldtype": "Data", + "options": "Phone" }, { "depends_on": "eval: doc.__islocal", @@ -312,7 +311,8 @@ "fieldtype": "Data", "label": "Mobile No.", "oldfieldname": "mobile_no", - "oldfieldtype": "Data" + "oldfieldtype": "Data", + "options": "Phone" }, { "depends_on": "eval: doc.__islocal", @@ -433,13 +433,21 @@ "fieldname": "contact_section", "fieldtype": "Section Break", "label": "Contact" + }, + { + "default": "Billing", + "depends_on": "eval: doc.__islocal", + "fieldname": "address_type", + "fieldtype": "Select", + "label": "Address Type", + "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nCurrent\nPermanent\nOther" } ], "icon": "fa fa-user", "idx": 5, "image_field": "image", "links": [], - "modified": "2020-01-13 16:16:48.885228", + "modified": "2020-04-08 22:26:11.687110", "modified_by": "Administrator", "module": "CRM", "name": "Lead", @@ -508,9 +516,8 @@ } ], "search_fields": "lead_name,lead_owner,status", - "sender_field": "email_id", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", "title_field": "title" -} +} \ No newline at end of file diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 985abfbc85d..74b358219dc 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -126,7 +126,7 @@ class Lead(SellingController): self.title = self.lead_name def create_address(self): - address_fields = ["address_title", "address_line1", "address_line2", + address_fields = ["address_type", "address_title", "address_line1", "address_line2", "city", "county", "state", "country", "pincode"] info_fields = ["email_id", "phone", "fax"] @@ -209,7 +209,7 @@ class Lead(SellingController): self.contact_doc.save() def flush_address_and_contact_fields(self): - fields = ['address_line1', 'address_line2', 'address_title', + fields = ['address_type', 'address_line1', 'address_line2', 'address_title', 'city', 'county', 'country', 'fax', 'pincode', 'state'] for field in fields: diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 98a350a5c13..6a54c5fc6ef 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -423,7 +423,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2020-03-20 12:28:45.228994", + "modified": "2020-04-07 09:05:39.391109", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 5e640e78c8a..1b071ea1b70 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -336,3 +336,27 @@ def make_opportunity_from_communication(communication, ignore_communication_link link_communication_to_document(doc, "Opportunity", opportunity.name, ignore_communication_links) return opportunity.name +@frappe.whitelist() +def get_events(start, end, filters=None): + """Returns events for Gantt / Calendar view rendering. + :param start: Start date-time. + :param end: End date-time. + :param filters: Filters (JSON). + """ + from frappe.desk.calendar import get_event_conditions + conditions = get_event_conditions("Opportunity", filters) + + data = frappe.db.sql(""" + select + distinct `tabOpportunity`.name, `tabOpportunity`.customer_name, `tabOpportunity`.opportunity_amount, + `tabOpportunity`.title, `tabOpportunity`.contact_date + from + `tabOpportunity` + where + (`tabOpportunity`.contact_date between %(start)s and %(end)s) + {conditions} + """.format(conditions=conditions), { + "start": start, + "end": end + }, as_dict=True, update={"allDay": 0}) + return data \ No newline at end of file diff --git a/erpnext/crm/doctype/opportunity/opportunity_calendar.js b/erpnext/crm/doctype/opportunity/opportunity_calendar.js new file mode 100644 index 00000000000..58fa2b8cd86 --- /dev/null +++ b/erpnext/crm/doctype/opportunity/opportunity_calendar.js @@ -0,0 +1,19 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt +frappe.views.calendar["Opportunity"] = { + field_map: { + "start": "contact_date", + "end": "contact_date", + "id": "name", + "title": "customer_name", + "allDay": "allDay" + }, + options: { + header: { + left: 'prev,next today', + center: 'title', + right: 'month' + } + }, + get_events_method: 'erpnext.crm.doctype.opportunity.opportunity.get_events' +} diff --git a/erpnext/education/desk_page/education/education.json b/erpnext/education/desk_page/education/education.json index bbd8ead43f9..fc2697f0d75 100644 --- a/erpnext/education/desk_page/education/education.json +++ b/erpnext/education/desk_page/education/education.json @@ -1,56 +1,69 @@ { "cards": [ { - "links": "[\n {\n \"label\": \"Student Attendance Tool\",\n \"name\": \"Student Attendance Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Assessment Result Tool\",\n \"name\": \"Assessment Result Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Group Creation Tool\",\n \"name\": \"Student Group Creation Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Program Enrollment Tool\",\n \"name\": \"Program Enrollment Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course Scheduling Tool\",\n \"name\": \"Course Scheduling Tool\",\n \"type\": \"doctype\"\n }\n]", - "title": "Tools" + "hidden": 0, + "label": "Tools", + "links": "[\n {\n \"label\": \"Student Attendance Tool\",\n \"name\": \"Student Attendance Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Assessment Result Tool\",\n \"name\": \"Assessment Result Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Group Creation Tool\",\n \"name\": \"Student Group Creation Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Program Enrollment Tool\",\n \"name\": \"Program Enrollment Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course Scheduling Tool\",\n \"name\": \"Course Scheduling Tool\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"dependencies\": [\n \"Program Enrollment\"\n ],\n \"doctype\": \"Program Enrollment\",\n \"is_query_report\": true,\n \"label\": \"Student and Guardian Contact Details\",\n \"name\": \"Student and Guardian Contact Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Student Attendance\"\n ],\n \"doctype\": \"Student Attendance\",\n \"is_query_report\": true,\n \"label\": \"Student Monthly Attendance Sheet\",\n \"name\": \"Student Monthly Attendance Sheet\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Fees\"\n ],\n \"doctype\": \"Fees\",\n \"is_query_report\": true,\n \"label\": \"Student Fee Collection\",\n \"name\": \"Student Fee Collection\",\n \"type\": \"report\"\n }\n]", - "title": "Other Reports" + "hidden": 0, + "label": "Other Reports", + "links": "[\n {\n \"dependencies\": [\n \"Program Enrollment\"\n ],\n \"doctype\": \"Program Enrollment\",\n \"is_query_report\": true,\n \"label\": \"Student and Guardian Contact Details\",\n \"name\": \"Student and Guardian Contact Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Student Attendance\"\n ],\n \"doctype\": \"Student Attendance\",\n \"is_query_report\": true,\n \"label\": \"Student Monthly Attendance Sheet\",\n \"name\": \"Student Monthly Attendance Sheet\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Fees\"\n ],\n \"doctype\": \"Fees\",\n \"is_query_report\": true,\n \"label\": \"Student Fee Collection\",\n \"name\": \"Student Fee Collection\",\n \"type\": \"report\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Student Category\",\n \"name\": \"Student Category\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Batch Name\",\n \"name\": \"Student Batch Name\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Grading Scale\",\n \"name\": \"Grading Scale\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Academic Term\",\n \"name\": \"Academic Term\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Academic Year\",\n \"name\": \"Academic Year\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Education Settings\",\n \"name\": \"Education Settings\",\n \"type\": \"doctype\"\n }\n]", - "title": "Settings" + "hidden": 0, + "label": "Settings", + "links": "[\n {\n \"label\": \"Student Category\",\n \"name\": \"Student Category\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Batch Name\",\n \"name\": \"Student Batch Name\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Grading Scale\",\n \"name\": \"Grading Scale\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Academic Term\",\n \"name\": \"Academic Term\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Academic Year\",\n \"name\": \"Academic Year\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Education Settings\",\n \"name\": \"Education Settings\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Article\",\n \"name\": \"Article\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Video\",\n \"name\": \"Video\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quiz\",\n \"name\": \"Quiz\",\n \"type\": \"doctype\"\n }\n]", - "title": "Content Masters" + "hidden": 0, + "label": "Content Masters", + "links": "[\n {\n \"label\": \"Article\",\n \"name\": \"Article\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Video\",\n \"name\": \"Video\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quiz\",\n \"name\": \"Quiz\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Student Attendance\",\n \"name\": \"Student Attendance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Leave Application\",\n \"name\": \"Student Leave Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Student Attendance\"\n ],\n \"doctype\": \"Student Attendance\",\n \"is_query_report\": true,\n \"label\": \"Absent Student Report\",\n \"name\": \"Absent Student Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Student Attendance\"\n ],\n \"doctype\": \"Student Attendance\",\n \"is_query_report\": true,\n \"label\": \"Student Batch-Wise Attendance\",\n \"name\": \"Student Batch-Wise Attendance\",\n \"type\": \"report\"\n }\n]", - "title": "Attendance" + "hidden": 0, + "label": "Attendance", + "links": "[\n {\n \"label\": \"Student Attendance\",\n \"name\": \"Student Attendance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Leave Application\",\n \"name\": \"Student Leave Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Student Attendance\"\n ],\n \"doctype\": \"Student Attendance\",\n \"is_query_report\": true,\n \"label\": \"Absent Student Report\",\n \"name\": \"Absent Student Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Student Attendance\"\n ],\n \"doctype\": \"Student Attendance\",\n \"is_query_report\": true,\n \"label\": \"Student Batch-Wise Attendance\",\n \"name\": \"Student Batch-Wise Attendance\",\n \"type\": \"report\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Student Applicant\",\n \"name\": \"Student Applicant\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Admission\",\n \"name\": \"Student Admission\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Program Enrollment\",\n \"name\": \"Program Enrollment\",\n \"type\": \"doctype\"\n }\n]", - "title": "Admission" + "hidden": 0, + "label": "Admission", + "links": "[\n {\n \"label\": \"Student Applicant\",\n \"name\": \"Student Applicant\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Admission\",\n \"name\": \"Student Admission\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Program Enrollment\",\n \"name\": \"Program Enrollment\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Assessment Plan\",\n \"name\": \"Assessment Plan\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Assessment Group\",\n \"link\": \"Tree/Assessment Group\",\n \"name\": \"Assessment Group\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Assessment Result\",\n \"name\": \"Assessment Result\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Assessment Criteria\",\n \"name\": \"Assessment Criteria\",\n \"type\": \"doctype\"\n }\n]", - "title": "Assessment" + "hidden": 0, + "label": "Assessment", + "links": "[\n {\n \"label\": \"Assessment Plan\",\n \"name\": \"Assessment Plan\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Assessment Group\",\n \"link\": \"Tree/Assessment Group\",\n \"name\": \"Assessment Group\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Assessment Result\",\n \"name\": \"Assessment Result\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Assessment Criteria\",\n \"name\": \"Assessment Criteria\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Student\",\n \"name\": \"Student\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Guardian\",\n \"name\": \"Guardian\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Log\",\n \"name\": \"Student Log\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Group\",\n \"name\": \"Student Group\",\n \"type\": \"doctype\"\n }\n]", - "title": "Student" + "hidden": 0, + "label": "Student", + "links": "[\n {\n \"label\": \"Student\",\n \"name\": \"Student\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Guardian\",\n \"name\": \"Guardian\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Log\",\n \"name\": \"Student Log\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Group\",\n \"name\": \"Student Group\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Program\",\n \"name\": \"Program\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course\",\n \"name\": \"Course\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Topic\",\n \"name\": \"Topic\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Instructor\",\n \"name\": \"Instructor\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Room\",\n \"name\": \"Room\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]", - "title": "Masters" + "hidden": 0, + "label": "Masters", + "links": "[\n {\n \"label\": \"Program\",\n \"name\": \"Program\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course\",\n \"name\": \"Course\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Topic\",\n \"name\": \"Topic\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Instructor\",\n \"name\": \"Instructor\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Room\",\n \"name\": \"Room\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Course Enrollment\",\n \"name\": \"Course Enrollment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course Activity\",\n \"name\": \"Course Activity\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quiz Activity\",\n \"name\": \"Quiz Activity\",\n \"type\": \"doctype\"\n }\n]", - "title": "LMS Activity" + "hidden": 0, + "label": "LMS Activity", + "links": "[\n {\n \"label\": \"Course Enrollment\",\n \"name\": \"Course Enrollment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course Activity\",\n \"name\": \"Course Activity\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quiz Activity\",\n \"name\": \"Quiz Activity\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Course Schedule\",\n \"name\": \"Course Schedule\",\n \"route\": \"#List/Course Schedule/Calendar\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course Scheduling Tool\",\n \"name\": \"Course Scheduling Tool\",\n \"type\": \"doctype\"\n }\n]", - "title": "Schedule" + "hidden": 0, + "label": "Schedule", + "links": "[\n {\n \"label\": \"Course Schedule\",\n \"name\": \"Course Schedule\",\n \"route\": \"#List/Course Schedule/Calendar\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course Scheduling Tool\",\n \"name\": \"Course Scheduling Tool\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Fees\",\n \"name\": \"Fees\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fee Schedule\",\n \"name\": \"Fee Schedule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fee Structure\",\n \"name\": \"Fee Structure\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fee Category\",\n \"name\": \"Fee Category\",\n \"type\": \"doctype\"\n }\n]", - "title": "Fees" + "hidden": 0, + "label": "Fees", + "links": "[\n {\n \"label\": \"Fees\",\n \"name\": \"Fees\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fee Schedule\",\n \"name\": \"Fee Schedule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fee Structure\",\n \"name\": \"Fee Structure\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fee Category\",\n \"name\": \"Fee Category\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"dependencies\": [\n \"Assessment Result\"\n ],\n \"doctype\": \"Assessment Result\",\n \"is_query_report\": true,\n \"label\": \"Course wise Assessment Report\",\n \"name\": \"Course wise Assessment Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Assessment Result\"\n ],\n \"doctype\": \"Assessment Result\",\n \"is_query_report\": true,\n \"label\": \"Final Assessment Grades\",\n \"name\": \"Final Assessment Grades\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Assessment Plan\"\n ],\n \"doctype\": \"Assessment Plan\",\n \"is_query_report\": true,\n \"label\": \"Assessment Plan Status\",\n \"name\": \"Assessment Plan Status\",\n \"type\": \"report\"\n },\n {\n \"label\": \"Student Report Generation Tool\",\n \"name\": \"Student Report Generation Tool\",\n \"type\": \"doctype\"\n }\n]", - "title": "Assessment Reports" + "hidden": 0, + "label": "Assessment Reports", + "links": "[\n {\n \"dependencies\": [\n \"Assessment Result\"\n ],\n \"doctype\": \"Assessment Result\",\n \"is_query_report\": true,\n \"label\": \"Course wise Assessment Report\",\n \"name\": \"Course wise Assessment Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Assessment Result\"\n ],\n \"doctype\": \"Assessment Result\",\n \"is_query_report\": true,\n \"label\": \"Final Assessment Grades\",\n \"name\": \"Final Assessment Grades\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Assessment Plan\"\n ],\n \"doctype\": \"Assessment Plan\",\n \"is_query_report\": true,\n \"label\": \"Assessment Plan Status\",\n \"name\": \"Assessment Plan Status\",\n \"type\": \"report\"\n },\n {\n \"label\": \"Student Report Generation Tool\",\n \"name\": \"Student Report Generation Tool\",\n \"type\": \"doctype\"\n }\n]" } ], "category": "Domains", @@ -64,7 +77,7 @@ "idx": 0, "is_standard": 1, "label": "Education", - "modified": "2020-03-12 16:30:37.217514", + "modified": "2020-04-01 11:28:51.011309", "modified_by": "Administrator", "module": "Education", "name": "Education", diff --git a/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py b/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py index c2ac0d7d7be..5bebd4385c6 100644 --- a/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py +++ b/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py @@ -14,7 +14,7 @@ def execute(filters=None): student_batch_name = filters.get("student_batch_name") columns = get_columns() - + program_enrollments = frappe.get_list("Program Enrollment", fields=["student", "student_name"], filters={"academic_year": academic_year, "program": program, "student_batch_name": student_batch_name}) @@ -46,9 +46,9 @@ def execute(filters=None): def get_columns(): columns = [ - _(" Group Roll No") + "::60", - _("Student ID") + ":Link/Student:90", - _("Student Name") + "::150", + _("Group Roll No") + "::60", + _("Student ID") + ":Link/Student:90", + _("Student Name") + "::150", _("Student Mobile No.") + "::110", _("Student Email ID") + "::125", _("Student Address") + "::175", @@ -84,10 +84,10 @@ def get_guardian_map(student_list): guardian_list = list(set([g.guardian for g in guardian_details])) or [''] - guardian_mobile_no = dict(frappe.db.sql("""select name, mobile_number from `tabGuardian` + guardian_mobile_no = dict(frappe.db.sql("""select name, mobile_number from `tabGuardian` where name in (%s)""" % ", ".join(['%s']*len(guardian_list)), tuple(guardian_list))) - guardian_email_id = dict(frappe.db.sql("""select name, email_address from `tabGuardian` + guardian_email_id = dict(frappe.db.sql("""select name, email_address from `tabGuardian` where name in (%s)""" % ", ".join(['%s']*len(guardian_list)), tuple(guardian_list))) for guardian in guardian_details: diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py index 3bc8db5e78a..cc75a0afbe0 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py @@ -121,7 +121,7 @@ def call_mws_method(mws_method, *args, **kwargs): time.sleep(delay) continue - mws_settings.enable_synch = 0 + mws_settings.enable_sync = 0 mws_settings.save() frappe.throw(_("Sync has been temporarily disabled because maximum retries have been exceeded")) diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py index 9925dc4a084..f713684d37c 100755 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py @@ -39,16 +39,19 @@ __all__ = [ # for a list of the end points and marketplace IDs MARKETPLACES = { - "CA" : "https://mws.amazonservices.ca", #A2EUQ1WTGCTBG2 - "US" : "https://mws.amazonservices.com", #ATVPDKIKX0DER", - "DE" : "https://mws-eu.amazonservices.com", #A1PA6795UKMFR9 - "ES" : "https://mws-eu.amazonservices.com", #A1RKKUPIHCS9HS - "FR" : "https://mws-eu.amazonservices.com", #A13V1IB3VIYZZH - "IN" : "https://mws.amazonservices.in", #A21TJRUUN4KGV - "IT" : "https://mws-eu.amazonservices.com", #APJ6JRA9NG5V4 - "UK" : "https://mws-eu.amazonservices.com", #A1F83G8C2ARO7P - "JP" : "https://mws.amazonservices.jp", #A1VC38T7YXB528 - "CN" : "https://mws.amazonservices.com.cn", #AAHKV2X7AFYLW + "CA": "https://mws.amazonservices.ca", #A2EUQ1WTGCTBG2 + "US": "https://mws.amazonservices.com", #ATVPDKIKX0DER", + "DE": "https://mws-eu.amazonservices.com", #A1PA6795UKMFR9 + "ES": "https://mws-eu.amazonservices.com", #A1RKKUPIHCS9HS + "FR": "https://mws-eu.amazonservices.com", #A13V1IB3VIYZZH + "IN": "https://mws.amazonservices.in", #A21TJRUUN4KGV + "IT": "https://mws-eu.amazonservices.com", #APJ6JRA9NG5V4 + "UK": "https://mws-eu.amazonservices.com", #A1F83G8C2ARO7P + "JP": "https://mws.amazonservices.jp", #A1VC38T7YXB528 + "CN": "https://mws.amazonservices.com.cn", #AAHKV2X7AFYLW + "AE": " https://mws.amazonservices.ae", #A2VIGQ35RCS4UG + "MX": "https://mws.amazonservices.com.mx", #A1AM78C64UM0Y8 + "BR": "https://mws.amazonservices.com", #A2Q3Y263D00KWC } diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.json b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.json index 607ca4fe1de..5a678e77d16 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.json +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.json @@ -1,974 +1,237 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2018-07-31 05:51:41.357047", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2018-07-31 05:51:41.357047", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enable_amazon", + "mws_credentials", + "seller_id", + "aws_access_key_id", + "mws_auth_token", + "secret_key", + "column_break_4", + "market_place_id", + "region", + "domain", + "section_break_13", + "company", + "warehouse", + "item_group", + "price_list", + "column_break_17", + "customer_group", + "territory", + "customer_type", + "market_place_account_group", + "section_break_12", + "after_date", + "taxes_charges", + "sync_products", + "sync_orders", + "column_break_10", + "enable_sync", + "max_retry_limit" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "enable_amazon", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Enable Amazon", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "enable_amazon", + "fieldtype": "Check", + "label": "Enable Amazon" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "fieldname": "mws_credentials", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "MWS Credentials", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "mws_credentials", + "fieldtype": "Section Break", + "label": "MWS Credentials" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "seller_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Seller ID", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "seller_id", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Seller ID", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "aws_access_key_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "AWS Access Key ID", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "aws_access_key_id", + "fieldtype": "Data", + "in_list_view": 1, + "label": "AWS Access Key ID", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mws_auth_token", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "MWS Auth Token", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "mws_auth_token", + "fieldtype": "Data", + "in_list_view": 1, + "label": "MWS Auth Token", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "secret_key", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Secret Key", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "secret_key", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Secret Key", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "market_place_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Market Place ID", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "market_place_id", + "fieldtype": "Data", + "label": "Market Place ID", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "region", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Region", - "length": 0, - "no_copy": 0, - "options": "\nIN\nCN\nJP\nBR\nAU\nES\nUK\nFR\nDE\nIT\nCA\nUS\nMX", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "region", + "fieldtype": "Select", + "label": "Region", + "options": "\nAE\nAU\nBR\nCA\nCN\nDE\nES\nFR\nIN\nJP\nIT\nMX\nUK\nUS", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "domain", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Domain", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "domain", + "fieldtype": "Data", + "label": "Domain", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_13", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_13", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "options": "Warehouse", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Group", - "length": 0, - "no_copy": 0, - "options": "Item Group", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "price_list", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price List", - "length": 0, - "no_copy": 0, - "options": "Price List", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "price_list", + "fieldtype": "Link", + "label": "Price List", + "options": "Price List", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_17", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "customer_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer Group", - "length": 0, - "no_copy": 0, - "options": "Customer Group", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "customer_group", + "fieldtype": "Link", + "label": "Customer Group", + "options": "Customer Group", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "territory", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Territory", - "length": 0, - "no_copy": 0, - "options": "Territory", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "territory", + "fieldtype": "Link", + "label": "Territory", + "options": "Territory", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "customer_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer Type", - "length": 0, - "no_copy": 0, - "options": "Individual\nCompany", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "customer_type", + "fieldtype": "Select", + "label": "Customer Type", + "options": "Individual\nCompany", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "market_place_account_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Market Place Account Group", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "market_place_account_group", + "fieldtype": "Link", + "label": "Market Place Account Group", + "options": "Account", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_12", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_12", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Amazon will synch data updated after this date", - "fieldname": "after_date", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "After Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Amazon will synch data updated after this date", + "fieldname": "after_date", + "fieldtype": "Datetime", + "label": "After Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Get financial breakup of Taxes and charges data by Amazon ", - "fieldname": "taxes_charges", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Synch Taxes and Charges", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "description": "Get financial breakup of Taxes and charges data by Amazon ", + "fieldname": "taxes_charges", + "fieldtype": "Check", + "label": "Sync Taxes and Charges" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Always synch your products from Amazon MWS before synching the Orders details", - "fieldname": "synch_products", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Synch Products", - "length": 0, - "no_copy": 0, - "options": "get_products_details", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Click this button to pull your Sales Order data from Amazon MWS.", - "fieldname": "synch_orders", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Synch Orders", - "length": 0, - "no_copy": 0, - "options": "get_order_details", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "3", + "fieldname": "max_retry_limit", + "fieldtype": "Int", + "label": "Max Retry Limit" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_10", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Always sync your products from Amazon MWS before synching the Orders details", + "fieldname": "sync_products", + "fieldtype": "Button", + "label": "Sync Products", + "options": "get_products_details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "description": "Check this to enable a scheduled Daily synchronization routine via scheduler", - "fieldname": "enable_synch", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Enable Scheduled Synch", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Click this button to pull your Sales Order data from Amazon MWS.", + "fieldname": "sync_orders", + "fieldtype": "Button", + "label": "Sync Orders", + "options": "get_order_details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "3", - "fieldname": "max_retry_limit", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Max Retry Limit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "description": "Check this to enable a scheduled Daily synchronization routine via scheduler", + "fieldname": "enable_sync", + "fieldtype": "Check", + "label": "Enable Scheduled Sync" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2018-09-07 16:45:44.439834", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "Amazon MWS Settings", - "name_case": "", - "owner": "Administrator", + ], + "issingle": 1, + "links": [], + "modified": "2020-04-07 14:26:20.174848", + "modified_by": "Administrator", + "module": "ERPNext Integrations", + "name": "Amazon MWS Settings", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py index c222afbb6c1..899b7ffe13d 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py @@ -7,14 +7,15 @@ import frappe from frappe.model.document import Document import dateutil from frappe.custom.doctype.custom_field.custom_field import create_custom_fields +from erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_methods import get_orders class AmazonMWSSettings(Document): def validate(self): if self.enable_amazon == 1: - self.enable_synch = 1 + self.enable_sync = 1 setup_custom_fields() else: - self.enable_synch = 0 + self.enable_sync = 0 def get_products_details(self): if self.enable_amazon == 1: @@ -27,7 +28,7 @@ class AmazonMWSSettings(Document): def schedule_get_order_details(): mws_settings = frappe.get_doc("Amazon MWS Settings") - if mws_settings.enable_synch and mws_settings.enable_amazon: + if mws_settings.enable_sync and mws_settings.enable_amazon: after_date = dateutil.parser.parse(mws_settings.after_date).strftime("%Y-%m-%d") get_orders(after_date = after_date) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 104ac570c69..d84c8234efa 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -2,15 +2,40 @@ // For license information, please see license.txt frappe.ui.form.on('Tally Migration', { - onload: function(frm) { + onload: function (frm) { + let reload_status = true; frappe.realtime.on("tally_migration_progress_update", function (data) { + if (reload_status) { + frappe.model.with_doc(frm.doc.doctype, frm.doc.name, () => { + frm.refresh_header(); + }); + reload_status = false; + } frm.dashboard.show_progress(data.title, (data.count / data.total) * 100, data.message); - if (data.count == data.total) { - window.setTimeout(title => frm.dashboard.hide_progress(title), 1500, data.title); + let error_occurred = data.count === -1; + if (data.count == data.total || error_occurred) { + window.setTimeout((title) => { + frm.dashboard.hide_progress(title); + frm.reload_doc(); + if (error_occurred) { + frappe.msgprint({ + message: __("An error has occurred during {0}. Check {1} for more details", + [ + repl("%(tally_document)s", { + tally_document: frm.docname + }), + "Error Log" + ] + ), + title: __("Tally Migration Error"), + indicator: "red" + }); + } + }, 2000, data.title); } }); }, - refresh: function(frm) { + refresh: function (frm) { if (frm.doc.master_data && !frm.doc.is_master_data_imported) { if (frm.doc.is_master_data_processed) { if (frm.doc.status != "Importing Master Data") { @@ -34,17 +59,17 @@ frappe.ui.form.on('Tally Migration', { } } }, - add_button: function(frm, label, method) { + add_button: function (frm, label, method) { frm.add_custom_button( label, - () => frm.call({ - doc: frm.doc, - method: method, - freeze: true, - callback: () => { - frm.remove_custom_button(label); - } - }) + () => { + frm.call({ + doc: frm.doc, + method: method, + freeze: true + }); + frm.reload_doc(); + } ); } }); diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json index 26415caf8d3..dc6f093ac9d 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json @@ -1,4 +1,5 @@ { + "actions": [], "beta": 1, "creation": "2019-02-01 14:27:09.485238", "doctype": "DocType", @@ -14,6 +15,7 @@ "tally_debtors_account", "company_section", "tally_company", + "default_uom", "column_break_8", "erpnext_company", "processed_files_section", @@ -43,6 +45,7 @@ "label": "Status" }, { + "description": "Data exported from Tally that consists of the Chart of Accounts, Customers, Suppliers, Addresses, Items and UOMs", "fieldname": "master_data", "fieldtype": "Attach", "in_list_view": 1, @@ -50,6 +53,7 @@ }, { "default": "Sundry Creditors", + "description": "Creditors Account set in Tally", "fieldname": "tally_creditors_account", "fieldtype": "Data", "label": "Tally Creditors Account", @@ -61,6 +65,7 @@ }, { "default": "Sundry Debtors", + "description": "Debtors Account set in Tally", "fieldname": "tally_debtors_account", "fieldtype": "Data", "label": "Tally Debtors Account", @@ -72,6 +77,7 @@ "fieldtype": "Section Break" }, { + "description": "Company Name as per Imported Tally Data", "fieldname": "tally_company", "fieldtype": "Data", "label": "Tally Company", @@ -82,9 +88,11 @@ "fieldtype": "Column Break" }, { + "description": "Your Company set in ERPNext", "fieldname": "erpnext_company", "fieldtype": "Data", - "label": "ERPNext Company" + "label": "ERPNext Company", + "read_only_depends_on": "eval:doc.is_master_data_processed == 1" }, { "fieldname": "processed_files_section", @@ -155,24 +163,28 @@ "options": "Cost Center" }, { + "default": "0", "fieldname": "is_master_data_processed", "fieldtype": "Check", "label": "Is Master Data Processed", "read_only": 1 }, { + "default": "0", "fieldname": "is_day_book_data_processed", "fieldtype": "Check", "label": "Is Day Book Data Processed", "read_only": 1 }, { + "default": "0", "fieldname": "is_day_book_data_imported", "fieldtype": "Check", "label": "Is Day Book Data Imported", "read_only": 1 }, { + "default": "0", "fieldname": "is_master_data_imported", "fieldtype": "Check", "label": "Is Master Data Imported", @@ -188,13 +200,23 @@ "fieldtype": "Column Break" }, { + "description": "Day Book Data exported from Tally that consists of all historic transactions", "fieldname": "day_book_data", "fieldtype": "Attach", "in_list_view": 1, "label": "Day Book Data" + }, + { + "default": "Unit", + "description": "UOM in case unspecified in imported data", + "fieldname": "default_uom", + "fieldtype": "Link", + "label": "Default UOM", + "options": "UOM" } ], - "modified": "2019-04-29 05:46:54.394967", + "links": [], + "modified": "2020-04-16 13:03:28.894919", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Tally Migration", diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 01eee5b61f2..13474e19ee1 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -4,20 +4,23 @@ from __future__ import unicode_literals -from decimal import Decimal import json import re import traceback import zipfile +from decimal import Decimal + +from bs4 import BeautifulSoup as bs + import frappe +from erpnext import encode_company_abbr +from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.model.document import Document from frappe.model.naming import getseries, revert_series_if_last from frappe.utils.data import format_datetime -from bs4 import BeautifulSoup as bs -from erpnext import encode_company_abbr -from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts + PRIMARY_ACCOUNT = "Primary" VOUCHER_CHUNK_SIZE = 500 @@ -39,13 +42,15 @@ class TallyMigration(Document): return string master_file = frappe.get_doc("File", {"file_url": data_file}) + master_file_path = master_file.get_full_path() - with zipfile.ZipFile(master_file.get_full_path()) as zf: - encoded_content = zf.read(zf.namelist()[0]) - try: - content = encoded_content.decode("utf-8-sig") - except UnicodeDecodeError: - content = encoded_content.decode("utf-16") + if zipfile.is_zipfile(master_file_path): + with zipfile.ZipFile(master_file_path) as zf: + encoded_content = zf.read(zf.namelist()[0]) + try: + content = encoded_content.decode("utf-8-sig") + except UnicodeDecodeError: + content = encoded_content.decode("utf-16") master = bs(sanitize(emptify(content)), "xml") collection = master.BODY.IMPORTDATA.REQUESTDATA @@ -58,13 +63,14 @@ class TallyMigration(Document): "file_name": key + ".json", "attached_to_doctype": self.doctype, "attached_to_name": self.name, - "content": json.dumps(value) + "content": json.dumps(value), + "is_private": True }).insert() setattr(self, key, f.file_url) def _process_master_data(self): def get_company_name(collection): - return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string + return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string.strip() def get_coa_customers_suppliers(collection): root_type_map = { @@ -97,17 +103,17 @@ class TallyMigration(Document): # If Ledger doesn't have PARENT field then don't create Account # For example "Profit & Loss A/c" if account.PARENT: - yield account.PARENT.string, account["NAME"], 0 + yield account.PARENT.string.strip(), account["NAME"], 0 def get_parent(account): if account.PARENT: - return account.PARENT.string + return account.PARENT.string.strip() return { ("Yes", "No"): "Application of Funds (Assets)", ("Yes", "Yes"): "Expenses", ("No", "Yes"): "Income", ("No", "No"): "Source of Funds (Liabilities)", - }[(account.ISDEEMEDPOSITIVE.string, account.ISREVENUE.string)] + }[(account.ISDEEMEDPOSITIVE.string.strip(), account.ISREVENUE.string.strip())] def get_children_and_parent_dict(accounts): children, parents = {}, {} @@ -145,38 +151,38 @@ class TallyMigration(Document): parties, addresses = [], [] for account in collection.find_all("LEDGER"): party_type = None - if account.NAME.string in customers: + if account.NAME.string.strip() in customers: party_type = "Customer" parties.append({ "doctype": party_type, - "customer_name": account.NAME.string, - "tax_id": account.INCOMETAXNUMBER.string if account.INCOMETAXNUMBER else None, + "customer_name": account.NAME.string.strip(), + "tax_id": account.INCOMETAXNUMBER.string.strip() if account.INCOMETAXNUMBER else None, "customer_group": "All Customer Groups", "territory": "All Territories", "customer_type": "Individual", }) - elif account.NAME.string in suppliers: + elif account.NAME.string.strip() in suppliers: party_type = "Supplier" parties.append({ "doctype": party_type, - "supplier_name": account.NAME.string, - "pan": account.INCOMETAXNUMBER.string if account.INCOMETAXNUMBER else None, + "supplier_name": account.NAME.string.strip(), + "pan": account.INCOMETAXNUMBER.string.strip() if account.INCOMETAXNUMBER else None, "supplier_group": "All Supplier Groups", "supplier_type": "Individual", }) if party_type: - address = "\n".join([a.string for a in account.find_all("ADDRESS")]) + address = "\n".join([a.string.strip() for a in account.find_all("ADDRESS")]) addresses.append({ "doctype": "Address", "address_line1": address[:140].strip(), "address_line2": address[140:].strip(), - "country": account.COUNTRYNAME.string if account.COUNTRYNAME else None, - "state": account.LEDSTATENAME.string if account.LEDSTATENAME else None, - "gst_state": account.LEDSTATENAME.string if account.LEDSTATENAME else None, - "pin_code": account.PINCODE.string if account.PINCODE else None, - "mobile": account.LEDGERPHONE.string if account.LEDGERPHONE else None, - "phone": account.LEDGERPHONE.string if account.LEDGERPHONE else None, - "gstin": account.PARTYGSTIN.string if account.PARTYGSTIN else None, + "country": account.COUNTRYNAME.string.strip() if account.COUNTRYNAME else None, + "state": account.LEDSTATENAME.string.strip() if account.LEDSTATENAME else None, + "gst_state": account.LEDSTATENAME.string.strip() if account.LEDSTATENAME else None, + "pin_code": account.PINCODE.string.strip() if account.PINCODE else None, + "mobile": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, + "phone": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, + "gstin": account.PARTYGSTIN.string.strip() if account.PARTYGSTIN else None, "links": [{"link_doctype": party_type, "link_name": account["NAME"]}], }) return parties, addresses @@ -184,41 +190,50 @@ class TallyMigration(Document): def get_stock_items_uoms(collection): uoms = [] for uom in collection.find_all("UNIT"): - uoms.append({"doctype": "UOM", "uom_name": uom.NAME.string}) + uoms.append({"doctype": "UOM", "uom_name": uom.NAME.string.strip()}) items = [] for item in collection.find_all("STOCKITEM"): + stock_uom = item.BASEUNITS.string.strip() if item.BASEUNITS else self.default_uom items.append({ "doctype": "Item", - "item_code" : item.NAME.string, - "stock_uom": item.BASEUNITS.string, + "item_code" : item.NAME.string.strip(), + "stock_uom": stock_uom.strip(), "is_stock_item": 0, "item_group": "All Item Groups", "item_defaults": [{"company": self.erpnext_company}] }) + return items, uoms + try: + self.publish("Process Master Data", _("Reading Uploaded File"), 1, 5) + collection = self.get_collection(self.master_data) + company = get_company_name(collection) + self.tally_company = company + self.erpnext_company = company - self.publish("Process Master Data", _("Reading Uploaded File"), 1, 5) - collection = self.get_collection(self.master_data) + self.publish("Process Master Data", _("Processing Chart of Accounts and Parties"), 2, 5) + chart_of_accounts, customers, suppliers = get_coa_customers_suppliers(collection) - company = get_company_name(collection) - self.tally_company = company - self.erpnext_company = company + self.publish("Process Master Data", _("Processing Party Addresses"), 3, 5) + parties, addresses = get_parties_addresses(collection, customers, suppliers) - self.publish("Process Master Data", _("Processing Chart of Accounts and Parties"), 2, 5) - chart_of_accounts, customers, suppliers = get_coa_customers_suppliers(collection) - self.publish("Process Master Data", _("Processing Party Addresses"), 3, 5) - parties, addresses = get_parties_addresses(collection, customers, suppliers) - self.publish("Process Master Data", _("Processing Items and UOMs"), 4, 5) - items, uoms = get_stock_items_uoms(collection) - data = {"chart_of_accounts": chart_of_accounts, "parties": parties, "addresses": addresses, "items": items, "uoms": uoms} - self.publish("Process Master Data", _("Done"), 5, 5) + self.publish("Process Master Data", _("Processing Items and UOMs"), 4, 5) + items, uoms = get_stock_items_uoms(collection) + data = {"chart_of_accounts": chart_of_accounts, "parties": parties, "addresses": addresses, "items": items, "uoms": uoms} - self.dump_processed_data(data) - self.is_master_data_processed = 1 - self.status = "" - self.save() + self.publish("Process Master Data", _("Done"), 5, 5) + self.dump_processed_data(data) + + self.is_master_data_processed = 1 + + except: + self.publish("Process Master Data", _("Process Failed"), -1, 5) + self.log() + + finally: + self.set_status() def publish(self, title, message, count, total): frappe.publish_realtime("tally_migration_progress_update", {"title": title, "message": message, "count": count, "total": total}) @@ -256,7 +271,6 @@ class TallyMigration(Document): except: self.log(address) - def create_items_uoms(items_file_url, uoms_file_url): uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url}) for uom in json.loads(uoms_file.get_content()): @@ -273,25 +287,35 @@ class TallyMigration(Document): except: self.log(item) - self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4) - create_company_and_coa(self.chart_of_accounts) - self.publish("Import Master Data", _("Importing Parties and Addresses"), 2, 4) - create_parties_and_addresses(self.parties, self.addresses) - self.publish("Import Master Data", _("Importing Items and UOMs"), 3, 4) - create_items_uoms(self.items, self.uoms) - self.publish("Import Master Data", _("Done"), 4, 4) - self.status = "" - self.is_master_data_imported = 1 - self.save() + try: + self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4) + create_company_and_coa(self.chart_of_accounts) + + self.publish("Import Master Data", _("Importing Parties and Addresses"), 2, 4) + create_parties_and_addresses(self.parties, self.addresses) + + self.publish("Import Master Data", _("Importing Items and UOMs"), 3, 4) + create_items_uoms(self.items, self.uoms) + + self.publish("Import Master Data", _("Done"), 4, 4) + + self.is_master_data_imported = 1 + + except: + self.publish("Import Master Data", _("Process Failed"), -1, 5) + self.log() + + finally: + self.set_status() def _process_day_book_data(self): def get_vouchers(collection): vouchers = [] for voucher in collection.find_all("VOUCHER"): - if voucher.ISCANCELLED.string == "Yes": + if voucher.ISCANCELLED.string.strip() == "Yes": continue inventory_entries = voucher.find_all("INVENTORYENTRIES.LIST") + voucher.find_all("ALLINVENTORYENTRIES.LIST") + voucher.find_all("INVENTORYENTRIESIN.LIST") + voucher.find_all("INVENTORYENTRIESOUT.LIST") - if voucher.VOUCHERTYPENAME.string not in ["Journal", "Receipt", "Payment", "Contra"] and inventory_entries: + if voucher.VOUCHERTYPENAME.string.strip() not in ["Journal", "Receipt", "Payment", "Contra"] and inventory_entries: function = voucher_to_invoice else: function = voucher_to_journal_entry @@ -307,15 +331,15 @@ class TallyMigration(Document): accounts = [] ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST") for entry in ledger_entries: - account = {"account": encode_company_abbr(entry.LEDGERNAME.string, self.erpnext_company), "cost_center": self.default_cost_center} - if entry.ISPARTYLEDGER.string == "Yes": - party_details = get_party(entry.LEDGERNAME.string) + account = {"account": encode_company_abbr(entry.LEDGERNAME.string.strip(), self.erpnext_company), "cost_center": self.default_cost_center} + if entry.ISPARTYLEDGER.string.strip() == "Yes": + party_details = get_party(entry.LEDGERNAME.string.strip()) if party_details: party_type, party_account = party_details account["party_type"] = party_type account["account"] = party_account - account["party"] = entry.LEDGERNAME.string - amount = Decimal(entry.AMOUNT.string) + account["party"] = entry.LEDGERNAME.string.strip() + amount = Decimal(entry.AMOUNT.string.strip()) if amount > 0: account["credit_in_account_currency"] = str(abs(amount)) else: @@ -324,21 +348,21 @@ class TallyMigration(Document): journal_entry = { "doctype": "Journal Entry", - "tally_guid": voucher.GUID.string, - "posting_date": voucher.DATE.string, + "tally_guid": voucher.GUID.string.strip(), + "posting_date": voucher.DATE.string.strip(), "company": self.erpnext_company, "accounts": accounts, } return journal_entry def voucher_to_invoice(voucher): - if voucher.VOUCHERTYPENAME.string in ["Sales", "Credit Note"]: + if voucher.VOUCHERTYPENAME.string.strip() in ["Sales", "Credit Note"]: doctype = "Sales Invoice" party_field = "customer" account_field = "debit_to" account_name = encode_company_abbr(self.tally_debtors_account, self.erpnext_company) price_list_field = "selling_price_list" - elif voucher.VOUCHERTYPENAME.string in ["Purchase", "Debit Note"]: + elif voucher.VOUCHERTYPENAME.string.strip() in ["Purchase", "Debit Note"]: doctype = "Purchase Invoice" party_field = "supplier" account_field = "credit_to" @@ -351,10 +375,10 @@ class TallyMigration(Document): invoice = { "doctype": doctype, - party_field: voucher.PARTYNAME.string, - "tally_guid": voucher.GUID.string, - "posting_date": voucher.DATE.string, - "due_date": voucher.DATE.string, + party_field: voucher.PARTYNAME.string.strip(), + "tally_guid": voucher.GUID.string.strip(), + "posting_date": voucher.DATE.string.strip(), + "due_date": voucher.DATE.string.strip(), "items": get_voucher_items(voucher, doctype), "taxes": get_voucher_taxes(voucher), account_field: account_name, @@ -375,15 +399,15 @@ class TallyMigration(Document): for entry in inventory_entries: qty, uom = entry.ACTUALQTY.string.strip().split() items.append({ - "item_code": entry.STOCKITEMNAME.string, - "description": entry.STOCKITEMNAME.string, + "item_code": entry.STOCKITEMNAME.string.strip(), + "description": entry.STOCKITEMNAME.string.strip(), "qty": qty.strip(), "uom": uom.strip(), "conversion_factor": 1, - "price_list_rate": entry.RATE.string.split("/")[0], + "price_list_rate": entry.RATE.string.strip().split("/")[0], "cost_center": self.default_cost_center, "warehouse": self.default_warehouse, - account_field: encode_company_abbr(entry.find_all("ACCOUNTINGALLOCATIONS.LIST")[0].LEDGERNAME.string, self.erpnext_company), + account_field: encode_company_abbr(entry.find_all("ACCOUNTINGALLOCATIONS.LIST")[0].LEDGERNAME.string.strip(), self.erpnext_company), }) return items @@ -391,13 +415,13 @@ class TallyMigration(Document): ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST") taxes = [] for entry in ledger_entries: - if entry.ISPARTYLEDGER.string == "No": - tax_account = encode_company_abbr(entry.LEDGERNAME.string, self.erpnext_company) + if entry.ISPARTYLEDGER.string.strip() == "No": + tax_account = encode_company_abbr(entry.LEDGERNAME.string.strip(), self.erpnext_company) taxes.append({ "charge_type": "Actual", "account_head": tax_account, "description": tax_account, - "tax_amount": entry.AMOUNT.string, + "tax_amount": entry.AMOUNT.string.strip(), "cost_center": self.default_cost_center, }) return taxes @@ -408,15 +432,24 @@ class TallyMigration(Document): elif frappe.db.exists({"doctype": "Customer", "customer_name": party}): return "Customer", encode_company_abbr(self.tally_debtors_account, self.erpnext_company) - self.publish("Process Day Book Data", _("Reading Uploaded File"), 1, 3) - collection = self.get_collection(self.day_book_data) - self.publish("Process Day Book Data", _("Processing Vouchers"), 2, 3) - vouchers = get_vouchers(collection) - self.publish("Process Day Book Data", _("Done"), 3, 3) - self.dump_processed_data({"vouchers": vouchers}) - self.status = "" - self.is_day_book_data_processed = 1 - self.save() + try: + self.publish("Process Day Book Data", _("Reading Uploaded File"), 1, 3) + collection = self.get_collection(self.day_book_data) + + self.publish("Process Day Book Data", _("Processing Vouchers"), 2, 3) + vouchers = get_vouchers(collection) + + self.publish("Process Day Book Data", _("Done"), 3, 3) + self.dump_processed_data({"vouchers": vouchers}) + + self.is_day_book_data_processed = 1 + + except: + self.publish("Process Day Book Data", _("Process Failed"), -1, 5) + self.log() + + finally: + self.set_status() def _import_day_book_data(self): def create_fiscal_years(vouchers): @@ -454,23 +487,31 @@ class TallyMigration(Document): "currency": "INR" }).insert() - frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable") - frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable") - frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account) + try: + frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable") + frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable") + frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account) - vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers}) - vouchers = json.loads(vouchers_file.get_content()) + vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers}) + vouchers = json.loads(vouchers_file.get_content()) - create_fiscal_years(vouchers) - create_price_list() - create_custom_fields(["Journal Entry", "Purchase Invoice", "Sales Invoice"]) + create_fiscal_years(vouchers) + create_price_list() + create_custom_fields(["Journal Entry", "Purchase Invoice", "Sales Invoice"]) - total = len(vouchers) - is_last = False - for index in range(0, total, VOUCHER_CHUNK_SIZE): - if index + VOUCHER_CHUNK_SIZE >= total: - is_last = True - frappe.enqueue_doc(self.doctype, self.name, "_import_vouchers", queue="long", timeout=3600, start=index+1, total=total, is_last=is_last) + total = len(vouchers) + is_last = False + + for index in range(0, total, VOUCHER_CHUNK_SIZE): + if index + VOUCHER_CHUNK_SIZE >= total: + is_last = True + frappe.enqueue_doc(self.doctype, self.name, "_import_vouchers", queue="long", timeout=3600, start=index+1, total=total, is_last=is_last) + + except: + self.log() + + finally: + self.set_status() def _import_vouchers(self, start, total, is_last=False): frappe.flags.in_migrate = True @@ -494,25 +535,26 @@ class TallyMigration(Document): frappe.flags.in_migrate = False def process_master_data(self): - self.status = "Processing Master Data" - self.save() + self.set_status("Processing Master Data") frappe.enqueue_doc(self.doctype, self.name, "_process_master_data", queue="long", timeout=3600) def import_master_data(self): - self.status = "Importing Master Data" - self.save() + self.set_status("Importing Master Data") frappe.enqueue_doc(self.doctype, self.name, "_import_master_data", queue="long", timeout=3600) def process_day_book_data(self): - self.status = "Processing Day Book Data" - self.save() + self.set_status("Processing Day Book Data") frappe.enqueue_doc(self.doctype, self.name, "_process_day_book_data", queue="long", timeout=3600) def import_day_book_data(self): - self.status = "Importing Day Book Data" - self.save() + self.set_status("Importing Day Book Data") frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600) def log(self, data=None): - message = "\n".join(["Data", json.dumps(data, default=str, indent=4), "Exception", traceback.format_exc()]) + data = data or self.status + message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) return frappe.log_error(title="Tally Migration Error", message=message) + + def set_status(self, status=""): + self.status = status + self.save() diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 258a72c308c..54798ba08f7 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -1,28 +1,49 @@ { "cards": [ { - "icon": "icon-cog", - "links": "[\n {\n \"label\": \"Healthcare Settings\",\n \"name\": \"Healthcare Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Medical Department\",\n \"name\": \"Medical Department\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Appointment Type\",\n \"name\": \"Appointment Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Prescription Dosage\",\n \"name\": \"Prescription Dosage\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Prescription Duration\",\n \"name\": \"Prescription Duration\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Complaint\",\n \"name\": \"Complaint\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Diagnosis\",\n \"name\": \"Diagnosis\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Lab Test Sample\",\n \"name\": \"Lab Test Sample\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Lab Test UOM\",\n \"name\": \"Lab Test UOM\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Antibiotic\",\n \"name\": \"Antibiotic\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Sensitivity\",\n \"name\": \"Sensitivity\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Lab Test Template\",\n \"name\": \"Lab Test Template\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Clinical Procedure Template\",\n \"name\": \"Clinical Procedure Template\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Healthcare Service Unit Type\",\n \"name\": \"Healthcare Service Unit Type\",\n \"type\": \"doctype\"\n }\n]", - "title": "Settings" - }, - { - "icon": "icon-list", - "links": "[\n {\n \"label\": \"Lab Test\",\n \"name\": \"Lab Test\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Sample Collection\",\n \"name\": \"Sample Collection\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Lab Test Report\",\n \"name\": \"Lab Test Report\",\n \"type\": \"report\"\n }\n]", - "title": "Laboratory" - }, - { - "icon": "icon-list", - "links": "[\n {\n \"label\": \"Patient\",\n \"name\": \"Patient\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Healthcare Practitioner\",\n \"name\": \"Healthcare Practitioner\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Practitioner Schedule\",\n \"name\": \"Practitioner Schedule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Medical Code Standard\",\n \"name\": \"Medical Code Standard\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Medical Code\",\n \"name\": \"Medical Code\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Healthcare Service Unit\",\n \"name\": \"Healthcare Service Unit\",\n \"type\": \"doctype\"\n }\n]", + "icon": "", + "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient\",\n\t\t\"label\": \"Patient\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Practitioner\",\n\t\t\"label\":\"Healthcare Practitioner\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Practitioner Schedule\",\n\t\t\"label\": \"Practitioner Schedule\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Department\",\n\t\t\"label\": \"Medical Department\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Service Unit Type\",\n\t\t\"label\": \"Healthcare Service Unit Type\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Service Unit\",\n\t\t\"label\": \"Healthcare Service Unit\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Code Standard\",\n\t\t\"label\": \"Medical Code Standard\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Code\",\n\t\t\"label\": \"Medical Code\"\n\t}\n]", "title": "Masters" }, + { + "icon": "", + "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Appointment Type\",\n\t\t\"label\": \"Appointment Type\"\n },\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Clinical Procedure Template\",\n\t\t\"label\": \"Clinical Procedure Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Prescription Dosage\",\n\t\t\"label\": \"Prescription Dosage\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Prescription Duration\",\n\t\t\"label\": \"Prescription Duration\"\n\t},\n\t{\n\t \"type\": \"doctype\",\n\t\t\"name\": \"Antibiotic\",\n\t\t\"label\": \"Antibiotic\"\n\t}\n]", + "title": "Consultation Setup" + }, { "icon": "icon-star", - "links": "[\n {\n \"label\": \"Patient Appointment\",\n \"name\": \"Patient Appointment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Patient Encounter\",\n \"name\": \"Patient Encounter\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Record Patient Vitals\",\n \"label\": \"Vital Signs\",\n \"name\": \"Vital Signs\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Patient History\",\n \"name\": \"patient_history\",\n \"type\": \"page\"\n },\n {\n \"label\": \"Appointment Analytics\",\n \"name\": \"appointment-analytic\",\n \"type\": \"page\"\n },\n {\n \"label\": \"Clinical Procedure\",\n \"name\": \"Clinical Procedure\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Inpatient Record\",\n \"name\": \"Inpatient Record\",\n \"type\": \"doctype\"\n }\n]", + "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Appointment\",\n\t\t\"label\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Clinical Procedure\",\n\t\t\"label\": \"Clinical Procedure\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Encounter\",\n\t\t\"label\": \"Patient Encounter\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Vital Signs\",\n\t\t\"label\": \"Vital Signs\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Complaint\",\n\t\t\"label\": \"Complaint\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Diagnosis\",\n\t\t\"label\": \"Diagnosis\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Fee Validity\",\n\t\t\"label\": \"Fee Validity\"\n\t}\n]", "title": "Consultation" + }, + { + "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Settings\",\n\t\t\"label\": \"Healthcare Settings\",\n\t\t\"onboard\": 1\n\t}\n]", + "title": "Settings" + }, + { + "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test Template\",\n\t\t\"label\": \"Lab Test Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test Sample\",\n\t\t\"label\": \"Lab Test Sample\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test UOM\",\n\t\t\"label\": \"Lab Test UOM\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sensitivity\",\n\t\t\"label\": \"Sensitivity\"\n\t}\n]", + "title": "Laboratory Setup" + }, + { + "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test\",\n\t\t\"label\": \"Lab Test\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sample Collection\",\n\t\t\"label\": \"Sample Collection\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Dosage Form\",\n\t\t\"label\": \"Dosage Form\"\n\t}\n]", + "title": "Laboratory" + }, + { + "links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t}\n]", + "title": "Records and History" + }, + { + "links": "[\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Patient Appointment Analytics\",\n\t\t\"doctype\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Lab Test Report\",\n\t\t\"doctype\": \"Lab Test\",\n\t\t\"label\": \"Lab Test Report\"\n\t}\n]", + "title": "Reports" } ], "category": "Domains", - "charts": [], + "charts": [ + { + "chart_name": "Patient Appointments", + "label": "Patient Appointments" + } + ], + "charts_label": "", "creation": "2020-03-02 17:23:17.919682", "developer_mode_only": 0, "disable_user_customization": 0, @@ -32,7 +53,7 @@ "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-03-12 16:30:36.952979", + "modified": "2020-03-26 16:10:44.629795", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", @@ -40,5 +61,37 @@ "pin_to_bottom": 0, "pin_to_top": 0, "restrict_to_domain": "Healthcare", - "shortcuts": [] + "shortcuts": [ + { + "format": "{} Open", + "is_query_report": 0, + "link_to": "Patient Appointment", + "stats_filter": "{\n \"status\": \"Open\"\n}", + "type": "DocType" + }, + { + "format": "{} Active", + "is_query_report": 0, + "link_to": "Patient", + "stats_filter": "{\n \"status\": \"Active\"\n}", + "type": "DocType" + }, + { + "format": "{} Vacant", + "is_query_report": 0, + "link_to": "Healthcare Service Unit", + "stats_filter": "{\n \"occupancy_status\": \"Vacant\",\n \"is_group\": 0\n}", + "type": "DocType" + }, + { + "is_query_report": 0, + "link_to": "Healthcare Practitioner", + "type": "DocType" + }, + { + "is_query_report": 0, + "link_to": "patient_history", + "type": "Page" + } + ] } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.json b/erpnext/healthcare/doctype/appointment_type/appointment_type.json index ceabce2de10..58753bb4f05 100644 --- a/erpnext/healthcare/doctype/appointment_type/appointment_type.json +++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.json @@ -1,213 +1,94 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:appointment_type", - "beta": 1, - "creation": "2016-07-22 11:52:34.953019", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:appointment_type", + "beta": 1, + "creation": "2016-07-22 11:52:34.953019", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "appointment_type", + "ip", + "default_duration", + "color" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 1, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "appointment_type", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Type", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 1, + "allow_in_quick_entry": 1, + "fieldname": "appointment_type", + "fieldtype": "Data", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Type", + "reqd": 1, + "translatable": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fieldname": "ip", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Inpatient", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "default": "0", + "fieldname": "ip", + "fieldtype": "Check", + "label": "Is Inpatient", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 1, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "description": "In Minutes", - "fieldname": "default_duration", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Default Duration", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_in_quick_entry": 1, + "bold": 1, + "fieldname": "default_duration", + "fieldtype": "Int", + "in_filter": 1, + "in_list_view": 1, + "label": "Default Duration (In Minutes)" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 1, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "color", - "fieldtype": "Color", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Color", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "allow_in_quick_entry": 1, + "fieldname": "color", + "fieldtype": "Color", + "in_list_view": 1, + "label": "Color", + "no_copy": 1, + "report_hide": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-08 12:57:54.544216", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Appointment Type", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-02-03 21:06:05.833050", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Appointment Type", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Physician", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Physician", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "appointment_type", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "restrict_to_domain": "Healthcare", + "search_fields": "appointment_type", + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type_dashboard.py b/erpnext/healthcare/doctype/appointment_type/appointment_type_dashboard.py new file mode 100644 index 00000000000..845e4466c11 --- /dev/null +++ b/erpnext/healthcare/doctype/appointment_type/appointment_type_dashboard.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'appointment_type', + 'transactions': [ + { + 'label': _('Patient Appointments'), + 'items': ['Patient Appointment'] + }, + ] + } diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js index fa9188449e3..5f36bdd95c6 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js @@ -4,244 +4,285 @@ frappe.ui.form.on('Clinical Procedure', { setup: function(frm) { frm.set_query('batch_no', 'items', function(doc, cdt, cdn) { - var item = locals[cdt][cdn]; - if(!item.item_code) { - frappe.throw(__("Please enter Item Code to get Batch Number")); + let item = locals[cdt][cdn]; + if (!item.item_code) { + frappe.throw(__('Please enter Item Code to get Batch Number')); } else { + let filters = {'item_code': item.item_code}; + if (frm.doc.status == 'In Progress') { - var filters = { - 'item_code': item.item_code, - 'posting_date': frm.doc.start_date || frappe.datetime.nowdate() - }; - if(frm.doc.warehouse) filters["warehouse"] = frm.doc.warehouse; - } else { - filters = { - 'item_code': item.item_code - }; + filters['posting_date'] = frm.doc.start_date || frappe.datetime.nowdate(); + if (frm.doc.warehouse) filters['warehouse'] = frm.doc.warehouse; } + return { - query : "erpnext.controllers.queries.get_batch_no", + query : 'erpnext.controllers.queries.get_batch_no', filters: filters }; } }); }, + refresh: function(frm) { - frm.set_query("patient", function () { + frm.set_query('patient', function () { return { - filters: {"disabled": 0} + filters: {'status': ['!=', 'Disabled']} }; }); - frm.set_query("appointment", function () { + + frm.set_query('appointment', function () { return { filters: { - "procedure_template": ["not in", null], - "status": ['in', 'Open, Scheduled'] + 'procedure_template': ['not in', null], + 'status': ['in', 'Open, Scheduled'] } }; }); - frm.set_query("service_unit", function(){ + + frm.set_query('service_unit', function() { return { filters: { - "is_group": false, - "allow_appointments": true + 'is_group': false, + 'allow_appointments': true } }; }); - frm.set_query("practitioner", function() { + + frm.set_query('practitioner', function() { return { filters: { 'department': frm.doc.medical_department } }; }); - if(frm.doc.consume_stock){ + + if (frm.doc.consume_stock) { frm.set_indicator_formatter('item_code', - function(doc) { return (doc.qty<=doc.actual_qty) ? "green" : "orange" ; }); + function(doc) { return (doc.qty<=doc.actual_qty) ? 'green' : 'orange' ; }); } - if (!frm.doc.__islocal && frm.doc.status == 'In Progress'){ + if (frm.doc.docstatus == 1) { + if (frm.doc.status == 'In Progress') { + let btn_label = ''; + let msg = ''; + if (frm.doc.consume_stock) { + btn_label = __('Complete and Consume'); + msg = __('Complete {0} and Consume Stock?', [frm.doc.name]); + } else { + btn_label = 'Complete'; + msg = __('Complete {0}?', [frm.doc.name]); + } - if(frm.doc.consume_stock){ - var btn_label = 'Complete and Consume'; - var msg = 'Complete '+frm.doc.name+' and Consume Stock?'; - }else{ - btn_label = 'Complete'; - msg = 'Complete '+frm.doc.name+'?'; - } + frm.add_custom_button(__(btn_label), function () { + frappe.confirm( + msg, + function() { + frappe.call({ + method: 'complete_procedure', + doc: frm.doc, + callback: function(r) { + if (r.message) { + frappe.show_alert({ + message: __('Stock Entry {0} created', + ['' + r.message + '']), + indicator: 'green' + }); + frm.reload_doc(); + } + } + }); + } + ); + }).addClass("btn-primary"); - frm.add_custom_button(__(btn_label), function () { - frappe.confirm( - __(msg), - function(){ - frappe.call({ - doc: frm.doc, - method: "complete", - callback: function(r) { - if(!r.exc){ + } else if (frm.doc.status == 'Pending') { + frm.add_custom_button(__('Start'), function() { + frappe.call({ + doc: frm.doc, + method: 'start_procedure', + callback: function(r) { + if (!r.exc) { + if (r.message == 'insufficient stock') { + let msg = __('Stock quantity to start the Procedure is not available in the Warehouse {0}. Do you want to record a Stock Entry?', + [frm.doc.warehouse.bold()]); + frappe.confirm( + msg, + function() { + frappe.call({ + doc: frm.doc, + method: 'make_material_receipt', + callback: function(r) { + if (!r.exc) { + cur_frm.reload_doc(); + let doclist = frappe.model.sync(r.message); + frappe.set_route('Form', doclist[0].doctype, doclist[0].name); + } + } + }); + } + ); + } else { cur_frm.reload_doc(); } } - }); - } - ); - }); - }else if (frm.doc.status == 'Draft') { - frm.add_custom_button(__("Start"), function () { - frappe.call({ - doc: frm.doc, - method: "start", - callback: function(r) { - if(!r.exc){ - if(frm.doc.status == 'Draft'){ - frappe.confirm( - __("Stock quantity to start procedure is not available in the warehouse. Do you want to record a Stock Transfer"), - function(){ - frappe.call({ - doc: frm.doc, - method: "make_material_transfer", - callback: function(r) { - if(!r.exc){ - cur_frm.reload_doc(); - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - } - }); - } - ); - }else{ - cur_frm.reload_doc(); - } } - } - }); - }); - } - if (frm.doc.__islocal){ - frm.set_df_property("consumables", "hidden", 1); - }else{ - frm.set_df_property("consumables", "hidden", 0); + }); + }).addClass("btn-primary"); + } } }, - onload: function(frm){ - if(frm.doc.status == 'Completed'){ - frm.set_df_property("items", "read_only", 1); - } - if(frm.is_new()) { - frm.add_fetch("procedure_template", "medical_department", "medical_department"); - frm.set_value("start_time", null); + + onload: function(frm) { + if (frm.is_new()) { + frm.add_fetch('procedure_template', 'medical_department', 'medical_department'); + frm.set_value('start_time', null); } }, + patient: function(frm) { - if(frm.doc.patient){ + if (frm.doc.patient) { frappe.call({ - "method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail", + 'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail', args: { patient: frm.doc.patient }, callback: function (data) { - let age = ""; - if(data.message.dob){ + let age = ''; + if (data.message.dob) { age = calculate_age(data.message.dob); - }else if (data.message.age){ + } else if (data.message.age) { age = data.message.age; - if(data.message.age_as_on){ - age = age+" as on "+data.message.age_as_on; + if (data.message.age_as_on) { + age = __('{0} as on {1}', [age, data.message.age_as_on]); } } - frm.set_value("patient_age", age); - frm.set_value("patient_sex", data.message.sex); + frm.set_value('patient_age', age); + frm.set_value('patient_sex', data.message.sex); } }); - }else{ - frm.set_value("patient_age", ""); - frm.set_value("patient_sex", ""); + } else { + frm.set_value('patient_age', ''); + frm.set_value('patient_sex', ''); } }, + appointment: function(frm) { - if(frm.doc.appointment){ + if (frm.doc.appointment) { frappe.call({ - "method": "frappe.client.get", + 'method': 'frappe.client.get', args: { - doctype: "Patient Appointment", + doctype: 'Patient Appointment', name: frm.doc.appointment }, - callback: function (data) { - frm.set_value("patient", data.message.patient); - frm.set_value("procedure_template", data.message.procedure_template); - frm.set_value("medical_department", data.message.department); - frm.set_value("start_date", data.message.appointment_date); - frm.set_value("start_time", data.message.appointment_time); - frm.set_value("notes", data.message.notes); - frm.set_value("service_unit", data.message.service_unit); + callback: function(data) { + frm.set_value('patient', data.message.patient); + frm.set_value('procedure_template', data.message.procedure_template); + frm.set_value('medical_department', data.message.department); + frm.set_value('start_date', data.message.appointment_date); + frm.set_value('start_time', data.message.appointment_time); + frm.set_value('notes', data.message.notes); + frm.set_value('service_unit', data.message.service_unit); } }); } }, + procedure_template: function(frm) { - if(frm.doc.procedure_template){ + if (frm.doc.procedure_template) { frappe.call({ - "method": "frappe.client.get", + 'method': 'frappe.client.get', args: { - doctype: "Clinical Procedure Template", + doctype: 'Clinical Procedure Template', name: frm.doc.procedure_template }, callback: function (data) { - frm.set_value("medical_department", data.message.medical_department); - frm.set_value("consume_stock", data.message.consume_stock); - if(!frm.doc.warehouse){ - frappe.call({ - method: "frappe.client.get_value", - args: { - doctype: "Stock Settings", - fieldname: "default_warehouse" - }, - callback: function (data) { - frm.set_value("warehouse", data.message.default_warehouse); - } - }); - } + frm.set_value('medical_department', data.message.medical_department); + frm.set_value('consume_stock', data.message.consume_stock); + frm.events.set_warehouse(frm); + frm.events.set_procedure_consumables(frm); } }); - }else{ - frm.set_value("consume_stock", 0); } }, + service_unit: function(frm) { - if(frm.doc.service_unit){ + if (frm.doc.service_unit) { frappe.call({ - method: "frappe.client.get_value", + method: 'frappe.client.get_value', args:{ - fieldname: "warehouse", - doctype: "Healthcare Service Unit", + fieldname: 'warehouse', + doctype: 'Healthcare Service Unit', filters:{name: frm.doc.service_unit}, }, callback: function(data) { - if(data.message){ - frm.set_value("warehouse", data.message.warehouse); + if (data.message) { + frm.set_value('warehouse', data.message.warehouse); } } }); } }, + practitioner: function(frm) { - if(frm.doc.practitioner){ + if (frm.doc.practitioner) { frappe.call({ - "method": "frappe.client.get", + 'method': 'frappe.client.get', args: { - doctype: "Healthcare Practitioner", + doctype: 'Healthcare Practitioner', name: frm.doc.practitioner }, callback: function (data) { - frappe.model.set_value(frm.doctype,frm.docname, "medical_department",data.message.department); + frappe.model.set_value(frm.doctype,frm.docname, 'medical_department',data.message.department); } }); } + }, + + set_warehouse: function(frm) { + if (!frm.doc.warehouse) { + frappe.call({ + method: 'frappe.client.get_value', + args: { + doctype: 'Stock Settings', + fieldname: 'default_warehouse' + }, + callback: function (data) { + frm.set_value('warehouse', data.message.default_warehouse); + } + }); + } + }, + + set_procedure_consumables: function(frm) { + frappe.call({ + method: 'erpnext.healthcare.doctype.clinical_procedure.clinical_procedure.get_procedure_consumables', + args: { + procedure_template: frm.doc.procedure_template + }, + callback: function(data) { + if (data.message) { + frm.doc.items = []; + $.each(data.message, function(i, v) { + let item = frm.add_child('items'); + item.item_code = v.item_code; + item.item_name = v.item_name; + item.uom = v.uom; + item.stock_uom = v.stock_uom; + item.qty = flt(v.qty); + item.transfer_qty = v.transfer_qty; + item.conversion_factor = v.conversion_factor; + item.invoice_separately_as_consumables = v.invoice_separately_as_consumables; + item.batch_no = v.batch_no; + }); + refresh_field('items'); + } + } + }); } + }); -cur_frm.set_query("procedure_template", function(doc) { +cur_frm.set_query('procedure_template', function(doc) { return { filters: { 'medical_department': doc.medical_department @@ -249,57 +290,51 @@ cur_frm.set_query("procedure_template", function(doc) { }; }); -cur_frm.set_query("appointment", function() { - return { - filters: { - status:['in',["Open"]] - } - }; -}); - frappe.ui.form.on('Clinical Procedure Item', { - qty: function(frm, cdt, cdn){ - var d = locals[cdt][cdn]; - frappe.model.set_value(cdt, cdn, "transfer_qty", d.qty*d.conversion_factor); + qty: function(frm, cdt, cdn) { + let d = locals[cdt][cdn]; + frappe.model.set_value(cdt, cdn, 'transfer_qty', d.qty*d.conversion_factor); }, - uom: function(doc, cdt, cdn){ - var d = locals[cdt][cdn]; - if(d.uom && d.item_code){ + + uom: function(doc, cdt, cdn) { + let d = locals[cdt][cdn]; + if (d.uom && d.item_code) { return frappe.call({ - method: "erpnext.stock.doctype.stock_entry.stock_entry.get_uom_details", + method: 'erpnext.stock.doctype.stock_entry.stock_entry.get_uom_details', args: { item_code: d.item_code, uom: d.uom, qty: d.qty }, callback: function(r) { - if(r.message) { + if (r.message) { frappe.model.set_value(cdt, cdn, r.message); } } }); } }, + item_code: function(frm, cdt, cdn) { - var d = locals[cdt][cdn]; + let d = locals[cdt][cdn]; let args = null; - if(d.item_code) { + if (d.item_code) { args = { - 'doctype' : "Clinical Procedure", + 'doctype' : 'Clinical Procedure', 'item_code' : d.item_code, 'company' : frm.doc.company, 'warehouse': frm.doc.warehouse }; return frappe.call({ - method: "erpnext.stock.get_item_details.get_item_details", + method: 'erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.get_item_details', args: {args: args}, callback: function(r) { - if(r.message) { - frappe.model.set_value(cdt, cdn, "item_name", r.message.item_name); - frappe.model.set_value(cdt, cdn, "stock_uom", r.message.stock_uom); - frappe.model.set_value(cdt, cdn, "conversion_factor", r.message.conversion_factor); - frappe.model.set_value(cdt, cdn, "actual_qty", r.message.actual_qty); - refresh_field("items"); + if (r.message) { + let d = locals[cdt][cdn]; + $.each(r.message, function(k, v) { + d[k] = v; + }); + refresh_field('items'); } } }); @@ -307,16 +342,16 @@ frappe.ui.form.on('Clinical Procedure Item', { } }); -var calculate_age = function(birth) { - var ageMS = Date.parse(Date()) - Date.parse(birth); - var age = new Date(); +let calculate_age = function(birth) { + let ageMS = Date.parse(Date()) - Date.parse(birth); + let age = new Date(); age.setTime(ageMS); - var years = age.getFullYear() - 1970; - return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)"; + let years = age.getFullYear() - 1970; + return years + ' Year(s) ' + age.getMonth() + ' Month(s) ' + age.getDate() + ' Day(s)'; }; // List Stock items -cur_frm.set_query("item_code", "items", function() { +cur_frm.set_query('item_code', 'items', function() { return { filters: { is_stock_item:1 diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json index 3097ba3b9ad..3c936bbf277 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json @@ -1,955 +1,267 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 1, - "creation": "2017-04-07 12:52:43.542429", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "naming_series:", + "beta": 1, + "creation": "2017-04-07 12:52:43.542429", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "inpatient_record", + "naming_series", + "procedure_template", + "appointment", + "patient", + "patient_sex", + "patient_age", + "prescription", + "medical_department", + "practitioner", + "column_break_7", + "status", + "service_unit", + "warehouse", + "start_date", + "start_time", + "sample", + "invoiced", + "notes", + "company", + "consumables_section", + "consume_stock", + "items", + "section_break_24", + "invoice_separately_as_consumables", + "consumption_invoiced", + "consumable_total_amount", + "column_break_27", + "consumption_details", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.inpatient_record", - "fieldname": "inpatient_record", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Inpatient Record", - "length": 0, - "no_copy": 0, - "options": "Inpatient Record", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "patient.inpatient_record", + "fieldname": "inpatient_record", + "fieldtype": "Link", + "label": "Inpatient Record", + "options": "Inpatient Record", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 0, - "options": "HLC-CPR-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "HLC-CPR-.YYYY.-" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "appointment", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Appointment", - "length": 0, - "no_copy": 0, - "options": "Patient Appointment", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "appointment", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Appointment", + "options": "Patient Appointment" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "inpatient_record.patient", - "fieldname": "patient", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Patient", - "length": 0, - "no_copy": 0, - "options": "Patient", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "inpatient_record.patient", + "fieldname": "patient", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Patient", + "options": "Patient", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient_age", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Age", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "patient_age", + "fieldtype": "Data", + "label": "Age", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient_sex", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Gender", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "patient_sex", + "fieldtype": "Link", + "label": "Gender", + "options": "Gender", + "read_only": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "prescription", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Procedure Prescription", - "length": 0, - "no_copy": 0, - "options": "Procedure Prescription", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "prescription", + "fieldtype": "Link", + "hidden": 1, + "label": "Procedure Prescription", + "options": "Procedure Prescription" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "medical_department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Medical Department", - "length": 0, - "no_copy": 0, - "options": "Medical Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "medical_department", + "fieldtype": "Link", + "label": "Medical Department", + "options": "Medical Department" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "practitioner", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_7", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "procedure_template", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Procedure Template", - "length": 0, - "no_copy": 0, - "options": "Clinical Procedure Template", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "procedure_template", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Procedure Template", + "options": "Clinical Procedure Template", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "service_unit", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Service Unit", - "length": 0, - "no_copy": 0, - "options": "Healthcare Service Unit", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "service_unit", + "fieldtype": "Link", + "label": "Service Unit", + "options": "Healthcare Service Unit", + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "mandatory_depends_on": "eval: doc.consume_stock == 1", + "options": "Warehouse" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "start_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Start Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "start_time", - "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "start_time", + "fieldtype": "Time", + "label": "Start Time" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sample", - "length": 0, - "no_copy": 0, - "options": "Sample Collection", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sample", + "fieldtype": "Link", + "label": "Sample", + "options": "Sample Collection" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "invoiced", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Invoiced", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "invoiced", + "fieldtype": "Check", + "label": "Invoiced", + "no_copy": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "notes", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Notes", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "notes", + "fieldtype": "Small Text", + "label": "Notes", + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "consume_stock", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Consume Stock", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "consume_stock", + "fieldtype": "Check", + "label": "Consume Stock" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.consume_stock == 1", - "fieldname": "consumables", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Consumables", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "items", + "fieldtype": "Table", + "label": "Consumables", + "options": "Clinical Procedure Item" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Consumables", - "length": 0, - "no_copy": 0, - "options": "Clinical Procedure Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "invoice_separately_as_consumables", + "fieldtype": "Check", + "hidden": 1, + "label": "Invoice Consumables Separately", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "invoice_separately_as_consumables", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Consumables Invoice Separately", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "invoice_separately_as_consumables", + "fieldname": "consumable_total_amount", + "fieldtype": "Currency", + "label": "Consumable Total Amount", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "invoice_separately_as_consumables", - "fieldname": "consumable_total_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Consumable Total Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "invoice_separately_as_consumables", + "fieldname": "consumption_details", + "fieldtype": "Small Text", + "label": "Consumption Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "invoice_separately_as_consumables", - "fieldname": "consumption_details", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Consumption Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "invoice_separately_as_consumables", + "fieldname": "consumption_invoiced", + "fieldtype": "Check", + "hidden": 1, + "label": "Consumption Invoiced", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "depends_on": "invoice_separately_as_consumables", - "fieldname": "consumption_invoiced", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Consumption Invoiced", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Draft\nSubmitted\nCancelled\nIn Progress\nCompleted\nPending", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "status", - "fieldtype": "Select", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Draft\nIn Progress\nCompleted\nPending\nCancelled", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Clinical Procedure", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "consumables_section", + "fieldtype": "Section Break", + "label": "Consumables" + }, + { + "fieldname": "column_break_27", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_24", + "fieldtype": "Section Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:49.438611", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Clinical Procedure", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-03-02 11:44:27.970651", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Clinical Procedure", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Nursing User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Nursing User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} + ], + "restrict_to_domain": "Healthcare", + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index 7c6f4d5999e..db3afc8807e 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -6,88 +6,101 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import cint, flt, nowdate, nowtime, cstr +from frappe.utils import flt, nowdate, nowtime, cstr from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_account from erpnext.healthcare.doctype.lab_test.lab_test import create_sample_doc from erpnext.stock.stock_ledger import get_previous_sle from erpnext.stock.get_item_details import get_item_details +from frappe.model.mapper import get_mapped_doc class ClinicalProcedure(Document): def validate(self): - if self.consume_stock and not self.status == 'Draft': - if not self.warehouse: - frappe.throw(_("Set warehouse for Procedure {0} ").format(self.name)) + self.set_status() + if self.consume_stock: self.set_actual_qty() if self.items: self.invoice_separately_as_consumables = False for item in self.items: - if item.invoice_separately_as_consumables == 1: + if item.invoice_separately_as_consumables: self.invoice_separately_as_consumables = True def before_insert(self): if self.consume_stock: - set_stock_items(self, self.procedure_template, "Clinical Procedure Template") - self.set_actual_qty(); + self.set_actual_qty() def after_insert(self): if self.prescription: - frappe.db.set_value("Procedure Prescription", self.prescription, "procedure_created", 1) + frappe.db.set_value('Procedure Prescription', self.prescription, 'procedure_created', 1) if self.appointment: - frappe.db.set_value("Patient Appointment", self.appointment, "status", "Closed") - template = frappe.get_doc("Clinical Procedure Template", self.procedure_template) + frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed') + template = frappe.get_doc('Clinical Procedure Template', self.procedure_template) if template.sample: - patient = frappe.get_doc("Patient", self.patient) + patient = frappe.get_doc('Patient', self.patient) sample_collection = create_sample_doc(template, patient, None) - frappe.db.set_value("Clinical Procedure", self.name, "sample", sample_collection.name) + frappe.db.set_value('Clinical Procedure', self.name, 'sample', sample_collection.name) self.reload() - def complete(self): + def set_status(self): + if self.docstatus == 0: + self.status = 'Draft' + elif self.docstatus == 1: + if self.status not in ['In Progress', 'Completed']: + self.status = 'Pending' + elif self.docstatus == 2: + self.status = 'Cancelled' + + def complete_procedure(self): if self.consume_stock and self.items: - create_stock_entry(self) - frappe.db.set_value("Clinical Procedure", self.name, "status", 'Completed') + stock_entry = make_stock_entry(self) if self.items: consumable_total_amount = 0 consumption_details = False - for item in self.items: - if item.invoice_separately_as_consumables: - price_list, price_list_currency = frappe.db.get_values("Price List", {"selling": 1}, ['name', 'currency'])[0] - args = { - 'doctype': "Sales Invoice", - 'item_code': item.item_code, - 'company': self.company, - 'warehouse': self.warehouse, - 'customer': frappe.db.get_value("Patient", self.patient, "customer"), - 'selling_price_list': price_list, - 'price_list_currency': price_list_currency, - 'plc_conversion_rate': 1.0, - 'conversion_rate': 1.0 - } - item_details = get_item_details(args) - item_price = item_details.price_list_rate * item.transfer_qty - item_consumption_details = item_details.item_name+"\t"+str(item.qty)+" "+item.uom+"\t"+str(item_price) - consumable_total_amount += item_price - if not consumption_details: - consumption_details = "Clinical Procedure ("+self.name+"):\n\t"+item_consumption_details - else: - consumption_details += "\n\t"+item_consumption_details - if consumable_total_amount > 0: - frappe.db.set_value("Clinical Procedure", self.name, "consumable_total_amount", consumable_total_amount) - frappe.db.set_value("Clinical Procedure", self.name, "consumption_details", consumption_details) + customer = frappe.db.get_value('Patient', self.patient, 'customer') + if customer: + for item in self.items: + if item.invoice_separately_as_consumables: + price_list, price_list_currency = frappe.db.get_values('Price List', {'selling': 1}, ['name', 'currency'])[0] + args = { + 'doctype': 'Sales Invoice', + 'item_code': item.item_code, + 'company': self.company, + 'warehouse': self.warehouse, + 'customer': customer, + 'selling_price_list': price_list, + 'price_list_currency': price_list_currency, + 'plc_conversion_rate': 1.0, + 'conversion_rate': 1.0 + } + item_details = get_item_details(args) + item_price = item_details.price_list_rate * item.qty + item_consumption_details = item_details.item_name + ' ' + str(item.qty) + ' ' + item.uom + ' ' + str(item_price) + consumable_total_amount += item_price + if not consumption_details: + consumption_details = _('Clinical Procedure ({0}):').format(self.name) + consumption_details += '\n\t' + item_consumption_details + if consumable_total_amount > 0: + frappe.db.set_value('Clinical Procedure', self.name, 'consumable_total_amount', consumable_total_amount) + frappe.db.set_value('Clinical Procedure', self.name, 'consumption_details', consumption_details) + else: + frappe.throw(_('Please set Customer in Patient {0}').format(frappe.bold(self.patient)), title=_('Customer Not Found')) - def start(self): + frappe.db.set_value('Clinical Procedure', self.name, 'status', 'Completed') + if self.consume_stock and self.items: + return stock_entry + + def start_procedure(self): allow_start = self.set_actual_qty() if allow_start: - self.status = 'In Progress' + self.db_set('status', 'In Progress') insert_clinical_procedure_to_medical_record(self) - else: - self.status = 'Draft' - self.save() + return 'success' + return 'insufficient stock' def set_actual_qty(self): - allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")) + allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock') allow_start = True for d in self.get('items'): @@ -95,15 +108,16 @@ class ClinicalProcedure(Document): # validate qty if not allow_negative_stock and d.actual_qty < d.qty: allow_start = False + break return allow_start - def make_material_transfer(self): - stock_entry = frappe.new_doc("Stock Entry") + def make_material_receipt(self, submit=False): + stock_entry = frappe.new_doc('Stock Entry') - stock_entry.purpose = "Material Transfer" + stock_entry.stock_entry_type = 'Material Receipt' stock_entry.to_warehouse = self.warehouse - expense_account = get_account(None, "expense_account", "Healthcare Settings", self.company) + expense_account = get_account(None, 'expense_account', 'Healthcare Settings', self.company) for item in self.items: if item.qty > item.actual_qty: se_child = stock_entry.append('items') @@ -111,7 +125,7 @@ class ClinicalProcedure(Document): se_child.item_name = item.item_name se_child.uom = item.uom se_child.stock_uom = item.stock_uom - se_child.qty = flt(item.qty-item.actual_qty) + se_child.qty = flt(item.qty - item.actual_qty) se_child.t_warehouse = self.warehouse # in stock uom se_child.transfer_qty = flt(item.transfer_qty) @@ -119,104 +133,130 @@ class ClinicalProcedure(Document): cost_center = frappe.get_cached_value('Company', self.company, 'cost_center') se_child.cost_center = cost_center se_child.expense_account = expense_account + if submit: + stock_entry.submit() + return stock_entry return stock_entry.as_dict() -@frappe.whitelist() + def get_stock_qty(item_code, warehouse): return get_previous_sle({ - "item_code": item_code, - "warehouse": warehouse, - "posting_date": nowdate(), - "posting_time": nowtime() - }).get("qty_after_transaction") or 0 + 'item_code': item_code, + 'warehouse': warehouse, + 'posting_date': nowdate(), + 'posting_time': nowtime() + }).get('qty_after_transaction') or 0 + + +@frappe.whitelist() +def get_procedure_consumables(procedure_template): + return get_items('Clinical Procedure Item', procedure_template, 'Clinical Procedure Template') + @frappe.whitelist() def set_stock_items(doc, stock_detail_parent, parenttype): - item_dict = get_item_dict("Clinical Procedure Item", stock_detail_parent, parenttype) + items = get_items('Clinical Procedure Item', stock_detail_parent, parenttype) - for d in item_dict: + for item in items: se_child = doc.append('items') - se_child.item_code = d["item_code"] - se_child.item_name = d["item_name"] - se_child.uom = d["uom"] - se_child.stock_uom = d["stock_uom"] - se_child.qty = flt(d["qty"]) + se_child.item_code = item.item_code + se_child.item_name = item.item_name + se_child.uom = item.uom + se_child.stock_uom = item.stock_uom + se_child.qty = flt(item.qty) # in stock uom - se_child.transfer_qty = flt(d["transfer_qty"]) - se_child.conversion_factor = flt(d["conversion_factor"]) - if d["batch_no"]: - se_child.batch_no = d["batch_no"] - if parenttype == "Clinical Procedure Template": - se_child.invoice_separately_as_consumables = d["invoice_separately_as_consumables"] + se_child.transfer_qty = flt(item.transfer_qty) + se_child.conversion_factor = flt(item.conversion_factor) + if item.batch_no: + se_child.batch_no = item.batch_no + if parenttype == 'Clinical Procedure Template': + se_child.invoice_separately_as_consumables = item.invoice_separately_as_consumables + return doc -def get_item_dict(table, parent, parenttype): - query = """select * from `tab{table}` where parent = '{parent}' and parenttype = '{parenttype}' """ - return frappe.db.sql(query.format(table=table, parent=parent, parenttype=parenttype), as_dict=True) +def get_items(table, parent, parenttype): + items = frappe.db.get_all(table, filters={ + 'parent': parent, + 'parenttype': parenttype + }, fields=['*']) -def create_stock_entry(doc): - stock_entry = frappe.new_doc("Stock Entry") - stock_entry = set_stock_items(stock_entry, doc.name, "Clinical Procedure") - stock_entry.purpose = "Material Issue" + return items + + +@frappe.whitelist() +def make_stock_entry(doc): + stock_entry = frappe.new_doc('Stock Entry') + stock_entry = set_stock_items(stock_entry, doc.name, 'Clinical Procedure') + stock_entry.stock_entry_type = 'Material Issue' stock_entry.from_warehouse = doc.warehouse stock_entry.company = doc.company - expense_account = get_account(None, "expense_account", "Healthcare Settings", doc.company) + expense_account = get_account(None, 'expense_account', 'Healthcare Settings', doc.company) for item_line in stock_entry.items: cost_center = frappe.get_cached_value('Company', doc.company, 'cost_center') - #item_line.s_warehouse = warehouse #deaful source warehouse set, stock entry to copy to lines item_line.cost_center = cost_center - #if not expense_account: - # expense_account = frappe.db.get_value("Item", item_line.item_code, "expense_account") item_line.expense_account = expense_account - stock_entry.insert(ignore_permissions = True) + stock_entry.save(ignore_permissions=True) stock_entry.submit() + return stock_entry.name + @frappe.whitelist() -def create_procedure(appointment): - appointment = frappe.get_doc("Patient Appointment",appointment) - procedure = frappe.new_doc("Clinical Procedure") - procedure.appointment = appointment.name - procedure.patient = appointment.patient - procedure.patient_age = appointment.patient_age - procedure.patient_sex = appointment.patient_sex - procedure.procedure_template = appointment.procedure_template - procedure.prescription = appointment.procedure_prescription - procedure.practitioner = appointment.practitioner - procedure.invoiced = appointment.invoiced - procedure.medical_department = appointment.department - procedure.start_date = appointment.appointment_date - procedure.start_time = appointment.appointment_time - procedure.notes = appointment.notes - procedure.service_unit = appointment.service_unit - procedure.company = appointment.company - consume_stock = frappe.db.get_value("Clinical Procedure Template", appointment.procedure_template, "consume_stock") - if consume_stock == 1: - procedure.consume_stock = True - warehouse = False - if appointment.service_unit: - warehouse = frappe.db.get_value("Healthcare Service Unit", appointment.service_unit, "warehouse") - if not warehouse: - warehouse = frappe.db.get_value("Stock Settings", None, "default_warehouse") - if warehouse: - procedure.warehouse = warehouse - return procedure.as_dict() +def make_procedure(source_name, target_doc=None): + def set_missing_values(source, target): + consume_stock = frappe.db.get_value('Clinical Procedure Template', source.procedure_template, 'consume_stock') + if consume_stock: + target.consume_stock = 1 + warehouse = None + if source.service_unit: + warehouse = frappe.db.get_value('Healthcare Service Unit', source.service_unit, 'warehouse') + if not warehouse: + warehouse = frappe.db.get_value('Stock Settings', None, 'default_warehouse') + if warehouse: + target.warehouse = warehouse + + set_stock_items(target, source.procedure_template, 'Clinical Procedure Template') + + doc = get_mapped_doc('Patient Appointment', source_name, { + 'Patient Appointment': { + 'doctype': 'Clinical Procedure', + 'field_map': [ + ['appointment', 'name'], + ['patient', 'patient'], + ['patient_age', 'patient_age'], + ['patient_sex', 'patient_sex'], + ['procedure_template', 'procedure_template'], + ['prescription', 'procedure_prescription'], + ['practitioner', 'practitioner'], + ['medical_department', 'department'], + ['start_date', 'appointment_date'], + ['start_time', 'appointment_time'], + ['notes', 'notes'], + ['service_unit', 'service_unit'], + ['company', 'company'], + ['invoiced', 'invoiced'] + ] + } + }, target_doc, set_missing_values) + + return doc + def insert_clinical_procedure_to_medical_record(doc): subject = cstr(doc.procedure_template) if doc.practitioner: - subject += " "+doc.practitioner + subject += ' ' + doc.practitioner if subject and doc.notes: - subject += "
"+doc.notes + subject += '
' + doc.notes - medical_record = frappe.new_doc("Patient Medical Record") + medical_record = frappe.new_doc('Patient Medical Record') medical_record.patient = doc.patient medical_record.subject = subject - medical_record.status = "Open" + medical_record.status = 'Open' medical_record.communication_date = doc.start_date - medical_record.reference_doctype = "Clinical Procedure" + medical_record.reference_doctype = 'Clinical Procedure' medical_record.reference_name = doc.name medical_record.reference_owner = doc.owner medical_record.save(ignore_permissions=True) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure_list.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure_list.js new file mode 100644 index 00000000000..c8601f96772 --- /dev/null +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure_list.js @@ -0,0 +1,11 @@ +frappe.listview_settings['Clinical Procedure'] = { + get_indicator: function(doc) { + var colors = { + 'Completed': 'green', + 'In Progress': 'orange', + 'Pending': 'orange', + 'Cancelled': 'grey' + }; + return [__(doc.status), colors[doc.status], 'status,=,' + doc.status]; + } +}; diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py index 09059e1bb46..207351ff209 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py @@ -4,6 +4,60 @@ from __future__ import unicode_literals import unittest +import frappe +from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_clinical_procedure_template class TestClinicalProcedure(unittest.TestCase): - pass + def test_procedure_template_item(self): + patient, medical_department, practitioner = create_healthcare_docs() + procedure_template = create_clinical_procedure_template() + self.assertTrue(frappe.db.exists('Item', procedure_template.item)) + + procedure_template.disabled = 1 + procedure_template.save() + self.assertEquals(frappe.db.get_value('Item', procedure_template.item, 'disabled'), 1) + + def test_consumables(self): + patient, medical_department, practitioner = create_healthcare_docs() + procedure_template = create_clinical_procedure_template() + procedure_template.allow_stock_consumption = 1 + consumable = create_consumable() + procedure_template.append('items', { + 'item_code': consumable.item_code, + 'qty': 1, + 'uom': consumable.stock_uom, + 'stock_uom': consumable.stock_uom + }) + procedure_template.save() + procedure = create_procedure(procedure_template, patient, practitioner) + result = procedure.start_procedure() + if result == 'insufficient stock': + procedure.make_material_receipt(submit=True) + result = procedure.start_procedure() + self.assertEqual(procedure.status, 'In Progress') + result = procedure.complete_procedure() + # check consumption + self.assertTrue(frappe.db.exists('Stock Entry', result)) + + +def create_consumable(): + if frappe.db.exists('Item', 'Syringe'): + return frappe.get_doc('Item', 'Syringe') + consumable = frappe.new_doc('Item') + consumable.item_code = 'Syringe' + consumable.item_group = '_Test Item Group' + consumable.stock_uom = 'Nos' + consumable.valuation_rate = 5.00 + consumable.save() + return consumable + +def create_procedure(procedure_template, patient, practitioner): + procedure = frappe.new_doc('Clinical Procedure') + procedure.procedure_template = procedure_template.name + procedure.patient = patient + procedure.practitioner = practitioner + procedure.consume_stock = procedure_template.allow_stock_consumption + procedure.items = procedure_template.items + procedure.warehouse = frappe.db.get_single_value('Stock Settings', 'default_warehouse') + procedure.submit() + return procedure \ No newline at end of file diff --git a/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json b/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json index 75151b1d3f5..a7dde0bcd0a 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json +++ b/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json @@ -1,431 +1,123 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 1, - "creation": "2017-10-05 16:15:10.876952", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "beta": 1, + "creation": "2017-10-05 16:15:10.876952", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "item_name", + "qty", + "barcode", + "uom", + "invoice_separately_as_consumables", + "column_break_5", + "batch_no", + "conversion_factor", + "stock_uom", + "transfer_qty", + "actual_qty" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 3, - "fieldname": "item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "columns": 3, + "fieldname": "item_code", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_global_search": 1, + "in_list_view": 1, + "label": "Item", + "options": "Item", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "barcode", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Barcode", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "barcode", + "fieldtype": "Data", + "label": "Barcode" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Quantity", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "UOM", - "length": 0, - "no_copy": 0, - "options": "UOM", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "uom", + "fieldtype": "Link", + "in_list_view": 1, + "label": "UOM", + "options": "UOM", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "invoice_separately_as_consumables", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Invoice Separately as Consumables", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "invoice_separately_as_consumables", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Invoice Separately as Consumables" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_5", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "batch_no", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Batch", - "length": 0, - "no_copy": 0, - "options": "Batch", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "batch_no", + "fieldtype": "Link", + "label": "Batch", + "options": "Batch" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "conversion_factor", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Conversion Factor", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "conversion_factor", + "fieldtype": "Float", + "label": "Conversion Factor", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stock_uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Stock UOM", - "length": 0, - "no_copy": 0, - "options": "UOM", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM", + "read_only": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transfer_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transfer Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "transfer_qty", + "fieldtype": "Float", + "label": "Transfer Qty", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "actual_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Actual Qty (at source/target)", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "actual_qty", + "fieldtype": "Float", + "label": "Actual Qty (at source/target)", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "search_index": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:33:16.833884", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Clinical Procedure Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-03-01 15:34:54.226722", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Clinical Procedure Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "restrict_to_domain": "Healthcare", + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js index 583728dd185..57f4cdf3b2f 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js @@ -3,170 +3,141 @@ frappe.ui.form.on('Clinical Procedure Template', { template: function(frm) { - if(!frm.doc.item_code) - frm.set_value("item_code", frm.doc.template); - if(!frm.doc.description) - frm.set_value("description", frm.doc.template); + if (!frm.doc.item_code) + frm.set_value('item_code', frm.doc.template); + if (!frm.doc.description) + frm.set_value('description', frm.doc.template); mark_change_in_item(frm); }, + rate: function(frm) { mark_change_in_item(frm); }, + is_billable: function (frm) { mark_change_in_item(frm); }, + item_group: function(frm) { mark_change_in_item(frm); }, + description: function(frm) { mark_change_in_item(frm); }, + medical_department: function(frm) { mark_change_in_item(frm); }, + refresh: function(frm) { - frm.fields_dict["items"].grid.set_column_disp("barcode", false); - frm.fields_dict["items"].grid.set_column_disp("batch_no", false); - cur_frm.set_df_property("item_code", "read_only", frm.doc.__islocal ? 0 : 1); - if(!frm.doc.__islocal) { + frm.fields_dict['items'].grid.set_column_disp('barcode', false); + frm.fields_dict['items'].grid.set_column_disp('batch_no', false); + + if (!frm.doc.__islocal) { cur_frm.add_custom_button(__('Change Item Code'), function() { change_template_code(frm.doc); - } ); - if(frm.doc.disabled == 1){ - cur_frm.add_custom_button(__('Enable Template'), function() { - enable_template(frm.doc); - } ); - } - else{ - cur_frm.add_custom_button(__('Disable Template'), function() { - disable_template(frm.doc); - } ); - } + }); } } }); -var mark_change_in_item = function(frm) { - if(!frm.doc.__islocal){ +let mark_change_in_item = function(frm) { + if (!frm.doc.__islocal) { frm.doc.change_in_item = 1; } }; -var disable_template = function(doc){ - frappe.call({ - method: "erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.disable_enable_template", - args: {status: 1, name: doc.name, item_code: doc.item_code, is_billable: doc.is_billable}, - callback: function(){ - cur_frm.reload_doc(); - } - }); -}; - -var enable_template = function(doc){ - frappe.call({ - method: "erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.disable_enable_template", - args: {status: 0, name: doc.name, item_code: doc.item_code, is_billable: doc.is_billable}, - callback: function(){ - cur_frm.reload_doc(); - } - }); -}; - - -var change_template_code = function(doc){ - var d = new frappe.ui.Dialog({ - title:__("Change Template Code"), +let change_template_code = function(doc) { + let d = new frappe.ui.Dialog({ + title:__('Change Item Code'), fields:[ { - "fieldtype": "Data", - "label": "Template Code", - "fieldname": "Item Code", - reqd:1 - }, - { - "fieldtype": "Button", - "label": __("Change Code"), - click: function() { - var values = d.get_values(); - if(!values) - return; - change_item_code_from_template(values["Item Code"], doc); - d.hide(); - } + 'fieldtype': 'Data', + 'label': 'Item Code', + 'fieldname': 'item_code', + reqd: 1 } - ] + ], + primary_action: function() { + let values = d.get_values(); + + if (values) { + frappe.call({ + 'method': 'erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.change_item_code_from_template', + 'args': {item_code: values.item_code, doc: doc}, + callback: function () { + cur_frm.reload_doc(); + frappe.show_alert({ + message: 'Item Code renamed successfully', + indicator: 'green' + }); + } + }); + } + d.hide(); + }, + primary_action_label: __('Change Item Code') }); d.show(); - d.set_values({ - 'Item Code': doc.item_code - }); -}; -var change_item_code_from_template = function(item_code, doc){ - frappe.call({ - "method": "erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.change_item_code_from_template", - "args": {item_code: item_code, doc: doc}, - callback: function () { - cur_frm.reload_doc(); - frappe.show_alert({ - message: "Item Code renamed successfully", - indicator: "green" - }); - } + d.set_values({ + 'item_code': doc.item_code }); }; frappe.ui.form.on('Clinical Procedure Item', { - qty: function(frm, cdt, cdn){ - var d = locals[cdt][cdn]; - frappe.model.set_value(cdt, cdn, "transfer_qty", d.qty*d.conversion_factor); + qty: function(frm, cdt, cdn) { + let d = locals[cdt][cdn]; + frappe.model.set_value(cdt, cdn, 'transfer_qty', d.qty * d.conversion_factor); }, + uom: function(doc, cdt, cdn){ - var d = locals[cdt][cdn]; - if(d.uom && d.item_code){ + let d = locals[cdt][cdn]; + if (d.uom && d.item_code) { return frappe.call({ - method: "erpnext.stock.doctype.stock_entry.stock_entry.get_uom_details", + method: 'erpnext.stock.doctype.stock_entry.stock_entry.get_uom_details', args: { item_code: d.item_code, uom: d.uom, qty: d.qty }, callback: function(r) { - if(r.message) { + if (r.message) { frappe.model.set_value(cdt, cdn, r.message); } } }); } }, + item_code: function(frm, cdt, cdn) { - var d = locals[cdt][cdn]; - if(d.item_code) { + let d = locals[cdt][cdn]; + if (d.item_code) { let args = { 'item_code' : d.item_code, 'transfer_qty' : d.transfer_qty, - 'company' : frm.doc.company, - 'quantity' : d.qty + 'quantity' : d.qty }; return frappe.call({ - doc: frm.doc, - method: "get_item_details", - args: args, + method: 'erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.get_item_details', + args: {args: args}, callback: function(r) { - if(r.message) { - var d = locals[cdt][cdn]; - $.each(r.message, function(k, v){ + if (r.message) { + let d = locals[cdt][cdn]; + $.each(r.message, function(k, v) { d[k] = v; }); - refresh_field("items"); + refresh_field('items'); } } }); } } }); + // List Stock items -cur_frm.set_query("item_code", "items", function() { +cur_frm.set_query('item_code', 'items', function() { return { filters: { is_stock_item:1 diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json index 26564a34ce4..9cfd682f1d2 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json @@ -1,807 +1,236 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:template", - "beta": 1, - "creation": "2017-10-05 14:59:55.438359", - "custom": 0, - "description": "Procedure Template", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:template", + "beta": 1, + "creation": "2017-10-05 14:59:55.438359", + "description": "Procedure Template", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "template", + "item", + "item_code", + "item_group", + "description", + "column_break_5", + "disabled", + "is_billable", + "rate", + "medical_department", + "consumables", + "consume_stock", + "items", + "sample_collection", + "sample", + "sample_uom", + "sample_qty", + "column_break_21", + "sample_details", + "change_in_item" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "template", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Template Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "fieldname": "template", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Template Name", + "reqd": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_code", + "fieldname": "item_code", + "fieldtype": "Data", + "label": "Item Code", + "options": "Item", + "read_only_depends_on": "eval: !doc.__islocal ", + "reqd": 1 + }, + { + "fieldname": "item_group", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 1, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "ignore_user_permissions": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Item Group", + "options": "Item Group", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Item Group", - "length": 0, - "no_copy": 0, - "options": "Item Group", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "medical_department", + "fieldtype": "Link", + "label": "Medical Department", + "options": "Medical Department" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "medical_department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Medical Department", - "length": 0, - "no_copy": 0, - "options": "Medical Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_5", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "is_billable", + "fieldtype": "Check", + "label": "Is Billable" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_billable", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Billable", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "is_billable", + "fieldname": "rate", + "fieldtype": "Float", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Rate", + "mandatory_depends_on": "is_billable" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_billable", - "fieldname": "rate", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rate", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "description", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "label": "Description", + "no_copy": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "consume_stock", + "fieldtype": "Check", + "label": "Allow Stock Consumption", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "section_break_9", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "consumables", + "fieldtype": "Section Break", + "label": "Consumables" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "consume_stock", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Stock Consumption", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.consume_stock == 1", + "fieldname": "items", + "fieldtype": "Table", + "ignore_user_permissions": 1, + "label": "Items", + "options": "Clinical Procedure Item" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.consume_stock == 1", - "fieldname": "consumables", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Consumables", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "sample_collection", + "fieldtype": "Section Break", + "label": "Sample Collection" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Items", - "length": 0, - "no_copy": 0, - "options": "Clinical Procedure Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sample", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Sample", + "options": "Lab Test Sample" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "depends_on": "", - "fieldname": "sample_collection", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sample Collection", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "sample.sample_uom", + "fieldname": "sample_uom", + "fieldtype": "Data", + "label": "Sample UOM", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sample", - "length": 0, - "no_copy": 0, - "options": "Lab Test Sample", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sample_qty", + "fieldtype": "Float", + "label": "Quantity" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "sample.sample_uom", - "fieldname": "sample_uom", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "UOM", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_21", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sample_details", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "label": "Collection Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_21", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "change_in_item", + "fieldtype": "Check", + "hidden": 1, + "label": "Change In Item", + "no_copy": 1, + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample_details", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Collection Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "change_in_item", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Change In Item", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "disabled", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Disabled", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item", - "length": 0, - "no_copy": 1, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "item", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Item", + "no_copy": 1, + "options": "Item", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-02-12 11:37:18.713344", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Clinical Procedure Template", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-02-28 14:16:13.184981", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Clinical Procedure Template", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Nursing User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 - }, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Nursing User", + "share": 1 + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Physician", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Physician", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "template", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "template", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 + ], + "restrict_to_domain": "Healthcare", + "search_fields": "template", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "template", + "track_changes": 1, + "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py index 5a71ca5f154..3f295afc3ec 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py @@ -7,126 +7,114 @@ import frappe, json from frappe import _ from frappe.model.document import Document from frappe.model.rename_doc import rename_doc -from frappe.utils import nowdate class ClinicalProcedureTemplate(Document): - def on_update(self): - #Item and Price List update --> if (change_in_item) - if(self.change_in_item and self.is_billable == 1 and self.item): - updating_item(self) - if(self.rate != 0.0): - updating_rate(self) - elif(self.is_billable == 0 and self.item): - frappe.db.set_value("Item",self.item,"disabled",1) - - frappe.db.set_value(self.doctype,self.name,"change_in_item",0) - self.reload() + def validate(self): + self.enable_disable_item() def after_insert(self): create_item_from_template(self) - #Call before delete the template - def on_trash(self): - if(self.item): - try: - frappe.delete_doc("Item",self.item) - except Exception: - frappe.throw(_("""Not permitted. Please disable the Procedure Template""")) + def on_update(self): + if self.change_in_item: + self.update_item_and_item_price() - def get_item_details(self, args=None): - item = frappe.db.sql("""select stock_uom, item_name - from `tabItem` - where name = %s - and disabled=0 - and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)""", - (args.get('item_code'), nowdate()), as_dict = 1) - if not item: - frappe.throw(_("Item {0} is not active or end of life has been reached").format(args.get('item_code'))) + def enable_disable_item(self): + if self.is_billable: + if self.disabled: + frappe.db.set_value('Item', self.item, 'disabled', 1) + else: + frappe.db.set_value('Item', self.item, 'disabled', 0) - item = item[0] + def update_item_and_item_price(self): + if self.is_billable and self.item: + item_doc = frappe.get_doc('Item', {'item_code': self.item}) + item_doc.item_name = self.template + item_doc.item_group = self.item_group + item_doc.description = self.description + item_doc.disabled = 0 + item_doc.save(ignore_permissions=True) - ret = { - 'uom' : item.stock_uom, - 'stock_uom' : item.stock_uom, - 'item_name' : item.item_name, - 'quantity' : 0, - 'transfer_qty' : 0, - 'conversion_factor' : 1 - } - return ret + if self.rate: + item_price = frappe.get_doc('Item Price', {'item_code': self.item}) + item_price.item_name = self.template + item_price.price_list_rate = self.rate + item_price.save() -def updating_item(self): - frappe.db.sql("""update `tabItem` set item_name=%s, item_group=%s, disabled=0, - description=%s, modified=NOW() where item_code=%s""", - (self.template, self.item_group , self.description, self.item)) -def updating_rate(self): - frappe.db.sql("""update `tabItem Price` set item_name=%s, price_list_rate=%s, modified=NOW() where - item_code=%s""",(self.template, self.rate, self.item)) + elif not self.is_billable and self.item: + frappe.db.set_value('Item', self.item, 'disabled', 1) + + self.db_set('change_in_item', 0) + + +@frappe.whitelist() +def get_item_details(args=None): + if not isinstance(args, dict): + args = json.loads(args) + + item = frappe.db.get_all('Item', + filters={ + 'disabled': 0, + 'name': args.get('item_code') + }, + fields=['stock_uom', 'item_name'] + ) + + if not item: + frappe.throw(_('Item {0} is not active').format(args.get('item_code'))) + + item = item[0] + ret = { + 'uom': item.stock_uom, + 'stock_uom': item.stock_uom, + 'item_name': item.item_name, + 'qty': 1, + 'transfer_qty': 0, + 'conversion_factor': 1 + } + return ret def create_item_from_template(doc): - disabled = 1 - - if(doc.is_billable == 1): + disabled = doc.disabled + if doc.is_billable and not doc.disabled: disabled = 0 - #insert item - item = frappe.get_doc({ - "doctype": "Item", - "item_code": doc.template, - "item_name":doc.template, - "item_group": doc.item_group, - "description":doc.description, - "is_sales_item": 1, - "is_service_item": 1, - "is_purchase_item": 0, - "is_stock_item": 0, - "show_in_website": 0, - "is_pro_applicable": 0, - "disabled": disabled, - "stock_uom": "Unit" - }).insert(ignore_permissions=True) + item = frappe.get_doc({ + 'doctype': 'Item', + 'item_code': doc.template, + 'item_name':doc.template, + 'item_group': doc.item_group, + 'description':doc.description, + 'is_sales_item': 1, + 'is_service_item': 1, + 'is_purchase_item': 0, + 'is_stock_item': 0, + 'show_in_website': 0, + 'is_pro_applicable': 0, + 'disabled': disabled, + 'stock_uom': 'Unit' + }).insert(ignore_permissions=True, ignore_mandatory=True) - #insert item price - #get item price list to insert item price - if(doc.rate != 0.0): - price_list_name = frappe.db.get_value("Price List", {"selling": 1}) - if(doc.rate): - make_item_price(item.name, price_list_name, doc.rate) - else: - make_item_price(item.name, price_list_name, 0.0) - #Set item to the template - frappe.db.set_value("Clinical Procedure Template", doc.name, "item", item.name) + make_item_price(item.name, doc.rate) + doc.db_set('item', item.name) - doc.reload() #refresh the doc after insert. - -def make_item_price(item, price_list_name, item_price): +def make_item_price(item, item_price): + price_list_name = frappe.db.get_value('Price List', {'selling': 1}) frappe.get_doc({ - "doctype": "Item Price", - "price_list": price_list_name, - "item_code": item, - "price_list_rate": item_price - }).insert(ignore_permissions=True) + 'doctype': 'Item Price', + 'price_list': price_list_name, + 'item_code': item, + 'price_list_rate': item_price + }).insert(ignore_permissions=True, ignore_mandatory=True) @frappe.whitelist() def change_item_code_from_template(item_code, doc): - args = json.loads(doc) - doc = frappe._dict(args) + doc = frappe._dict(json.loads(doc)) - if(frappe.db.exists({ - "doctype": "Item", - "item_code": item_code})): - frappe.throw(_("Code {0} already exist").format(item_code)) + if frappe.db.exists('Item', {'item_code': item_code}): + frappe.throw(_('Item with Item Code {0} already exists').format(item_code)) else: - rename_doc("Item", doc.item_code, item_code, ignore_permissions=True) - frappe.db.set_value("Clinical Procedure Template", doc.name, "item_code", item_code) + rename_doc('Item', doc.item_code, item_code, ignore_permissions=True) + frappe.db.set_value('Clinical Procedure Template', doc.name, 'item_code', item_code) return -@frappe.whitelist() -def disable_enable_template(status, name, item_code): - frappe.db.set_value("Clinical Procedure Template", name, "disabled", status) - if (frappe.db.exists({ #set Item's disabled field to status - "doctype": "Item", - "item_code": item_code})): - frappe.db.set_value("Item", item_code, "disabled", status) - - return diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template_dashboard.py b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template_dashboard.py new file mode 100644 index 00000000000..9aab5216e1e --- /dev/null +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template_dashboard.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'procedure_template', + 'transactions': [ + { + 'label': _('Consultations'), + 'items': ['Clinical Procedure'] + } + ] + } diff --git a/erpnext/healthcare/doctype/codification_table/codification_table.json b/erpnext/healthcare/doctype/codification_table/codification_table.json index e65ae77fbf5..9a917b4ffff 100644 --- a/erpnext/healthcare/doctype/codification_table/codification_table.json +++ b/erpnext/healthcare/doctype/codification_table/codification_table.json @@ -1,140 +1,56 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 1, - "creation": "2017-06-22 13:09:23.159579", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_copy": 1, + "beta": 1, + "creation": "2017-06-22 13:09:23.159579", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "medical_code", + "code", + "description" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "medical_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Medical Code", - "length": 0, - "no_copy": 0, - "options": "Medical Code", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "medical_code", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Medical Code", + "options": "Medical Code", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "medical_code.code", - "fieldname": "code", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Code", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "code", + "fieldtype": "Data", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Code", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "medical_code.description", - "fieldname": "description", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "description", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Description", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-05-16 22:43:27.047479", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Codification Table", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-02-26 13:17:49.016293", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Codification Table", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "restrict_to_domain": "Healthcare", + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json b/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json index 5647d3c88e9..5e4d59cacf7 100644 --- a/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json +++ b/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json @@ -1,381 +1,116 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 1, - "creation": "2016-09-16 16:41:45.533374", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_copy": 1, + "beta": 1, + "creation": "2016-09-16 16:41:45.533374", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "drug_code", + "drug_name", + "dosage", + "period", + "dosage_form", + "column_break_7", + "comment", + "usage_interval", + "interval", + "interval_uom", + "update_schedule" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "drug_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Drug Code", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "drug_code", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Drug", + "options": "Item", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "drug_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Description/Strength", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fetch_from": "drug_code.item_name", + "fieldname": "drug_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Drug Name / Description" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "dosage", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Dosage", - "length": 0, - "no_copy": 0, - "options": "Prescription Dosage", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "dosage", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Dosage", + "options": "Prescription Dosage" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "period", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Period", - "length": 0, - "no_copy": 0, - "options": "Prescription Duration", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "period", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Period", + "options": "Prescription Duration" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "dosage_form", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Dosage Form", - "length": 0, - "no_copy": 0, - "options": "Dosage Form", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "dosage_form", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Dosage Form", + "options": "Dosage Form" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_7", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "comment", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Comment", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "comment", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Comment" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "use_interval", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Dosage by time interval", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "depends_on": "use_interval", + "fieldname": "interval", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Interval" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "use_interval", - "fieldname": "interval", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Interval", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "default": "1", + "fieldname": "update_schedule", + "fieldtype": "Check", + "hidden": 1, + "label": "Update Schedule", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "use_interval", - "fieldname": "in_every", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Interval UOM", - "length": 0, - "no_copy": 0, - "options": "\nHour\nDay", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "depends_on": "use_interval", + "fieldname": "interval_uom", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Interval UOM", + "options": "\nHour\nDay" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fieldname": "update_schedule", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Update Schedule", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "default": "0", + "fieldname": "usage_interval", + "fieldtype": "Check", + "label": "Dosage by Time Interval" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2017-10-04 17:09:54.998517", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Drug Prescription", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-02-26 17:02:42.741338", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Drug Prescription", + "owner": "Administrator", + "permissions": [], + "restrict_to_domain": "Healthcare", + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/drug_prescription/drug_prescription.py b/erpnext/healthcare/doctype/drug_prescription/drug_prescription.py index 0d99198fdef..68a2dc5d3c3 100755 --- a/erpnext/healthcare/doctype/drug_prescription/drug_prescription.py +++ b/erpnext/healthcare/doctype/drug_prescription/drug_prescription.py @@ -12,21 +12,21 @@ class DrugPrescription(Document): dosage = None period = None - if(self.dosage): - dosage = frappe.get_doc("Prescription Dosage",self.dosage) + if self.dosage: + dosage = frappe.get_doc('Prescription Dosage', self.dosage) for item in dosage.dosage_strength: quantity += item.strength - if(self.period and self.interval): - period = frappe.get_doc("Prescription Duration",self.period) - if(self.interval < period.get_days()): - quantity = quantity*(period.get_days()/self.interval) + if self.period and self.interval: + period = frappe.get_doc('Prescription Duration', self.period) + if self.interval < period.get_days(): + quantity = quantity * (period.get_days()/self.interval) - elif(self.interval and self.in_every and self.period): - period = frappe.get_doc("Prescription Duration",self.period) - interval_in = self.in_every - if(interval_in == 'Day' and (self.interval < period.get_days())): + elif self.interval and self.interval_uom and self.period: + period = frappe.get_doc('Prescription Duration', self.period) + interval_in = self.interval_uom + if interval_in == 'Day' and self.interval < period.get_days(): quantity = period.get_days()/self.interval - elif(interval_in == 'Hour' and (self.interval < period.get_hours())): + elif interval_in == 'Hour' and self.interval < period.get_hours(): quantity = period.get_hours()/self.interval if quantity > 0: return quantity diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.json b/erpnext/healthcare/doctype/fee_validity/fee_validity.json index 802f04a359c..b001bf024ce 100644 --- a/erpnext/healthcare/doctype/fee_validity/fee_validity.json +++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.json @@ -1,259 +1,134 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "beta": 1, - "creation": "2017-01-05 10:56:29.564806", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_copy": 1, + "allow_import": 1, + "beta": 1, + "creation": "2017-01-05 10:56:29.564806", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "practitioner", + "patient", + "column_break_3", + "status", + "section_break_5", + "section_break_3", + "max_visits", + "visited", + "ref_appointments", + "column_break_6", + "start_date", + "valid_till" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "practitioner", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "practitioner", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "read_only": 1, + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Patient", - "length": 0, - "no_copy": 0, - "options": "Patient", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "patient", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Patient", + "options": "Patient", + "read_only": 1, + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "max_visit", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Max number of visit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "visited", + "fieldtype": "Int", + "label": "Visited yet", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "visited", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Visited yet", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "valid_till", + "fieldtype": "Date", + "label": "Valid till", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "valid_till", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Valid till", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_3", + "fieldtype": "Section Break", + "label": "Validity", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ref_invoice", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Inv", - "length": 0, - "no_copy": 0, - "options": "Sales Invoice", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "max_visits", + "fieldtype": "Int", + "label": "Max number of visit", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Completed\nPending", + "read_only": 1 + }, + { + "fetch_from": "ref_appointment.appointment_date", + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date", + "read_only": 1 + }, + { + "fieldname": "ref_appointments", + "fieldtype": "Table MultiSelect", + "label": "Reference Appointments", + "options": "Fee Validity Reference", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "section_break_5", + "fieldtype": "Section Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-16 12:43:45.635230", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Fee Validity", - "name_case": "", - "owner": "Administrator", + ], + "in_create": 1, + "links": [], + "modified": "2020-03-17 20:25:06.487418", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Fee Validity", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "practitioner, patient", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "practitioner", - "track_changes": 0, - "track_seen": 0 + ], + "quick_entry": 1, + "restrict_to_domain": "Healthcare", + "search_fields": "practitioner, patient", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "practitioner" } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.py b/erpnext/healthcare/doctype/fee_validity/fee_validity.py index 90285459ce5..058bc971929 100644 --- a/erpnext/healthcare/doctype/fee_validity/fee_validity.py +++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.py @@ -9,28 +9,55 @@ from frappe.utils import getdate import datetime class FeeValidity(Document): - pass + def validate(self): + self.update_status() + self.set_start_date() -def update_fee_validity(fee_validity, date, ref_invoice=None): - max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") - valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") - if not valid_days: - valid_days = 1 - if not max_visit: - max_visit = 1 - date = getdate(date) - valid_till = date + datetime.timedelta(days=int(valid_days)) - fee_validity.max_visit = max_visit + def update_status(self): + if self.visited >= self.max_visits: + self.status = 'Completed' + else: + self.status = 'Pending' + + def set_start_date(self): + self.start_date = getdate() + for appointment in self.ref_appointments: + appointment_date = frappe.db.get_value('Patient Appointment', appointment.appointment, 'appointment_date') + if getdate(appointment_date) < self.start_date: + self.start_date = getdate(appointment_date) + + +def create_fee_validity(appointment): + if not check_is_new_patient(appointment): + return + + fee_validity = frappe.new_doc('Fee Validity') + fee_validity.practitioner = appointment.practitioner + fee_validity.patient = appointment.patient + fee_validity.max_visits = frappe.db.get_single_value('Healthcare Settings', 'max_visits') or 1 + valid_days = frappe.db.get_single_value('Healthcare Settings', 'valid_days') or 1 fee_validity.visited = 1 - fee_validity.valid_till = valid_till - fee_validity.ref_invoice = ref_invoice + fee_validity.valid_till = getdate(appointment.appointment_date) + datetime.timedelta(days=int(valid_days)) + fee_validity.append('ref_appointments', { + 'appointment': appointment.name + }) fee_validity.save(ignore_permissions=True) return fee_validity +def check_is_new_patient(appointment): + validity_exists = frappe.db.exists('Fee Validity', { + 'practitioner': appointment.practitioner, + 'patient': appointment.patient + }) + if validity_exists: + return False -def create_fee_validity(practitioner, patient, date, ref_invoice=None): - fee_validity = frappe.new_doc("Fee Validity") - fee_validity.practitioner = practitioner - fee_validity.patient = patient - fee_validity = update_fee_validity(fee_validity, date, ref_invoice) - return fee_validity + appointment_exists = frappe.db.get_all('Patient Appointment', { + 'name': ('!=', appointment.name), + 'status': ('!=', 'Cancelled'), + 'patient': appointment.patient, + 'practitioner': appointment.practitioner + }) + if len(appointment_exists) and appointment_exists[0]: + return False + return True \ No newline at end of file diff --git a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py index 26b14504630..cdf692e68bd 100644 --- a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py +++ b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py @@ -5,100 +5,44 @@ from __future__ import unicode_literals import frappe import unittest -from frappe.utils.make_random import get_random -from frappe.utils import nowdate, add_days, getdate +from frappe.utils import nowdate, add_days +from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_appointment, create_healthcare_service_items test_dependencies = ["Company"] class TestFeeValidity(unittest.TestCase): - def test_fee_validity(self): + def setUp(self): frappe.db.sql("""delete from `tabPatient Appointment`""") frappe.db.sql("""delete from `tabFee Validity`""") - patient = get_random("Patient") - practitioner = get_random("Healthcare Practitioner") - department = get_random("Medical Department") + frappe.db.sql("""delete from `tabPatient`""") - if not patient: - patient = frappe.new_doc("Patient") - patient.patient_name = "_Test Patient" - patient.sex = "Male" - patient.save(ignore_permissions=True) - patient = patient.name + def test_fee_validity(self): + item = create_healthcare_service_items() + healthcare_settings = frappe.get_single("Healthcare Settings") + healthcare_settings.enable_free_follow_ups = 1 + healthcare_settings.max_visits = 2 + healthcare_settings.valid_days = 7 + healthcare_settings.automate_appointment_invoicing = 1 + healthcare_settings.op_consulting_charge_item = item + healthcare_settings.save(ignore_permissions=True) + patient, medical_department, practitioner = create_healthcare_docs() - if not department: - medical_department = frappe.new_doc("Medical Department") - medical_department.department = "_Test Medical Department" - medical_department.save(ignore_permissions=True) - department = medical_department.name - - if not practitioner: - practitioner = frappe.new_doc("Healthcare Practitioner") - practitioner.first_name = "_Test Healthcare Practitioner" - practitioner.department = department - practitioner.save(ignore_permissions=True) - practitioner = practitioner.name - - - - frappe.db.set_value("Healthcare Settings", None, "max_visit", 2) - frappe.db.set_value("Healthcare Settings", None, "valid_days", 7) - - appointment = create_appointment(patient, practitioner, nowdate(), department) + # appointment should not be invoiced. Check Fee Validity created for new patient + appointment = create_appointment(patient, practitioner, nowdate()) invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced") self.assertEqual(invoiced, 0) - invoice_appointment(appointment) - - appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4), department) - invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced") - self.assertTrue(invoiced) - - appointment = create_appointment(patient, practitioner, add_days(nowdate(), 5), department) + # appointment should not be invoiced as it is within fee validity + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4)) invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced") self.assertEqual(invoiced, 0) - appointment = create_appointment(patient, practitioner, add_days(nowdate(), 10), department) + # appointment should be invoiced as it is within fee validity but the max_visits are exceeded + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 5), invoice=1) invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced") - self.assertEqual(invoiced, 0) + self.assertEqual(invoiced, 1) -def create_appointment(patient, practitioner, appointment_date, department): - appointment = frappe.new_doc("Patient Appointment") - appointment.patient = patient - appointment.practitioner = practitioner - appointment.department = department - appointment.appointment_date = appointment_date - appointment.company = "_Test Company" - appointment.duration = 15 - appointment.save(ignore_permissions=True) - return appointment - -def invoice_appointment(appointment_doc): - if not appointment_doc.name: - return False - sales_invoice = frappe.new_doc("Sales Invoice") - sales_invoice.customer = frappe.get_value("Patient", appointment_doc.patient, "customer") - sales_invoice.due_date = getdate() - sales_invoice.is_pos = 0 - sales_invoice.company = appointment_doc.company - sales_invoice.debit_to = "_Test Receivable - _TC" - - create_invoice_items(appointment_doc, sales_invoice) - - sales_invoice.save(ignore_permissions=True) - sales_invoice.submit() - -def create_invoice_items(appointment, invoice): - item_line = invoice.append("items") - item_line.item_name = "Consulting Charges" - item_line.description = "Consulting Charges: " + appointment.practitioner - item_line.uom = "Nos" - item_line.conversion_factor = 1 - item_line.income_account = "_Test Account Cost for Goods Sold - _TC" - item_line.cost_center = "_Test Cost Center - _TC" - item_line.rate = 250 - item_line.amount = 250 - item_line.qty = 1 - item_line.reference_dt = "Patient Appointment" - item_line.reference_dn = appointment.name - - return invoice + # appointment should be invoiced as it is not within fee validity and the max_visits are exceeded + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 10), invoice=1) + invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced") + self.assertEqual(invoiced, 1) \ No newline at end of file diff --git a/erpnext/healthcare/page/appointment_analytic/__init__.py b/erpnext/healthcare/doctype/fee_validity_reference/__init__.py similarity index 100% rename from erpnext/healthcare/page/appointment_analytic/__init__.py rename to erpnext/healthcare/doctype/fee_validity_reference/__init__.py diff --git a/erpnext/healthcare/doctype/fee_validity_reference/fee_validity_reference.json b/erpnext/healthcare/doctype/fee_validity_reference/fee_validity_reference.json new file mode 100644 index 00000000000..40f128e973d --- /dev/null +++ b/erpnext/healthcare/doctype/fee_validity_reference/fee_validity_reference.json @@ -0,0 +1,32 @@ +{ + "actions": [], + "creation": "2020-03-13 16:08:42.859996", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "appointment" + ], + "fields": [ + { + "fieldname": "appointment", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Patient Appointment", + "options": "Patient Appointment", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-03-15 00:27:02.076470", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Fee Validity Reference", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/fee_validity_reference/fee_validity_reference.py b/erpnext/healthcare/doctype/fee_validity_reference/fee_validity_reference.py new file mode 100644 index 00000000000..c8192808320 --- /dev/null +++ b/erpnext/healthcare/doctype/fee_validity_reference/fee_validity_reference.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class FeeValidityReference(Document): + pass diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js index efca484d6ac..4ab3b6e9c1b 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js @@ -4,7 +4,7 @@ frappe.ui.form.on('Healthcare Practitioner', { setup: function(frm) { frm.set_query('account', 'accounts', function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; + let d = locals[cdt][cdn]; return { filters: { 'root_type': 'Income', @@ -16,23 +16,28 @@ frappe.ui.form.on('Healthcare Practitioner', { }, refresh: function(frm) { frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Healthcare Practitioner'}; - if(!frm.is_new()) { + + if (!frm.is_new()) { frappe.contacts.render_address_and_contact(frm); + } else { + frappe.contacts.clear_address_and_contact(frm); } - frm.set_query("service_unit", "practitioner_schedules", function(){ + + frm.set_query('service_unit', 'practitioner_schedules', function(){ return { filters: { - "is_group": false, - "allow_appointments": true + 'is_group': false, + 'allow_appointments': true } }; }); + set_query_service_item(frm, 'inpatient_visit_charge_item'); set_query_service_item(frm, 'op_consulting_charge_item'); } }); -var set_query_service_item = function(frm, service_item_field) { +let set_query_service_item = function(frm, service_item_field) { frm.set_query(service_item_field, function() { return { filters: { @@ -43,62 +48,62 @@ var set_query_service_item = function(frm, service_item_field) { }); }; -frappe.ui.form.on("Healthcare Practitioner", "user_id",function(frm) { - if(frm.doc.user_id){ +frappe.ui.form.on('Healthcare Practitioner', 'user_id',function(frm) { + if (frm.doc.user_id) { frappe.call({ - "method": "frappe.client.get", + 'method': 'frappe.client.get', args: { - doctype: "User", + doctype: 'User', name: frm.doc.user_id }, callback: function (data) { frappe.model.get_value('Employee', {'user_id': frm.doc.user_id}, 'name', function(data) { - if(data){ - if(!frm.doc.employee || frm.doc.employee != data.name) - frappe.model.set_value(frm.doctype,frm.docname, "employee", data.name); - }else{ - frappe.model.set_value(frm.doctype,frm.docname, "employee", ""); + if (data) { + if (!frm.doc.employee || frm.doc.employee != data.name) + frappe.model.set_value(frm.doctype, frm.docname, 'employee', data.name); + } else { + frappe.model.set_value(frm.doctype, frm.docname, 'employee', ''); } } ); - if(!frm.doc.first_name || frm.doc.first_name != data.message.first_name) - frappe.model.set_value(frm.doctype,frm.docname, "first_name", data.message.first_name); - if(!frm.doc.middle_name || frm.doc.middle_name != data.message.middle_name) - frappe.model.set_value(frm.doctype,frm.docname, "middle_name", data.message.middle_name); - if(!frm.doc.last_name || frm.doc.last_name != data.message.last_name) - frappe.model.set_value(frm.doctype,frm.docname, "last_name", data.message.last_name); - if(!frm.doc.mobile_phone || frm.doc.mobile_phone != data.message.mobile_no) - frappe.model.set_value(frm.doctype,frm.docname, "mobile_phone", data.message.mobile_no); + if (!frm.doc.first_name || frm.doc.first_name != data.message.first_name) + frappe.model.set_value(frm.doctype,frm.docname, 'first_name', data.message.first_name); + if (!frm.doc.middle_name || frm.doc.middle_name != data.message.middle_name) + frappe.model.set_value(frm.doctype,frm.docname, 'middle_name', data.message.middle_name); + if (!frm.doc.last_name || frm.doc.last_name != data.message.last_name) + frappe.model.set_value(frm.doctype,frm.docname, 'last_name', data.message.last_name); + if (!frm.doc.mobile_phone || frm.doc.mobile_phone != data.message.mobile_no) + frappe.model.set_value(frm.doctype,frm.docname, 'mobile_phone', data.message.mobile_no); } }); } }); -frappe.ui.form.on("Healthcare Practitioner", "employee", function(frm) { - if(frm.doc.employee){ +frappe.ui.form.on('Healthcare Practitioner', 'employee', function(frm) { + if (frm.doc.employee){ frappe.call({ - "method": "frappe.client.get", + 'method': 'frappe.client.get', args: { - doctype: "Employee", + doctype: 'Employee', name: frm.doc.employee }, callback: function (data) { - if(!frm.doc.user_id || frm.doc.user_id != data.message.user_id) - frm.set_value("user_id", data.message.user_id); - if(!frm.doc.designation || frm.doc.designation != data.message.designation) - frappe.model.set_value(frm.doctype,frm.docname, "designation", data.message.designation); - if(!frm.doc.first_name || !frm.doc.user_id){ - frappe.model.set_value(frm.doctype,frm.docname, "first_name", data.message.employee_name); - frappe.model.set_value(frm.doctype,frm.docname, "middle_name", ""); - frappe.model.set_value(frm.doctype,frm.docname, "last_name", ""); + if (!frm.doc.user_id || frm.doc.user_id != data.message.user_id) + frm.set_value('user_id', data.message.user_id); + if (!frm.doc.designation || frm.doc.designation != data.message.designation) + frappe.model.set_value(frm.doctype,frm.docname, 'designation', data.message.designation); + if (!frm.doc.first_name || !frm.doc.user_id){ + frappe.model.set_value(frm.doctype,frm.docname, 'first_name', data.message.first_name); + frappe.model.set_value(frm.doctype,frm.docname, 'middle_name', ''); + frappe.model.set_value(frm.doctype,frm.docname, 'last_name', data.message.last_name); } - if(!frm.doc.mobile_phone || !frm.doc.user_id) - frappe.model.set_value(frm.doctype,frm.docname, "mobile_phone", data.message.cell_number); - if(!frm.doc.address || frm.doc.address != data.message.current_address) - frappe.model.set_value(frm.doctype,frm.docname, "address", data.message.current_address); + if (!frm.doc.mobile_phone || !frm.doc.user_id) + frappe.model.set_value(frm.doctype,frm.docname, 'mobile_phone', data.message.cell_number); + if (!frm.doc.address || frm.doc.address != data.message.current_address) + frappe.model.set_value(frm.doctype,frm.docname, 'address', data.message.current_address); } }); } diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json index 4a848a081da..fd5b6e12f66 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json @@ -1,1041 +1,327 @@ { + "actions": [], "allow_copy": 1, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, - "autoname": "", + "autoname": "naming_series:", "beta": 1, "creation": "2016-02-23 11:20:53.565119", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, + "engine": "InnoDB", + "field_order": [ + "basic_details_section", + "naming_series", + "first_name", + "middle_name", + "last_name", + "practitioner_name", + "gender", + "image", + "column_break_7", + "status", + "mobile_phone", + "residence_phone", + "office_phone", + "employee_and_user_details_section", + "employee", + "department", + "designation", + "column_break_17", + "user_id", + "hospital", + "appointments", + "practitioner_schedules", + "charges", + "op_consulting_charge_item", + "op_consulting_charge", + "column_break_18", + "inpatient_visit_charge_item", + "inpatient_visit_charge", + "account_details", + "default_currency", + "accounts", + "address_and_contacts_section", + "address_html", + "column_break_19", + "contact_html" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "first_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, "label": "First Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "middle_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Middle Name (Optional)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Middle Name (Optional)" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "last_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Last Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Last Name" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "image", "fieldtype": "Attach Image", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Image", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "employee", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Employee" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "user_id", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "User", - "length": 0, - "no_copy": 0, "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "fetch_from": "employee", "fieldname": "designation", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Designation", - "length": 0, - "no_copy": 0, "options": "Designation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "department", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Medical Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Medical Department", + "options": "Medical Department" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_7", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "hospital", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Hospital", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Hospital" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "mobile_phone", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Mobile", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Mobile" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "residence_phone", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Phone (R)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Phone (R)" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "office_phone", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Phone (Office)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Phone (Office)" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fieldname": "active", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Active", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "collapsible": 1, "fieldname": "appointments", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Appointments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Appointments" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "practitioner_schedules", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Practitioner Schedules", - "length": 0, - "no_copy": 0, - "options": "Practitioner Service Unit Schedule", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Practitioner Service Unit Schedule" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "collapsible": 1, "fieldname": "charges", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Charges", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Charges" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "op_consulting_charge_item", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Out Patient Consulting Charge Item", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "op_consulting_charge", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "OP Consulting Charge", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Out Patient Consulting Charge", + "options": "Currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_18", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "inpatient_visit_charge_item", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Inpatient Visit Charge Item", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "inpatient_visit_charge", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Inpatient Visit Charge", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Inpatient Visit Charge" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contacts_and_address", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contacts and Address", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "depends_on": "eval: !doc.__islocal", "fieldname": "address_html", "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Address HTML", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_19", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "depends_on": "eval: !doc.__islocal", "fieldname": "contact_html", "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Contact HTML", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "collapsible": 1, "fieldname": "account_details", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Account Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Account Details" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "accounts", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Income Account", - "length": 0, - "no_copy": 0, - "options": "Party Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Party Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "default_currency", "fieldtype": "Link", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Default Currency", - "length": 0, "no_copy": 1, "options": "Currency", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, + "report_hide": 1 + }, + { + "bold": 1, + "fieldname": "practitioner_name", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Full Name", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "HLC-PRAC-.YYYY.-", "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "set_only_once": 1 + }, + { + "fieldname": "gender", + "fieldtype": "Link", + "label": "Gender", + "options": "Gender" + }, + { + "fieldname": "employee_and_user_details_section", + "fieldtype": "Section Break", + "label": "Employee and User Details" + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "default": "Active", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "\nActive\nDisabled", + "reqd": 1 + }, + { + "fieldname": "basic_details_section", + "fieldtype": "Section Break", + "label": "Basic Details" + }, + { + "collapsible": 1, + "depends_on": "eval: !doc.__islocal", + "fieldname": "address_and_contacts_section", + "fieldtype": "Section Break", + "label": "Address and Contacts" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, "image_field": "image", - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-23 08:54:51.442105", + "links": [], + "modified": "2020-04-06 13:44:24.759623", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Practitioner", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Laboratory User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Physician", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Nursing User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Healthcare", - "search_fields": "first_name,mobile_phone,office_phone", + "search_fields": "practitioner_name, mobile_phone, office_phone", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "title_field": "first_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "title_field": "practitioner_name", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py index ad32e946312..0c13b6af9db 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py @@ -5,10 +5,10 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -from frappe import throw, _ -from frappe.utils import cstr +from frappe import _ from erpnext.accounts.party import validate_party_accounts from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address +from frappe.model.naming import append_number_if_name_exists from frappe.desk.reportview import build_match_conditions, get_filters_cond class HealthcarePractitioner(Document): @@ -16,63 +16,66 @@ class HealthcarePractitioner(Document): load_address_and_contact(self) def autoname(self): - # practitioner first_name and last_name - self.name = " ".join(filter(None, - [cstr(self.get(f)).strip() for f in ["first_name","middle_name","last_name"]])) + # concat first and last name + self.name = self.practitioner_name + + if frappe.db.exists('Healthcare Practitioner', self.name): + self.name = append_number_if_name_exists('Contact', self.name) def validate(self): + self.set_full_name() validate_party_accounts(self) if self.inpatient_visit_charge_item: - validate_service_item(self.inpatient_visit_charge_item, "Configure a service Item for Inpatient Visit Charge Item") + validate_service_item(self.inpatient_visit_charge_item, 'Configure a service Item for Inpatient Consulting Charge Item') if self.op_consulting_charge_item: - validate_service_item(self.op_consulting_charge_item, "Configure a service Item for Out Patient Consulting Charge Item") + validate_service_item(self.op_consulting_charge_item, 'Configure a service Item for Out Patient Consulting Charge Item') if self.user_id: - self.validate_for_enabled_user_id() - self.validate_duplicate_user_id() - existing_user_id = frappe.db.get_value("Healthcare Practitioner", self.name, "user_id") - if self.user_id != existing_user_id: - frappe.permissions.remove_user_permission( - "Healthcare Practitioner", self.name, existing_user_id) - + self.validate_user_id() else: - existing_user_id = frappe.db.get_value("Healthcare Practitioner", self.name, "user_id") + existing_user_id = frappe.db.get_value('Healthcare Practitioner', self.name, 'user_id') if existing_user_id: frappe.permissions.remove_user_permission( - "Healthcare Practitioner", self.name, existing_user_id) + 'Healthcare Practitioner', self.name, existing_user_id) def on_update(self): if self.user_id: - frappe.permissions.add_user_permission("Healthcare Practitioner", self.name, self.user_id) + frappe.permissions.add_user_permission('Healthcare Practitioner', self.name, self.user_id) + def set_full_name(self): + if self.last_name: + self.practitioner_name = ' '.join(filter(None, [self.first_name, self.last_name])) + else: + self.practitioner_name = self.first_name - def validate_for_enabled_user_id(self): - enabled = frappe.db.get_value("User", self.user_id, "enabled") - if enabled is None: - frappe.throw(_("User {0} does not exist").format(self.user_id)) - if enabled == 0: - frappe.throw(_("User {0} is disabled").format(self.user_id)) + def validate_user_id(self): + if not frappe.db.exists('User', self.user_id): + frappe.throw(_('User {0} does not exist').format(self.user_id)) + elif not frappe.db.exists('User', self.user_id, 'enabled'): + frappe.throw(_('User {0} is disabled').format(self.user_id)) - def validate_duplicate_user_id(self): - practitioner = frappe.db.sql_list("""select name from `tabHealthcare Practitioner` where - user_id=%s and name!=%s""", (self.user_id, self.name)) + # check duplicate + practitioner = frappe.db.exists('Healthcare Practitioner', { + 'user_id': self.user_id, + 'name': ('!=', self.name) + }) if practitioner: - throw(_("User {0} is already assigned to Healthcare Practitioner {1}").format( - self.user_id, practitioner[0]), frappe.DuplicateEntryError) + frappe.throw(_('User {0} is already assigned to Healthcare Practitioner {1}').format( + self.user_id, practitioner)) def on_trash(self): delete_contact_and_address('Healthcare Practitioner', self.name) def validate_service_item(item, msg): - if frappe.db.get_value("Item", item, "is_stock_item") == 1: + if frappe.db.get_value('Item', item, 'is_stock_item'): frappe.throw(_(msg)) def get_practitioner_list(doctype, txt, searchfield, start, page_len, filters=None): - fields = ["name", "first_name", "mobile_phone"] + fields = ['name', 'practitioner_name', 'mobile_phone'] filters = { - 'name': ("like", "%%%s%%" % txt) + 'name': ('like', '%%%s%%' % txt) } - return frappe.get_all("Healthcare Practitioner", fields = fields, - filters = filters, start=start, page_length=page_len, order_by="name, first_name", as_list=1) + return frappe.get_all('Healthcare Practitioner', fields = fields, + filters = filters, start=start, page_length=page_len, order_by='name, practitioner_name', as_list=1) diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner_dashboard.py b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner_dashboard.py index 70c0b3c098d..bcee44430ac 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner_dashboard.py +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner_dashboard.py @@ -9,7 +9,11 @@ def get_data(): 'transactions': [ { 'label': _('Appointments and Patient Encounters'), - 'items': ['Patient Appointment', 'Patient Encounter'] + 'items': ['Patient Appointment', 'Patient Encounter', 'Fee Validity'] + }, + { + 'label': _('Consultation'), + 'items': ['Clinical Procedure', 'Lab Test'] } ] } diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.js b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.js index 8480a526b44..2cdd5506565 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.js +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.js @@ -3,7 +3,7 @@ frappe.ui.form.on('Healthcare Service Unit', { onload: function(frm) { - frm.list_route = "Tree/Healthcare Service Unit"; + frm.list_route = 'Tree/Healthcare Service Unit'; // get query select healthcare service unit frm.fields_dict['parent_healthcare_service_unit'].get_query = function(doc) { @@ -16,32 +16,32 @@ frappe.ui.form.on('Healthcare Service Unit', { }; }, refresh: function(frm) { - frm.trigger("set_root_readonly"); - frm.set_df_property("service_unit_type", "reqd", 1); - frm.add_custom_button(__("Healthcare Service Unit Tree"), function() { - frappe.set_route("Tree", "Healthcare Service Unit"); + frm.trigger('set_root_readonly'); + frm.set_df_property('service_unit_type', 'reqd', 1); + frm.add_custom_button(__('Healthcare Service Unit Tree'), function() { + frappe.set_route('Tree', 'Healthcare Service Unit'); }); }, set_root_readonly: function(frm) { // read-only for root healthcare service unit - frm.set_intro(""); - if(!frm.doc.parent_healthcare_service_unit) { + frm.set_intro(''); + if (!frm.doc.parent_healthcare_service_unit) { frm.set_read_only(); - frm.set_intro(__("This is a root healthcare service unit and cannot be edited."), true); + frm.set_intro(__('This is a root healthcare service unit and cannot be edited.'), true); } }, allow_appointments: function(frm) { - if(!frm.doc.allow_appointments){ - frm.set_value("overlap_appointments", false); + if (!frm.doc.allow_appointments) { + frm.set_value('overlap_appointments', false); } }, is_group: function(frm) { - if(frm.doc.is_group == 1){ - frm.set_value("allow_appointments", false); - frm.set_df_property("service_unit_type", "reqd", 0); + if (frm.doc.is_group == 1) { + frm.set_value('allow_appointments', false); + frm.set_df_property('service_unit_type', 'reqd', 0); } - else{ - frm.set_df_property("service_unit_type", "reqd", 1); + else { + frm.set_df_property('service_unit_type', 'reqd', 1); } } }); diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json index 8601f69c075..ea4ae846f7d 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json @@ -9,6 +9,7 @@ "doctype": "DocType", "document_type": "Setup", "editable_grid": 1, + "engine": "InnoDB", "field_order": [ "healthcare_service_unit_name", "parent_healthcare_service_unit", @@ -18,6 +19,7 @@ "overlap_appointments", "inpatient_occupancy", "occupancy_status", + "column_break_9", "warehouse", "company", "lft", @@ -99,9 +101,13 @@ "fieldtype": "Select", "label": "Occupancy Status", "no_copy": 1, - "options": "Vacant\nOccupied", + "options": "\nVacant\nOccupied", "read_only": 1 }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { "bold": 1, "depends_on": "eval:doc.is_group != 1", @@ -153,13 +159,11 @@ "report_hide": 1 } ], - "is_tree": 1, "links": [], - "modified": "2020-03-18 18:02:23.713439", + "modified": "2020-03-26 16:13:08.675952", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit", - "nsm_parent_field": "parent_healthcare_service_unit", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py index b40869348d6..13cc43d2be4 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py @@ -23,9 +23,9 @@ class HealthcareServiceUnit(NestedSet): self.validate_one_root() def validate(self): - if self.is_group == 1: + if self.is_group: self.allow_appointments = 0 self.overlap_appointments = 0 self.inpatient_occupancy = 0 - elif self.allow_appointments != 1: + elif not self.allow_appointments: self.overlap_appointments = 0 diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.js b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.js index 288ebc40b4e..eb33ab68c0d 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.js +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.js @@ -2,118 +2,85 @@ // For license information, please see license.txt frappe.ui.form.on('Healthcare Service Unit Type', { + refresh: function(frm) { + frm.set_df_property('item_code', 'read_only', frm.doc.__islocal ? 0 : 1); + if (!frm.doc.__islocal && frm.doc.is_billable) { + frm.add_custom_button(__('Change Item Code'), function() { + change_item_code(cur_frm, frm.doc); + }); + } + }, + service_unit_type: function(frm) { set_item_details(frm); - if(!frm.doc.__islocal){ + + if (!frm.doc.__islocal) { frm.doc.change_in_item = 1; } }, + is_billable: function(frm) { set_item_details(frm); }, - refresh: function(frm) { - frm.set_df_property("item_code", "read_only", frm.doc.__islocal ? 0 : 1); - if(!frm.doc.__islocal) { - frm.add_custom_button(__('Change Item Code'), function() { - change_item_code(cur_frm,frm.doc); - } ); - if(frm.doc.disabled == 1){ - frm.add_custom_button(__('Enable'), function() { - enable(cur_frm); - } ); - } - else{ - frm.add_custom_button(__('Disable'), function() { - disable(cur_frm); - } ); - } - } - }, + rate: function(frm) { - if(!frm.doc.__islocal){ + if (!frm.doc.__islocal) { frm.doc.change_in_item = 1; } }, item_group: function(frm) { - if(!frm.doc.__islocal){ + if (!frm.doc.__islocal) { frm.doc.change_in_item = 1; } }, description: function(frm) { - if(!frm.doc.__islocal){ + if (!frm.doc.__islocal) { frm.doc.change_in_item = 1; } } }); -var disable = function(frm){ - var doc = frm.doc; - frappe.call({ - method: "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.disable_enable", - args: {status: 1, doc_name: doc.name, item: doc.item, is_billable: doc.is_billable}, - callback: function(){ - cur_frm.reload_doc(); - } - }); +let set_item_details = function(frm) { + if (frm.doc.service_unit_type && frm.doc.is_billable) { + if (!frm.doc.item_code) + frm.set_value('item_code', frm.doc.service_unit_type); + if (!frm.doc.description) + frm.set_value('description', frm.doc.service_unit_type); + if (!frm.doc.item_group) + frm.set_value('item_group', 'Services'); + } }; -var enable = function(frm){ - var doc = frm.doc; - frappe.call({ - method: "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.disable_enable", - args: {status: 0, doc_name: doc.name, item: doc.item, is_billable: doc.is_billable}, - callback: function(){ - cur_frm.reload_doc(); - } - }); -}; - -var change_item_code = function(frm, doc){ - var d = new frappe.ui.Dialog({ - title:__("Change Item Code"), - fields:[ +let change_item_code = function(frm, doc) { + let d = new frappe.ui.Dialog({ + title: __('Change Item Code'), + fields: [ { - "fieldtype": "Data", - "label": "Item Code", - "fieldname": "Item Code", - reqd:1 - }, - { - "fieldtype": "Button", - "label": __("Change Code"), - click: function() { - var values = d.get_values(); - if(!values) - return; - change_item_code_from_unit_type(values["Item Code"], doc); - d.hide(); - } + 'fieldtype': 'Data', + 'label': 'Item Code', + 'fieldname': 'item_code', + 'default': doc.item_code, + reqd: 1, } - ] + ], + primary_action: function() { + let values = d.get_values(); + if (values) { + frappe.call({ + "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}, + callback: function () { + frm.reload_doc(); + } + }); + } + d.hide(); + }, + primary_action_label: __("Change Template Code") }); + d.show(); d.set_values({ 'Item Code': frm.doc.item_code }); - - var change_item_code_from_unit_type = function(item_code, doc){ - frappe.call({ - "method": "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.change_item_code", - "args": {item: doc.item, item_code: item_code, doc_name: doc.name}, - callback: function () { - frm.reload_doc(); - } - }); - }; -}; - -var set_item_details = function(frm) { - if(frm.doc.service_unit_type && frm.doc.is_billable == 1){ - if(!frm.doc.item_code) - frm.set_value("item_code", frm.doc.service_unit_type); - if(!frm.doc.description) - frm.set_value("description", frm.doc.service_unit_type); - if(!frm.doc.item_group) - frm.set_value("item_group", 'Services'); - } }; diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json index 40681e9f77d..5fa47d91bc2 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json @@ -1,588 +1,164 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:service_unit_type", - "beta": 0, - "creation": "2018-07-11 16:47:51.414675", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:service_unit_type", + "creation": "2018-07-11 16:47:51.414675", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "disabled", + "service_unit_type", + "allow_appointments", + "overlap_appointments", + "inpatient_occupancy", + "is_billable", + "item_details", + "item", + "item_code", + "item_group", + "uom", + "no_of_hours", + "column_break_11", + "rate", + "description", + "change_in_item" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "service_unit_type", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Service Unit Type", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "fieldname": "service_unit_type", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Service Unit Type", + "reqd": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "default": "0", - "depends_on": "eval:doc.inpatient_occupancy != 1", - "fieldname": "allow_appointments", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Appointments", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "default": "0", + "depends_on": "eval:doc.inpatient_occupancy != 1", + "fieldname": "allow_appointments", + "fieldtype": "Check", + "label": "Allow Appointments", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "default": "0", - "depends_on": "eval:doc.allow_appointments == 1 && doc.inpatient_occupany != 1", - "fieldname": "overlap_appointments", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Overlap", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "default": "0", + "depends_on": "eval:doc.allow_appointments == 1 && doc.inpatient_occupany != 1", + "fieldname": "overlap_appointments", + "fieldtype": "Check", + "label": "Allow Overlap", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "default": "0", - "depends_on": "eval:doc.allow_appointments != 1", - "fieldname": "inpatient_occupancy", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Inpatient Occupancy", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "default": "0", + "depends_on": "eval:doc.allow_appointments != 1", + "fieldname": "inpatient_occupancy", + "fieldtype": "Check", + "label": "Inpatient Occupancy", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.inpatient_occupancy == 1 && doc.allow_appointments != 1", - "fieldname": "is_billable", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Billable", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "default": "0", + "depends_on": "eval:doc.inpatient_occupancy == 1 && doc.allow_appointments != 1", + "fieldname": "is_billable", + "fieldtype": "Check", + "label": "Is Billable" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_billable", - "fieldname": "item_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "is_billable", + "fieldname": "item_details", + "fieldtype": "Section Break", + "label": "Item Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item", + "fieldtype": "Link", + "label": "Item", + "options": "Item", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_code", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Data", + "label": "Item Code", + "mandatory_depends_on": "eval: doc.is_billable == 1" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Group", - "length": 0, - "no_copy": 0, - "options": "Item Group", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_group", + "fieldtype": "Link", + "label": "Item Group", + "mandatory_depends_on": "eval: doc.is_billable == 1", + "options": "Item Group" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "UOM", - "length": 0, - "no_copy": 0, - "options": "UOM", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "uom", + "fieldtype": "Link", + "label": "UOM", + "mandatory_depends_on": "eval: doc.is_billable == 1", + "options": "UOM" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "no_of_hours", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "UOM Conversion in Hours", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "no_of_hours", + "fieldtype": "Int", + "label": "UOM Conversion in Hours", + "mandatory_depends_on": "eval: doc.is_billable == 1" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rate", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rate / UOM", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "rate", + "fieldtype": "Currency", + "label": "Rate / UOM" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "disabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Disabled", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "change_in_item", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Change in Item", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "change_in_item", + "fieldtype": "Check", + "hidden": 1, + "label": "Change in Item" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-08 13:00:23.751635", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Healthcare Service Unit Type", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-01-30 16:06:00.624496", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Healthcare Service Unit Type", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "service_unit_type", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "restrict_to_domain": "Healthcare", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "service_unit_type" } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py index 43f01c86b94..286ecc0ff8e 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py @@ -10,109 +10,107 @@ from frappe.model.rename_doc import rename_doc class HealthcareServiceUnitType(Document): def validate(self): - if self.is_billable == 1: - if not self.uom or not self.item_group or not self.description or not self.no_of_hours > 0: - frappe.throw(_("Configure Item Fields like UOM, Item Group, Description and No of Hours.")) + if self.is_billable: + if self.disabled: + frappe.db.set_value('Item', self.item, 'disabled', 1) + else: + frappe.db.set_value('Item', self.item, 'disabled', 0) def after_insert(self): if self.inpatient_occupancy and self.is_billable: create_item(self) def on_trash(self): - if(self.item): + if self.item: try: - frappe.delete_doc("Item",self.item) + frappe.delete_doc('Item', self.item) except Exception: - frappe.throw(_("""Not permitted. Please disable the Service Unit Type""")) + frappe.throw(_('Not permitted. Please disable the Service Unit Type')) def on_update(self): - if(self.change_in_item and self.is_billable == 1 and self.item): - updating_item(self) - item_price = item_price_exist(self) - if not item_price: - if(self.rate != 0.0): - price_list_name = frappe.db.get_value("Price List", {"selling": 1}) - if(self.rate): - make_item_price(self.item_code, price_list_name, self.rate) - else: - make_item_price(self.item_code, price_list_name, 0.0) - else: - frappe.db.set_value("Item Price", item_price, "price_list_rate", self.rate) + if self.change_in_item and self.is_billable and self.item: + update_item(self) - frappe.db.set_value(self.doctype,self.name,"change_in_item",0) - elif(self.is_billable == 0 and self.item): - frappe.db.set_value("Item",self.item,"disabled",1) + item_price = item_price_exists(self) + + if not item_price: + price_list_name = frappe.db.get_value('Price List', {'selling': 1}) + if self.rate: + make_item_price(self.item_code, price_list_name, self.rate) + else: + make_item_price(self.item_code, price_list_name, 0.0) + else: + frappe.db.set_value('Item Price', item_price, 'price_list_rate', self.rate) + + frappe.db.set_value(self.doctype, self.name, 'change_in_item',0) + elif not self.is_billable and self.item: + frappe.db.set_value('Item', self.item, 'disabled', 1) self.reload() -def item_price_exist(doc): - item_price = frappe.db.exists({ - "doctype": "Item Price", - "item_code": doc.item_code}) - if(item_price): - return item_price[0][0] - else: - return False -def updating_item(doc): - frappe.db.sql("""update `tabItem` set item_name=%s, item_group=%s, disabled=0, standard_rate=%s, - description=%s, modified=NOW() where item_code=%s""", - (doc.service_unit_type, doc.item_group , doc.rate, doc.description, doc.item)) +def item_price_exists(doc): + item_price = frappe.db.exists({'doctype': 'Item Price', 'item_code': doc.item_code}) + if len(item_price): + return item_price[0][0] + return False def create_item(doc): - #insert item + # insert item item = frappe.get_doc({ - "doctype": "Item", - "item_code": doc.item_code, - "item_name":doc.service_unit_type, - "item_group": doc.item_group, - "description":doc.description, - "is_sales_item": 1, - "is_service_item": 1, - "is_purchase_item": 0, - "is_stock_item": 0, - "show_in_website": 0, - "is_pro_applicable": 0, - "disabled": 0, - "stock_uom": doc.uom - }).insert(ignore_permissions=True) + 'doctype': 'Item', + 'item_code': doc.item_code, + 'item_name': doc.service_unit_type, + 'item_group': doc.item_group, + 'description': doc.description or doc.item_code, + 'is_sales_item': 1, + 'is_service_item': 1, + 'is_purchase_item': 0, + 'is_stock_item': 0, + 'show_in_website': 0, + 'is_pro_applicable': 0, + 'disabled': 0, + 'stock_uom': doc.uom + }).insert(ignore_permissions=True, ignore_mandatory=True) - #insert item price - #get item price list to insert item price - if(doc.rate != 0.0): - price_list_name = frappe.db.get_value("Price List", {"selling": 1}) - if(doc.rate): - make_item_price(item.name, price_list_name, doc.rate) - item.standard_rate = doc.rate - else: - make_item_price(item.name, price_list_name, 0.0) - item.standard_rate = 0.0 - item.save(ignore_permissions = True) - #Set item to the Doc - frappe.db.set_value("Healthcare Service Unit Type", doc.name, "item", item.name) + # insert item price + # get item price list to insert item price + price_list_name = frappe.db.get_value('Price List', {'selling': 1}) + if doc.rate: + make_item_price(item.name, price_list_name, doc.rate) + item.standard_rate = doc.rate + else: + make_item_price(item.name, price_list_name, 0.0) + item.standard_rate = 0.0 - doc.reload() #refresh the doc after insert. + item.save(ignore_permissions=True) + + # Set item in the doc + doc.db_set('item', item.name) def make_item_price(item, price_list_name, item_price): frappe.get_doc({ - "doctype": "Item Price", - "price_list": price_list_name, - "item_code": item, - "price_list_rate": item_price - }).insert(ignore_permissions=True) + 'doctype': 'Item Price', + 'price_list': price_list_name, + 'item_code': item, + 'price_list_rate': item_price + }).insert(ignore_permissions=True, ignore_mandatory=True) + +def update_item(doc): + item = frappe.get_doc("Item", doc.item) + if item: + item.update({ + "item_name": doc.service_unit_type, + "item_group": doc.item_group, + "disabled": 0, + "standard_rate": doc.rate, + "description": doc.description + }) + item.db_update() @frappe.whitelist() def change_item_code(item, item_code, doc_name): - item_exist = frappe.db.exists({ - "doctype": "Item", - "item_code": item_code}) - if(item_exist): - frappe.throw(_("Code {0} already exist").format(item_code)) + if frappe.db.exists({'doctype': 'Item', 'item_code': item_code}): + frappe.throw(_('Item with Item Code {0} already exists').format(item_code)) else: - rename_doc("Item", item, item_code, ignore_permissions=True) - frappe.db.set_value("Healthcare Service Unit Type", doc_name, "item_code", item_code) - -@frappe.whitelist() -def disable_enable(status, doc_name, item=None, is_billable=None): - frappe.db.set_value("Healthcare Service Unit Type", doc_name, "disabled", status) - if(is_billable == 1): - frappe.db.set_value("Item", item, "disabled", status) + rename_doc('Item', item, item_code, ignore_permissions=True) + frappe.db.set_value('Healthcare Service Unit Type', doc_name, 'item_code', item_code) diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type_dashboard.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type_dashboard.py new file mode 100644 index 00000000000..0ac548b3ffd --- /dev/null +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type_dashboard.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'service_unit_type', + 'transactions': [ + { + 'label': _('Healthcare Service Units'), + 'items': ['Healthcare Service Unit'] + }, + ] + } diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.py index 3c5b64fd930..01cf4b0a494 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.py @@ -3,6 +3,31 @@ # See license.txt from __future__ import unicode_literals import unittest +import frappe class TestHealthcareServiceUnitType(unittest.TestCase): - pass + def test_item_creation(self): + unit_type = get_unit_type() + self.assertTrue(frappe.db.exists('Item', unit_type.item)) + + # check item disabled + unit_type.disabled = 1 + unit_type.save() + self.assertEqual(frappe.db.get_value('Item', unit_type.item, 'disabled'), 1) + + +def get_unit_type(): + if frappe.db.exists('Healthcare Service Unit Type', 'Inpatient Rooms'): + return frappe.get_doc('Healthcare Service Unit Type', 'Inpatient Rooms') + + unit_type = frappe.new_doc('Healthcare Service Unit Type') + unit_type.service_unit_type = 'Inpatient Rooms' + unit_type.inpatient_occupancy = 1 + unit_type.is_billable = 1 + unit_type.item_code = 'Inpatient Rooms' + unit_type.item_group = 'Services' + unit_type.uom = 'Hour' + unit_type.no_of_hours = 1 + unit_type.rate = 4000 + unit_type.save() + return unit_type \ No newline at end of file diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json index 95d9e44cddb..de086201795 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json @@ -7,40 +7,40 @@ "engine": "InnoDB", "field_order": [ "sb_op_settings", - "patient_master_name", - "manage_customer", + "patient_name_by", + "link_customer_to_patient", "default_medical_code_standard", "column_break_9", "collect_registration_fee", "registration_fee", - "manage_appointment_invoice_automatically", - "max_visit", + "automate_appointment_invoicing", + "enable_free_follow_ups", + "max_visits", "valid_days", "healthcare_service_items", "inpatient_visit_charge_item", "op_consulting_charge_item", "column_break_13", "clinical_procedure_consumable_item", - "out_patient_sms_alerts", - "reg_sms", - "reg_msg", - "app_con", - "app_con_msg", - "no_con", - "column_break_16", - "app_rem", - "app_rem_msg", - "rem_before", "sb_in_ac", "income_account", - "sb_r_ac", "receivable_account", + "out_patient_sms_alerts", + "send_registration_msg", + "registration_msg", + "send_appointment_confirmation", + "appointment_confirmation_msg", + "avoid_confirmation", + "column_break_16", + "send_appointment_reminder", + "appointment_reminder_msg", + "remind_before", "sb_lab_settings", - "create_test_on_si_submit", - "require_sample_collection", - "require_test_result_approval", + "create_lab_test_on_si_submit", + "create_sample_collection_for_lab_test", "column_break_34", "employee_name_and_designation_in_print", + "lab_test_approval_required", "custom_signature_in_print", "laboratory_sms_alerts", "sms_printed", @@ -53,19 +53,6 @@ "fieldtype": "Section Break", "label": "Out Patient Settings" }, - { - "fieldname": "patient_master_name", - "fieldtype": "Select", - "label": "Patient Name By", - "options": "Patient Name\nNaming Series" - }, - { - "default": "1", - "description": "If checked, a customer will be created, mapped to Patient.\nPatient Invoices will be created against this Customer. You can also select existing Customer while creating Patient.", - "fieldname": "manage_customer", - "fieldtype": "Check", - "label": "Manage Customer" - }, { "fieldname": "default_medical_code_standard", "fieldtype": "Link", @@ -78,6 +65,7 @@ }, { "default": "0", + "description": "Checking this will create new Patients with a Disabled status by default and will only be enabled after invoicing the Registration Fee.", "fieldname": "collect_registration_fee", "fieldtype": "Check", "label": "Collect Fee for Patient Registration" @@ -91,27 +79,19 @@ "options": "Currency" }, { - "default": "0", - "description": "Manage Appointment Invoice submit and cancel automatically for Patient Encounter", - "fieldname": "manage_appointment_invoice_automatically", - "fieldtype": "Check", - "label": "Invoice Appointments Automatically" - }, - { - "fieldname": "max_visit", - "fieldtype": "Int", - "label": "Patient Encounters in valid days" - }, - { + "depends_on": "eval:doc.enable_free_follow_ups == 1", + "description": "Time period (Valid number of days) for free consultations", "fieldname": "valid_days", "fieldtype": "Int", - "label": "Valid number of days" + "label": "Valid Number of Days", + "mandatory_depends_on": "eval:doc.enable_free_follow_ups == 1" }, { "collapsible": 1, + "description": "You can configure default Items for billing consultation charges, procedure consumption items and inpatient visits", "fieldname": "healthcare_service_items", "fieldtype": "Section Break", - "label": "Healthcare Service Items" + "label": "Default Healthcare Service Items" }, { "fieldname": "inpatient_visit_charge_item", @@ -141,87 +121,25 @@ "fieldtype": "Section Break", "label": "Out Patient SMS Alerts" }, - { - "default": "0", - "fieldname": "reg_sms", - "fieldtype": "Check", - "label": "Patient Registration" - }, - { - "default": "Hello {{doc.patient}}, Thank you for registering with {{doc.company}}. Your ID is {{doc.id}} . Please note this ID for future reference. \nThank You, Get well soon!", - "depends_on": "reg_sms", - "fieldname": "reg_msg", - "fieldtype": "Small Text", - "ignore_xss_filter": 1, - "label": "Registration Message" - }, - { - "default": "0", - "fieldname": "app_con", - "fieldtype": "Check", - "label": "Appointment Confirmation" - }, - { - "default": "Hello {{doc.patient}}, You have scheduled an appointment with {{doc.practitioner}} by {{doc.start_dt}} at {{doc.company}}.\nThank you, Good day!", - "depends_on": "app_con", - "fieldname": "app_con_msg", - "fieldtype": "Small Text", - "ignore_xss_filter": 1, - "label": "Confirmation Message" - }, - { - "default": "0", - "depends_on": "app_con", - "description": "Do not confirm if appointment is created for the same day", - "fieldname": "no_con", - "fieldtype": "Check", - "label": "Avoid Confirmation" - }, { "fieldname": "column_break_16", "fieldtype": "Column Break" }, - { - "default": "0", - "fieldname": "app_rem", - "fieldtype": "Check", - "label": "Appointment Reminder" - }, - { - "default": "Hello {{doc.patient}}, You have an appointment with {{doc.practitioner}} by {{doc.appointment_time}} at {{doc.company}}.\nThank you, Good day!\n", - "depends_on": "app_rem", - "fieldname": "app_rem_msg", - "fieldtype": "Small Text", - "ignore_xss_filter": 1, - "label": "Reminder Message" - }, - { - "depends_on": "app_rem", - "fieldname": "rem_before", - "fieldtype": "Time", - "label": "Remind Before" - }, { "collapsible": 1, - "description": "Default income accounts to be used if not set in Healthcare Practitioner to book Appointment charges.", "fieldname": "sb_in_ac", "fieldtype": "Section Break", - "label": "Income Account" + "label": "Default Accounts" }, { + "description": "Default income accounts to be used if not set in Healthcare Practitioner to book Appointment charges.", "fieldname": "income_account", "fieldtype": "Table", "label": "Income Account", "options": "Party Account" }, { - "collapsible": 1, - "description": "Default receivable accounts to be used if not set in Patient to book Appointment charges.", - "fieldname": "sb_r_ac", - "fieldtype": "Section Break", - "label": "Receivable Account" - }, - { + "description": "Default receivable accounts to be used to book Appointment charges.", "fieldname": "receivable_account", "fieldtype": "Table", "label": "Receivable Account", @@ -233,31 +151,13 @@ "fieldtype": "Section Break", "label": "Laboratory Settings" }, - { - "default": "0", - "fieldname": "create_test_on_si_submit", - "fieldtype": "Check", - "label": "Create Lab Test(s) on Sales Invoice Submit" - }, - { - "default": "0", - "description": "Create documents for sample collection", - "fieldname": "require_sample_collection", - "fieldtype": "Check", - "label": "Manage Sample Collection" - }, - { - "default": "0", - "fieldname": "require_test_result_approval", - "fieldtype": "Check", - "label": "Require Lab Test Approval" - }, { "fieldname": "column_break_34", "fieldtype": "Column Break" }, { "default": "1", + "description": "Check this if you want the Name and Designation of the Employee associated with the User who submits the document to be printed in the Lab Test Report.", "fieldname": "employee_name_and_designation_in_print", "fieldtype": "Check", "label": "Employee name and designation in print" @@ -279,7 +179,7 @@ "fieldname": "sms_printed", "fieldtype": "Small Text", "ignore_xss_filter": 1, - "label": "Result Printed" + "label": "Result Printed Message" }, { "fieldname": "column_break_28", @@ -290,12 +190,123 @@ "fieldname": "sms_emailed", "fieldtype": "Small Text", "ignore_xss_filter": 1, - "label": "Result Emailed" + "label": "Result Emailed Message" + }, + { + "default": "0", + "description": "Checking this will restrict printing and emailing of Lab Test documents unless they have the status as Approved.", + "fieldname": "lab_test_approval_required", + "fieldtype": "Check", + "label": "Do not print or email Lab Tests without Approval" + }, + { + "default": "1", + "description": "If checked, a customer will be created, mapped to Patient.\nPatient Invoices will be created against this Customer. You can also select existing Customer while creating Patient.", + "fieldname": "link_customer_to_patient", + "fieldtype": "Check", + "label": "Link Customer to Patient" + }, + { + "default": "0", + "description": "Checking this will create Lab Test(s) specified in the Sales Invoice on submission.", + "fieldname": "create_lab_test_on_si_submit", + "fieldtype": "Check", + "label": "Create Lab Test(s) on Sales Invoice Submission" + }, + { + "default": "0", + "description": "Checking this will create a Sample Collection document every time you create a Lab Test", + "fieldname": "create_sample_collection_for_lab_test", + "fieldtype": "Check", + "label": "Create Sample Collection document for Lab Test" + }, + { + "fieldname": "patient_name_by", + "fieldtype": "Select", + "label": "Patient Name By", + "options": "Patient Name\nNaming Series" + }, + { + "default": "0", + "description": "Manage Appointment Invoice submit and cancel automatically for Patient Encounter", + "fieldname": "automate_appointment_invoicing", + "fieldtype": "Check", + "label": "Automate Appointment Invoicing" + }, + { + "default": "0", + "fieldname": "send_registration_msg", + "fieldtype": "Check", + "label": "Patient Registration" + }, + { + "default": "Hello {{doc.patient}}, Thank you for registering with {{doc.company}}. Your ID is {{doc.id}} . Please note this ID for future reference. \nThank You, Get well soon!", + "depends_on": "send_registration_msg", + "fieldname": "registration_msg", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "label": "Registration Message" + }, + { + "default": "0", + "fieldname": "send_appointment_confirmation", + "fieldtype": "Check", + "label": "Appointment Confirmation" + }, + { + "default": "Hello {{doc.patient}}, You have scheduled an appointment with {{doc.practitioner}} by {{doc.start_dt}} at {{doc.company}}.\nThank you, Good day!", + "depends_on": "send_appointment_confirmation", + "fieldname": "appointment_confirmation_msg", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "label": "Confirmation Message" + }, + { + "default": "0", + "depends_on": "send_appointment_confirmation", + "description": "Do not confirm if appointment is created for the same day", + "fieldname": "avoid_confirmation", + "fieldtype": "Check", + "label": "Avoid Confirmation" + }, + { + "default": "0", + "fieldname": "send_appointment_reminder", + "fieldtype": "Check", + "label": "Appointment Reminder" + }, + { + "default": "Hello {{doc.patient}}, You have an appointment with {{doc.practitioner}} by {{doc.appointment_time}} at {{doc.company}}.\nThank you, Good day!\n", + "depends_on": "send_appointment_reminder", + "fieldname": "appointment_reminder_msg", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "label": "Reminder Message" + }, + { + "depends_on": "send_appointment_reminder", + "fieldname": "remind_before", + "fieldtype": "Time", + "label": "Remind Before" + }, + { + "depends_on": "eval:doc.enable_free_follow_ups == 1", + "description": "The number of free follow ups (Patient Encounters in valid days) allowed", + "fieldname": "max_visits", + "fieldtype": "Int", + "label": "Number of Patient Encounters in Valid Days", + "mandatory_depends_on": "eval:doc.enable_free_follow_ups == 1" + }, + { + "default": "0", + "fieldname": "enable_free_follow_ups", + "fieldtype": "Check", + "label": "Enable Free Follow-ups" } ], "issingle": 1, "links": [], - "modified": "2020-01-23 13:31:43.699711", + "modified": "2020-03-26 11:25:21.842092", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Settings", diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py index 8555e80f848..a16fceb74dd 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py @@ -11,69 +11,80 @@ import json class HealthcareSettings(Document): def validate(self): - for key in ["collect_registration_fee","manage_customer","patient_master_name", - "require_test_result_approval","require_sample_collection", "default_medical_code_standard"]: + for key in ['collect_registration_fee', 'link_customer_to_patient', 'patient_name_by', + 'lab_test_approval_required', 'create_sample_collection_for_lab_test', 'default_medical_code_standard']: frappe.db.set_default(key, self.get(key, "")) - if(self.collect_registration_fee): - if self.registration_fee <= 0 : - frappe.throw(_("Registration fee can not be Zero")) + + if self.collect_registration_fee: + if self.registration_fee <= 0: + frappe.throw(_('Registration Fee cannot be negative or zero')) + if self.inpatient_visit_charge_item: - validate_service_item(self.inpatient_visit_charge_item, "Configure a service Item for Inpatient Visit Charge Item") + validate_service_item(self.inpatient_visit_charge_item) if self.op_consulting_charge_item: - validate_service_item(self.op_consulting_charge_item, "Configure a service Item for Out Patient Consulting Charge Item") + validate_service_item(self.op_consulting_charge_item) if self.clinical_procedure_consumable_item: - validate_service_item(self.clinical_procedure_consumable_item, "Configure a service Item for Clinical Procedure Consumable Item") + validate_service_item(self.clinical_procedure_consumable_item) + + +def validate_service_item(item): + if frappe.db.get_value('Item', item, 'is_stock_item'): + frappe.throw(_('Configure a service Item for {0}').format(item)) @frappe.whitelist() def get_sms_text(doc): - sms_text = {} - doc = frappe.get_doc("Lab Test",doc) - #doc = json.loads(doc) - context = {"doc": doc, "alert": doc, "comments": None} - emailed = frappe.db.get_value("Healthcare Settings", None, "sms_emailed") - sms_text['emailed'] = frappe.render_template(emailed, context) - printed = frappe.db.get_value("Healthcare Settings", None, "sms_printed") - sms_text['printed'] = frappe.render_template(printed, context) - return sms_text + sms_text = {} + doc = frappe.get_doc('Lab Test', doc) + context = {'doc': doc, 'alert': doc, 'comments': None} + + emailed = frappe.db.get_value('Healthcare Settings', None, 'sms_emailed') + sms_text['emailed'] = frappe.render_template(emailed, context) + + printed = frappe.db.get_value('Healthcare Settings', None, 'sms_printed') + sms_text['printed'] = frappe.render_template(printed, context) + + return sms_text def send_registration_sms(doc): - if (frappe.db.get_value("Healthcare Settings", None, "reg_sms")=='1'): - if doc.mobile: - context = {"doc": doc, "alert": doc, "comments": None} - if doc.get("_comments"): - context["comments"] = json.loads(doc.get("_comments")) - messages = frappe.db.get_value("Healthcare Settings", None, "reg_msg") - messages = frappe.render_template(messages, context) - number = [doc.mobile] - send_sms(number,messages) - else: - frappe.msgprint(doc.name + " Has no mobile number to send registration SMS", alert=True) - + if frappe.db.get_single_value('Healthcare Settings', 'send_registration_msg'): + if doc.mobile: + context = {'doc': doc, 'alert': doc, 'comments': None} + if doc.get('_comments'): + context['comments'] = json.loads(doc.get('_comments')) + messages = frappe.db.get_single_value('Healthcare Settings', 'registration_msg') + messages = frappe.render_template(messages, context) + number = [doc.mobile] + send_sms(number,messages) + else: + frappe.msgprint(doc.name + ' has no mobile number to send registration SMS', alert=True) def get_receivable_account(company): - receivable_account = get_account(None, "receivable_account", "Healthcare Settings", company) - if receivable_account: - return receivable_account - return frappe.get_cached_value('Company', company, "default_receivable_account") + receivable_account = get_account(None, 'receivable_account', 'Healthcare Settings', company) + if receivable_account: + return receivable_account + + return frappe.get_cached_value('Company', company, 'default_receivable_account') def get_income_account(practitioner, company): - if(practitioner): - income_account = get_account("Healthcare Practitioner", None, practitioner, company) - if income_account: - return income_account - income_account = get_account(None, "income_account", "Healthcare Settings", company) - if income_account: - return income_account - return frappe.get_cached_value('Company', company, "default_income_account") + # check income account in Healthcare Practitioner + if practitioner: + income_account = get_account('Healthcare Practitioner', None, practitioner, company) + if income_account: + return income_account + + # else check income account in Healthcare Settings + income_account = get_account(None, 'income_account', 'Healthcare Settings', company) + if income_account: + return income_account + + # else return default income account of company + return frappe.get_cached_value('Company', company, 'default_income_account') def get_account(parent_type, parent_field, parent, company): - if(parent_type): - return frappe.db.get_value("Party Account", - {"parenttype": parent_type, "parent": parent, "company": company}, "account") - if(parent_field): - return frappe.db.get_value("Party Account", - {"parentfield": parent_field, "parent": parent, "company": company}, "account") + if parent_type: + return frappe.db.get_value('Party Account', + {'parenttype': parent_type, 'parent': parent, 'company': company}, 'account') -def validate_service_item(item, msg): - if frappe.db.get_value("Item", item, "is_stock_item") == 1: - frappe.throw(_(msg)) + if parent_field: + return frappe.db.get_value('Party Account', + {'parentfield': parent_field, 'parent': parent, 'company': company}, 'account') diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py index f10725ed9b6..e15324c55bf 100644 --- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py @@ -8,11 +8,12 @@ import unittest from frappe.utils import now_datetime, today from frappe.utils.make_random import get_random from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge +from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient class TestInpatientRecord(unittest.TestCase): def test_admit_and_discharge(self): frappe.db.sql("""delete from `tabInpatient Record`""") - patient = get_patient() + patient = create_patient() # Schedule Admission ip_record = create_inpatient(patient) ip_record.save(ignore_permissions = True) @@ -41,7 +42,7 @@ class TestInpatientRecord(unittest.TestCase): def test_validate_overlap_admission(self): frappe.db.sql("""delete from `tabInpatient Record`""") - patient = get_patient() + patient = create_patient() ip_record = create_inpatient(patient) ip_record.save(ignore_permissions = True) @@ -75,17 +76,6 @@ def create_inpatient(patient): inpatient_record.scheduled_date = today() return inpatient_record -def get_patient(): - patient = get_random("Patient") - if not patient: - patient = frappe.new_doc("Patient") - patient.patient_name = "Test Patient" - patient.sex = "Male" - patient.save(ignore_permissions=True) - return patient.name - return patient - - def get_healthcare_service_unit(): service_unit = get_random("Healthcare Service Unit", filters={"inpatient_occupancy": 1}) if not service_unit: diff --git a/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json b/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json index d6691d4295d..0720bb4eecd 100644 --- a/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json +++ b/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json @@ -1,238 +1,78 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 1, - "creation": "2016-09-16 16:53:06.882970", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, + "actions": [], + "allow_copy": 1, + "beta": 1, + "creation": "2016-09-16 16:53:06.882970", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "lab_test_code", + "lab_test_name", + "invoiced", + "column_break_4", + "lab_test_comment", + "lab_test_created" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Test Code", - "length": 0, - "no_copy": 0, - "options": "Lab Test Template", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_code", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Lab Test", + "options": "Lab Test Template", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "lab_test_code.lab_test_name", - "fieldname": "lab_test_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Test", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "lab_test_code.lab_test_name", + "fieldname": "lab_test_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Lab Test Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "invoiced", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Invoiced", - "length": 0, - "no_copy": 1, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "invoiced", + "fieldtype": "Check", + "label": "Invoiced", + "no_copy": 1, + "read_only": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_comment", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Comments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_comment", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Comments" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_created", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Test Created", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "lab_test_created", + "fieldtype": "Check", + "hidden": 1, + "label": "Test Created", + "no_copy": 1, + "print_hide": 1, + "report_hide": 1, + "search_index": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-09-04 09:02:18.592637", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Lab Prescription", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-02-26 17:03:00.255560", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Lab Prescription", + "owner": "Administrator", + "permissions": [], + "restrict_to_domain": "Healthcare", + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js index b60e70fd766..5b3f4c705ad 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test.js @@ -29,7 +29,7 @@ frappe.ui.form.on('Lab Test', { get_lab_test_prescribed(frm); }); } - if(frm.doc.docstatus==1 && frm.doc.status!='Approved' && frm.doc.status!='Rejected' && frappe.defaults.get_default("require_test_result_approval") && frappe.user.has_role("LabTest Approver")){ + if(frm.doc.docstatus==1 && frm.doc.status!='Approved' && frm.doc.status!='Rejected' && frappe.defaults.get_default("lab_test_approval_required") && frappe.user.has_role("LabTest Approver")){ frm.add_custom_button(__('Approve'), function() { status_update(1,frm); }); diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json index 00e613a179f..ccbc24b3fb6 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.json +++ b/erpnext/healthcare/doctype/lab_test/lab_test.json @@ -1,1597 +1,478 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 1, - "creation": "2016-03-29 17:34:47.509094", - "custom": 0, - "default_print_format": "", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, + "actions": [], + "allow_copy": 1, + "allow_import": 1, + "autoname": "naming_series:", + "beta": 1, + "creation": "2016-03-29 17:34:47.509094", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "inpatient_record", + "naming_series", + "invoiced", + "patient", + "patient_name", + "patient_age", + "patient_sex", + "practitioner", + "email", + "mobile", + "company", + "c_b", + "department", + "status", + "submitted_date", + "approved_date", + "sample", + "result_date", + "employee", + "employee_name", + "employee_designation", + "user", + "report_preference", + "sb_first", + "lab_test_name", + "column_break_26", + "template", + "lab_test_group", + "sb_normal", + "normal_test_items", + "sb_special", + "special_test_items", + "sb_sensitivity", + "sensitivity_test_items", + "sb_comments", + "lab_test_comment", + "sb_customresult", + "custom_result", + "email_sent", + "sms_sent", + "printed", + "normal_toggle", + "special_toggle", + "sensitivity_toggle", + "amended_from", + "prescription" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.inpatient_record", - "fieldname": "inpatient_record", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Inpatient Record", - "length": 0, - "no_copy": 0, - "options": "Inpatient Record", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "patient.inpatient_record", + "fieldname": "inpatient_record", + "fieldtype": "Link", + "label": "Inpatient Record", + "options": "Inpatient Record", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 0, - "options": "LP-", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "LP-", + "print_hide": 1, + "report_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "invoiced", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Invoiced", - "length": 0, - "no_copy": 1, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "invoiced", + "fieldtype": "Check", + "label": "Invoiced", + "no_copy": 1, + "read_only": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "inpatient_record.patient", - "fieldname": "patient", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Patient", - "length": 0, - "no_copy": 0, - "options": "Patient", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "inpatient_record.patient", + "fieldname": "patient", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Patient", + "options": "Patient", + "reqd": 1, + "search_index": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.patient_name", - "fieldname": "patient_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Patient Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "patient.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "label": "Patient Name", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient_age", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Age", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "patient_age", + "fieldtype": "Data", + "label": "Age", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient_sex", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Gender", - "length": 0, - "no_copy": 0, - "options": "\nMale\nFemale\nOther", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "patient_sex", + "fieldtype": "Link", + "label": "Gender", + "options": "Gender", + "print_hide": 1, + "report_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "practitioner", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "practitioner", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "email", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Email", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "email", + "fieldtype": "Data", + "hidden": 1, + "label": "Email", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mobile", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Mobile", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "mobile", + "fieldtype": "Data", + "hidden": 1, + "label": "Mobile", + "print_hide": 1, + "read_only": 1, + "report_hide": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 1, + "label": "Company", + "options": "Company", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "c_b", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "c_b", + "fieldtype": "Column Break", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Medical Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "department", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Department", + "options": "Medical Department", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Draft\nCompleted\nApproved\nRejected\nCancelled", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "status", + "fieldtype": "Select", + "hidden": 1, + "label": "Status", + "options": "Draft\nCompleted\nApproved\nRejected\nCancelled", + "print_hide": 1, + "report_hide": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "submitted_date", - "fieldtype": "Datetime", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Submitted Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "submitted_date", + "fieldtype": "Datetime", + "hidden": 1, + "label": "Submitted Date", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "approved_date", - "fieldtype": "Datetime", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Approved Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "approved_date", + "fieldtype": "Datetime", + "hidden": 1, + "label": "Approved Date", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sample ID", - "length": 0, - "no_copy": 0, - "options": "Sample Collection", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sample", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_filter": 1, + "label": "Sample ID", + "options": "Sample Collection", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "result_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Result Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "result_date", + "fieldtype": "Date", + "label": "Result Date", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Lab Technician", - "length": 0, - "no_copy": 1, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "employee", + "fieldtype": "Link", + "label": "Lab Technician", + "no_copy": 1, + "options": "Employee", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Technician Name", - "length": 0, - "no_copy": 1, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Technician Name", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.designation", - "fieldname": "employee_designation", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Designation", - "length": 0, - "no_copy": 1, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.designation", + "fieldname": "employee_designation", + "fieldtype": "Data", + "label": "Designation", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "user", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "User", - "length": 0, - "no_copy": 1, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "user", + "fieldtype": "Link", + "hidden": 1, + "label": "User", + "no_copy": 1, + "options": "User", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "report_preference", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Report Preference", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "report_preference", + "fieldtype": "Data", + "label": "Report Preference", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_first", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sb_first", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Test Name", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_name", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Test Name", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "report_hide": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_26", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "template", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Test Template", - "length": 0, - "no_copy": 0, - "options": "Lab Test Template", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "template", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Test Template", + "options": "Lab Test Template", + "print_hide": 1, + "report_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_group", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Test Group", - "length": 0, - "no_copy": 0, - "options": "Item Group", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_group", + "fieldtype": "Link", + "hidden": 1, + "label": "Test Group", + "options": "Item Group", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_normal", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sb_normal", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "normal_test_items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "options": "Normal Test Items", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "normal_test_items", + "fieldtype": "Table", + "options": "Normal Test Items" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_special", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sb_special", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "special_test_items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "options": "Special Test Items", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "special_test_items", + "fieldtype": "Table", + "options": "Special Test Items", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_sensitivity", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sb_sensitivity", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sensitivity_test_items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "options": "Sensitivity Test Items", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sensitivity_test_items", + "fieldtype": "Table", + "options": "Sensitivity Test Items", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_comments", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sb_comments", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_comment", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Comments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_comment", + "fieldtype": "Text", + "ignore_xss_filter": 1, + "label": "Comments" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "sb_customresult", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Custom Result", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "sb_customresult", + "fieldtype": "Section Break", + "label": "Custom Result" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "custom_result", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Custom Result", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "custom_result", + "fieldtype": "Text Editor", + "ignore_xss_filter": 1, + "label": "Custom Result" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "email_sent", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "email_sent", + "fieldtype": "Check", + "hidden": 1, + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "sms_sent", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "sms_sent", + "fieldtype": "Check", + "hidden": 1, + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "printed", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "printed", + "fieldtype": "Check", + "hidden": 1, + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "normal_toggle", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "normal_toggle", + "fieldtype": "Check", + "hidden": 1, + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "special_toggle", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "special_toggle", + "fieldtype": "Check", + "hidden": 1, + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "sensitivity_toggle", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "sensitivity_toggle", + "fieldtype": "Check", + "hidden": 1, + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Lab Test", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Lab Test", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "prescription", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Prescription", - "length": 0, - "no_copy": 1, - "options": "Lab Prescription", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "prescription", + "fieldtype": "Link", + "hidden": 1, + "label": "Prescription", + "no_copy": 1, + "options": "Lab Prescription", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "", - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-09-04 10:19:33.422304", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Lab Test", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-03-23 19:37:06.617764", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Lab Test", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Laboratory User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Laboratory User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "LabTest Approver", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "LabTest Approver", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Physician", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Physician", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "patient,practitioner,lab_test_name,sample", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "patient", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 + ], + "restrict_to_domain": "Healthcare", + "search_fields": "patient,practitioner,lab_test_name,sample", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "patient", + "track_changes": 1, + "track_seen": 1 } \ 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 86094896154..4e4015d2f09 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -191,20 +191,24 @@ def create_specials(template, lab_test): special.template = template.name def create_sample_doc(template, patient, invoice): - if(template.sample): - sample_exist = frappe.db.exists({ + if template.sample: + sample_exists = frappe.db.exists({ "doctype": "Sample Collection", "patient": patient.name, "docstatus": 0, - "sample": template.sample}) - if sample_exist : - #Update Sample Collection by adding quantity - sample_collection = frappe.get_doc("Sample Collection",sample_exist[0][0]) - quantity = int(sample_collection.sample_quantity)+int(template.sample_quantity) - if(template.sample_collection_details): - sample_collection_details = sample_collection.sample_collection_details+"\n==============\n"+"Test :"+template.lab_test_name+"\n"+"Collection Detials:\n\t"+template.sample_collection_details - frappe.db.set_value("Sample Collection", sample_collection.name, "sample_collection_details",sample_collection_details) - frappe.db.set_value("Sample Collection", sample_collection.name, "sample_quantity",quantity) + "sample": template.sample + }) + if sample_exists: + # update Sample Collection by adding quantity + 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 += (template.get("lab_test_name") or template.get("template")) + "\n" + 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) else: #create Sample Collection for template, copy vals from Invoice @@ -216,15 +220,15 @@ def create_sample_doc(template, patient, invoice): sample_collection.patient_sex = patient.sex sample_collection.sample = template.sample sample_collection.sample_uom = template.sample_uom - sample_collection.sample_quantity = template.sample_quantity - if(template.sample_collection_details): - sample_collection.sample_collection_details = "Test :"+template.lab_test_name+"\n"+"Collection Detials:\n\t"+template.sample_collection_details + sample_collection.sample_qty = template.sample_qty + if(template.sample_details): + sample_collection.sample_details = "Test :" + (template.get("lab_test_name") or template.get("template")) +"\n"+"Collection Detials:\n\t"+template.sample_details sample_collection.save(ignore_permissions=True) return sample_collection def create_sample_collection(lab_test, template, patient, invoice): - if(frappe.db.get_value("Healthcare Settings", None, "require_sample_collection") == "1"): + if(frappe.db.get_value("Healthcare Settings", None, "create_sample_collection_for_lab_test") == "1"): sample_collection = create_sample_doc(template, patient, invoice) if(sample_collection): lab_test.sample = sample_collection.name @@ -290,10 +294,14 @@ def insert_lab_test_to_medical_record(doc): comment = "" if item.lab_test_comment: comment = str(item.lab_test_comment) - event = "" + table_row = item.lab_test_name + if item.lab_test_event: - event = item.lab_test_event - table_row = item.lab_test_name +" "+ event +" "+ item.result_value + table_row += " " + item.lab_test_event + + if item.result_value: + table_row += " " + item.result_value + if item.normal_range: table_row += " normal_range("+item.normal_range+")" table_row += " "+comment diff --git a/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.json b/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.json index 46c04c696e2..2830038eafd 100644 --- a/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.json +++ b/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.json @@ -1,145 +1,68 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:sample", - "beta": 1, - "creation": "2016-04-04 17:35:44.823951", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "actions": [], + "allow_copy": 1, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:sample", + "beta": 1, + "creation": "2016-04-04 17:35:44.823951", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "sample", + "sample_uom" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Sample", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "sample", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Sample", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fieldname": "sample_uom", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "UOM", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "bold": 1, + "fieldname": "sample_uom", + "fieldtype": "Link", + "in_list_view": 1, + "label": "UOM", + "options": "Lab Test UOM" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-08-31 13:46:22.508908", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Lab Test Sample", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-01-29 23:02:02.249839", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Lab Test Sample", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Laboratory User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Laboratory User", + "share": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "sample", - "track_changes": 1, - "track_seen": 0 + ], + "quick_entry": 1, + "restrict_to_domain": "Healthcare", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "sample", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json index fcfecba5767..a606bc4b1d0 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json @@ -35,8 +35,8 @@ "sb_sample_collection", "sample", "sample_uom", - "sample_quantity", - "sample_collection_details", + "sample_qty", + "sample_details", "change_in_item" ], "fields": [ @@ -105,7 +105,7 @@ "description": "If unchecked, the item wont be appear in Sales Invoice, but can be used in group test creation. ", "fieldname": "is_billable", "fieldtype": "Check", - "label": "Is billable", + "label": "Is Billable", "search_index": 1 }, { @@ -114,7 +114,8 @@ "fieldname": "lab_test_rate", "fieldtype": "Currency", "in_list_view": 1, - "label": "Standard Selling Rate" + "label": "Rate", + "mandatory_depends_on": "eval:doc.is_billable == 1" }, { "depends_on": "eval:doc.lab_test_template_type == 'Single'", @@ -209,18 +210,6 @@ "label": "UOM", "read_only": 1 }, - { - "default": "0", - "fieldname": "sample_quantity", - "fieldtype": "Float", - "label": "Quantity" - }, - { - "fieldname": "sample_collection_details", - "fieldtype": "Text", - "ignore_xss_filter": 1, - "label": "Collection Details" - }, { "default": "0", "fieldname": "change_in_item", @@ -236,11 +225,23 @@ "fieldname": "disabled", "fieldtype": "Check", "label": "Disabled" + }, + { + "default": "0", + "fieldname": "sample_qty", + "fieldtype": "Float", + "label": "Quantity" + }, + { + "fieldname": "sample_details", + "fieldtype": "Text", + "ignore_xss_filter": 1, + "label": "Collection Details" } ], "links": [], - "modified": "2020-01-21 21:02:16.108347", - "modified_by": "ruchamahabal2@gmail.com", + "modified": "2020-03-25 16:53:01.740103", + "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test Template", "owner": "Administrator", diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py index fd7ae801d97..6bbb4f1c3a0 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py @@ -99,13 +99,10 @@ def create_item_from_template(doc): # get item price list to insert item price if doc.lab_test_rate != 0.0: price_list_name = frappe.db.get_value("Price List", {"selling": 1}) - if(doc.lab_test_rate): + if doc.lab_test_rate: make_item_price(item.name, price_list_name, doc.lab_test_rate) - item.standard_rate = doc.lab_test_rate else: make_item_price(item.name, price_list_name, 0.0) - item.standard_rate = 0.0 - item.save(ignore_permissions = True) # Set item in the template frappe.db.set_value("Lab Test Template", doc.name, "item", item.name) diff --git a/erpnext/healthcare/doctype/patient/patient.js b/erpnext/healthcare/doctype/patient/patient.js index 1a34fe8076f..d5df9567ec5 100644 --- a/erpnext/healthcare/doctype/patient/patient.js +++ b/erpnext/healthcare/doctype/patient/patient.js @@ -3,128 +3,77 @@ frappe.ui.form.on('Patient', { refresh: function (frm) { - frm.set_query("patient", "patient_relation", function () { + frm.set_query('patient', 'patient_relation', function () { return { filters: [ - ["Patient", "name", "!=", frm.doc.name] + ['Patient', 'name', '!=', frm.doc.name] ] }; }); - if (frappe.defaults.get_default("patient_master_name") != "Naming Series") { - frm.toggle_display("naming_series", false); + + if (frappe.defaults.get_default('patient_name_by') != 'Naming Series') { + frm.toggle_display('naming_series', false); } else { erpnext.toggle_naming_series(); } - if (frappe.defaults.get_default("collect_registration_fee") && frm.doc.disabled == 1) { + + if (frappe.defaults.get_default('collect_registration_fee') && frm.doc.status == 'Disabled') { frm.add_custom_button(__('Invoice Patient Registration'), function () { - btn_invoice_registration(frm); + invoice_registration(frm); }); } - if (frm.doc.patient_name && frappe.user.has_role("Physician")) { - frm.add_custom_button(__('Patient History'), function () { - frappe.route_options = { "patient": frm.doc.name }; - frappe.set_route("patient_history"); - },"View"); + + if (frm.doc.patient_name && frappe.user.has_role('Physician')) { + frm.add_custom_button(__('Patient History'), function() { + frappe.route_options = {'patient': frm.doc.name}; + frappe.set_route('patient_history'); + },'View'); } - if (!frm.doc.__islocal && (frappe.user.has_role("Nursing User") || frappe.user.has_role("Physician"))) { + + if (!frm.doc.__islocal && (frappe.user.has_role('Nursing User') || frappe.user.has_role('Physician'))) { frm.add_custom_button(__('Vital Signs'), function () { - btn_create_vital_signs(frm); - }, "Create"); + create_vital_signs(frm); + }, 'Create'); frm.add_custom_button(__('Medical Record'), function () { create_medical_record(frm); - }, "Create"); + }, 'Create'); frm.add_custom_button(__('Patient Encounter'), function () { - btn_create_encounter(frm); - }, "Create"); + create_encounter(frm); + }, 'Create'); } }, onload: function (frm) { if(!frm.doc.dob){ - $(frm.fields_dict['age_html'].wrapper).html(""); + $(frm.fields_dict['age_html'].wrapper).html(''); } 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)); } } }); -frappe.ui.form.on("Patient", "dob", function(frm) { - if(frm.doc.dob) { - var today = new Date(); - var birthDate = new Date(frm.doc.dob); - if(today < birthDate){ - frappe.msgprint(__("Please select a valid Date")); - frappe.model.set_value(frm.doctype,frm.docname, "dob", ""); +frappe.ui.form.on('Patient', 'dob', function(frm) { + if (frm.doc.dob) { + let today = new Date(); + let birthDate = new Date(frm.doc.dob); + if (today < birthDate){ + frappe.msgprint(__('Please select a valid Date')); + frappe.model.set_value(frm.doctype,frm.docname, 'dob', ''); } - else{ - var age_str = get_age(frm.doc.dob); - $(frm.fields_dict['age_html'].wrapper).html("AGE : " + age_str); + else { + let age_str = get_age(frm.doc.dob); + $(frm.fields_dict['age_html'].wrapper).html('AGE : ' + age_str); } } else { - $(frm.fields_dict['age_html'].wrapper).html(""); + $(frm.fields_dict['age_html'].wrapper).html(''); } }); -var create_medical_record = function (frm) { - frappe.route_options = { - "patient": frm.doc.name, - "status": "Open", - "reference_doctype": "Patient Medical Record", - "reference_owner": frm.doc.owner - }; - frappe.new_doc("Patient Medical Record"); -}; - -var get_age = function (birth) { - var ageMS = Date.parse(Date()) - Date.parse(birth); - var age = new Date(); - age.setTime(ageMS); - var years = age.getFullYear() - 1970; - return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)"; -}; - -var btn_create_vital_signs = function (frm) { - if (!frm.doc.name) { - frappe.throw(__("Please save the patient first")); - } - frappe.route_options = { - "patient": frm.doc.name, - }; - frappe.new_doc("Vital Signs"); -}; - -var btn_create_encounter = function (frm) { - if (!frm.doc.name) { - frappe.throw(__("Please save the patient first")); - } - frappe.route_options = { - "patient": frm.doc.name, - }; - frappe.new_doc("Patient Encounter"); -}; - -var btn_invoice_registration = function (frm) { - frappe.call({ - doc: frm.doc, - method: "invoice_patient_registration", - callback: function(data){ - if(!data.exc){ - if(data.message.invoice){ - /* frappe.show_alert(__('Sales Invoice {0} created', - ['' + data.message.invoice+ ''])); */ - frappe.set_route("Form", "Sales Invoice", data.message.invoice); - } - cur_frm.reload_doc(); - } - } - }); -}; - frappe.ui.form.on('Patient Relation', { patient_relation_add: function(frm){ frm.fields_dict['patient_relation'].grid.get_field('patient').get_query = function(doc){ - var patient_list = []; + let patient_list = []; if(!doc.__islocal) patient_list.push(doc.name); $.each(doc.patient_relation, function(idx, val){ if (val.patient) patient_list.push(val.patient); @@ -133,3 +82,56 @@ frappe.ui.form.on('Patient Relation', { }; } }); + +let create_medical_record = function (frm) { + frappe.route_options = { + 'patient': frm.doc.name, + 'status': 'Open', + 'reference_doctype': 'Patient Medical Record', + 'reference_owner': frm.doc.owner + }; + frappe.new_doc('Patient Medical Record'); +}; + +let get_age = function (birth) { + let ageMS = Date.parse(Date()) - Date.parse(birth); + let age = new Date(); + age.setTime(ageMS); + let years = age.getFullYear() - 1970; + return years + ' Year(s) ' + age.getMonth() + ' Month(s) ' + age.getDate() + ' Day(s)'; +}; + +let create_vital_signs = function (frm) { + if (!frm.doc.name) { + frappe.throw(__('Please save the patient first')); + } + frappe.route_options = { + 'patient': frm.doc.name, + }; + frappe.new_doc('Vital Signs'); +}; + +let create_encounter = function (frm) { + if (!frm.doc.name) { + frappe.throw(__('Please save the patient first')); + } + frappe.route_options = { + 'patient': frm.doc.name, + }; + frappe.new_doc('Patient Encounter'); +}; + +let invoice_registration = function (frm) { + frappe.call({ + doc: frm.doc, + method: 'invoice_patient_registration', + callback: function(data) { + if (!data.exc) { + if (data.message.invoice) { + frappe.set_route('Form', 'Sales Invoice', data.message.invoice); + } + cur_frm.reload_doc(); + } + } + }); +}; diff --git a/erpnext/healthcare/doctype/patient/patient.json b/erpnext/healthcare/doctype/patient/patient.json index 0136f72f5be..4258e4011de 100644 --- a/erpnext/healthcare/doctype/patient/patient.json +++ b/erpnext/healthcare/doctype/patient/patient.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_copy": 1, "allow_import": 1, "allow_rename": 1, @@ -11,23 +12,29 @@ "engine": "InnoDB", "field_order": [ "basic_info", - "inpatient_status", - "inpatient_record", "naming_series", + "first_name", + "middle_name", + "last_name", "patient_name", "sex", "blood_group", "dob", "age_html", - "status", "image", "column_break_14", + "status", + "inpatient_status", + "inpatient_record", "customer", - "report_preference", "mobile", "email", "phone", - "disabled", + "report_preference", + "personal_and_social_history", + "occupation", + "column_break_25", + "marital_status", "sb_relation", "patient_relation", "allergy_medical_and_surgical_history", @@ -36,10 +43,6 @@ "column_break_20", "medical_history", "surgical_history", - "personal_and_social_history", - "occupation", - "column_break_25", - "marital_status", "risk_factors", "tobacco_past_use", "tobacco_current_use", @@ -78,28 +81,28 @@ { "fieldname": "naming_series", "fieldtype": "Select", - "label": "Patient ID", + "label": "Series", "options": "HLC-PAT-.YYYY.-", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "set_only_once": 1 }, { "bold": 1, "fieldname": "patient_name", "fieldtype": "Data", - "in_filter": 1, + "in_global_search": 1, + "in_list_view": 1, "in_standard_filter": 1, "label": "Full Name", - "no_copy": 1, - "oldfieldtype": "Data", - "reqd": 1, + "read_only": 1, "search_index": 1 }, { "fieldname": "sex", - "fieldtype": "Select", + "fieldtype": "Link", "label": "Gender", - "options": "\nMale\nFemale\nOther", + "options": "Gender", "reqd": 1 }, { @@ -125,15 +128,15 @@ "report_hide": 1 }, { - "default": "Active", "fieldname": "status", "fieldtype": "Select", - "hidden": 1, + "in_filter": 1, + "in_list_view": 1, "label": "Status", "no_copy": 1, - "options": "Active\nDormant\nOpen", + "options": "Active\nDisabled", "print_hide": 1, - "report_hide": 1 + "read_only": 1 }, { "fieldname": "image", @@ -149,12 +152,12 @@ "fieldtype": "Column Break" }, { + "description": "If \"Link Customer to Patient\" is checked in Healthcare Settings and an existing Customer is not selected then, a Customer will be created for this Patient for recording transactions in Accounts module.", "fieldname": "customer", "fieldtype": "Link", "ignore_user_permissions": 1, "label": "Customer", - "options": "Customer", - "set_only_once": 1 + "options": "Customer" }, { "fieldname": "report_preference", @@ -183,19 +186,8 @@ "fieldname": "phone", "fieldtype": "Data", "in_filter": 1, - "in_list_view": 1, "label": "Phone" }, - { - "default": "0", - "fieldname": "disabled", - "fieldtype": "Check", - "hidden": 1, - "label": "Disabled", - "no_copy": 1, - "print_hide": 1, - "report_hide": 1 - }, { "collapsible": 1, "fieldname": "sb_relation", @@ -276,25 +268,25 @@ "fieldname": "tobacco_past_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Tobacco Past Use" + "label": "Tobacco Consumption Habbits (Past)" }, { "fieldname": "tobacco_current_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Tobacco Current Use" + "label": "Tobacco Consumption Habbits (Present)" }, { "fieldname": "alcohol_past_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Alcohol Past Use" + "label": "Alcohol Consumption Habbits (Past)" }, { "fieldname": "alcohol_current_use", "fieldtype": "Data", "ignore_user_permissions": 1, - "label": "Alcohol Current Use" + "label": "Alcohol Consumption Habbits (Present)" }, { "fieldname": "column_break_32", @@ -342,12 +334,30 @@ "label": "Default Currency", "options": "Currency", "print_hide": 1 + }, + { + "fieldname": "last_name", + "fieldtype": "Data", + "label": "Last Name" + }, + { + "fieldname": "first_name", + "fieldtype": "Data", + "label": "First Name", + "oldfieldtype": "Data", + "reqd": 1 + }, + { + "fieldname": "middle_name", + "fieldtype": "Data", + "label": "Middle Name (optional)" } ], "icon": "fa fa-user", "image_field": "image", + "links": [], "max_attachments": 50, - "modified": "2019-09-25 23:30:49.905893", + "modified": "2020-04-06 12:55:30.807744", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient", diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py index e3eea96f859..e304a0bbc36 100644 --- a/erpnext/healthcare/doctype/patient/patient.py +++ b/erpnext/healthcare/doctype/patient/patient.py @@ -6,46 +6,57 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import cint, cstr, getdate, flt +from frappe.utils import cint, cstr, getdate import dateutil from frappe.model.naming import set_name_by_naming_series -from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account,send_registration_sms +from frappe.utils.nestedset import get_root_of +from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account, send_registration_sms class Patient(Document): - def after_insert(self): - if(frappe.db.get_value("Healthcare Settings", None, "manage_customer") == '1' and not self.customer): - create_customer(self) - if(frappe.db.get_value("Healthcare Settings", None, "collect_registration_fee") == '1'): - frappe.db.set_value("Patient", self.name, "disabled", 1) - else: - send_registration_sms(self) - self.reload() - - def on_update(self): + def validate(self): + self.set_full_name() self.add_as_website_user() + 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'): + frappe.db.set_value('Patient', self.name, 'status', 'Disabled') + else: + send_registration_sms(self) + + def set_full_name(self): + if self.last_name: + self.patient_name = ' '.join(filter(None, [self.first_name, self.last_name])) + else: + self.patient_name = self.first_name + def add_as_website_user(self): - if(self.email): - if not frappe.db.exists ("User", self.email): + if self.email: + if not frappe.db.exists ('User', self.email): user = frappe.get_doc({ - "doctype": "User", - "first_name": self.patient_name, - "email": self.email, - "user_type": "Website User" + 'doctype': 'User', + 'first_name': self.first_name, + 'last_name': self.last_name, + 'email': self.email, + 'user_type': 'Website User' }) user.flags.ignore_permissions = True - user.add_roles("Patient") + user.add_roles('Patient') def autoname(self): - patient_master_name = frappe.defaults.get_global_default('patient_master_name') - if patient_master_name == 'Patient Name': + patient_name_by = frappe.db.get_single_value('Healthcare Settings', 'patient_name_by') + if patient_name_by == 'Patient Name': self.name = self.get_patient_name() else: set_name_by_naming_series(self) def get_patient_name(self): + self.set_full_name() name = self.patient_name - if frappe.db.get_value("Patient", name): + if frappe.db.get_value('Patient', name): count = frappe.db.sql("""select ifnull(MAX(CAST(SUBSTRING_INDEX(name, ' ', -1) AS UNSIGNED)), 0) from tabPatient where name like %s""", "%{0} - %".format(name), as_list=1)[0][0] count = cint(count) + 1 @@ -54,56 +65,62 @@ class Patient(Document): return name def get_age(self): - age_str = "" + age_str = '' if self.dob: - born = getdate(self.dob) - age = dateutil.relativedelta.relativedelta(getdate(), born) - age_str = str(age.years) + " year(s) " + str(age.months) + " month(s) " + str(age.days) + " day(s)" + dob = getdate(self.dob) + age = dateutil.relativedelta.relativedelta(getdate(), dob) + age_str = str(age.years) + ' year(s) ' + str(age.months) + ' month(s) ' + str(age.days) + ' day(s)' return age_str def invoice_patient_registration(self): - frappe.db.set_value("Patient", self.name, "disabled", 0) - send_registration_sms(self) - if(flt(frappe.get_value("Healthcare Settings", None, "registration_fee"))>0): + if frappe.db.get_single_value('Healthcare Settings', 'registration_fee'): company = frappe.defaults.get_user_default('company') if not company: - company = frappe.db.get_value("Global Defaults", None, "default_company") + company = frappe.db.get_single_value('Global Defaults', 'default_company') + sales_invoice = make_invoice(self.name, company) sales_invoice.save(ignore_permissions=True) + frappe.db.set_value('Patient', self.name, 'status', 'Active') + send_registration_sms(self) + return {'invoice': sales_invoice.name} def create_customer(doc): - customer_group = frappe.get_value("Selling Settings", None, "customer_group") - territory = frappe.get_value("Selling Settings", None, "territory") + customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group') + territory = frappe.db.get_single_value('Selling Settings', 'territory') if not (customer_group and territory): - customer_group = "Commercial" - territory = "Rest Of The World" - frappe.msgprint(_("Please set default customer group and territory in Selling Settings"), alert=True) - customer = frappe.get_doc({"doctype": "Customer", - "customer_name": doc.patient_name, - "customer_group": customer_group, - "territory" : territory, - "customer_type": "Individual" - }).insert(ignore_permissions=True) - frappe.db.set_value("Patient", doc.name, "customer", customer.name) - frappe.msgprint(_("Customer {0} is created.").format(customer.name), alert=True) + customer_group = get_root_of('Customer Group') + territory = get_root_of('Territory') + frappe.msgprint(_('Please set default customer group and territory in Selling Settings'), alert=True) + + customer = frappe.get_doc({ + 'doctype': 'Customer', + 'customer_name': doc.patient_name, + 'customer_group': customer_group, + 'territory' : territory, + 'customer_type': 'Individual' + }).insert(ignore_permissions=True, ignore_mandatory=True) + + frappe.db.set_value('Patient', doc.name, 'customer', customer.name) + frappe.msgprint(_('Customer {0} is created.').format(customer.name), alert=True) def make_invoice(patient, company): - sales_invoice = frappe.new_doc("Sales Invoice") - sales_invoice.customer = frappe.get_value("Patient", patient, "customer") + uom = frappe.db.exists('UOM', 'Nos') or frappe.db.get_single_value('Stock Settings', 'stock_uom') + sales_invoice = frappe.new_doc('Sales Invoice') + sales_invoice.customer = frappe.db.get_value('Patient', patient, 'customer') sales_invoice.due_date = getdate() sales_invoice.company = company - sales_invoice.is_pos = '0' + sales_invoice.is_pos = 0 sales_invoice.debit_to = get_receivable_account(company) - item_line = sales_invoice.append("items") - item_line.item_name = "Registeration Fee" - item_line.description = "Registeration Fee" + item_line = sales_invoice.append('items') + item_line.item_name = 'Registeration Fee' + item_line.description = 'Registeration Fee' item_line.qty = 1 - item_line.uom = "Nos" + item_line.uom = uom item_line.conversion_factor = 1 item_line.income_account = get_income_account(None, company) - item_line.rate = frappe.get_value("Healthcare Settings", None, "registration_fee") + item_line.rate = frappe.db.get_single_value('Healthcare Settings', 'registration_fee') item_line.amount = item_line.rate sales_invoice.set_missing_values() return sales_invoice @@ -112,7 +129,7 @@ def make_invoice(patient, company): def get_patient_detail(patient): patient_dict = frappe.db.sql("""select * from tabPatient where name=%s""", (patient), as_dict=1) if not patient_dict: - frappe.throw(_("Patient not found")) + frappe.throw(_('Patient not found')) vital_sign = frappe.db.sql("""select * from `tabVital Signs` where patient=%s order by signs_date desc limit 1""", (patient), as_dict=1) diff --git a/erpnext/healthcare/doctype/patient/test_patient.py b/erpnext/healthcare/doctype/patient/test_patient.py index aebaa6b989b..9274b6f5e85 100644 --- a/erpnext/healthcare/doctype/patient/test_patient.py +++ b/erpnext/healthcare/doctype/patient/test_patient.py @@ -4,8 +4,31 @@ from __future__ import unicode_literals import unittest - -# test_records = frappe.get_test_records('Patient') +import frappe +from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient class TestPatient(unittest.TestCase): - pass + def test_customer_created(self): + frappe.db.sql("""delete from `tabPatient`""") + frappe.db.set_value('Healthcare Settings', None, 'link_customer_to_patient', 1) + patient = create_patient() + self.assertTrue(frappe.db.get_value('Patient', patient, 'customer')) + + def test_patient_registration(self): + frappe.db.sql("""delete from `tabPatient`""") + settings = frappe.get_single('Healthcare Settings') + settings.collect_registration_fee = 1 + settings.registration_fee = 500 + settings.save() + + patient = create_patient() + patient = frappe.get_doc('Patient', patient) + self.assertEqual(patient.status, 'Disabled') + + # check sales invoice and patient status + result = patient.invoice_patient_registration() + self.assertTrue(frappe.db.exists('Sales Invoice', result.get('invoice'))) + self.assertTrue(patient.status, 'Active') + + settings.collect_registration_fee = 0 + settings.save() diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index 858145eef3c..efa6b249b31 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -1,6 +1,6 @@ // Copyright (c) 2016, ESS LLP and contributors // For license information, please see license.txt -frappe.provide("erpnext.queries"); +frappe.provide('erpnext.queries'); frappe.ui.form.on('Patient Appointment', { setup: function(frm) { frm.custom_make_buttons = { @@ -8,151 +8,188 @@ frappe.ui.form.on('Patient Appointment', { 'Patient Encounter': 'Patient Encounter' }; }, + + onload: function(frm) { + if (frm.is_new()) { + frm.set_value('appointment_time', null); + frm.disable_save(); + } + }, + refresh: function(frm) { - frm.set_query("patient", function () { + frm.set_query('patient', function () { return { - filters: {"disabled": 0} + filters: {'status': 'Active'} }; }); - frm.set_query("practitioner", function() { + frm.set_query('practitioner', function() { return { filters: { 'department': frm.doc.department } }; }); - frm.set_query("service_unit", function(){ + frm.set_query('service_unit', function(){ return { filters: { - "is_group": false, - "allow_appointments": true + 'is_group': 0, + 'allow_appointments': 1 } }; }); - if(frm.doc.patient){ + + if (frm.is_new()) { + frm.page.set_primary_action(__('Check Availability'), function() { + if (!frm.doc.patient) { + frappe.msgprint({ + title: __('Not Allowed'), + message: __('Please select Patient first'), + indicator: 'red' + }); + } else { + frappe.call({ + method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.check_payment_fields_reqd', + args: {'patient': frm.doc.patient}, + callback: function(data) { + if (data.message == true) { + if (frm.doc.mode_of_payment && frm.doc.paid_amount) { + check_and_set_availability(frm); + } + if (!frm.doc.mode_of_payment) { + frappe.msgprint({ + title: __('Not Allowed'), + message: __('Please select a Mode of Payment first'), + indicator: 'red' + }); + } + if (!frm.doc.paid_amount) { + frappe.msgprint({ + title: __('Not Allowed'), + message: __('Please set the Paid Amount first'), + indicator: 'red' + }); + } + } else { + check_and_set_availability(frm); + } + } + }); + } + }); + } else { + frm.page.set_primary_action(__('Save'), () => frm.save()); + } + + if (frm.doc.patient) { frm.add_custom_button(__('Patient History'), function() { - frappe.route_options = {"patient": frm.doc.patient}; - frappe.set_route("patient_history"); - },__("View")); + frappe.route_options = {'patient': frm.doc.patient}; + frappe.set_route('patient_history'); + }, __('View')); } - if(frm.doc.status == "Open"){ + + if (frm.doc.status == 'Open' || (frm.doc.status == 'Scheduled' && !frm.doc.__islocal)) { frm.add_custom_button(__('Cancel'), function() { - btn_update_status(frm, "Cancelled"); + update_status(frm, 'Cancelled'); }); frm.add_custom_button(__('Reschedule'), function() { check_and_set_availability(frm); }); - if(frm.doc.procedure_template){ - frm.add_custom_button(__("Procedure"),function(){ - btn_create_procedure(frm); - },"Create"); - } - else{ - frm.add_custom_button(__("Patient Encounter"),function(){ - btn_create_encounter(frm); - },"Create"); + + if (frm.doc.procedure_template) { + frm.add_custom_button(__('Clinical Procedure'), function(){ + frappe.model.open_mapped_doc({ + method: 'erpnext.healthcare.doctype.clinical_procedure.clinical_procedure.make_procedure', + frm: frm, + }); + }, __('Create')); + } else { + frm.add_custom_button(__('Patient Encounter'), function() { + frappe.model.open_mapped_doc({ + method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.make_encounter', + frm: frm, + }); + }, __('Create')); } frm.add_custom_button(__('Vital Signs'), function() { - btn_create_vital_signs(frm); - },"Create"); + create_vital_signs(frm); + }, __('Create')); } - if(frm.doc.status == "Scheduled" && !frm.doc.__islocal){ - frm.add_custom_button(__('Cancel'), function() { - btn_update_status(frm, "Cancelled"); - }); - frm.add_custom_button(__('Reschedule'), function() { - check_and_set_availability(frm); - }); - if(frm.doc.procedure_template){ - frm.add_custom_button(__("Procedure"),function(){ - btn_create_procedure(frm); - },"Create"); - } - else{ - frm.add_custom_button(__("Patient Encounter"),function(){ - btn_create_encounter(frm); - },"Create"); - } + }, - frm.add_custom_button(__('Vital Signs'), function() { - btn_create_vital_signs(frm); - },"Create"); + patient: function(frm) { + if (frm.doc.patient) { + frm.trigger('toggle_payment_fields'); } - if(frm.doc.status == "Pending"){ - frm.add_custom_button(__('Set Open'), function() { - btn_update_status(frm, "Open"); - }); - frm.add_custom_button(__('Cancel'), function() { - btn_update_status(frm, "Cancelled"); - }); - } - frm.set_df_property("get_procedure_from_encounter", "read_only", frm.doc.__islocal ? 0 : 1); - frappe.db.get_value('Healthcare Settings', {name: 'Healthcare Settings'}, 'manage_appointment_invoice_automatically', (r) => { - if(r.manage_appointment_invoice_automatically == 1){ - frm.set_df_property("mode_of_payment", "hidden", 0); - frm.set_df_property("paid_amount", "hidden", 0); - frm.set_df_property("mode_of_payment", "reqd", 1); - frm.set_df_property("paid_amount", "reqd", 1); - } - else{ - frm.set_df_property("mode_of_payment", "hidden", 1); - frm.set_df_property("paid_amount", "hidden", 1); - frm.set_df_property("mode_of_payment", "reqd", 0); - frm.set_df_property("paid_amount", "reqd", 0); + }, + + get_procedure_from_encounter: function(frm) { + get_prescribed_procedure(frm); + }, + + toggle_payment_fields: function(frm) { + frappe.call({ + method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.check_payment_fields_reqd', + args: {'patient': frm.doc.patient}, + callback: function(data) { + if (data.message.fee_validity) { + // if fee validity exists and automated appointment invoicing is enabled, + // show payment fields as non-mandatory + frm.toggle_display('mode_of_payment', 0); + frm.toggle_display('paid_amount', 0); + frm.toggle_reqd('mode_of_payment', 0); + frm.toggle_reqd('paid_amount', 0); + } else { + // if automated appointment invoicing is disabled, hide fields + frm.toggle_display('mode_of_payment', data.message ? 1 : 0); + frm.toggle_display('paid_amount', data.message ? 1 : 0); + frm.toggle_reqd('mode_of_payment', data.message ? 1 : 0); + frm.toggle_reqd('paid_amount', data.message ? 1 :0); + } } }); - }, - check_availability: function(frm) { - check_and_set_availability(frm); - }, - onload:function(frm){ - if(frm.is_new()) { - frm.set_value("appointment_time", null); - frm.disable_save(); - } - }, - get_procedure_from_encounter: function(frm) { - get_procedure_prescribed(frm); } }); -var check_and_set_availability = function(frm) { - var selected_slot = null; - var service_unit = null; - var duration = null; +let check_and_set_availability = function(frm) { + let selected_slot = null; + let service_unit = null; + let duration = null; show_availability(); function show_empty_state(practitioner, appointment_date) { frappe.msgprint({ title: __('Not Available'), - message: __("Healthcare Practitioner {0} not available on {1}", [practitioner.bold(), appointment_date.bold()]), + message: __('Healthcare Practitioner {0} not available on {1}', [practitioner.bold(), appointment_date.bold()]), indicator: 'red' }); } function show_availability() { let selected_practitioner = ''; - var d = new frappe.ui.Dialog({ - title: __("Available slots"), + let d = new frappe.ui.Dialog({ + title: __('Available slots'), 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: '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: 'Date', reqd:1, fieldname: 'appointment_date', label: 'Date'}, + { fieldtype: 'Date', reqd: 1, fieldname: 'appointment_date', label: 'Date'}, { fieldtype: 'Section Break'}, { fieldtype: 'HTML', fieldname: 'available_slots'} + ], - primary_action_label: __("Book"), + primary_action_label: __('Book'), primary_action: function() { frm.set_value('appointment_time', selected_slot); - frm.set_value('service_unit', service_unit || ''); frm.set_value('duration', duration); frm.set_value('practitioner', d.get_value('practitioner')); frm.set_value('department', d.get_value('department')); frm.set_value('appointment_date', d.get_value('appointment_date')); + if (service_unit) { + frm.set_value('service_unit', service_unit); + } d.hide(); frm.enable_save(); frm.save(); @@ -167,16 +204,16 @@ var check_and_set_availability = function(frm) { 'appointment_date': frm.doc.appointment_date }); - d.fields_dict["department"].df.onchange = () => { + d.fields_dict['department'].df.onchange = () => { d.set_values({ 'practitioner': '' }); - var department = d.get_value('department'); - if(department){ + let department = d.get_value('department'); + if (department) { d.fields_dict.practitioner.get_query = function() { return { filters: { - "department": department + 'department': department } }; }; @@ -188,13 +225,13 @@ var check_and_set_availability = function(frm) { // Field Change Handler - var fd = d.fields_dict; + let fd = d.fields_dict; - d.fields_dict["appointment_date"].df.onchange = () => { + d.fields_dict['appointment_date'].df.onchange = () => { show_slots(d, fd); }; - d.fields_dict["practitioner"].df.onchange = () => { - if(d.get_value('practitioner') && d.get_value('practitioner') != selected_practitioner){ + d.fields_dict['practitioner'].df.onchange = () => { + if (d.get_value('practitioner') && d.get_value('practitioner') != selected_practitioner) { selected_practitioner = d.get_value('practitioner'); show_slots(d, fd); } @@ -203,8 +240,8 @@ var check_and_set_availability = function(frm) { } function show_slots(d, fd) { - if (d.get_value('appointment_date') && d.get_value('practitioner')){ - fd.available_slots.html(""); + if (d.get_value('appointment_date') && d.get_value('practitioner')) { + fd.available_slots.html(''); frappe.call({ method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_availability_data', args: { @@ -212,13 +249,13 @@ var check_and_set_availability = function(frm) { date: d.get_value('appointment_date') }, callback: (r) => { - var data = r.message; - if(data.slot_details.length > 0) { - var $wrapper = d.fields_dict.available_slots.$wrapper; + let data = r.message; + if (data.slot_details.length > 0) { + let $wrapper = d.fields_dict.available_slots.$wrapper; // make buttons for each slot - var slot_details = data.slot_details; - var slot_html = ""; + let slot_details = data.slot_details; + let slot_html = ''; for (let i = 0; i < slot_details.length; i++) { slot_html = slot_html + ``; slot_html = slot_html + `
` + slot_details[i].avail_slot.map(slot => { @@ -232,14 +269,14 @@ var check_and_set_availability = function(frm) { 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_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)){ + if (slot_start_time.isBefore(end_time) && slot_to_time.isAfter(booked_moment)) { // There is an overlap disabled = 'disabled="disabled"'; return false; @@ -263,7 +300,7 @@ var check_and_set_availability = function(frm) { // blue button when clicked $wrapper.on('click', 'button', function() { - var $btn = $(this); + let $btn = $(this); $wrapper.find('button').removeClass('btn-primary'); $btn.addClass('btn-primary'); selected_slot = $btn.attr('data-name'); @@ -273,48 +310,57 @@ var check_and_set_availability = function(frm) { d.get_primary_btn().attr('disabled', null); }); - }else { - // fd.available_slots.html("Please select a valid date.".bold()) + } else { + // fd.available_slots.html('Please select a valid date.'.bold()) show_empty_state(d.get_value('practitioner'), d.get_value('appointment_date')); } }, freeze: true, - freeze_message: __("Fetching records......") + freeze_message: __('Fetching records......') }); - }else{ - fd.available_slots.html("Appointment date and Healthcare Practitioner are Mandatory".bold()); + } else { + fd.available_slots.html(__('Appointment date and Healthcare Practitioner are Mandatory').bold()); } } }; -var get_procedure_prescribed = function(frm){ - if(frm.doc.patient){ +let get_prescribed_procedure = function(frm) { + if (frm.doc.patient) { 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}, - callback: function(r){ - show_procedure_templates(frm, r.message); + callback: function(r) { + if (r.message && r.message.length) { + show_procedure_templates(frm, r.message); + } else { + frappe.msgprint({ + title: __('Not Found'), + message: __('No Prescribed Procedures found for the selected Patient') + }); + } } }); - } - else{ - frappe.msgprint(__("Please select Patient to get prescribed procedure")); + } else { + frappe.msgprint({ + title: __('Not Allowed'), + message: __('Please select a Patient first') + }); } }; -var show_procedure_templates = function(frm, result){ - var d = new frappe.ui.Dialog({ - title: __("Prescribed Procedures"), +let show_procedure_templates = function(frm, result){ + let d = new frappe.ui.Dialog({ + title: __('Prescribed Procedures'), fields: [ { - fieldtype: "HTML", fieldname: "procedure_template" + fieldtype: 'HTML', fieldname: 'procedure_template' } ] }); - var html_field = d.fields_dict.procedure_template.$wrapper; + let html_field = d.fields_dict.procedure_template.$wrapper; html_field.empty(); - $.each(result, function(x, y){ - var row = $(repl('
\ + $.each(result, function(x, y) { + let row = $(repl('
\
%(encounter)s
%(consulting_practitioner)s
%(encounter_date)s
\
%(procedure_template)s
%(practitioner)s
%(date)s
\
\ @@ -326,76 +372,47 @@ var show_procedure_templates = function(frm, result){ 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() { - frm.doc.procedure_template = $(this).attr("data-procedure-template"); - frm.doc.procedure_prescription = $(this).attr("data-name"); - frm.doc.practitioner = $(this).attr("data-practitioner"); - frm.doc.appointment_date = $(this).attr("data-date"); - frm.doc.department = $(this).attr("data-department"); - refresh_field("procedure_template"); - refresh_field("procedure_prescription"); - refresh_field("appointment_date"); - refresh_field("practitioner"); - refresh_field("department"); + frm.doc.procedure_template = $(this).attr('data-procedure-template'); + frm.doc.procedure_prescription = $(this).attr('data-name'); + frm.doc.practitioner = $(this).attr('data-practitioner'); + frm.doc.appointment_date = $(this).attr('data-date'); + frm.doc.department = $(this).attr('data-department'); + refresh_field('procedure_template'); + refresh_field('procedure_prescription'); + refresh_field('appointment_date'); + refresh_field('practitioner'); + refresh_field('department'); d.hide(); return false; }); }); - if(!result){ - var msg = "There are no procedure prescribed for "+frm.doc.patient; + if (!result) { + let msg = __('There are no procedure prescribed for ') + frm.doc.patient; $(repl('
%(msg)s
', {msg: msg})).appendTo(html_field); } d.show(); }; -var btn_create_procedure = function(frm){ - var doc = frm.doc; - frappe.call({ - method:"erpnext.healthcare.doctype.clinical_procedure.clinical_procedure.create_procedure", - args: {appointment: doc.name}, - callback: function(data){ - if(!data.exc){ - var doclist = frappe.model.sync(data.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - } - }); -}; - -var btn_create_encounter = function(frm){ - var doc = frm.doc; - frappe.call({ - method:"erpnext.healthcare.doctype.patient_appointment.patient_appointment.create_encounter", - args: {appointment: doc.name}, - callback: function(data){ - if(!data.exc){ - var doclist = frappe.model.sync(data.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - } - }); -}; - -var btn_create_vital_signs = function (frm) { - if(!frm.doc.patient){ - frappe.throw(__("Please select patient")); +let create_vital_signs = function(frm) { + if (!frm.doc.patient) { + frappe.throw(__('Please select patient')); } frappe.route_options = { - "patient": frm.doc.patient, - "appointment": frm.doc.name, + 'patient': frm.doc.patient, + 'appointment': frm.doc.name, }; - frappe.new_doc("Vital Signs"); + frappe.new_doc('Vital Signs'); }; -var btn_update_status = function(frm, status){ - var doc = frm.doc; +let update_status = function(frm, status){ + let doc = frm.doc; frappe.confirm(__('Are you sure you want to cancel this appointment?'), function() { 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}, - callback: function(data){ - if(!data.exc){ + callback: function(data) { + if (!data.exc) { frm.reload_doc(); } } @@ -404,60 +421,60 @@ var btn_update_status = function(frm, status){ ); }; -frappe.ui.form.on("Patient Appointment", "practitioner", function(frm) { - if(frm.doc.practitioner){ +frappe.ui.form.on('Patient Appointment', 'practitioner', function(frm) { + if (frm.doc.practitioner) { frappe.call({ - "method": "frappe.client.get", + method: 'frappe.client.get', args: { - doctype: "Healthcare Practitioner", + doctype: 'Healthcare Practitioner', name: frm.doc.practitioner }, callback: function (data) { - frappe.model.set_value(frm.doctype,frm.docname, "department",data.message.department); - frappe.model.set_value(frm.doctype,frm.docname, "paid_amount",data.message.op_consulting_charge); + frappe.model.set_value(frm.doctype, frm.docname, 'department', data.message.department); + frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.op_consulting_charge); } }); } }); -frappe.ui.form.on("Patient Appointment", "patient", function(frm) { - if(frm.doc.patient){ +frappe.ui.form.on('Patient Appointment', 'patient', function(frm) { + if (frm.doc.patient) { frappe.call({ - "method": "frappe.client.get", + method: 'frappe.client.get', args: { - doctype: "Patient", + doctype: 'Patient', name: frm.doc.patient }, callback: function (data) { - var age = null; - if(data.message.dob){ + let age = null; + if (data.message.dob) { age = calculate_age(data.message.dob); } - frappe.model.set_value(frm.doctype,frm.docname, "patient_age", age); + frappe.model.set_value(frm.doctype,frm.docname, 'patient_age', age); } }); } }); -frappe.ui.form.on("Patient Appointment", "appointment_type", function(frm) { - if(frm.doc.appointment_type) { +frappe.ui.form.on('Patient Appointment', 'appointment_type', function(frm) { + if (frm.doc.appointment_type) { frappe.call({ - "method": "frappe.client.get", + method: 'frappe.client.get', args: { - doctype: "Appointment Type", + doctype: 'Appointment Type', name: frm.doc.appointment_type }, - callback: function (data) { - frappe.model.set_value(frm.doctype,frm.docname, "duration",data.message.default_duration); + callback: function(data) { + frappe.model.set_value(frm.doctype,frm.docname, 'duration',data.message.default_duration); } }); } }); -var calculate_age = function(birth) { - var ageMS = Date.parse(Date()) - Date.parse(birth); - var age = new Date(); +let calculate_age = function(birth) { + let ageMS = Date.parse(Date()) - Date.parse(birth); + let age = new Date(); age.setTime(ageMS); - var years = age.getFullYear() - 1970; - return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)"; + let years = age.getFullYear() - 1970; + return years + ' Year(s) ' + age.getMonth() + ' Month(s) ' + age.getDate() + ' Day(s)'; }; diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index ee9f013084b..7f9a671d47e 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -1,1177 +1,333 @@ { + "actions": [], "allow_copy": 1, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, "allow_import": 1, - "allow_rename": 0, - "autoname": "OP-.######", + "autoname": "naming_series:", "beta": 1, "creation": "2017-05-04 11:52:40.941507", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Document", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "naming_series", + "patient", + "patient_name", + "patient_sex", + "patient_age", + "inpatient_record", + "column_break_1", + "status", + "procedure_template", + "get_procedure_from_encounter", + "procedure_prescription", + "service_unit", + "section_break_12", + "practitioner", + "department", + "appointment_type", + "column_break_17", + "appointment_date", + "appointment_time", + "appointment_datetime", + "duration", + "section_break_16", + "mode_of_payment", + "paid_amount", + "company", + "column_break_2", + "invoiced", + "ref_sales_invoice", + "section_break_3", + "notes", + "referring_practitioner", + "reminded" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "patient.inpatient_record", "fieldname": "inpatient_record", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Inpatient Record", - "length": 0, - "no_copy": 0, "options": "Inpatient Record", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "inpatient_record.patient", "fieldname": "patient", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Patient", - "length": 0, - "no_copy": 0, "options": "Patient", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, "search_index": 1, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "appointment_type", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Appointment Type", - "length": 0, - "no_copy": 0, "options": "Appointment Type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "In Minutes", "fieldname": "duration", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Duration", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Duration (In Minutes)" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_1", "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Scheduled", "depends_on": "eval:!doc.__islocal", "fieldname": "status", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, + "in_list_view": 1, "label": "Status", - "length": 0, - "no_copy": 0, - "options": "\nScheduled\nOpen\nClosed\nPending\nCancelled", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, + "options": "\nScheduled\nOpen\nClosed\nCancelled", "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "procedure_template", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Procedure", - "length": 0, - "no_copy": 0, + "label": "Clinical Procedure Template", "options": "Clinical Procedure Template", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "depends_on": "eval:doc.__islocal && doc.patient", "fieldname": "get_procedure_from_encounter", "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Get prescribed procedures", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Get Prescribed Clinical Procedures" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "procedure_prescription", "fieldtype": "Link", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Procedure Prescription", - "length": 0, "no_copy": 1, "options": "Procedure Prescription", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "service_unit", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Service Unit", - "length": 0, - "no_copy": 0, "options": "Healthcare Service Unit", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.__islocal", - "fieldname": "check_availability", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Check availability", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_12", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "practitioner", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, "search_index": 1, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "department", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Department", - "length": 0, - "no_copy": 0, "options": "Medical Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, "search_index": 1, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_17", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "appointment_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "appointment_time", "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_16", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", "fetch_from": "patient.patient_name", "fieldname": "patient_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Patient Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "patient.sex", "fieldname": "patient_sex", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, + "fieldtype": "Link", "label": "Gender", - "length": 0, "no_copy": 1, - "options": "", - "permlevel": 0, - "precision": "", + "options": "Gender", "print_hide": 1, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_21", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "patient_age", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Patient Age", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "section_break_1", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "appointment_datetime", "fieldtype": "Datetime", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date TIme", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", + "label": "Appointment Datetime", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, + "read_only": 1, "report_hide": 1, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "mode_of_payment", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Mode of Payment", - "length": 0, - "no_copy": 0, - "options": "Mode of Payment", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Mode of Payment" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "paid_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Paid Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Paid Amount" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", "fieldname": "invoiced", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Invoiced", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "company", "fieldtype": "Link", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Company", - "length": 0, "no_copy": 1, "options": "Company", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "section_break_3", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Info", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "More Info" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "notes", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Notes", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Notes" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "referring_practitioner", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Referring Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "options": "Healthcare Practitioner" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", "fieldname": "reminded", "fieldtype": "Check", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Reminded", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 + }, + { + "fieldname": "ref_sales_invoice", + "fieldtype": "Link", + "label": "Reference Sales Invoice", + "options": "Sales Invoice", + "read_only": 1 + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "HLC-APP-.YYYY.-", + "set_only_once": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-14 13:24:01.529536", + "links": [], + "modified": "2020-03-27 11:27:33.773195", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Healthcare Administrator", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Physician", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Nursing User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Healthcare", "search_fields": "patient, practitioner, department, appointment_date, appointment_time", "show_name_in_global_search": 1, @@ -1179,6 +335,5 @@ "sort_order": "DESC", "title_field": "patient", "track_changes": 1, - "track_seen": 1, - "track_views": 0 -} + "track_seen": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 5f48c9ffe45..a2d9d0240f2 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -6,26 +6,42 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document import json -from frappe.utils import getdate, add_days, get_time +from frappe.utils import getdate, get_time +from frappe.model.mapper import get_mapped_doc from frappe import _ import datetime from frappe.core.doctype.sms_settings.sms_settings import send_sms 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.utils import validity_exists, service_item_and_practitioner_charge +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 class PatientAppointment(Document): - def on_update(self): - today = datetime.date.today() + def validate(self): + self.validate_overlaps() + self.set_appointment_datetime() + self.validate_customer_created() + self.set_status() + + def after_insert(self): + self.update_prescription_details() + invoice_appointment(self) + self.update_fee_validity() + send_confirmation_msg(self) + + def set_status(self): + today = getdate() appointment_date = getdate(self.appointment_date) - # If appointment created for today set as open - if today == appointment_date: - frappe.db.set_value("Patient Appointment", self.name, "status", "Open") - self.reload() + # If appointment is created for today set status as Open else Scheduled + if appointment_date == today: + self.status = 'Open' + elif appointment_date > today: + self.status = 'Scheduled' + + def validate_overlaps(self): + end_time = datetime.datetime.combine(getdate(self.appointment_date), get_time(self.appointment_time)) \ + + datetime.timedelta(minutes=float(self.duration)) - def validate(self): - end_time = datetime.datetime.combine(getdate(self.appointment_date), get_time(self.appointment_time)) + datetime.timedelta(minutes=float(self.duration)) overlaps = frappe.db.sql(""" select name, practitioner, patient, appointment_time, duration @@ -41,135 +57,153 @@ class PatientAppointment(Document): self.appointment_time, end_time.time(), self.appointment_time, end_time.time(), self.appointment_time)) if overlaps: - frappe.throw(_("""Appointment overlaps with {0}.
{1} has appointment scheduled - with {2} at {3} having {4} minute(s) duration.""").format(overlaps[0][0], overlaps[0][1], overlaps[0][2], overlaps[0][3], overlaps[0][4])) + overlapping_details = _('Appointment overlaps with ') + overlapping_details += "{0}
".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 after_insert(self): + def set_appointment_datetime(self): + self.appointment_datetime = "%s %s" % (self.appointment_date, self.appointment_time or "00:00:00") + + def validate_customer_created(self): + if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'): + if not frappe.db.get_value('Patient', self.patient, 'customer'): + msg = _("Please set a Customer linked to the Patient") + msg += " {0}".format(self.patient) + frappe.throw(msg, title=_('Customer Not Found')) + + def update_prescription_details(self): if self.procedure_prescription: - frappe.db.set_value("Procedure Prescription", self.procedure_prescription, "appointment_booked", True) + frappe.db.set_value('Procedure Prescription', self.procedure_prescription, 'appointment_booked', 1) if self.procedure_template: - comments = frappe.db.get_value("Procedure Prescription", self.procedure_prescription, "comments") + comments = frappe.db.get_value('Procedure Prescription', self.procedure_prescription, 'comments') if comments: - frappe.db.set_value("Patient Appointment", self.name, "notes", comments) - # Check fee validity exists - appointment = self - validity_exist = validity_exists(appointment.practitioner, appointment.patient) - if validity_exist: - fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0]) + frappe.db.set_value('Patient Appointment', self.name, 'notes', comments) - # Check if the validity is valid - appointment_date = getdate(appointment.appointment_date) - if (fee_validity.valid_till >= appointment_date) and (fee_validity.visited < fee_validity.max_visit): - visited = fee_validity.visited + 1 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) - if fee_validity.ref_invoice: - frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", True) - frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till)) - confirm_sms(self) + def update_fee_validity(self): + fee_validity = manage_fee_validity(self) + if fee_validity: + frappe.msgprint(_('{0} has fee validity till {1}').format(self.patient, fee_validity.valid_till)) - if frappe.db.get_value("Healthcare Settings", None, "manage_appointment_invoice_automatically") == '1' and \ - frappe.db.get_value("Patient Appointment", self.name, "invoiced") != 1: - invoice_appointment(self) @frappe.whitelist() -def invoice_appointment(appointment_doc): - if not appointment_doc.name: - return False - sales_invoice = frappe.new_doc("Sales Invoice") - sales_invoice.customer = frappe.get_value("Patient", appointment_doc.patient, "customer") - sales_invoice.appointment = appointment_doc.name - sales_invoice.due_date = getdate() - sales_invoice.is_pos = True - sales_invoice.company = appointment_doc.company - sales_invoice.debit_to = get_receivable_account(appointment_doc.company) - - item_line = sales_invoice.append("items") - service_item, practitioner_charge = service_item_and_practitioner_charge(appointment_doc) - item_line.item_code = service_item - item_line.description = "Consulting Charges: " + appointment_doc.practitioner - item_line.income_account = get_income_account(appointment_doc.practitioner, appointment_doc.company) - item_line.rate = practitioner_charge - item_line.amount = practitioner_charge - item_line.qty = 1 - item_line.reference_dt = "Patient Appointment" - item_line.reference_dn = appointment_doc.name - - payments_line = sales_invoice.append("payments") - payments_line.mode_of_payment = appointment_doc.mode_of_payment - payments_line.amount = appointment_doc.paid_amount - - sales_invoice.set_missing_values(for_validate = True) - - sales_invoice.save(ignore_permissions=True) - sales_invoice.submit() - frappe.msgprint(_("Sales Invoice {0} created as paid").format(sales_invoice.name), alert=True) - -def appointment_cancel(appointment_id): - appointment = frappe.get_doc("Patient Appointment", appointment_id) - # If invoiced --> fee_validity update with -1 visit - if appointment.invoiced: - sales_invoice = exists_sales_invoice(appointment) - if sales_invoice and cancel_sales_invoice(sales_invoice): - frappe.msgprint( - _("Appointment {0} and Sales Invoice {1} cancelled").format(appointment.name, sales_invoice.name) - ) - else: - validity = validity_exists(appointment.practitioner, appointment.patient) - if validity: - fee_validity = frappe.get_doc("Fee Validity", validity[0][0]) - if appointment_valid_in_fee_validity(appointment, fee_validity.valid_till, True, fee_validity.ref_invoice): - visited = fee_validity.visited - 1 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) - frappe.msgprint( - _("Appointment cancelled, Please review and cancel the invoice {0}").format(fee_validity.ref_invoice) - ) - else: - frappe.msgprint(_("Appointment cancelled")) - else: - frappe.msgprint(_("Appointment cancelled")) - else: - frappe.msgprint(_("Appointment cancelled")) - -def appointment_valid_in_fee_validity(appointment, valid_end_date, invoiced, ref_invoice): - valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") - max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") - valid_start_date = add_days(getdate(valid_end_date), -int(valid_days)) - - # Appointments which has same fee validity range with the appointment - appointments = frappe.get_list("Patient Appointment",{'patient': appointment.patient, 'invoiced': invoiced, - 'appointment_date':("<=", getdate(valid_end_date)), 'appointment_date':(">=", getdate(valid_start_date)), - 'practitioner': appointment.practitioner}, order_by="appointment_date desc", limit=int(max_visit)) - - if appointments and len(appointments) > 0: - appointment_obj = appointments[len(appointments)-1] - sales_invoice = exists_sales_invoice(appointment_obj) - if sales_invoice.name == ref_invoice: - return True +def check_payment_fields_reqd(patient): + automate_invoicing = frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing') + free_follow_ups = frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups') + if automate_invoicing: + if free_follow_ups: + fee_validity = frappe.db.exists('Fee Validity', {'patient': patient, 'status': 'Pending'}) + if fee_validity: + return {'fee_validity': fee_validity} + if check_is_new_patient(patient): + return False + return True return False +def invoice_appointment(appointment_doc): + automate_invoicing = frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing') + appointment_invoiced = frappe.db.get_value('Patient Appointment', appointment_doc.name, 'invoiced') + enable_free_follow_ups = frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups') + if enable_free_follow_ups: + fee_validity = check_fee_validity(appointment_doc) + if fee_validity and fee_validity.status == 'Completed': + fee_validity = None + elif not fee_validity: + if frappe.db.exists('Fee Validity Reference', {'appointment': appointment_doc.name}): + return + if check_is_new_patient(appointment_doc.patient, appointment_doc.name): + return + else: + fee_validity = None + + if automate_invoicing and not appointment_invoiced and not fee_validity: + sales_invoice = frappe.new_doc('Sales Invoice') + sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer') + sales_invoice.appointment = appointment_doc.name + sales_invoice.due_date = getdate() + sales_invoice.is_pos = 1 + sales_invoice.company = appointment_doc.company + sales_invoice.debit_to = get_receivable_account(appointment_doc.company) + + item = sales_invoice.append('items', {}) + item = get_appointment_item(appointment_doc, item) + + payment = sales_invoice.append('payments', {}) + payment.mode_of_payment = appointment_doc.mode_of_payment + payment.amount = appointment_doc.paid_amount + + sales_invoice.set_missing_values(for_validate=True) + sales_invoice.flags.ignore_mandatory = True + sales_invoice.save(ignore_permissions=True) + sales_invoice.submit() + frappe.msgprint(_('Sales Invoice {0} created as paid'.format(sales_invoice.name)), alert=True) + frappe.db.set_value('Patient Appointment', appointment_doc.name, 'invoiced', 1) + frappe.db.set_value('Patient Appointment', appointment_doc.name, 'ref_sales_invoice', sales_invoice.name) + + +def check_is_new_patient(patient, name=None): + filters = {'patient': patient, 'status': ('!=','Cancelled')} + if name: + filters['name'] = ('!=', name) + + has_previous_appointment = frappe.db.exists('Patient Appointment', filters) + if has_previous_appointment: + return False + return True + + +def get_appointment_item(appointment_doc, item): + service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment_doc) + item.item_code = service_item + item.description = _('Consulting Charges: {0}').format(appointment_doc.practitioner) + item.income_account = get_income_account(appointment_doc.practitioner, appointment_doc.company) + item.cost_center = frappe.get_cached_value('Company', appointment_doc.company, 'cost_center') + item.rate = practitioner_charge + item.amount = practitioner_charge + item.qty = 1 + item.reference_dt = 'Patient Appointment' + item.reference_dn = appointment_doc.name + return item + + +def cancel_appointment(appointment_id): + appointment = frappe.get_doc('Patient Appointment', appointment_id) + if appointment.invoiced: + sales_invoice = check_sales_invoice_exists(appointment) + if sales_invoice and cancel_sales_invoice(sales_invoice): + msg = _('Appointment {0} and Sales Invoice {1} cancelled').format(appointment.name, sales_invoice.name) + else: + msg = _('Appointment Cancelled. Please review and cancel the invoice {0}').format(sales_invoice.name) + else: + fee_validity = manage_fee_validity(appointment) + msg = _('Appointment Cancelled.') + if fee_validity: + msg += _('Fee Validity {0} updated.').format(fee_validity.name) + + frappe.msgprint(msg) + + def cancel_sales_invoice(sales_invoice): - if frappe.db.get_value("Healthcare Settings", None, "manage_appointment_invoice_automatically") == '1': + if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'): if len(sales_invoice.items) == 1: sales_invoice.cancel() return True return False -def exists_sales_invoice_item(appointment): - return frappe.db.exists( - "Sales Invoice Item", - { - "reference_dt": "Patient Appointment", - "reference_dn": appointment.name - } - ) -def exists_sales_invoice(appointment): - sales_item_exist = exists_sales_invoice_item(appointment) - if sales_item_exist: - sales_invoice = frappe.get_doc("Sales Invoice", frappe.db.get_value("Sales Invoice Item", sales_item_exist, "parent")) +def check_sales_invoice_exists(appointment): + sales_invoice = frappe.db.get_value('Sales Invoice Item', { + 'reference_dt': 'Patient Appointment', + 'reference_dn': appointment.name + }, 'parent') + + if sales_invoice: + sales_invoice = frappe.get_doc('Sales Invoice', sales_invoice) return sales_invoice return False + @frappe.whitelist() def get_availability_data(date, practitioner): """ @@ -180,169 +214,163 @@ def get_availability_data(date, practitioner): """ date = getdate(date) - weekday = date.strftime("%A") + weekday = date.strftime('%A') - available_slots = [] - slot_details = [] - practitioner_schedule = None + practitioner_doc = frappe.get_doc('Healthcare Practitioner', practitioner) + check_employee_wise_availability(date, practitioner_doc) + + if practitioner_doc.practitioner_schedules: + slot_details = get_available_slots(practitioner_doc, date) + else: + frappe.throw(_('{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner master').format( + practitioner), title=_('Practitioner Schedule Not Found')) + + + if not slot_details: + # TODO: return available slots in nearby dates + frappe.throw(_('Healthcare Practitioner not available on {0}').format(weekday), title=_('Not Available')) + + return {'slot_details': slot_details} + + +def check_employee_wise_availability(date, practitioner_doc): employee = None - - practitioner_obj = frappe.get_doc("Healthcare Practitioner", practitioner) - - # Get practitioner employee relation - if practitioner_obj.employee: - employee = practitioner_obj.employee - elif practitioner_obj.user_id: - if frappe.db.exists({ - "doctype": "Employee", - "user_id": practitioner_obj.user_id - }): - employee = frappe.get_doc("Employee", {"user_id": practitioner_obj.user_id}).name + if practitioner_doc.employee: + employee = practitioner_doc.employee + elif practitioner_doc.user_id: + employee = frappe.db.get_value('Employee', {'user_id': practitioner_doc.user_id}, 'name') if employee: - # Check if it is Holiday + # check holiday if is_holiday(employee, date): - frappe.throw(_("{0} is a company holiday").format(date)) + frappe.throw(_('{0} is a holiday'.format(date)), title=_('Not Available')) - # Check if He/She on Leave + # check leave status leave_record = frappe.db.sql("""select half_day from `tabLeave Application` where employee = %s and %s between from_date and to_date and docstatus = 1""", (employee, date), as_dict=True) if leave_record: if leave_record[0].half_day: - frappe.throw(_("{0} on Half day Leave on {1}").format(practitioner, date)) + frappe.throw(_('{0} is on a Half day Leave on {1}').format(practitioner_doc.name, date), title=_('Not Available')) else: - frappe.throw(_("{0} on Leave on {1}").format(practitioner, date)) + frappe.throw(_('{0} is on Leave on {1}').format(practitioner_doc.name, date), title=_('Not Available')) - # get practitioners schedule - if practitioner_obj.practitioner_schedules: - for schedule in practitioner_obj.practitioner_schedules: - if schedule.schedule: - practitioner_schedule = frappe.get_doc("Practitioner Schedule", schedule.schedule) - else: - frappe.throw(_("{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner master").format(practitioner)) - if practitioner_schedule: - available_slots = [] - for t in practitioner_schedule.time_slots: - if weekday == t.day: - available_slots.append(t) +def get_available_slots(practitioner_doc, date): + available_slots = [] + slot_details = [] + weekday = date.strftime('%A') + practitioner = practitioner_doc.name - if available_slots: - appointments = [] + for schedule_entry in practitioner_doc.practitioner_schedules: + if schedule_entry.schedule: + practitioner_schedule = frappe.get_doc('Practitioner Schedule', schedule_entry.schedule) + else: + frappe.throw(_('{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner').format( + frappe.bold(practitioner)), title=_('Practitioner Schedule Not Found')) - if schedule.service_unit: - slot_name = schedule.schedule+" - "+schedule.service_unit - allow_overlap = frappe.get_value('Healthcare Service Unit', schedule.service_unit, 'overlap_appointments') - if allow_overlap: - # fetch all appointments to practitioner by service unit - appointments = frappe.get_all( - "Patient Appointment", - filters={"practitioner": practitioner, "service_unit": schedule.service_unit, "appointment_date": date, "status": ["not in",["Cancelled"]]}, - fields=["name", "appointment_time", "duration", "status"]) - else: - # fetch all appointments to service unit - appointments = frappe.get_all( - "Patient Appointment", - filters={"service_unit": schedule.service_unit, "appointment_date": date, "status": ["not in",["Cancelled"]]}, - fields=["name", "appointment_time", "duration", "status"]) - else: - slot_name = schedule.schedule - # fetch all appointments to practitioner without service unit - appointments = frappe.get_all( - "Patient Appointment", - filters={"practitioner": practitioner, "service_unit": '', "appointment_date": date, "status": ["not in",["Cancelled"]]}, - fields=["name", "appointment_time", "duration", "status"]) + if practitioner_schedule: + available_slots = [] + for time_slot in practitioner_schedule.time_slots: + if weekday == time_slot.day: + available_slots.append(time_slot) - slot_details.append({"slot_name":slot_name, "service_unit":schedule.service_unit, - "avail_slot":available_slots, 'appointments': appointments}) + if available_slots: + appointments = [] + # fetch all appointments to practitioner by service unit + filters = { + 'practitioner': practitioner, + 'service_unit': schedule_entry.service_unit, + 'appointment_date': date, + 'status': ['not in',['Cancelled']] + } - else: - frappe.throw(_("{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner master").format(practitioner)) + if schedule_entry.service_unit: + slot_name = schedule_entry.schedule + ' - ' + schedule_entry.service_unit + allow_overlap = frappe.get_value('Healthcare Service Unit', schedule_entry.service_unit, 'overlap_appointments') + if not allow_overlap: + # fetch all appointments to service unit + filters.pop('practitioner') + else: + slot_name = schedule_entry.schedule + # fetch all appointments to practitioner without service unit + filters['practitioner'] = practitioner + filters.pop('service_unit') - if not available_slots and not slot_details: - # TODO: return available slots in nearby dates - frappe.throw(_("Healthcare Practitioner not available on {0}").format(weekday)) + appointments = frappe.get_all( + 'Patient Appointment', + filters=filters, + fields=['name', 'appointment_time', 'duration', 'status']) - return { - "slot_details": slot_details - } + slot_details.append({'slot_name':slot_name, 'service_unit':schedule_entry.service_unit, + 'avail_slot':available_slots, 'appointments': appointments}) + + return slot_details @frappe.whitelist() def update_status(appointment_id, status): - frappe.db.set_value("Patient Appointment", appointment_id, "status", status) + frappe.db.set_value('Patient Appointment', appointment_id, 'status', status) appointment_booked = True - if status == "Cancelled": + if status == 'Cancelled': appointment_booked = False - appointment_cancel(appointment_id) + cancel_appointment(appointment_id) - procedure_prescription = frappe.db.get_value("Patient Appointment", appointment_id, "procedure_prescription") + procedure_prescription = frappe.db.get_value('Patient Appointment', appointment_id, 'procedure_prescription') if procedure_prescription: - frappe.db.set_value("Procedure Prescription", procedure_prescription, "appointment_booked", appointment_booked) + frappe.db.set_value('Procedure Prescription', procedure_prescription, 'appointment_booked', appointment_booked) -@frappe.whitelist() -def set_open_appointments(): - today = getdate() - frappe.db.sql( - "update `tabPatient Appointment` set status='Open' where status = 'Scheduled'" - " and appointment_date = %s", today) - - -@frappe.whitelist() -def set_pending_appointments(): - today = getdate() - frappe.db.sql( - "update `tabPatient Appointment` set status='Pending' where status in " - "('Scheduled','Open') and appointment_date < %s", today) - - -def confirm_sms(doc): - if frappe.db.get_value("Healthcare Settings", None, "app_con") == '1': - message = frappe.db.get_value("Healthcare Settings", None, "app_con_msg") +def send_confirmation_msg(doc): + if frappe.db.get_single_value('Healthcare Settings', 'send_appointment_confirmation'): + message = frappe.db.get_single_value('Healthcare Settings', 'appointment_confirmation_msg') send_message(doc, message) + @frappe.whitelist() -def create_encounter(appointment): - appointment = frappe.get_doc("Patient Appointment", appointment) - encounter = frappe.new_doc("Patient Encounter") - encounter.appointment = appointment.name - encounter.patient = appointment.patient - encounter.practitioner = appointment.practitioner - encounter.visit_department = appointment.department - encounter.patient_sex = appointment.patient_sex - encounter.encounter_date = appointment.appointment_date - if appointment.invoiced: - encounter.invoiced = True - return encounter.as_dict() +def make_encounter(source_name, target_doc=None): + doc = get_mapped_doc('Patient Appointment', source_name, { + 'Patient Appointment': { + 'doctype': 'Patient Encounter', + 'field_map': [ + ['appointment', 'name'], + ['patient', 'patient'], + ['practitioner', 'practitioner'], + ['medical_department', 'department'], + ['patient_sex', 'patient_sex'], + ['encounter_date', 'appointment_date'], + ['invoiced', 'invoiced'] + ] + } + }, target_doc) + return doc -def remind_appointment(): - if frappe.db.get_value("Healthcare Settings", None, "app_rem") == '1': - rem_before = datetime.datetime.strptime(frappe.get_value("Healthcare Settings", None, "rem_before"), "%H:%M:%S") - rem_dt = datetime.datetime.now() + datetime.timedelta( - hours=rem_before.hour, minutes=rem_before.minute, seconds=rem_before.second) +def send_appointment_reminder(): + if frappe.db.get_single_value('Healthcare Settings', 'send_appointment_reminder'): + remind_before = datetime.datetime.strptime(frappe.db.get_single_value('Healthcare Settings', 'remind_before'), '%H:%M:%S') + reminder_dt = datetime.datetime.now() + datetime.timedelta( + hours=remind_before.hour, minutes=remind_before.minute, seconds=remind_before.second) - appointment_list = frappe.db.sql( - "select name from `tabPatient Appointment` where start_dt between %s and %s and reminded = 0 ", - (datetime.datetime.now(), rem_dt) - ) + appointment_list = frappe.db.get_all('Patient Appointment', { + 'appointment_datetime': ['between', (datetime.datetime.now(), reminder_dt)], + 'reminded': 0, + 'status': ['!=', 'Cancelled'] + }) - for i in range(0, len(appointment_list)): - doc = frappe.get_doc("Patient Appointment", appointment_list[i][0]) - message = frappe.db.get_value("Healthcare Settings", None, "app_rem_msg") + for appointment in appointment_list: + doc = frappe.get_doc('Patient Appointment', appointment.name) + message = frappe.db.get_single_value('Healthcare Settings', 'appointment_reminder_msg') send_message(doc, message) - frappe.db.set_value("Patient Appointment", doc.name, "reminded",1) - + frappe.db.set_value('Patient Appointment', doc.name, 'reminded', 1) def send_message(doc, message): - patient = frappe.get_doc("Patient", doc.patient) + patient = frappe.get_doc('Patient', doc.patient) if patient.mobile: - context = {"doc": doc, "alert": doc, "comments": None} - if doc.get("_comments"): - context["comments"] = json.loads(doc.get("_comments")) + context = {'doc': doc, 'alert': doc, 'comments': None} + if doc.get('_comments'): + context['comments'] = json.loads(doc.get('_comments')) # jinja to string convertion happens here message = frappe.render_template(message, context) @@ -359,7 +387,7 @@ def get_events(start, end, filters=None): :param filters: Filters (JSON). """ from frappe.desk.calendar import get_event_conditions - conditions = get_event_conditions("Patient Appointment", filters) + conditions = get_event_conditions('Patient Appointment', filters) data = frappe.db.sql(""" select @@ -381,10 +409,21 @@ def get_events(start, end, filters=None): return data + @frappe.whitelist() def get_procedure_prescribed(patient): return frappe.db.sql("""select pp.name, pp.procedure, pp.parent, ct.practitioner, ct.encounter_date, pp.practitioner, pp.date, pp.department from `tabPatient Encounter` ct, `tabProcedure Prescription` pp where ct.patient=%(patient)s and pp.parent=ct.name and pp.appointment_booked=0 - order by ct.creation desc""", {"patient": patient}) + order by ct.creation desc""", {'patient': patient}) + + +def update_appointment_status(): + # update the status of appointments daily + appointments = frappe.get_all('Patient Appointment', { + 'status': ('not in', ['Closed', 'Cancelled']) + }, as_dict=1) + + for appointment in appointments: + frappe.get_doc('Patient Appointment', appointment.name).set_status() diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_list.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_list.js index 701cb69806e..721887b4593 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_list.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_list.js @@ -3,4 +3,14 @@ */ frappe.listview_settings['Patient Appointment'] = { filters: [["status", "=", "Open"]], + get_indicator: function(doc) { + var colors = { + "Open": "orange", + "Scheduled": "yellow", + "Closed": "green", + "Cancelled": "red", + "Expired": "grey" + }; + return [__(doc.status), colors[doc.status], "status,=," + doc.status]; + } }; diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py index 3536a5f9513..7075af5d00c 100644 --- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py @@ -3,8 +3,144 @@ # See license.txt from __future__ import unicode_literals import unittest - -# test_records = frappe.get_test_records('Patient Appointment') +import frappe +from erpnext.healthcare.doctype.patient_appointment.patient_appointment import update_status +from frappe.utils import nowdate, add_days +from frappe.utils.make_random import get_random class TestPatientAppointment(unittest.TestCase): - pass + def setUp(self): + frappe.db.sql("""delete from `tabPatient Appointment`""") + frappe.db.sql("""delete from `tabFee Validity""") + + def test_status(self): + patient, medical_department, practitioner = create_healthcare_docs() + frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0) + appointment = create_appointment(patient, practitioner, nowdate()) + self.assertEquals(appointment.status, 'Open') + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2)) + self.assertEquals(appointment.status, 'Scheduled') + create_encounter(appointment) + self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed') + + def test_invoicing(self): + patient, medical_department, practitioner = create_healthcare_docs() + frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0) + frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0) + appointment = create_appointment(patient, practitioner, nowdate()) + self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 0) + + frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), invoice=1) + self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 1) + self.assertTrue(frappe.db.get_value('Patient Appointment', appointment.name, 'ref_sales_invoice')) + + def test_appointment_cancel(self): + patient, medical_department, practitioner = create_healthcare_docs() + frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 1) + appointment = create_appointment(patient, practitioner, nowdate()) + fee_validity = frappe.db.get_value('Fee Validity Reference', {'appointment': appointment.name}, 'parent') + # fee validity created + self.assertTrue(fee_validity) + + visited = frappe.db.get_value('Fee Validity', fee_validity, 'visited') + update_status(appointment.name, 'Cancelled') + # check fee validity updated + self.assertEqual(frappe.db.get_value('Fee Validity', fee_validity, 'visited'), visited - 1) + + frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0) + frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) + appointment = create_appointment(patient, practitioner, nowdate(), invoice=1) + update_status(appointment.name, 'Cancelled') + # check invoice cancelled + sales_invoice = frappe.db.get_value('Patient Appointment', appointment.name, 'ref_sales_invoice') + self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice, 'status'), 'Cancelled') + + +def create_healthcare_docs(): + patient = create_patient() + practitioner = frappe.db.exists('Healthcare Practitioner', '_Test Healthcare Practitioner') + medical_department = frappe.db.exists('Medical Department', '_Test Medical Department') + + if not medical_department: + medical_department = frappe.new_doc('Medical Department') + medical_department.department = '_Test Medical Department' + medical_department.save(ignore_permissions=True) + medical_department = medical_department.name + + if not practitioner: + practitioner = frappe.new_doc('Healthcare Practitioner') + practitioner.first_name = '_Test Healthcare Practitioner' + practitioner.gender = 'Female' + practitioner.department = medical_department + practitioner.op_consulting_charge = 500 + practitioner.inpatient_visit_charge = 500 + practitioner.save(ignore_permissions=True) + practitioner = practitioner.name + + return patient, medical_department, practitioner + +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=None): + encounter = frappe.new_doc('Patient Encounter') + if appointment: + encounter.appointment = appointment.name + encounter.patient = appointment.patient + encounter.practitioner = appointment.practitioner + encounter.encounter_date = appointment.appointment_date + encounter.encounter_time = appointment.appointment_time + encounter.save() + encounter.submit() + return encounter + +def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0): + item = create_healthcare_service_items() + frappe.db.set_value('Healthcare Settings', None, 'inpatient_visit_charge_item', item) + frappe.db.set_value('Healthcare Settings', None, 'op_consulting_charge_item', item) + appointment = frappe.new_doc('Patient Appointment') + appointment.patient = patient + appointment.practitioner = practitioner + appointment.department = '_Test Medical Department' + appointment.appointment_date = appointment_date + appointment.company = '_Test Company' + appointment.duration = 15 + if invoice: + appointment.mode_of_payment = 'Cash' + appointment.paid_amount = 500 + if procedure_template: + appointment.procedure_template = create_clinical_procedure_template().get('name') + appointment.save(ignore_permissions=True) + return appointment + +def create_healthcare_service_items(): + if frappe.db.exists('Item', 'HLC-SI-001'): + return 'HLC-SI-001' + item = frappe.new_doc('Item') + item.item_code = 'HLC-SI-001' + item.item_name = 'Consulting Charges' + item.item_group = 'Services' + item.is_stock_item = 0 + item.save() + return item.name + +def create_clinical_procedure_template(): + if frappe.db.exists('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.template = 'Knee Surgery and Rehab' + template.item_code = 'Knee Surgery and Rehab' + template.item_group = 'Services' + template.is_billable = 1 + template.description = 'Knee Surgery and Rehab' + template.rate = 50000 + template.save() + return template \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 088bc8161b1..83c5d2be9c0 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -19,297 +19,237 @@ frappe.ui.form.on('Patient Encounter', { refresh: function(frm) { refresh_field('drug_prescription'); refresh_field('lab_test_prescription'); - if (!frm.doc.__islocal){ - frappe.call({ - method: 'frappe.client.get_value', - args: { - doctype: 'Patient', - fieldname: 'inpatient_status', - filters: {name: frm.doc.patient} - }, - callback: function(data) { - if(data.message && data.message.inpatient_status == "Admission Scheduled" || data.message.inpatient_status == "Admitted"){ - frm.add_custom_button(__('Schedule Discharge'), function() { - schedule_discharge(frm); - }); - } - else if(data.message.inpatient_status != "Discharge Scheduled"){ - frm.add_custom_button(__('Schedule Admission'), function() { - schedule_inpatient(frm); - }); - } - } - }); - } - frm.add_custom_button(__('Patient History'), function() { - if (frm.doc.patient) { - frappe.route_options = {"patient": frm.doc.patient}; - frappe.set_route("patient_history"); - } else { - frappe.msgprint(__("Please select Patient")); + + if (!frm.doc.__islocal) { + + if (frm.doc.inpatient_status == 'Admission Scheduled' || frm.doc.inpatient_status == 'Admitted') { + frm.add_custom_button(__('Schedule Discharge'), function() { + schedule_discharge(frm); + }); + } else if (frm.doc.inpatient_status != 'Discharge Scheduled') { + frm.add_custom_button(__('Schedule Admission'), function() { + schedule_inpatient(frm); + }); } - },"View"); - frm.add_custom_button(__('Vital Signs'), function() { - btn_create_vital_signs(frm); - },"Create"); - frm.add_custom_button(__('Medical Record'), function() { - create_medical_record(frm); - },"Create"); - frm.add_custom_button(__("Procedure"),function(){ - btn_create_procedure(frm); - },"Create"); + frm.add_custom_button(__('Patient History'), function() { + if (frm.doc.patient) { + frappe.route_options = {'patient': frm.doc.patient}; + frappe.set_route('patient_history'); + } else { + frappe.msgprint(__('Please select Patient')); + } + },'View'); - frm.set_query("patient", function () { + frm.add_custom_button(__('Vital Signs'), function() { + create_vital_signs(frm); + },'Create'); + + frm.add_custom_button(__('Medical Record'), function() { + create_medical_record(frm); + },'Create'); + + frm.add_custom_button(__('Clinical Procedure'), function() { + create_procedure(frm); + },'Create'); + + } + + frm.set_query('patient', function() { return { - filters: {"disabled": 0} + filters: {'status': 'Active'} }; }); - frm.set_query("drug_code", "drug_prescription", function() { + + frm.set_query('drug_code', 'drug_prescription', function() { return { filters: { - is_stock_item:'1' + is_stock_item: 1 } }; }); - frm.set_query("lab_test_code", "lab_test_prescription", function() { + + frm.set_query('lab_test_code', 'lab_test_prescription', function() { return { filters: { - is_billable:'1' + is_billable: 1 } }; }); - frm.set_query("medical_code", "codification_table", function() { - return { - filters: { - medical_code_standard: frappe.defaults.get_default("default_medical_code_standard") - } - }; - }); - frm.set_query("appointment", function() { + + frm.set_query('appointment', function() { return { filters: { // Scheduled filter for demo ... - status:['in',["Open","Scheduled"]] + status:['in',['Open','Scheduled']] } }; }); - frm.set_df_property("appointment", "read_only", frm.doc.__islocal ? 0:1); - frm.set_df_property("patient", "read_only", frm.doc.__islocal ? 0:1); - frm.set_df_property("patient_age", "read_only", frm.doc.__islocal ? 0:1); - frm.set_df_property("patient_sex", "read_only", frm.doc.__islocal ? 0:1); - frm.set_df_property("type", "read_only", frm.doc.__islocal ? 0:1); - frm.set_df_property("practitioner", "read_only", frm.doc.__islocal ? 0:1); - frm.set_df_property("visit_department", "read_only", frm.doc.__islocal ? 0:1); - frm.set_df_property("encounter_date", "read_only", frm.doc.__islocal ? 0:1); - frm.set_df_property("encounter_time", "read_only", frm.doc.__islocal ? 0:1); - } -}); -var schedule_inpatient = function(frm) { - frappe.call({ - method: "erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_inpatient", - args: {patient: frm.doc.patient, encounter_id: frm.doc.name, practitioner: frm.doc.practitioner}, - callback: function(data) { - if(!data.exc){ - frm.reload_doc(); - } - }, - freeze: true, - freeze_message: "Process Inpatient Scheduling" - }); -}; + frm.set_df_property('patient', 'read_only', frm.doc.appointment ? 1 : 0); + }, -var schedule_discharge = function(frm) { - frappe.call({ - method: "erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_discharge", - args: {patient: frm.doc.patient, encounter_id: frm.doc.name, practitioner: frm.doc.practitioner}, - callback: function(data) { - if(!data.exc){ - frm.reload_doc(); - } - }, - freeze: true, - freeze_message: "Process Discharge" - }); -}; + appointment: function(frm) { + frm.events.set_appointment_fields(frm); + }, -var create_medical_record = function (frm) { - if(!frm.doc.patient){ - frappe.throw(__("Please select patient")); - } - frappe.route_options = { - "patient": frm.doc.patient, - "status": "Open", - "reference_doctype": "Patient Medical Record", - "reference_owner": frm.doc.owner - }; - frappe.new_doc("Patient Medical Record"); -}; + patient: function(frm) { + frm.events.set_patient_info(frm); + }, -var btn_create_vital_signs = function (frm) { - if(!frm.doc.patient){ - frappe.throw(__("Please select patient")); - } - frappe.route_options = { - "patient": frm.doc.patient, - "appointment": frm.doc.appointment - }; - frappe.new_doc("Vital Signs"); -}; - -var btn_create_procedure = function (frm) { - if(!frm.doc.patient){ - frappe.throw(__("Please select patient")); - } - frappe.route_options = { - "patient": frm.doc.patient, - "medical_department": frm.doc.visit_department - }; - frappe.new_doc("Clinical Procedure"); -}; - -frappe.ui.form.on("Patient Encounter", "appointment", function(frm){ - if(frm.doc.appointment){ - frappe.call({ - "method": "frappe.client.get", - args: { - doctype: "Patient Appointment", - name: frm.doc.appointment - }, - callback: function (data) { - frappe.model.set_value(frm.doctype,frm.docname, "patient", data.message.patient); - frappe.model.set_value(frm.doctype,frm.docname, "type", data.message.appointment_type); - frappe.model.set_value(frm.doctype,frm.docname, "practitioner", data.message.practitioner); - frappe.model.set_value(frm.doctype,frm.docname, "invoiced", data.message.invoiced); - } - }); - } - else{ - frappe.model.set_value(frm.doctype,frm.docname, "patient", ""); - frappe.model.set_value(frm.doctype,frm.docname, "type", ""); - frappe.model.set_value(frm.doctype,frm.docname, "practitioner", ""); - frappe.model.set_value(frm.doctype,frm.docname, "invoiced", 0); - } -}); - -frappe.ui.form.on("Patient Encounter", "practitioner", function(frm) { - if(frm.doc.practitioner){ - frappe.call({ - "method": "frappe.client.get", - args: { - doctype: "Healthcare Practitioner", - name: frm.doc.practitioner - }, - callback: function (data) { - frappe.model.set_value(frm.doctype,frm.docname, "visit_department",data.message.department); - } - }); - } -}); - -frappe.ui.form.on("Patient Encounter", "symptoms_select", function(frm) { - if(frm.doc.symptoms_select){ - var symptoms = null; - if(frm.doc.symptoms) - symptoms = frm.doc.symptoms + "\n" +frm.doc.symptoms_select; - else - symptoms = frm.doc.symptoms_select; - frappe.model.set_value(frm.doctype,frm.docname, "symptoms", symptoms); - frappe.model.set_value(frm.doctype,frm.docname, "symptoms_select", null); - } -}); -frappe.ui.form.on("Patient Encounter", "diagnosis_select", function(frm) { - if(frm.doc.diagnosis_select){ - var diagnosis = null; - if(frm.doc.diagnosis) - diagnosis = frm.doc.diagnosis + "\n" +frm.doc.diagnosis_select; - else - diagnosis = frm.doc.diagnosis_select; - frappe.model.set_value(frm.doctype,frm.docname, "diagnosis", diagnosis); - frappe.model.set_value(frm.doctype,frm.docname, "diagnosis_select", null); - } -}); - -frappe.ui.form.on("Patient Encounter", "patient", function(frm) { - if(frm.doc.patient){ - frappe.call({ - "method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail", - args: { - patient: frm.doc.patient - }, - callback: function (data) { - var age = ""; - if(data.message.dob){ - age = calculate_age(data.message.dob); - } - frappe.model.set_value(frm.doctype,frm.docname, "patient_age", age); - frappe.model.set_value(frm.doctype,frm.docname, "patient_sex", data.message.sex); - } - }); - } -}); - -frappe.ui.form.on("Drug Prescription", { - drug_code: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - if(child.drug_code){ + set_appointment_fields: function(frm) { + if (frm.doc.appointment) { frappe.call({ - "method": "frappe.client.get", + method: 'frappe.client.get', args: { - doctype: "Item", - name: child.drug_code, + doctype: 'Patient Appointment', + name: frm.doc.appointment }, - callback: function (data) { - frappe.model.set_value(cdt, cdn, 'drug_name',data.message.item_name); + callback: function(data) { + let values = { + 'patient':data.message.patient, + 'type': data.message.appointment_type, + 'practitioner': data.message.practitioner, + 'invoiced': data.message.invoiced + }; + frm.set_value(values); } }); } + else { + let values = { + 'patient': '', + 'patient_name': '', + 'type': '', + 'practitioner': '', + 'invoiced': 0, + 'patient_sex': '', + 'patient_age': '', + 'inpatient_record': '', + 'inpatient_status': '' + }; + frm.set_value(values); + } }, + + set_patient_info: function(frm) { + if (frm.doc.patient) { + frappe.call({ + method: 'erpnext.healthcare.doctype.patient.patient.get_patient_detail', + args: { + patient: frm.doc.patient + }, + callback: function(data) { + let age = ''; + if (data.message.dob) { + age = calculate_age(data.message.dob); + } + frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', age); + frappe.model.set_value(frm.doctype, frm.docname, 'patient_sex', data.message.sex); + if (data.message.inpatient_record) { + frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_record', data.message.inpatient_record); + frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_status', data.message.inpatient_status); + } + } + }); + } else { + frappe.model.set_value(frm.doctype, frm.docname, 'patient_sex', ''); + frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', ''); + frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_record', ''); + frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_status', ''); + } + } +}); + +let schedule_inpatient = function(frm) { + frappe.call({ + method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_inpatient', + args: {patient: frm.doc.patient, encounter_id: frm.doc.name, practitioner: frm.doc.practitioner}, + callback: function(data) { + if (!data.exc) { + frm.reload_doc(); + } + }, + freeze: true, + freeze_message: __('Process Inpatient Scheduling') + }); +}; + +let schedule_discharge = function(frm) { + frappe.call({ + method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_discharge', + args: {patient: frm.doc.patient, encounter_id: frm.doc.name, practitioner: frm.doc.practitioner}, + callback: function(data) { + if (!data.exc) { + frm.reload_doc(); + } + }, + freeze: true, + freeze_message: 'Process Discharge' + }); +}; + +let create_medical_record = function (frm) { + if (!frm.doc.patient) { + frappe.throw(__('Please select patient')); + } + frappe.route_options = { + 'patient': frm.doc.patient, + 'status': 'Open', + 'reference_doctype': 'Patient Medical Record', + 'reference_owner': frm.doc.owner + }; + frappe.new_doc('Patient Medical Record'); +}; + +let create_vital_signs = function (frm) { + if (!frm.doc.patient) { + frappe.throw(__('Please select patient')); + } + frappe.route_options = { + 'patient': frm.doc.patient, + 'appointment': frm.doc.appointment, + 'encounter': frm.doc.name + }; + frappe.new_doc('Vital Signs'); +}; + +let create_procedure = function (frm) { + if (!frm.doc.patient) { + frappe.throw(__('Please select patient')); + } + frappe.route_options = { + 'patient': frm.doc.patient, + 'medical_department': frm.doc.medical_department + }; + frappe.new_doc('Clinical Procedure'); +}; + +frappe.ui.form.on('Drug Prescription', { dosage: function(frm, cdt, cdn){ frappe.model.set_value(cdt, cdn, 'update_schedule', 1); - var child = locals[cdt][cdn]; - if(child.dosage){ - frappe.model.set_value(cdt, cdn, 'in_every', 'Day'); + let child = locals[cdt][cdn]; + if (child.dosage) { + frappe.model.set_value(cdt, cdn, 'interval_uom', 'Day'); frappe.model.set_value(cdt, cdn, 'interval', 1); } }, - period: function(frm, cdt, cdn){ + period: function(frm, cdt, cdn) { frappe.model.set_value(cdt, cdn, 'update_schedule', 1); }, - in_every: function(frm, cdt, cdn){ + interval_uom: function(frm, cdt, cdn) { frappe.model.set_value(cdt, cdn, 'update_schedule', 1); - var child = locals[cdt][cdn]; - if(child.in_every == "Hour"){ + let child = locals[cdt][cdn]; + if (child.interval_uom == 'Hour') { frappe.model.set_value(cdt, cdn, 'dosage', null); } } }); -frappe.ui.form.on("Procedure Prescription", { - procedure: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - if(child.procedure){ - frappe.call({ - "method": "frappe.client.get_value", - args: { - doctype: "Clinical Procedure Template", - fieldname: "medical_department", - filters: {name: child.procedure} - }, - callback: function (data) { - frappe.model.set_value(cdt, cdn, 'department',data.message.medical_department); - } - }); - } - } -}); - - -var calculate_age = function(birth) { - var ageMS = Date.parse(Date()) - Date.parse(birth); - var age = new Date(); +let calculate_age = function(birth) { + let ageMS = Date.parse(Date()) - Date.parse(birth); + let age = new Date(); age.setTime(ageMS); - var years = age.getFullYear() - 1970; - return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)"; + let years = age.getFullYear() - 1970; + return years + ' Year(s) ' + age.getMonth() + ' Month(s) ' + age.getDate() + ' Day(s)'; }; diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index 91c9d7bcf2c..d00e7bc7dd2 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -1,1183 +1,324 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 1, - "creation": "2016-04-21 10:53:44.637684", - "custom": 0, - "default_print_format": "", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, + "actions": [], + "allow_copy": 1, + "allow_import": 1, + "autoname": "naming_series:", + "beta": 1, + "creation": "2016-04-21 10:53:44.637684", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "naming_series", + "appointment", + "appointment_type", + "patient", + "patient_name", + "patient_sex", + "patient_age", + "company", + "column_break_6", + "practitioner", + "medical_department", + "encounter_date", + "encounter_time", + "invoiced", + "section_break_1", + "inpatient_record", + "column_break_17", + "inpatient_status", + "sb_symptoms", + "symptoms", + "symptoms_in_print", + "physical_examination", + "diagnosis", + "diagnosis_in_print", + "codification", + "codification_table", + "sb_drug_prescription", + "drug_prescription", + "sb_test_prescription", + "lab_test_prescription", + "sb_procedures", + "procedure_prescription", + "encounter_comment", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.inpatient_record", - "fieldname": "inpatient_record", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Inpatient Record", - "length": 0, - "no_copy": 0, - "options": "Inpatient Record", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "inpatient_record", + "fieldtype": "Link", + "label": "Inpatient Record", + "options": "Inpatient Record", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_1", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "section_break_1", + "fieldtype": "Section Break", + "label": "Inpatient Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 0, - "options": "HLC-ENC-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "HLC-ENC-.YYYY.-", + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "appointment", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Appointment", - "length": 0, - "no_copy": 0, - "options": "Patient Appointment", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "appointment", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Appointment", + "options": "Patient Appointment", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "type", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Type", - "length": 0, - "no_copy": 1, - "options": "Appointment Type", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "inpatient_record.patient", + "fieldname": "patient", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Patient", + "options": "Patient", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "inpatient_record.patient", - "fieldname": "patient", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Patient", - "length": 0, - "no_copy": 0, - "options": "Patient", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "patient.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "label": "Patient Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_from": "patient.patient_name", - "fieldname": "patient_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Patient Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "patient_age", + "fieldtype": "Data", + "label": "Age", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient_age", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Age", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "patient_sex", + "fieldtype": "Link", + "label": "Gender", + "options": "Gender", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient_sex", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Gender", - "length": 0, - "no_copy": 0, - "options": "\nMale\nFemale\nOther", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 1, + "label": "Company", + "options": "Company" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "practitioner", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "practitioner", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "encounter_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Encounter Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "visit_department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Medical Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "encounter_time", + "fieldtype": "Time", + "label": "Encounter Time", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "encounter_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Encounter Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "invoiced", + "fieldtype": "Check", + "label": "Invoiced", + "no_copy": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "encounter_time", - "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Encounter Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sb_symptoms", + "fieldtype": "Section Break", + "label": "Encounter Impression" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "invoiced", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Invoiced", - "length": 0, - "no_copy": 1, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "symptoms", + "fieldtype": "Table MultiSelect", + "ignore_xss_filter": 1, + "label": "Symptoms", + "no_copy": 1, + "options": "Patient Encounter Symptom" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "sb_symptoms", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Encounter Impression", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval: doc.symptoms != ''", + "fieldname": "symptoms_in_print", + "fieldtype": "Check", + "label": "In print", + "no_copy": 1, + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "symptoms_select", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Complaints", - "length": 0, - "no_copy": 1, - "options": "Complaint", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "physical_examination", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "symptoms", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "diagnosis", + "fieldtype": "Table MultiSelect", + "ignore_xss_filter": 1, + "label": "Diagnosis", + "no_copy": 1, + "options": "Patient Encounter Diagnosis" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "symptoms_in_print", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In print", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "1", + "depends_on": "eval: doc.diagnosis != ''", + "fieldname": "diagnosis_in_print", + "fieldtype": "Check", + "label": "In print", + "no_copy": 1, + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "physical_examination", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "codification", + "fieldtype": "Section Break", + "label": "Medical Coding" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "diagnosis_select", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Diagnosis", - "length": 0, - "no_copy": 1, - "options": "Diagnosis", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "codification_table", + "fieldtype": "Table", + "label": "Medical Coding", + "options": "Codification Table" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "diagnosis", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sb_drug_prescription", + "fieldtype": "Section Break", + "label": "Medication" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "depends_on": "", - "fieldname": "diagnosis_in_print", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In print", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "drug_prescription", + "fieldtype": "Table", + "label": "Drug Prescription", + "options": "Drug Prescription" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "codification", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Medical Coding", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sb_test_prescription", + "fieldtype": "Section Break", + "label": "Investigation" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "codification_table", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Medical Coding", - "length": 0, - "no_copy": 0, - "options": "Codification Table", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_prescription", + "fieldtype": "Table", + "label": "Lab Prescription", + "options": "Lab Prescription" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_drug_prescription", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Medication", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sb_procedures", + "fieldtype": "Section Break", + "label": "Procedures" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "drug_prescription", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Medication", - "length": 0, - "no_copy": 0, - "options": "Drug Prescription", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "procedure_prescription", + "fieldtype": "Table", + "label": "Procedure Prescription", + "no_copy": 1, + "options": "Procedure Prescription" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_test_prescription", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Investigations", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "encounter_comment", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "label": "Review Details", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_prescription", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Investigations", - "length": 0, - "no_copy": 0, - "options": "Lab Prescription", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Patient Encounter", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_procedures", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Procedures", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "appointment_type", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Appointment Type", + "no_copy": 1, + "options": "Appointment Type", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "procedure_prescription", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Procedures", - "length": 0, - "no_copy": 1, - "options": "Procedure Prescription", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "practitioner.department", + "fieldname": "medical_department", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Department", + "options": "Medical Department", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "encounter_comment", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Review Details", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "inpatient_status", + "fieldtype": "Data", + "label": "Inpatient Status", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Patient Encounter", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "column_break_17", + "fieldtype": "Column Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-09-04 11:50:52.217175", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Patient Encounter", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-02-27 12:42:21.751964", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient Encounter", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Physician", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Physician", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "patient, practitioner, visit_department, encounter_date, encounter_time", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "patient", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 + ], + "restrict_to_domain": "Healthcare", + "search_fields": "patient, practitioner, medical_department, encounter_date, encounter_time", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "patient", + "track_changes": 1, + "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py index fe7ac8eb33e..ade4748ece1 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py @@ -6,59 +6,63 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils import cstr +from frappe import _ class PatientEncounter(Document): def on_update(self): - if(self.appointment): - frappe.db.set_value("Patient Appointment", self.appointment, "status", "Closed") - update_encounter_to_medical_record(self) + if self.appointment: + frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed') + update_encounter_medical_record(self) def after_insert(self): insert_encounter_to_medical_record(self) def on_cancel(self): - if(self.appointment): - frappe.db.set_value("Patient Appointment", self.appointment, "status", "Open") + if self.appointment: + frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Open') delete_medical_record(self) def insert_encounter_to_medical_record(doc): subject = set_subject_field(doc) - medical_record = frappe.new_doc("Patient Medical Record") + medical_record = frappe.new_doc('Patient Medical Record') medical_record.patient = doc.patient medical_record.subject = subject - medical_record.status = "Open" + medical_record.status = 'Open' medical_record.communication_date = doc.encounter_date - medical_record.reference_doctype = "Patient Encounter" + medical_record.reference_doctype = 'Patient Encounter' medical_record.reference_name = doc.name medical_record.reference_owner = doc.owner medical_record.save(ignore_permissions=True) -def update_encounter_to_medical_record(encounter): - medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s", (encounter.name)) +def update_encounter_medical_record(encounter): + medical_record_id = frappe.db.exists('Patient Medical Record', {'reference_name': encounter.name}) + if medical_record_id and medical_record_id[0][0]: subject = set_subject_field(encounter) - frappe.db.set_value("Patient Medical Record", medical_record_id[0][0], "subject", subject) + frappe.db.set_value('Patient Medical Record', medical_record_id[0][0], 'subject', subject) else: insert_encounter_to_medical_record(encounter) def delete_medical_record(encounter): - frappe.db.sql("""delete from `tabPatient Medical Record` where reference_name = %s""", (encounter.name)) + frappe.db.delete_doc_if_exists('Patient Medical Record', 'reference_name', encounter.name) def set_subject_field(encounter): - subject = encounter.practitioner+"
" - if(encounter.symptoms): - subject += "Symptoms: "+ cstr(encounter.symptoms)+".
" + subject = encounter.practitioner + '\n' + if encounter.symptoms: + subject += _('Symptoms: ') + cstr(encounter.symptoms) + '\n' else: - subject += "No Symptoms
" - if(encounter.diagnosis): - subject += "Diagnosis: "+ cstr(encounter.diagnosis)+".
" + subject += _('No Symptoms') + '\n' + + if encounter.diagnosis: + subject += _('Diagnosis: ') + cstr(encounter.diagnosis) + '\n' else: - subject += "No Diagnosis
" - if(encounter.drug_prescription): - subject +="\nDrug(s) Prescribed. " - if(encounter.lab_test_prescription): - subject += "\nTest(s) Prescribed." - if(encounter.procedure_prescription): - subject += "\nProcedure(s) Prescribed." + subject += _('No Diagnosis') + '\n' + + if encounter.drug_prescription: + subject += '\n' + _('Drug(s) Prescribed.') + if encounter.lab_test_prescription: + subject += '\n' + _('Test(s) Prescribed.') + if encounter.procedure_prescription: + subject += '\n' + _('Procedure(s) Prescribed.') return subject diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter_list.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter_list.js index 93c02f60c5c..d8f63bd0fa5 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter_list.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter_list.js @@ -2,5 +2,5 @@ (c) ESS 2015-16 */ frappe.listview_settings['Patient Encounter'] = { - filters:[["docstatus","!=","1"]] + filters:[["docstatus","!=","2"]] }; diff --git a/erpnext/healthcare/doctype/patient_encounter_diagnosis/__init__.py b/erpnext/healthcare/doctype/patient_encounter_diagnosis/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/patient_encounter_diagnosis/patient_encounter_diagnosis.json b/erpnext/healthcare/doctype/patient_encounter_diagnosis/patient_encounter_diagnosis.json new file mode 100644 index 00000000000..00ca309d63e --- /dev/null +++ b/erpnext/healthcare/doctype/patient_encounter_diagnosis/patient_encounter_diagnosis.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "beta": 1, + "creation": "2020-02-26 16:48:16.835105", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "diagnosis" + ], + "fields": [ + { + "fieldname": "diagnosis", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Diagnosis", + "options": "Diagnosis", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-02-26 16:58:16.480583", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient Encounter Diagnosis", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_encounter_diagnosis/patient_encounter_diagnosis.py b/erpnext/healthcare/doctype/patient_encounter_diagnosis/patient_encounter_diagnosis.py new file mode 100644 index 00000000000..34b0cf8a580 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_encounter_diagnosis/patient_encounter_diagnosis.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class PatientEncounterDiagnosis(Document): + pass diff --git a/erpnext/healthcare/doctype/patient_encounter_symptom/__init__.py b/erpnext/healthcare/doctype/patient_encounter_symptom/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/patient_encounter_symptom/patient_encounter_symptom.json b/erpnext/healthcare/doctype/patient_encounter_symptom/patient_encounter_symptom.json new file mode 100644 index 00000000000..bc921458674 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_encounter_symptom/patient_encounter_symptom.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "beta": 1, + "creation": "2020-02-26 16:47:00.525657", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "complaint" + ], + "fields": [ + { + "fieldname": "complaint", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Complaint", + "options": "Complaint", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-02-26 16:57:37.997481", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient Encounter Symptom", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_encounter_symptom/patient_encounter_symptom.py b/erpnext/healthcare/doctype/patient_encounter_symptom/patient_encounter_symptom.py new file mode 100644 index 00000000000..bdb7bb218eb --- /dev/null +++ b/erpnext/healthcare/doctype/patient_encounter_symptom/patient_encounter_symptom.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class PatientEncounterSymptom(Document): + pass diff --git a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json index c6a6b44ce74..3655e24cb98 100644 --- a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json +++ b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json @@ -1,457 +1,155 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 1, - "creation": "2016-06-09 11:30:44.972056", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "actions": [], + "allow_copy": 1, + "allow_import": 1, + "autoname": "naming_series:", + "beta": 1, + "creation": "2016-06-09 11:30:44.972056", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "naming_series", + "patient", + "status", + "column_break_2", + "attach", + "section_break_4", + "subject", + "section_break_8", + "communication_date", + "reference_doctype", + "reference_name", + "column_break_9", + "reference_owner", + "user" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 0, - "options": "HLC-PMR-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "HLC-PMR-.YYYY.-", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Patient", - "length": 0, - "no_copy": 0, - "options": "Patient", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "patient", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Patient", + "options": "Patient", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "attach", - "fieldtype": "Attach", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "attach", + "fieldtype": "Attach", + "label": "Attach Medical Record" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subject", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subject", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "subject", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "label": "Subject" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Open\nClose", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Open\nClose", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "communication_date", - "fieldtype": "Date", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "communication_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Datetime", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_doctype", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reference DocType", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference DocType", + "options": "DocType", + "read_only": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_name", - "fieldtype": "Dynamic Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reference Name", - "length": 0, - "no_copy": 0, - "options": "reference_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Reference Name", + "options": "reference_doctype", + "read_only": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "reference_name.owner", - "fieldname": "reference_owner", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Owner", - "length": 0, - "no_copy": 1, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "reference_name.owner", + "fieldname": "reference_owner", + "fieldtype": "Data", + "label": "Reference Owner", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "__user", - "fieldname": "user", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "User", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "__user", + "fieldname": "user", + "fieldtype": "Link", + "label": "User", + "options": "User", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 1, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:37.927284", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Patient Medical Record", - "name_case": "", - "owner": "Administrator", + ], + "in_create": 1, + "links": [], + "modified": "2020-03-23 19:26:59.308383", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient Medical Record", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Physician", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Physician", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "patient, subject, communication_date, reference_doctype, reference_name", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "patient", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 -} + ], + "restrict_to_domain": "Healthcare", + "search_fields": "patient, subject, communication_date, reference_doctype, reference_name", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "patient", + "track_changes": 1, + "track_seen": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py index 460774a7ec8..e5a5e4c010d 100644 --- a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py +++ b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py @@ -3,8 +3,86 @@ # See license.txt from __future__ import unicode_literals import unittest - -# test_records = frappe.get_test_records('Patient Medical Record') +import frappe +from frappe.utils import nowdate +from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_encounter, create_healthcare_docs, create_appointment class TestPatientMedicalRecord(unittest.TestCase): - pass + def setUp(self): + frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0) + frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) + + def test_medical_record(self): + patient, medical_department, practitioner = create_healthcare_docs() + appointment = create_appointment(patient, practitioner, nowdate(), invoice=1) + encounter = create_encounter(appointment) + # check for encounter + medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': encounter.name}) + self.assertTrue(medical_rec) + + vital_signs = create_vital_signs(appointment) + # check for vital signs + medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': vital_signs.name}) + self.assertTrue(medical_rec) + + appointment = create_appointment(patient, practitioner, nowdate(), invoice=1, procedure_template=1) + procedure = create_procedure(appointment) + procedure.start_procedure() + procedure.complete_procedure() + # check for clinical procedure + medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': procedure.name}) + self.assertTrue(medical_rec) + + template = create_lab_test_template(medical_department) + lab_test = create_lab_test(template, patient) + # check for lab test + medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': lab_test.name}) + self.assertTrue(medical_rec) + + +def create_procedure(appointment): + if appointment: + procedure = frappe.new_doc('Clinical Procedure') + procedure.procedure_template = appointment.procedure_template + procedure.appointment = appointment.name + procedure.patient = appointment.patient + procedure.practitioner = appointment.practitioner + procedure.medical_department = appointment.department + procedure.start_dt = appointment.appointment_date + procedure.start_time = appointment.appointment_time + procedure.save() + procedure.submit() + return procedure + +def create_vital_signs(appointment): + vital_signs = frappe.new_doc('Vital Signs') + vital_signs.patient = appointment.patient + vital_signs.signs_date = appointment.appointment_date + vital_signs.signs_time = appointment.appointment_time + vital_signs.temperature = 38.5 + vital_signs.save() + vital_signs.submit() + return vital_signs + +def create_lab_test_template(medical_department): + if frappe.db.exists('Lab Test Template', 'Blood Test'): + return 'Blood Test' + + template = frappe.new_doc('Lab Test Template') + template.lab_test_name = 'Blood Test' + template.lab_test_code = 'Blood Test' + template.lab_test_group = 'Services' + template.department = medical_department + template.is_billable = 1 + template.lab_test_rate = 2000 + template.save() + return template.name + +def create_lab_test(template, patient): + lab_test = frappe.new_doc('Lab Test') + lab_test.patient = patient + lab_test.patient_sex = frappe.db.get_value('Patient', patient, 'sex') + lab_test.template = template + lab_test.save() + lab_test.submit() + return lab_test \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_relation/patient_relation.json b/erpnext/healthcare/doctype/patient_relation/patient_relation.json index 209a3746a6e..376f7f76d66 100644 --- a/erpnext/healthcare/doctype/patient_relation/patient_relation.json +++ b/erpnext/healthcare/doctype/patient_relation/patient_relation.json @@ -1,134 +1,52 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 1, - "creation": "2017-04-26 15:40:11.561855", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_copy": 1, + "beta": 1, + "creation": "2017-04-26 15:40:11.561855", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "patient", + "relation", + "description" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "relation", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Relation", - "length": 0, - "no_copy": 0, - "options": "\nFather\nMother\nSpouse\nSiblings\nFamily", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "relation", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Relation", + "options": "\nFather\nMother\nSpouse\nSiblings\nFamily\nOther", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Patient", - "length": 0, - "no_copy": 0, - "options": "Patient", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "patient", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Patient", + "options": "Patient", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "description", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "label": "Description" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2017-10-04 16:12:45.485333", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Patient Relation", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-01-29 12:45:40.081899", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient Relation", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "restrict_to_domain": "Healthcare", + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.js b/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.js index f247856b92f..7cb7c4b65e6 100644 --- a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.js +++ b/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.js @@ -5,9 +5,9 @@ frappe.ui.form.on('Practitioner Schedule', { refresh: function(frm) { cur_frm.fields_dict["time_slots"].grid.wrapper.find('.grid-add-row').hide(); cur_frm.fields_dict["time_slots"].grid.add_custom_button(__('Add Time Slots'), () => { - var d = new frappe.ui.Dialog({ + let d = new frappe.ui.Dialog({ fields: [ - {fieldname: 'days', label: __('Select Days'), fieldtype:'MultiSelect', + {fieldname: 'days', label: __('Select Days'), fieldtype: 'MultiSelect', options:[ {value:'Sunday', label:__('Sunday')}, {value:'Monday', label:__('Monday')}, @@ -17,40 +17,41 @@ frappe.ui.form.on('Practitioner Schedule', { {value:'Friday', label:__('Friday')}, {value:'Saturday', label:__('Saturday')}, ], reqd: 1}, - {fieldname: 'from_time', label:__('From'), fieldtype:'Time', + {fieldname: 'from_time', label: __('From'), fieldtype: 'Time', 'default': '09:00:00', reqd: 1}, - {fieldname: 'to_time', label:__('To'), fieldtype:'Time', + {fieldname: 'to_time', label: __('To'), fieldtype: 'Time', 'default': '12:00:00', reqd: 1}, - {fieldname: 'duration', label:__('Appointment Duration (mins)'), + {fieldname: 'duration', label: __('Appointment Duration (mins)'), fieldtype:'Int', 'default': 15, reqd: 1}, ], primary_action_label: __('Add Timeslots'), primary_action: () => { - var values = d.get_values(); - if(values) { + let values = d.get_values(); + if (values) { let slot_added = false; values.days.split(',').forEach(function(day){ day = $.trim(day); - if(['Sunday', 'Monday', 'Tuesday', 'Wednesday', + if (['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'].includes(day)){ add_slots(day); } }); + function check_overlap_or_add_slot(week_day, cur_time, end_time, add_slots_to_child){ let overlap = false; - while(cur_time < end_time) { + while (cur_time < end_time) { let add_to_child = true; let to_time = cur_time.clone().add(values.duration, 'minutes'); - if(to_time <= end_time) { - if(frm.doc.time_slots){ + if (to_time <= end_time) { + if (frm.doc.time_slots){ frm.doc.time_slots.forEach(function(slot) { - if(slot.day == week_day){ + if (slot.day == week_day){ let slot_from_moment = moment(slot.from_time, 'HH:mm:ss'); let slot_to_moment = moment(slot.to_time, 'HH:mm:ss'); - if(cur_time.isSame(slot_from_moment) || cur_time.isBetween(slot_from_moment, slot_to_moment) || - to_time.isSame(slot_to_moment) || to_time.isBetween(slot_from_moment, slot_to_moment)){ + if (cur_time.isSame(slot_from_moment) || cur_time.isBetween(slot_from_moment, slot_to_moment) || + to_time.isSame(slot_to_moment) || to_time.isBetween(slot_from_moment, slot_to_moment)) { overlap = true; - if(add_slots_to_child){ + if (add_slots_to_child) { frappe.show_alert({ message:__('Time slot skiped, the slot {0} to {1} overlap exisiting slot {2} to {3}', [cur_time.format('HH:mm:ss'), to_time.format('HH:mm:ss'), slot.from_time, slot.to_time]), @@ -63,7 +64,7 @@ frappe.ui.form.on('Practitioner Schedule', { }); } // add a new timeslot - if(add_to_child && add_slots_to_child){ + if (add_to_child && add_slots_to_child) { frm.add_child('time_slots', { from_time: cur_time.format('HH:mm:ss'), to_time: to_time.format('HH:mm:ss'), @@ -76,10 +77,11 @@ frappe.ui.form.on('Practitioner Schedule', { } return overlap; } - function add_slots(week_day){ + + function add_slots(week_day) { let cur_time = moment(values.from_time, 'HH:mm:ss'); let end_time = moment(values.to_time, 'HH:mm:ss'); - if(check_overlap_or_add_slot(week_day, cur_time, end_time, false)){ + if (check_overlap_or_add_slot(week_day, cur_time, end_time, false)) { frappe.confirm(__('Schedules for {0} overlaps, do you want to proceed after skiping overlaped slots ?', [week_day]), function() { // if Yes @@ -88,21 +90,22 @@ frappe.ui.form.on('Practitioner Schedule', { function() { // if No frappe.show_alert({ - message:__('Slots for {0} are not added to the schedule', [week_day]), - indicator:'red' + message: __('Slots for {0} are not added to the schedule', [week_day]), + indicator: 'red' }); } ); - } - else{ + } else { check_overlap_or_add_slot(week_day, cur_time, end_time, true); } } + frm.refresh_field('time_slots'); - if(slot_added){ + + if (slot_added) { frappe.show_alert({ - message:__('Time slots added'), - indicator:'green' + message: __('Time slots added'), + indicator: 'green' }); } } diff --git a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json b/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json index 08a1b86969c..cff100cc704 100644 --- a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json +++ b/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json @@ -1,160 +1,71 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:schedule_name", - "beta": 1, - "creation": "2017-05-03 17:28:03.926787", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:schedule_name", + "beta": 1, + "creation": "2017-05-03 17:28:03.926787", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "disabled", + "schedule_details_section", + "schedule_name", + "time_slots" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "schedule_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Schedule Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "schedule_name", + "fieldtype": "Data", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Schedule Name", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "time_slots", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Time Slots", - "length": 0, - "no_copy": 0, - "options": "Healthcare Schedule Time Slot", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "time_slots", + "fieldtype": "Table", + "label": "Time Slots", + "options": "Healthcare Schedule Time Slot" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "disabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Disabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled", + "print_hide": 1 + }, + { + "fieldname": "schedule_details_section", + "fieldtype": "Section Break", + "label": "Schedule Details" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-06-29 14:55:34.795995", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Practitioner Schedule", - "name_case": "", - "owner": "rmehta@gmail.com", + ], + "links": [], + "modified": "2020-01-31 12:21:45.975488", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Practitioner Schedule", + "owner": "rmehta@gmail.com", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "restrict_to_domain": "Healthcare", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json b/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json index 236c2b84023..e4c01d79c10 100644 --- a/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json +++ b/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json @@ -1,338 +1,99 @@ { - "allow_copy": 1, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 1, - "creation": "2017-11-17 15:52:48.324157", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_copy": 1, + "beta": 1, + "creation": "2017-11-17 15:52:48.324157", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "procedure", + "procedure_name", + "department", + "practitioner", + "date", + "comments", + "appointment_booked", + "procedure_created", + "invoiced" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "procedure", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Procedure", - "length": 0, - "no_copy": 0, - "options": "Clinical Procedure Template", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "procedure", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Clinical Procedure", + "options": "Clinical Procedure Template", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "procedure.template", - "fieldname": "procedure_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Procedure Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "procedure.template", + "fieldname": "procedure_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Procedure Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Medical Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "procedure.medical_department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Medical Department" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "practitioner", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Referral", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "practitioner", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Referring Practitioner", + "options": "Healthcare Practitioner" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "comments", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Comments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "comments", + "fieldtype": "Data", + "label": "Comments" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "appointment_booked", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Appointment Booked", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "appointment_booked", + "fieldtype": "Check", + "hidden": 1, + "label": "Appointment Booked", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "procedure_created", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Procedure Created", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "procedure_created", + "fieldtype": "Check", + "hidden": 1, + "label": "Procedure Created", + "no_copy": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "invoiced", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Invoiced", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "invoiced", + "fieldtype": "Check", + "label": "Invoiced", + "read_only": 1, + "search_index": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:33:35.939816", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Procedure Prescription", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-02-26 15:42:33.988081", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Procedure Prescription", + "owner": "Administrator", + "permissions": [], + "restrict_to_domain": "Healthcare", + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.js b/erpnext/healthcare/doctype/sample_collection/sample_collection.js index 9934ce48451..2f5278b2d53 100644 --- a/erpnext/healthcare/doctype/sample_collection/sample_collection.js +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.js @@ -3,7 +3,7 @@ frappe.ui.form.on('Sample Collection', { refresh: function(frm) { - if(frappe.defaults.get_default("require_sample_collection")){ + if(frappe.defaults.get_default("create_sample_collection_for_lab_test")){ frm.add_custom_button(__("View Lab Tests"), function() { frappe.route_options = {"sample": frm.doc.name}; frappe.set_route("List", "Lab Test"); diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json index 783fc3d0440..39cead88621 100644 --- a/erpnext/healthcare/doctype/sample_collection/sample_collection.json +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.json @@ -1,687 +1,199 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 1, - "creation": "2016-04-05 15:58:18.076977", - "custom": 0, - "default_print_format": "", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, + "actions": [], + "allow_copy": 1, + "allow_import": 1, + "autoname": "naming_series:", + "beta": 1, + "creation": "2016-04-05 15:58:18.076977", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "inpatient_record", + "naming_series", + "invoiced", + "patient", + "column_break_4", + "patient_age", + "patient_sex", + "company", + "section_break_6", + "sample", + "sample_uom", + "sample_qty", + "column_break_10", + "collected_by", + "collected_time", + "num_print", + "amended_from", + "section_break_15", + "sample_details" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.inpatient_record", - "fieldname": "inpatient_record", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Inpatient Record", - "length": 0, - "no_copy": 0, - "options": "Inpatient Record", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "patient.inpatient_record", + "fieldname": "inpatient_record", + "fieldtype": "Link", + "label": "Inpatient Record", + "options": "Inpatient Record", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 1, - "options": "HLC-SC-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "HLC-SC-.YYYY.-", + "print_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "invoiced", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Invoiced", - "length": 0, - "no_copy": 1, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "invoiced", + "fieldtype": "Check", + "label": "Invoiced", + "no_copy": 1, + "read_only": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "inpatient_record.patient", - "fieldname": "patient", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Patient", - "length": 0, - "no_copy": 0, - "options": "Patient", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "inpatient_record.patient", + "fieldname": "patient", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_standard_filter": 1, + "label": "Patient", + "options": "Patient", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient_age", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Age", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "patient_age", + "fieldtype": "Data", + "label": "Age" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.sex", - "fieldname": "patient_sex", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Gender", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "patient.sex", + "fieldname": "patient_sex", + "fieldtype": "Data", + "label": "Gender" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 1, + "label": "Company", + "options": "Company", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Sample", - "length": 0, - "no_copy": 0, - "options": "Lab Test Sample", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sample", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Sample", + "options": "Lab Test Sample", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "sample.sample_uom", - "fieldname": "sample_uom", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "UOM", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "sample.sample_uom", + "fieldname": "sample_uom", + "fieldtype": "Data", + "in_list_view": 1, + "label": "UOM" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "sample_quantity", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_10", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "collected_by", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Collected By", + "options": "User" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "collected_by", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Collected By", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "collected_time", + "fieldtype": "Datetime", + "label": "Collected Time" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "collected_time", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Collected Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "1", + "fieldname": "num_print", + "fieldtype": "Int", + "label": "No. of print", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fieldname": "num_print", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "No. of print", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Sample Collection", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Sample Collection", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_15", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_15", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "sample_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Quantity" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample_collection_details", - "fieldtype": "Long Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Collection Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "sample_details", + "fieldtype": "Long Text", + "ignore_xss_filter": 1, + "label": "Collection Details" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:39.128989", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Sample Collection", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-03-25 16:55:52.376834", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Sample Collection", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Laboratory User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Laboratory User", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "patient, sample", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "patient", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 -} + ], + "restrict_to_domain": "Healthcare", + "search_fields": "patient, sample", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "patient", + "track_changes": 1, + "track_seen": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.js b/erpnext/healthcare/doctype/vital_signs/vital_signs.js index a5f9d612952..78509e0323c 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.js +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.js @@ -1,49 +1,52 @@ // Copyright (c) 2016, ESS LLP and contributors // For license information, please see license.txt -frappe.ui.form.on("Vital Signs", "height", function(frm) { - if(frm.doc.height && frm.doc.weight){ - calculate_bmi(frm); +frappe.ui.form.on('Vital Signs', { + height: function(frm) { + if (frm.doc.height && frm.doc.weight) { + calculate_bmi(frm); + } + }, + + weight: function(frm) { + if (frm.doc.height && frm.doc.weight) { + calculate_bmi(frm); + } + }, + + bp_systolic: function(frm) { + if (frm.doc.bp_systolic && frm.doc.bp_diastolic) { + set_bp(frm); + } + }, + + bp_diastolic: function(frm) { + if (frm.doc.bp_systolic && frm.doc.bp_diastolic) { + set_bp(frm); + } } }); -frappe.ui.form.on("Vital Signs", "weight", function(frm) { - if(frm.doc.height && frm.doc.weight){ - calculate_bmi(frm); - } -}); - -var calculate_bmi = function(frm){ +let calculate_bmi = function(frm){ // Reference https://en.wikipedia.org/wiki/Body_mass_index // bmi = weight (in Kg) / height * height (in Meter) - var bmi = (frm.doc.weight/(frm.doc.height*frm.doc.height)).toFixed(2); - var bmi_note = null; - if(bmi<18.5){ - bmi_note = "Underweight"; - }else if(bmi>=18.5 && bmi<25){ - bmi_note = "Normal"; - }else if(bmi>=25 && bmi<30){ - bmi_note = "Overweight"; - }else if(bmi>=30){ - bmi_note = "Obese"; + let bmi = (frm.doc.weight / (frm.doc.height * frm.doc.height)).toFixed(2); + let bmi_note = null; + + if (bmi<18.5) { + bmi_note = 'Underweight'; + } else if (bmi>=18.5 && bmi<25) { + bmi_note = 'Normal'; + } else if (bmi>=25 && bmi<30) { + bmi_note = 'Overweight'; + } else if (bmi>=30) { + bmi_note = 'Obese'; } - frappe.model.set_value(frm.doctype,frm.docname, "bmi", bmi); - frappe.model.set_value(frm.doctype,frm.docname, "nutrition_note", bmi_note); + frappe.model.set_value(frm.doctype,frm.docname, 'bmi', bmi); + frappe.model.set_value(frm.doctype,frm.docname, 'nutrition_note', bmi_note); }; -frappe.ui.form.on("Vital Signs", "bp_systolic", function(frm) { - if(frm.doc.bp_systolic && frm.doc.bp_diastolic){ - set_bp(frm); - } -}); - -frappe.ui.form.on("Vital Signs", "bp_diastolic", function(frm) { - if(frm.doc.bp_systolic && frm.doc.bp_diastolic){ - set_bp(frm); - } -}); - -var set_bp = function(frm){ - var bp = frm.doc.bp_systolic+"/"+frm.doc.bp_diastolic+" mmHg"; - frappe.model.set_value(frm.doctype,frm.docname, "bp", bp); +let set_bp = function(frm){ + let bp = frm.doc.bp_systolic+ '/' + frm.doc.bp_diastolic + ' mmHg'; + frappe.model.set_value(frm.doctype,frm.docname, 'bp', bp); }; diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.json b/erpnext/healthcare/doctype/vital_signs/vital_signs.json index 1503f835a47..75726dbe070 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.json +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.json @@ -1,991 +1,273 @@ { + "actions": [], "allow_copy": 1, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, "allow_import": 1, - "allow_rename": 0, "beta": 1, "creation": "2017-02-02 11:00:24.853005", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "inpatient_record", + "patient", + "patient_name", + "appointment", + "encounter", + "column_break_2", + "signs_date", + "signs_time", + "sb_vs", + "temperature", + "pulse", + "respiratory_rate", + "tongue", + "abdomen", + "column_break_8", + "reflexes", + "bp_systolic", + "bp_diastolic", + "bp", + "vital_signs_note", + "sb_nutrition_values", + "height", + "weight", + "bmi", + "column_break_14", + "nutrition_note", + "company", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "patient.inpatient_record", "fieldname": "inpatient_record", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Inpatient Record", - "length": 0, - "no_copy": 0, "options": "Inpatient Record", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "inpatient_record.patient", "fieldname": "patient", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Patient", - "length": 0, - "no_copy": 0, "options": "Patient", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "patient.patient_name", "fieldname": "patient_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Patient Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "appointment", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Appointment", - "length": 0, - "no_copy": 1, "options": "Patient Appointment", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "encounter", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Patient Encounter", - "length": 0, "no_copy": 1, "options": "Patient Encounter", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Today", "fieldname": "signs_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", "fieldname": "signs_time", "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sb_vs", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Vital Signs", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Vital Signs" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Presence of a fever (temp > 38.5 \u00b0C/101.3 \u00b0F or sustained temp > 38 \u00b0C/100.4 \u00b0F)", "fieldname": "temperature", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Body Temperature", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Body Temperature" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Adults' pulse rate is anywhere between 50 and 80 beats per minute.", "fieldname": "pulse", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Heart Rate / Pulse", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Heart Rate / Pulse" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Normal reference range for an adult is 16\u201320 breaths/minute (RCP 2012)", "fieldname": "respiratory_rate", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Respiratory rate", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Respiratory rate" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "tongue", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Tongue", - "length": 0, - "no_copy": 0, - "options": "\nCoated\nVery Coated\nNormal\nFurry\nCuts", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "\nCoated\nVery Coated\nNormal\nFurry\nCuts" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "abdomen", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Abdomen", - "length": 0, - "no_copy": 0, - "options": "\nNormal\nBloated\nFull\nFluid\nConstipated", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "\nNormal\nBloated\nFull\nFluid\nConstipated" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_8", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "reflexes", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Reflexes", - "length": 0, - "no_copy": 0, - "options": "\nNormal\nHyper\nVery Hyper\nOne Sided", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "\nNormal\nHyper\nVery Hyper\nOne Sided" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "bp_systolic", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Blood Pressure (systolic)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Blood Pressure (systolic)" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "bp_diastolic", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Blood Pressure (diastolic)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Blood Pressure (diastolic)" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Normal resting blood pressure in an adult is approximately 120 mmHg systolic, and 80 mmHg diastolic, abbreviated \"120/80 mmHg\"", "fieldname": "bp", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Blood Pressure", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "vital_signs_note", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Notes", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Notes" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sb_nutrition_values", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Nutrition Values", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Nutrition Values" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "height", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Height (In Meter)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Height (In Meter)" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "weight", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Weight (In Kilogram)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Weight (In Kilogram)" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0.00", "fieldname": "bmi", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "BMI", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_14", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "nutrition_note", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Notes", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Notes" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "company", "fieldtype": "Link", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Company" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "amended_from", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Amended From", - "length": 0, "no_copy": 1, "options": "Vital Signs", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-23 14:14:05.933292", + "links": [], + "modified": "2020-03-04 17:19:29.549889", "modified_by": "Administrator", "module": "Healthcare", "name": "Vital Signs", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, "cancel": 1, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Physician", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 }, { - "amend": 0, "cancel": 1, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Nursing User", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Healthcare", "search_fields": "patient, signs_date", "show_name_in_global_search": 1, @@ -993,6 +275,5 @@ "sort_order": "DESC", "title_field": "patient", "track_changes": 1, - "track_seen": 1, - "track_views": 0 + "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.py b/erpnext/healthcare/doctype/vital_signs/vital_signs.py index bf4dace22b3..959e8504c47 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.py +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils import cstr +from frappe import _ class VitalSigns(Document): def on_submit(self): @@ -16,34 +17,35 @@ class VitalSigns(Document): def insert_vital_signs_to_medical_record(doc): subject = set_subject_field(doc) - medical_record = frappe.new_doc("Patient Medical Record") + medical_record = frappe.new_doc('Patient Medical Record') medical_record.patient = doc.patient medical_record.subject = subject - medical_record.status = "Open" + medical_record.status = 'Open' medical_record.communication_date = doc.signs_date - medical_record.reference_doctype = "Vital Signs" + medical_record.reference_doctype = 'Vital Signs' medical_record.reference_name = doc.name medical_record.reference_owner = doc.owner + medical_record.flags.ignore_mandatory = True medical_record.save(ignore_permissions=True) def delete_vital_signs_from_medical_record(doc): - medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s",(doc.name)) - if medical_record_id and medical_record_id[0][0]: - frappe.delete_doc("Patient Medical Record", medical_record_id[0][0]) + medical_record = frappe.db.get_value('Patient Medical Record', {'reference_name': doc.name}) + if medical_record: + frappe.delete_doc('Patient Medical Record', medical_record) def set_subject_field(doc): - subject = "" + subject = '' if(doc.temperature): - subject += "Temperature: \n"+ cstr(doc.temperature)+". " + subject += _('Temperature: ') + '\n'+ cstr(doc.temperature) + '. ' if(doc.pulse): - subject += "Pulse: \n"+ cstr(doc.pulse)+". " + subject += _('Pulse: ') + '\n' + cstr(doc.pulse) + '. ' if(doc.respiratory_rate): - subject += "Respiratory Rate: \n"+ cstr(doc.respiratory_rate)+". " + subject += _('Respiratory Rate: ') + '\n' + cstr(doc.respiratory_rate) + '. ' if(doc.bp): - subject += "BP: \n"+ cstr(doc.bp)+". " + subject += _('BP: ') + '\n' + cstr(doc.bp) + '. ' if(doc.bmi): - subject += "BMI: \n"+ cstr(doc.bmi)+". " + subject += _('BMI: ') + '\n' + cstr(doc.bmi) + '. ' if(doc.nutrition_note): - subject += "Note: \n"+ cstr(doc.nutrition_note)+". " + subject += _('Note: ') + '\n' + cstr(doc.nutrition_note) + '. ' return subject diff --git a/erpnext/healthcare/page/appointment_analytic/appointment_analytic.js b/erpnext/healthcare/page/appointment_analytic/appointment_analytic.js deleted file mode 100644 index 9f2e552efcb..00000000000 --- a/erpnext/healthcare/page/appointment_analytic/appointment_analytic.js +++ /dev/null @@ -1,205 +0,0 @@ -frappe.pages['appointment-analytic'].on_page_load = function(wrapper) { - frappe.ui.make_app_page({ - parent: wrapper, - title: 'Appointment Analytics', - single_column: true - }); - new erpnext.AppointmentAnalytics(wrapper); - frappe.breadcrumbs.add("Medical"); -}; - -erpnext.AppointmentAnalytics = frappe.views.TreeGridReport.extend({ - init: function(wrapper) { - this._super({ - title: __("Appointment Analytics"), - parent: $(wrapper).find('.layout-main'), - page: wrapper.page, - doctypes: ["Patient Appointment", "Healthcare Practitioner", "Medical Department", "Appointment Type", "Patient"], - tree_grid: { show: true } - }); - - this.tree_grids = { - "Medical Department": { - label: __("Department"), - show: true, - item_key: "practitioner", - parent_field: "department", - formatter: function(item) { - return item.name; - } - }, - "Healthcare Practitioner": { - label: __("Healthcare Practitioner"), - show: true, - item_key: "practitioner", - formatter: function(item) { - return item.name; - } - }, - }; - }, - setup_columns: function() { - this.tree_grid = this.tree_grids[this.tree_type]; - - var std_columns = [ - {id: "name", name: this.tree_grid.label, field: "name", width: 300}, - {id: "total", name: "Total", field: "total", plot: false, - formatter: this.currency_formatter} - ]; - - this.make_date_range_columns(); - this.columns = std_columns.concat(this.columns); - }, - filters: [ - {fieldtype:"Select", label: __("Tree Type"), fieldname: "tree_type", - options:["Healthcare Practitioner", "Medical Department"], filter: function(val, item, opts, me) { - return me.apply_zero_filter(val, item, opts, me);}}, - {fieldtype:"Select", label: __("Status"), fieldname: "status", - options:[ - {label: __("Select Status"), value: "Select Status..."}, - {label: __("Open"), value: "Open"}, - {label: __("Closed"), value: "Closed"}, - {label: __("Pending"), value: "Pending"}, - {label: __("Scheduled"), value: "Scheduled"}, - {label: __("Cancelled"), value: "Cancelled"}]}, - {fieldtype:"Select", label: __("Type"), link:"Appointment Type", fieldname: "type", - default_value: __("Select Type...")}, - {fieldtype:"Select", label: __("Healthcare Practitioner"), link:"Healthcare Practitioner", fieldname: "practitioner", - default_value: __("Select Healthcare Practitioner..."), filter: function(val, item, opts) { - return val == opts.default_value || item.name == val || item._show; - }, link_formatter: {filter_input: "practitioner"}}, - {fieldtype:"Select", label: __("Department"), link:"Medical Department", fieldname: "department", - default_value: __("Select Department..."), filter: function(val, item, opts) { - return val == opts.default_value || item.department == val || item._show; - }, link_formatter: {filter_input: "department"}}, - {fieldtype:"Date", label: __("From Date"), fieldname: "from_date"}, - {fieldtype:"Date", label: __("To Date"), fieldname: "to_date"}, - {fieldtype:"Select", label: __("Range"), fieldname: "range", - options:[{label: __("Daily"), value: "Daily"}, {label: __("Weekly"), value: "Weekly"}, - {label: __("Monthly"), value: "Monthly"}, {label: __("Quarterly"), value: "Quarterly"}, - {label: __("Yearly"), value: "Yearly"}]} - ], - setup_filters: function() { - this._super(); - this.trigger_refresh_on_change(["tree_type", "practitioner", "department", "status", "type"]); - - // this.show_zero_check() - }, - init_filter_values: function() { - this._super(); - this.filter_inputs.range.val('Quarterly'); - }, - prepare_data: function() { - var me = this; - if (!this.tl) { - this.tl = frappe.report_dump.data["Patient Appointment"]; - } - if(!this.data || me.item_type != me.tree_type) { - var items = null; - if(me.tree_type=='Healthcare Practitioner') { - items = frappe.report_dump.data["Healthcare Practitioner"]; - } if(me.tree_type=='Medical Department') { - items = this.prepare_tree("Healthcare Practitioner", "Medical Department"); - } - me.item_type = me.tree_type; - me.parent_map = {}; - me.item_by_name = {}; - me.data = []; - - $.each(items, function(i, v) { - var d = copy_dict(v); - - me.data.push(d); - me.item_by_name[d.name] = d; - if(d[me.tree_grid.parent_field]) { - me.parent_map[d.name] = d[me.tree_grid.parent_field]; - } - me.reset_item_values(d); - }); - - this.set_indent(); - - - } else { - // otherwise, only reset values - $.each(this.data, function(i, d) { - me.reset_item_values(d); - }); - } - this.prepare_balances(); - if(me.tree_grid.show) { - this.set_totals(false); - this.update_groups(); - } else { - this.set_totals(true); - } - - - }, - prepare_balances: function() { - var me = this; - var from_date = frappe.datetime.str_to_obj(this.from_date); - var status = this.status; - var type = this.type; - var to_date = frappe.datetime.str_to_obj(this.to_date); - $.each(this.tl, function(i, tl) { - if (me.is_default('company') ? true : tl.company === me.company) { - - var date = frappe.datetime.str_to_obj(tl.appointment_date); - if (date >= from_date && date <= to_date) { - var item = me.item_by_name[tl[me.tree_grid.item_key]] || - me.item_by_name['Not Set']; - - var d = tl.appointment_date.split(" ")[0]; - if(status == "Select Status..." && type=="Select Type...") - { - item[me.column_map[d].field] += 1; - - }else if (status !== "Select Status..." && type == "Select Type..."){ - if(status === tl.status){item[me.column_map[d].field] += 1;} - }else if (status == "Select Status..." && type !== "Select Type..."){ - if(type === tl.appointment_type){item[me.column_map[d].field] += 1;} - }else { - if(type === tl.appointment_type && status === tl.status){item[me.column_map[d].field] += 1;} - } - } - } - }); - }, - update_groups: function() { - var me = this; - - $.each(this.data, function(i, item) { - var parent = me.parent_map[item.name]; - while(parent) { - var parent_group = me.item_by_name[parent]; - - $.each(me.columns, function(c, col) { - if (col.formatter == me.currency_formatter) { - parent_group[col.field] = - flt(parent_group[col.field]) - + flt(item[col.field]); - } - }); - parent = me.parent_map[parent]; - } - }); - }, - set_totals: function(sort) { - var me = this; - $.each(this.data, function(i, d) { - d.total = 0.0; - $.each(me.columns, function(i, col) { - if(col.formatter==me.currency_formatter && !col.hidden && col.field!="total") - d.total += d[col.field]; - }); - }); - - if(sort)this.data = this.data.sort(function(a, b) { return b.total - a.total; }); - - if(!this.checked) { - this.data[0].checked = true; - } - } - -}); diff --git a/erpnext/healthcare/page/appointment_analytic/appointment_analytic.json b/erpnext/healthcare/page/appointment_analytic/appointment_analytic.json deleted file mode 100644 index ac5ca1a266e..00000000000 --- a/erpnext/healthcare/page/appointment_analytic/appointment_analytic.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "content": null, - "creation": "2016-08-18 12:29:52.497819", - "docstatus": 0, - "doctype": "Page", - "idx": 0, - "modified": "2018-08-06 11:40:53.082863", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "appointment-analytic", - "owner": "Administrator", - "page_name": "Appointment Analytics", - "restrict_to_domain": "Healthcare", - "roles": [ - { - "role": "Physician" - } - ], - "script": null, - "standard": "Yes", - "style": null, - "system_page": 0, - "title": "Appointment Analytics" -} \ No newline at end of file diff --git a/erpnext/healthcare/print_format/encounter_print/encounter_print.json b/erpnext/healthcare/print_format/encounter_print/encounter_print.json index 20e44b79049..ec1e0f202a2 100644 --- a/erpnext/healthcare/print_format/encounter_print/encounter_print.json +++ b/erpnext/healthcare/print_format/encounter_print/encounter_print.json @@ -1,22 +1,22 @@ { - "align_labels_right": 0, - "creation": "2017-04-10 14:05:53.355863", - "custom_format": 1, - "disabled": 0, - "doc_type": "Patient Encounter", - "docstatus": 0, - "doctype": "Print Format", - "font": "Default", - "html": "
\n {% if letter_head and not no_letterhead -%}\n
{{ letter_head }}
\n
\n {% else %}\n
\n

{{doc.name}}

\n
\n {%- endif %}\n
\n
\n {% if doc.appointment %}\n\t
\n\t\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t: {{doc.appointment}}\n\t\t\t
\n\t\t
\n\t\t{%- endif -%}\n\n
\n\t\t
\n\t\t\t \n\t\t
\n {% if doc.patient %}\n\t\t
\n\t\t\t : {{doc.patient}}\n\t\t
\n {% else %}\n
\n\t\t\t : Patient Name\n\t\t
\n {%- endif -%}\n\t\t
\n\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t : {{doc.patient_age}}\n\t\t\t
\n\t\t
\n\n
\n
\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t : {{doc.patient_sex}}\n\t\t\t
\n
\n\n
\n
\n\n
\n\t
\n\t\t \n\t
\n {% if doc.practitioner %}\n\t
\n\t\t\t: {{doc.practitioner}}\n\t
\n {%- endif -%}\n\t
\n\n {% if doc.encounter_date %}\n\t
\n\t\t
\n\t\t\n\t\t
\n\t\t
\n\t\t: {{doc.encounter_date}}\n\t\t
\n
\n\t {%- endif -%}\n {% if doc.encounter_time %}\n\t
\n\t\t
\n\t\t\n\t\t
\n\t\t
\n\t\t: {{doc.encounter_time}}\n\t\t
\n
\n\t {%- endif -%}\n {% if doc.visit_department %}\n\t
\n\t\t
\n\t\t\n\t\t
\n\t\t
\n\t\t: {{doc.visit_department}}\n\t\t
\n
\n {%- endif -%}\n
\n\n
\n\n
\n
\n
\n {% if doc.symptoms_in_print%}\n {% if doc.symptoms %}\n Complaints:\n {{doc.symptoms}}\n \t
\n {%- endif -%}\n {%- endif -%}\n\n {% if doc.diagnosis_in_print%}\n {% if doc.diagnosis %}\n \t Diagnosis:\n {{doc.diagnosis}}\n
\n {%- endif -%}\n {%- endif -%}\n\n
\n\n
\n {% if doc.drug_prescription %}\n
\n Rx,\n \n \n \n\n {%- for row in doc.drug_prescription -%}\n \n \n \t\n \t\n \n \n\t {%- endfor -%}\n \n
\n {%- if row.drug_name -%}{{ row.drug_name }}{%- endif -%}\n \n {%- if row.dosage -%}{{ row.dosage }}{%- endif -%}\n \n {%- if row.period -%}{{ row.period }}{%- endif -%}\n\t\t \n\t\t\t
\n {%- if row.comment -%}{{ row.comment }}{%- endif -%}\n
\n\t\t
\n\n\n {%- endif -%}\n
\n\n\n
\n {% if doc.lab_test_prescription %}\n Investigations,\n \n \n \n\n {%- for row in doc.lab_test_prescription -%}\n \n \n \n \n\n\t {%- endfor -%}\n \n
\n {%- if row.lab_test_name -%}{{ row.lab_test_name }}{%- endif -%}\n \n\t\t\t
\n {%- if row.lab_test_comment -%}{{ row.lab_test_comment }}{%- endif -%}\n
\n\t\t
\n\n\n {%- endif -%}\n
\n
\n {% if doc.encounter_comment %}\n
\n {{doc.encounter_comment}}\n {%- endif -%}\n
\n", - "idx": 0, - "line_breaks": 0, - "modified": "2018-09-04 11:52:54.473702", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Encounter Print", - "owner": "Administrator", - "print_format_builder": 0, - "print_format_type": "Server", - "show_section_headings": 0, + "align_labels_right": 0, + "creation": "2017-04-10 14:05:53.355863", + "custom_format": 1, + "disabled": 0, + "doc_type": "Patient Encounter", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "html": "
\n {% if letter_head and not no_letterhead -%}\n
{{ letter_head }}
\n
\n {% else %}\n
\n

{{doc.name}}

\n
\n {%- endif %}\n
\n
\n {% if doc.appointment %}\n\t
\n\t\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t: {{doc.appointment}}\n\t\t\t
\n\t\t
\n\t\t{%- endif -%}\n\n
\n\t\t
\n\t\t\t \n\t\t
\n {% if doc.patient %}\n\t\t
\n\t\t\t : {{doc.patient}}\n\t\t
\n {% else %}\n
\n\t\t\t : Patient Name\n\t\t
\n {%- endif -%}\n\t\t
\n\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t : {{doc.patient_age}}\n\t\t\t
\n\t\t
\n\n
\n
\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t : {{doc.patient_sex}}\n\t\t\t
\n
\n\n
\n
\n\n
\n\t
\n\t\t \n\t
\n {% if doc.practitioner %}\n\t
\n\t\t\t: {{doc.practitioner}}\n\t
\n {%- endif -%}\n\t
\n\n {% if doc.encounter_date %}\n\t
\n\t\t
\n\t\t\n\t\t
\n\t\t
\n\t\t: {{doc.encounter_date}}\n\t\t
\n
\n\t {%- endif -%}\n {% if doc.encounter_time %}\n\t
\n\t\t
\n\t\t\n\t\t
\n\t\t
\n\t\t: {{doc.encounter_time}}\n\t\t
\n
\n\t {%- endif -%}\n {% if doc.medical_department %}\n\t
\n\t\t
\n\t\t\n\t\t
\n\t\t
\n\t\t: {{doc.visit_department}}\n\t\t
\n
\n {%- endif -%}\n
\n\n
\n\n
\n
\n
\n {% if doc.symptoms_in_print%}\n {% if doc.symptoms %}\n Complaints:\n {{doc.symptoms}}\n \t
\n {%- endif -%}\n {%- endif -%}\n\n {% if doc.diagnosis_in_print%}\n {% if doc.diagnosis %}\n \t Diagnosis:\n {{doc.diagnosis}}\n
\n {%- endif -%}\n {%- endif -%}\n\n
\n\n
\n {% if doc.drug_prescription %}\n
\n Rx,\n \n \n \n\n {%- for row in doc.drug_prescription -%}\n \n \n \t\n \t\n \n \n\t {%- endfor -%}\n \n
\n {%- if row.drug_name -%}{{ row.drug_name }}{%- endif -%}\n \n {%- if row.dosage -%}{{ row.dosage }}{%- endif -%}\n \n {%- if row.period -%}{{ row.period }}{%- endif -%}\n\t\t \n\t\t\t
\n {%- if row.comment -%}{{ row.comment }}{%- endif -%}\n
\n\t\t
\n\n\n {%- endif -%}\n
\n\n\n
\n {% if doc.lab_test_prescription %}\n Investigations,\n \n \n \n\n {%- for row in doc.lab_test_prescription -%}\n \n \n \n \n\n\t {%- endfor -%}\n \n
\n {%- if row.lab_test_name -%}{{ row.lab_test_name }}{%- endif -%}\n \n\t\t\t
\n {%- if row.lab_test_comment -%}{{ row.lab_test_comment }}{%- endif -%}\n
\n\t\t
\n\n\n {%- endif -%}\n
\n
\n {% if doc.encounter_comment %}\n
\n {{doc.encounter_comment}}\n {%- endif -%}\n
\n", + "idx": 0, + "line_breaks": 0, + "modified": "2018-09-04 11:52:54.473702", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Encounter Print", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Server", + "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/healthcare/print_format/lab_test_print/lab_test_print.json b/erpnext/healthcare/print_format/lab_test_print/lab_test_print.json index 6badc6f82dc..e8e95d8439c 100644 --- a/erpnext/healthcare/print_format/lab_test_print/lab_test_print.json +++ b/erpnext/healthcare/print_format/lab_test_print/lab_test_print.json @@ -1,22 +1,22 @@ { - "align_labels_right": 0, - "creation": "2017-04-24 15:38:45.332473", - "custom_format": 1, - "disabled": 0, - "doc_type": "Lab Test", - "docstatus": 0, - "doctype": "Print Format", - "font": "Default", - "html": "
\n {% if letter_head and not no_letterhead -%}\n
{{ letter_head }}
\n
\n {%- endif %}\n\n {% if (doc.docstatus != 1) %}\n Lab Tests have to be Submitted for Print .. !\n {% elif (frappe.db.get_value(\"Healthcare Settings\", \"None\", \"require_test_result_approval\") == '1' and doc.approval_status != \"Approved\") %}\n Lab Tests have to be Approved for Print .. !\n {%- else -%}\n
\n
\n\n
\n
\n \n
\n {% if doc.patient %}\n
\n : {{doc.patient}}\n
\n {% else %}\n
\n : Patient Name\n
\n {%- endif -%}\n
\n\n
\n
\n \n
\n
\n : {{doc.patient_age}}\n
\n
\n\n
\n
\n \n
\n
\n : {{doc.patient_sex}}\n
\n
\n\n
\n\n
\n\n
\n
\n \n
\n {% if doc.practitioner %}\n
\n : {{doc.practitioner}}\n
\n {%- endif -%}\n
\n\n {% if doc.sample_date %}\n
\n
\n \n
\n
\n : {{doc.sample_date}}\n
\n
\n {%- endif -%}\n\n {% if doc.result_date %}\n
\n
\n \n
\n
\n : {{doc.result_date}}\n
\n
\n {%- endif -%}\n\n
\n\n
\n\n
\n

Department of {{doc.department}}

\n
\n\n \n \n {%- if doc.normal_test_items -%}\n \n \n \n \n \n\n {%- if doc.normal_test_items|length > 1 %}\n \n {%- endif -%}\n\n {%- for row in doc.normal_test_items -%}\n \n \n\n \n\n \n \n\n {%- endfor -%}\n {%- endif -%}\n \n
Name of TestResultNormal Range
{{ doc.lab_test_name }}
\n {%- if doc.normal_test_items|length > 1 %}  {%- endif -%}\n {%- if row.lab_test_name -%}{{ row.lab_test_name }}\n {%- else -%}   {%- endif -%}\n {%- if row.lab_test_event -%}   {{ row.lab_test_event }}{%- endif -%}\n \n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%} \n {%- if row.lab_test_uom -%}{{ row.lab_test_uom }}{%- endif -%}\n \n
\n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n
\n
\n\n \n \n {%- if doc.special_test_items -%}\n \n \n \n \n \n {%- for row in doc.special_test_items -%}\n \n \n \n \n\n {%- endfor -%}\n {%- endif -%}\n\n {%- if doc.sensitivity_test_items -%}\n \n \n \n \n {%- for row in doc.sensitivity_test_items -%}\n \n \n \n \n\n {%- endfor -%}\n {%- endif -%}\n\n \n
Name of TestResult
{{ doc.lab_test_name }}
  {{ row.lab_test_particulars }} \n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%}\n
AntibioticSensitivity
{{ row.antibiotic }} {{ row.antibiotic_sensitivity }}
\n {%- endif -%}\n\n
\n {%- if (frappe.db.get_value(\"Healthcare Settings\", \"None\", \"employee_name_and_designation_in_print\") == '1') -%}\n
{{doc.employee_name}}
\n
{{doc.employee_designation}}
\n {%- else -%}\n
{{frappe.db.get_value(\"Healthcare Settings\", \"None\", \"custom_signature_in_print\") }}
\n {%- endif -%}\n
\n
\n", - "idx": 0, - "line_breaks": 0, - "modified": "2018-09-04 12:03:47.066918", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Lab Test Print", - "owner": "Administrator", - "print_format_builder": 0, - "print_format_type": "Server", - "show_section_headings": 0, + "align_labels_right": 0, + "creation": "2017-04-24 15:38:45.332473", + "custom_format": 1, + "disabled": 0, + "doc_type": "Lab Test", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "html": "
\n {% if letter_head and not no_letterhead -%}\n
{{ letter_head }}
\n
\n {%- endif %}\n\n {% if (doc.docstatus != 1) %}\n Lab Tests have to be Submitted for Print .. !\n {% elif (frappe.db.get_value(\"Healthcare Settings\", \"None\", \"lab_test_approval_required\") == '1' and doc.approval_status != \"Approved\") %}\n Lab Tests have to be Approved for Print .. !\n {%- else -%}\n
\n
\n\n
\n
\n \n
\n {% if doc.patient %}\n
\n : {{doc.patient}}\n
\n {% else %}\n
\n : Patient Name\n
\n {%- endif -%}\n
\n\n
\n
\n \n
\n
\n : {{doc.patient_age}}\n
\n
\n\n
\n
\n \n
\n
\n : {{doc.patient_sex}}\n
\n
\n\n
\n\n
\n\n
\n
\n \n
\n {% if doc.practitioner %}\n
\n : {{doc.practitioner}}\n
\n {%- endif -%}\n
\n\n {% if doc.sample_date %}\n
\n
\n \n
\n
\n : {{doc.sample_date}}\n
\n
\n {%- endif -%}\n\n {% if doc.result_date %}\n
\n
\n \n
\n
\n : {{doc.result_date}}\n
\n
\n {%- endif -%}\n\n
\n\n
\n\n
\n

Department of {{doc.department}}

\n
\n\n \n \n {%- if doc.normal_test_items -%}\n \n \n \n \n \n\n {%- if doc.normal_test_items|length > 1 %}\n \n {%- endif -%}\n\n {%- for row in doc.normal_test_items -%}\n \n \n\n \n\n \n \n\n {%- endfor -%}\n {%- endif -%}\n \n
Name of TestResultNormal Range
{{ doc.lab_test_name }}
\n {%- if doc.normal_test_items|length > 1 %}  {%- endif -%}\n {%- if row.lab_test_name -%}{{ row.lab_test_name }}\n {%- else -%}   {%- endif -%}\n {%- if row.lab_test_event -%}   {{ row.lab_test_event }}{%- endif -%}\n \n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%} \n {%- if row.lab_test_uom -%}{{ row.lab_test_uom }}{%- endif -%}\n \n
\n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n
\n
\n\n \n \n {%- if doc.special_test_items -%}\n \n \n \n \n \n {%- for row in doc.special_test_items -%}\n \n \n \n \n\n {%- endfor -%}\n {%- endif -%}\n\n {%- if doc.sensitivity_test_items -%}\n \n \n \n \n {%- for row in doc.sensitivity_test_items -%}\n \n \n \n \n\n {%- endfor -%}\n {%- endif -%}\n\n \n
Name of TestResult
{{ doc.lab_test_name }}
  {{ row.lab_test_particulars }} \n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%}\n
AntibioticSensitivity
{{ row.antibiotic }} {{ row.antibiotic_sensitivity }}
\n {%- endif -%}\n\n
\n {%- if (frappe.db.get_value(\"Healthcare Settings\", \"None\", \"employee_name_and_designation_in_print\") == '1') -%}\n
{{doc.employee_name}}
\n
{{doc.employee_designation}}
\n {%- else -%}\n
{{frappe.db.get_value(\"Healthcare Settings\", \"None\", \"custom_signature_in_print\") }}
\n {%- endif -%}\n
\n
\n", + "idx": 0, + "line_breaks": 0, + "modified": "2018-09-04 12:03:47.066918", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Lab Test Print", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Server", + "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/healthcare/report/patient_appointment_analytics/__init__.py b/erpnext/healthcare/report/patient_appointment_analytics/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.js b/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.js new file mode 100644 index 00000000000..18d252ede13 --- /dev/null +++ b/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.js @@ -0,0 +1,128 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports['Patient Appointment Analytics'] = { + "filters": [ + { + fieldname: 'tree_type', + label: __('Tree Type'), + fieldtype: 'Select', + options: ['Healthcare Practitioner', 'Medical Department'], + default: 'Healthcare Practitioner', + reqd: 1 + }, + { + fieldname: 'status', + label: __('Appointment Status'), + fieldtype: 'Select', + options:[ + {label: __('Scheduled'), value: 'Scheduled'}, + {label: __('Open'), value: 'Open'}, + {label: __('Closed'), value: 'Closed'}, + {label: __('Expired'), value: 'Expired'}, + {label: __('Cancelled'), value: 'Cancelled'} + ] + }, + { + fieldname: 'appointment_type', + label: __('Appointment Type'), + fieldtype: 'Link', + options: 'Appointment Type' + }, + { + fieldname: 'practitioner', + label: __('Healthcare Practitioner'), + fieldtype: 'Link', + options: 'Healthcare Practitioner' + }, + { + fieldname: 'department', + label: __('Medical Department'), + fieldtype: 'Link', + options: 'Medical Department' + }, + { + fieldname: 'from_date', + label: __('From Date'), + fieldtype: 'Date', + default: frappe.defaults.get_user_default('year_start_date'), + reqd: 1 + }, + { + fieldname: 'to_date', + label: __('To Date'), + fieldtype: 'Date', + default: frappe.defaults.get_user_default('year_end_date'), + reqd: 1 + }, + { + fieldname: 'range', + label: __('Range'), + fieldtype: 'Select', + options:[ + {label: __('Weekly'), value: 'Weekly'}, + {label: __('Monthly'), value: 'Monthly'}, + {label: __('Quarterly'), value: 'Quarterly'}, + {label: __('Yearly'), value: 'Yearly'} + ], + default: 'Monthly', + reqd: 1 + } + ], + after_datatable_render: function(datatable_obj) { + $(datatable_obj.wrapper).find(".dt-row-0").find('input[type=checkbox]').click(); + }, + get_datatable_options(options) { + return Object.assign(options, { + checkboxColumn: true, + events: { + onCheckRow: function(data) { + row_name = data[2].content; + length = data.length; + + row_values = data.slice(3,length-1).map(function (column) { + return column.content; + }) + + entry = { + 'name': row_name, + 'values': row_values + } + + let raw_data = frappe.query_report.chart.data; + let new_datasets = raw_data.datasets; + + let found = false; + for (let i=0; i < new_datasets.length;i++) { + if (new_datasets[i].name == row_name) { + found = true; + new_datasets.splice(i,1); + break; + } + } + + if (!found) { + new_datasets.push(entry); + } + + let new_data = { + labels: raw_data.labels, + datasets: new_datasets + } + + setTimeout(() => { + frappe.query_report.chart.update(new_data) + }, 500) + + + setTimeout(() => { + frappe.query_report.chart.draw(true); + }, 1000) + + frappe.query_report.raw_chart_data = new_data; + }, + } + }) + }, +}; diff --git a/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.json b/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.json new file mode 100644 index 00000000000..64750c012f1 --- /dev/null +++ b/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.json @@ -0,0 +1,36 @@ +{ + "add_total_row": 1, + "creation": "2020-03-02 15:13:16.273493", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-03-02 15:13:16.273493", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient Appointment Analytics", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Patient Appointment", + "report_name": "Patient Appointment Analytics", + "report_type": "Script Report", + "roles": [ + { + "role": "Healthcare Administrator" + }, + { + "role": "LabTest Approver" + }, + { + "role": "Physician" + }, + { + "role": "Nursing User" + }, + { + "role": "Laboratory User" + } + ] +} \ No newline at end of file diff --git a/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.py b/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.py new file mode 100644 index 00000000000..9c35dbb3ea5 --- /dev/null +++ b/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.py @@ -0,0 +1,194 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import getdate, flt, add_to_date, add_days +from frappe import _ , scrub +from six import iteritems +from erpnext.accounts.utils import get_fiscal_year + +def execute(filters=None): + return Analytics(filters).run() + +class Analytics(object): + def __init__(self, filters=None): + """Patient Appointment Analytics Report.""" + self.filters = frappe._dict(filters or {}) + self.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + self.get_period_date_ranges() + + def run(self): + self.get_columns() + self.get_data() + self.get_chart_data() + + return self.columns, self.data, None, self.chart + + def get_period_date_ranges(self): + from dateutil.relativedelta import relativedelta, MO + from_date, to_date = getdate(self.filters.from_date), getdate(self.filters.to_date) + + increment = { + 'Monthly': 1, + 'Quarterly': 3, + 'Half-Yearly': 6, + 'Yearly': 12 + }.get(self.filters.range, 1) + + if self.filters.range in ['Monthly', 'Quarterly']: + from_date = from_date.replace(day=1) + elif self.filters.range == 'Yearly': + from_date = get_fiscal_year(from_date)[1] + else: + from_date = from_date + relativedelta(from_date, weekday=MO(-1)) + + self.periodic_daterange = [] + for dummy in range(1, 53): + if self.filters.range == 'Weekly': + period_end_date = add_days(from_date, 6) + else: + period_end_date = add_to_date(from_date, months=increment, days=-1) + + if period_end_date > to_date: + period_end_date = to_date + + self.periodic_daterange.append(period_end_date) + + from_date = add_days(period_end_date, 1) + if period_end_date == to_date: + break + + def get_columns(self): + self.columns = [] + + if self.filters.tree_type == 'Healthcare Practitioner': + self.columns.append({ + 'label': _('Healthcare Practitioner'), + 'options': 'Healthcare Practitioner', + 'fieldname': 'practitioner', + 'fieldtype': 'Link', + 'width': 200 + }) + + elif self.filters.tree_type == 'Medical Department': + self.columns.append({ + 'label': _('Medical Department'), + 'fieldname': 'department', + 'fieldtype': 'Link', + 'options': 'Medical Department', + 'width': 150 + }) + + for end_date in self.periodic_daterange: + period = self.get_period(end_date) + self.columns.append({ + 'label': _(period), + 'fieldname': scrub(period), + 'fieldtype': 'Int', + 'width': 120 + }) + + self.columns.append({ + 'label': _('Total'), + 'fieldname': 'total', + 'fieldtype': 'Int', + 'width': 120 + }) + + def get_data(self): + if self.filters.tree_type == 'Healthcare Practitioner': + self.get_appointments_based_on_healthcare_practitioner() + self.get_rows() + + elif self.filters.tree_type == 'Medical Department': + self.get_appointments_based_on_medical_department() + self.get_rows() + + def get_period(self, appointment_date): + if self.filters.range == 'Weekly': + period = 'Week ' + str(appointment_date.isocalendar()[1]) + elif self.filters.range == 'Monthly': + period = str(self.months[appointment_date.month - 1]) + elif self.filters.range == 'Quarterly': + period = 'Quarter ' + str(((appointment_date.month - 1) // 3) + 1) + else: + year = get_fiscal_year(appointment_date, company=self.filters.company) + period = str(year[0]) + + if getdate(self.filters.from_date).year != getdate(self.filters.to_date).year: + period += ' ' + str(appointment_date.year) + + return period + + def get_appointments_based_on_healthcare_practitioner(self): + filters = self.get_common_filters() + + self.entries = frappe.db.get_all('Patient Appointment', + fields=['appointment_date', 'name', 'patient', 'practitioner'], + filters=filters + ) + + def get_appointments_based_on_medical_department(self): + filters = self.get_common_filters() + if not filters.get('department'): + filters['department'] = ('!=', '') + + self.entries = frappe.db.get_all('Patient Appointment', + fields=['appointment_date', 'name', 'patient', 'practitioner', 'department'], + filters=filters + ) + + def get_common_filters(self): + filters = {} + filters['appointment_date'] = ('between', [self.filters.from_date, self.filters.to_date]) + for entry in ['appointment_type', 'practitioner', 'department', 'status']: + if self.filters.get(entry): + filters[entry] = self.filters.get(entry) + + return filters + + def get_rows(self): + self.data = [] + self.get_periodic_data() + + for entity, period_data in iteritems(self.appointment_periodic_data): + if self.filters.tree_type == 'Healthcare Practitioner': + row = {'practitioner': entity} + elif self.filters.tree_type == 'Medical Department': + row = {'department': entity} + + total = 0 + for end_date in self.periodic_daterange: + period = self.get_period(end_date) + amount = flt(period_data.get(period, 0.0)) + row[scrub(period)] = amount + total += amount + + row['total'] = total + + self.data.append(row) + + def get_periodic_data(self): + self.appointment_periodic_data = frappe._dict() + + for d in self.entries: + period = self.get_period(d.get('appointment_date')) + if self.filters.tree_type == 'Healthcare Practitioner': + self.appointment_periodic_data.setdefault(d.practitioner, frappe._dict()).setdefault(period, 0.0) + self.appointment_periodic_data[d.practitioner][period] += 1 + + elif self.filters.tree_type == 'Medical Department': + self.appointment_periodic_data.setdefault(d.department, frappe._dict()).setdefault(period, 0.0) + self.appointment_periodic_data[d.department][period] += 1 + + def get_chart_data(self): + length = len(self.columns) + labels = [d.get("label") for d in self.columns[1:length - 1]] + self.chart = { + "data": { + 'labels': labels, + 'datasets': [] + }, + "type": "line" + } \ No newline at end of file diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 97bb98f6779..246242ad84c 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -4,417 +4,471 @@ from __future__ import unicode_literals import frappe -import datetime from frappe import _ import math -from frappe.utils import time_diff_in_hours, rounded, getdate, add_days +from frappe.utils import time_diff_in_hours, rounded from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account -from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity, update_fee_validity +from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple @frappe.whitelist() def get_healthcare_services_to_invoice(patient): - patient = frappe.get_doc("Patient", patient) + patient = frappe.get_doc('Patient', patient) if patient: - if patient.customer: - item_to_invoice = [] - patient_appointments = frappe.get_list("Patient Appointment",{'patient': patient.name, 'invoiced': False}, - order_by="appointment_date") - if patient_appointments: - fee_validity_details = [] - valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") - max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") - for patient_appointment in patient_appointments: - patient_appointment_obj = frappe.get_doc("Patient Appointment", patient_appointment['name']) + validate_customer_created(patient) + items_to_invoice = [] + patient_appointments = frappe.get_list( + 'Patient Appointment', + fields='*', + filters={'patient': patient.name, 'invoiced': 0}, + order_by='appointment_date' + ) + if patient_appointments: + items_to_invoice = get_fee_validity(patient_appointments) - if patient_appointment_obj.procedure_template: - if frappe.db.get_value("Clinical Procedure Template", patient_appointment_obj.procedure_template, "is_billable") == 1: - item_to_invoice.append({'reference_type': 'Patient Appointment', 'reference_name': patient_appointment_obj.name, 'service': patient_appointment_obj.procedure_template}) - else: - practitioner_exist_in_list = False - skip_invoice = False - if fee_validity_details: - for validity in fee_validity_details: - if validity['practitioner'] == patient_appointment_obj.practitioner: - practitioner_exist_in_list = True - if validity['valid_till'] >= patient_appointment_obj.appointment_date: - validity['visits'] = validity['visits']+1 - if int(max_visit) > validity['visits']: - skip_invoice = True - if not skip_invoice: - validity['visits'] = 1 - validity['valid_till'] = patient_appointment_obj.appointment_date + datetime.timedelta(days=int(valid_days)) - if not practitioner_exist_in_list: - valid_till = patient_appointment_obj.appointment_date + datetime.timedelta(days=int(valid_days)) - visits = 0 - validity_exist = validity_exists(patient_appointment_obj.practitioner, patient_appointment_obj.patient) - if validity_exist: - fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0]) - valid_till = fee_validity.valid_till - visits = fee_validity.visited - fee_validity_details.append({'practitioner': patient_appointment_obj.practitioner, - 'valid_till': valid_till, 'visits': visits}) + encounters = get_encounters_to_invoice(patient) + lab_tests = get_lab_tests_to_invoice(patient) + clinical_procedures = get_clinical_procedures_to_invoice(patient) + inpatient_services = get_inpatient_services_to_invoice(patient) - if not skip_invoice: - practitioner_charge = 0 - income_account = None - service_item = None - if patient_appointment_obj.practitioner: - service_item, practitioner_charge = service_item_and_practitioner_charge(patient_appointment_obj) - income_account = get_income_account(patient_appointment_obj.practitioner, patient_appointment_obj.company) - item_to_invoice.append({'reference_type': 'Patient Appointment', 'reference_name': patient_appointment_obj.name, - 'service': service_item, 'rate': practitioner_charge, - 'income_account': income_account}) + items_to_invoice += encounters + lab_tests + clinical_procedures + inpatient_services + return items_to_invoice - encounters = frappe.get_list("Patient Encounter", {'patient': patient.name, 'invoiced': False, 'docstatus': 1}) - if encounters: - for encounter in encounters: - encounter_obj = frappe.get_doc("Patient Encounter", encounter['name']) - if not encounter_obj.appointment: - practitioner_charge = 0 - income_account = None - service_item = None - if encounter_obj.practitioner: - service_item, practitioner_charge = service_item_and_practitioner_charge(encounter_obj) - income_account = get_income_account(encounter_obj.practitioner, encounter_obj.company) +def validate_customer_created(patient): + if not frappe.db.get_value('Patient', patient.name, 'customer'): + msg = _("Please set a Customer linked to the Patient") + msg += " {0}".format(patient.name) + frappe.throw(msg, title=_('Customer Not Found')) - item_to_invoice.append({'reference_type': 'Patient Encounter', 'reference_name': encounter_obj.name, - 'service': service_item, 'rate': practitioner_charge, - 'income_account': income_account}) +def get_fee_validity(patient_appointments): + if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'): + return - lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False, 'docstatus': 1}) - if lab_tests: - for lab_test in lab_tests: - lab_test_obj = frappe.get_doc("Lab Test", lab_test['name']) - if frappe.db.get_value("Lab Test Template", lab_test_obj.template, "is_billable") == 1: - item_to_invoice.append({'reference_type': 'Lab Test', 'reference_name': lab_test_obj.name, - 'service': frappe.db.get_value("Lab Test Template", lab_test_obj.template, "item")}) - - lab_rxs = frappe.db.sql("""select lp.name from `tabPatient Encounter` et, `tabLab Prescription` lp - where et.patient=%s and lp.parent=et.name and lp.lab_test_created=0 and lp.invoiced=0""", (patient.name)) - if lab_rxs: - for lab_rx in lab_rxs: - rx_obj = frappe.get_doc("Lab Prescription", lab_rx[0]) - if rx_obj.lab_test_code and (frappe.db.get_value("Lab Test Template", rx_obj.lab_test_code, "is_billable") == 1): - item_to_invoice.append({'reference_type': 'Lab Prescription', 'reference_name': rx_obj.name, - 'service': frappe.db.get_value("Lab Test Template", rx_obj.lab_test_code, "item")}) - - procedures = frappe.get_list("Clinical Procedure", {'patient': patient.name, 'invoiced': False}) - if procedures: - for procedure in procedures: - procedure_obj = frappe.get_doc("Clinical Procedure", procedure['name']) - if not procedure_obj.appointment: - if procedure_obj.procedure_template and (frappe.db.get_value("Clinical Procedure Template", procedure_obj.procedure_template, "is_billable") == 1): - item_to_invoice.append({'reference_type': 'Clinical Procedure', 'reference_name': procedure_obj.name, - 'service': frappe.db.get_value("Clinical Procedure Template", procedure_obj.procedure_template, "item")}) - - procedure_rxs = frappe.db.sql("""select pp.name from `tabPatient Encounter` et, - `tabProcedure Prescription` pp where et.patient=%s and pp.parent=et.name and - pp.procedure_created=0 and pp.invoiced=0 and pp.appointment_booked=0""", (patient.name)) - if procedure_rxs: - for procedure_rx in procedure_rxs: - rx_obj = frappe.get_doc("Procedure Prescription", procedure_rx[0]) - if frappe.db.get_value("Clinical Procedure Template", rx_obj.procedure, "is_billable") == 1: - item_to_invoice.append({'reference_type': 'Procedure Prescription', 'reference_name': rx_obj.name, - 'service': frappe.db.get_value("Clinical Procedure Template", rx_obj.procedure, "item")}) - - procedures = frappe.get_list("Clinical Procedure", - {'patient': patient.name, 'invoice_separately_as_consumables': True, 'consumption_invoiced': False, - 'consume_stock': True, 'status': 'Completed'}) - if procedures: - service_item = get_healthcare_service_item('clinical_procedure_consumable_item') - if not service_item: - msg = _(("Please Configure {0} in ").format("Clinical Procedure Consumable Item") \ - + """Healthcare Settings""") - frappe.throw(msg) - for procedure in procedures: - procedure_obj = frappe.get_doc("Clinical Procedure", procedure['name']) - item_to_invoice.append({'reference_type': 'Clinical Procedure', 'reference_name': procedure_obj.name, - 'service': service_item, 'rate': procedure_obj.consumable_total_amount, 'description': procedure_obj.consumption_details}) - - inpatient_services = frappe.db.sql("""select io.name, io.parent from `tabInpatient Record` ip, - `tabInpatient Occupancy` io where ip.patient=%s and io.parent=ip.name and - io.left=1 and io.invoiced=0""", (patient.name)) - if inpatient_services: - for inpatient_service in inpatient_services: - inpatient_occupancy = frappe.get_doc("Inpatient Occupancy", inpatient_service[0]) - service_unit_type = frappe.get_doc("Healthcare Service Unit Type", frappe.db.get_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "service_unit_type")) - if service_unit_type and service_unit_type.is_billable == 1: - hours_occupied = time_diff_in_hours(inpatient_occupancy.check_out, inpatient_occupancy.check_in) - qty = 0.5 - if hours_occupied > 0: - actual_qty = hours_occupied / service_unit_type.no_of_hours - floor = math.floor(actual_qty) - decimal_part = actual_qty - floor - if decimal_part > 0.5: - qty = rounded(floor + 1, 1) - elif decimal_part < 0.5 and decimal_part > 0: - qty = rounded(floor + 0.5, 1) - if qty <= 0: - qty = 0.5 - item_to_invoice.append({'reference_type': 'Inpatient Occupancy', 'reference_name': inpatient_occupancy.name, - 'service': service_unit_type.item, 'qty': qty}) - - return item_to_invoice + items_to_invoice = [] + for appointment in patient_appointments: + if appointment.procedure_template: + if frappe.db.get_value('Clinical Procedure Template', appointment.procedure_template, 'is_billable'): + items_to_invoice.append({ + 'reference_type': 'Patient Appointment', + 'reference_name': appointment.name, + 'service': appointment.procedure_template + }) else: - frappe.throw(_("The Patient {0} do not have customer refrence to invoice").format(patient.name)) + fee_validity = frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name}) + if not fee_validity: + practitioner_charge = 0 + income_account = None + service_item = None + if appointment.practitioner: + service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment) + income_account = get_income_account(appointment.practitioner, appointment.company) + items_to_invoice.append({ + 'reference_type': 'Patient Appointment', + 'reference_name': appointment.name, + 'service': service_item, + 'rate': practitioner_charge, + 'income_account': income_account + }) -def service_item_and_practitioner_charge(doc): - is_ip = doc_is_ip(doc) - if is_ip: - service_item = get_practitioner_service_item(doc.practitioner, "inpatient_visit_charge_item") + return items_to_invoice + + +def get_encounters_to_invoice(patient): + encounters_to_invoice = [] + encounters = frappe.get_list( + 'Patient Encounter', + fields=['*'], + filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1} + ) + if encounters: + for encounter in encounters: + if not encounter.appointment: + practitioner_charge = 0 + income_account = None + service_item = None + if encounter.practitioner: + service_item, practitioner_charge = get_service_item_and_practitioner_charge(encounter) + income_account = get_income_account(encounter.practitioner, encounter.company) + + encounters_to_invoice.append({ + 'reference_type': 'Patient Encounter', + 'reference_name': encounter.name, + 'service': service_item, + 'rate': practitioner_charge, + 'income_account': income_account + }) + + return encounters_to_invoice + + +def get_lab_tests_to_invoice(patient): + lab_tests_to_invoice = [] + lab_tests = frappe.get_list( + 'Lab Test', + fields=['name', 'template'], + filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1} + ) + for lab_test in lab_tests: + item, is_billable = frappe.get_cached_value('Lab Test Template', lab_test.lab_test_code, ['item', 'is_billable']) + if is_billable: + lab_tests_to_invoice.append({ + 'reference_type': 'Lab Test', + 'reference_name': lab_test.name, + 'service': item + }) + + lab_prescriptions = frappe.db.sql( + ''' + SELECT + lp.name, lp.lab_test_code + FROM + `tabPatient Encounter` et, `tabLab Prescription` lp + WHERE + et.patient=%s + and lp.parent=et.name + and lp.lab_test_created=0 + and lp.invoiced=0 + ''', (patient.name), as_dict=1) + + for prescription in lab_prescriptions: + item, is_billable = frappe.get_cached_value('Lab Test Template', prescription.lab_test_code, ['item', 'is_billable']) + if prescription.lab_test_code and is_billable: + lab_tests_to_invoice.append({ + 'reference_type': 'Lab Prescription', + 'reference_name': prescription.name, + 'service': item + }) + + return lab_tests_to_invoice + + +def get_clinical_procedures_to_invoice(patient): + clinical_procedures_to_invoice = [] + procedures = frappe.get_list( + 'Clinical Procedure', + fields='*', + filters={'patient': patient.name, 'invoiced': False} + ) + for procedure in procedures: + if not procedure.appointment: + item, is_billable = frappe.get_cached_value('Clinical Procedure Template', procedure.procedure_template, ['item', 'is_billable']) + if procedure.procedure_template and is_billable: + clinical_procedures_to_invoice.append({ + 'reference_type': 'Clinical Procedure', + 'reference_name': procedure.name, + 'service': item + }) + + # consumables + if procedure.invoice_separately_as_consumables and procedure.consume_stock \ + and procedure.status == 'Completed' and not procedure.consumption_invoiced: + + service_item = get_healthcare_service_item('clinical_procedure_consumable_item') + if not service_item: + msg = _('Please Configure Clinical Procedure Consumable Item in ') + msg += '''Healthcare Settings''' + frappe.throw(msg, title=_('Missing Configuration')) + + clinical_procedures_to_invoice.append({ + 'reference_type': 'Clinical Procedure', + 'reference_name': procedure.name, + 'service': service_item, + 'rate': procedure.consumable_total_amount, + 'description': procedure.consumption_details + }) + + procedure_prescriptions = frappe.db.sql( + ''' + SELECT + pp.name, pp.procedure + FROM + `tabPatient Encounter` et, `tabProcedure Prescription` pp + WHERE + et.patient=%s + and pp.parent=et.name + and pp.procedure_created=0 + and pp.invoiced=0 + and pp.appointment_booked=0 + ''', (patient.name), as_dict=1) + + for prescription in procedure_prescriptions: + item, is_billable = frappe.get_cached_value('Clinical Procedure Template', prescription.procedure, ['item', 'is_billable']) + if is_billable: + clinical_procedures_to_invoice.append({ + 'reference_type': 'Procedure Prescription', + 'reference_name': prescription.name, + 'service': item + }) + + return clinical_procedures_to_invoice + + +def get_inpatient_services_to_invoice(patient): + services_to_invoice = [] + inpatient_services = frappe.db.sql( + ''' + SELECT + io.* + FROM + `tabInpatient Record` ip, `tabInpatient Occupancy` io + WHERE + ip.patient=%s + and io.parent=ip.name + and io.left=1 + and io.invoiced=0 + ''', (patient.name), as_dict=1) + + for inpatient_occupancy in inpatient_services: + service_unit_type = frappe.db.get_value('Healthcare Service Unit', inpatient_occupancy.service_unit, 'service_unit_type') + service_unit_type = frappe.get_cached_doc('Healthcare Service Unit Type', service_unit_type) + if service_unit_type and service_unit_type.is_billable: + hours_occupied = time_diff_in_hours(inpatient_occupancy.check_out, inpatient_occupancy.check_in) + qty = 0.5 + if hours_occupied > 0: + actual_qty = hours_occupied / service_unit_type.no_of_hours + floor = math.floor(actual_qty) + decimal_part = actual_qty - floor + if decimal_part > 0.5: + qty = rounded(floor + 1, 1) + elif decimal_part < 0.5 and decimal_part > 0: + qty = rounded(floor + 0.5, 1) + if qty <= 0: + qty = 0.5 + services_to_invoice.append({ + 'reference_type': 'Inpatient Occupancy', + 'reference_name': inpatient_occupancy.name, + 'service': service_unit_type.item, 'qty': qty + }) + + return services_to_invoice + + +def get_service_item_and_practitioner_charge(doc): + is_inpatient = doc.inpatient_record + if is_inpatient: + service_item = get_practitioner_service_item(doc.practitioner, 'inpatient_visit_charge_item') if not service_item: - service_item = get_healthcare_service_item("inpatient_visit_charge_item") + service_item = get_healthcare_service_item('inpatient_visit_charge_item') else: - service_item = get_practitioner_service_item(doc.practitioner, "op_consulting_charge_item") + service_item = get_practitioner_service_item(doc.practitioner, 'op_consulting_charge_item') if not service_item: - service_item = get_healthcare_service_item("op_consulting_charge_item") + service_item = get_healthcare_service_item('op_consulting_charge_item') if not service_item: - throw_config_service_item(is_ip) + throw_config_service_item(is_inpatient) - practitioner_charge = get_practitioner_charge(doc.practitioner, is_ip) + practitioner_charge = get_practitioner_charge(doc.practitioner, is_inpatient) if not practitioner_charge: - throw_config_practitioner_charge(is_ip, doc.practitioner) + throw_config_practitioner_charge(is_inpatient, doc.practitioner) return service_item, practitioner_charge -def throw_config_service_item(is_ip): - service_item_lable = "Out Patient Consulting Charge Item" - if is_ip: - service_item_lable = "Inpatient Visit Charge Item" - msg = _(("Please Configure {0} in ").format(service_item_lable) \ - + """Healthcare Settings""") - frappe.throw(msg) +def throw_config_service_item(is_inpatient): + service_item_label = _('Out Patient Consulting Charge Item') + if is_inpatient: + service_item_label = _('Inpatient Visit Charge Item') -def throw_config_practitioner_charge(is_ip, practitioner): - charge_name = "OP Consulting Charge" - if is_ip: - charge_name = "Inpatient Visit Charge" + msg = _(('Please Configure {0} in ').format(service_item_label) \ + + '''Healthcare Settings''') + frappe.throw(msg, title=_('Missing Configuration')) + + +def throw_config_practitioner_charge(is_inpatient, practitioner): + charge_name = _('OP Consulting Charge') + if is_inpatient: + charge_name = _('Inpatient Visit Charge') + + msg = _(('Please Configure {0} for Healthcare Practitioner').format(charge_name) \ + + ''' {0}'''.format(practitioner)) + frappe.throw(msg, title=_('Missing Configuration')) - msg = _(("Please Configure {0} for Healthcare Practitioner").format(charge_name) \ - + """ {0}""".format(practitioner)) - frappe.throw(msg) def get_practitioner_service_item(practitioner, service_item_field): - return frappe.db.get_value("Healthcare Practitioner", practitioner, service_item_field) + return frappe.db.get_value('Healthcare Practitioner', practitioner, service_item_field) + def get_healthcare_service_item(service_item_field): - return frappe.db.get_value("Healthcare Settings", None, service_item_field) + return frappe.db.get_single_value('Healthcare Settings', service_item_field) -def doc_is_ip(doc): - is_ip = False - if doc.inpatient_record: - is_ip = True - return is_ip -def get_practitioner_charge(practitioner, is_ip): - if is_ip: - practitioner_charge = frappe.db.get_value("Healthcare Practitioner", practitioner, "inpatient_visit_charge") +def get_practitioner_charge(practitioner, is_inpatient): + if is_inpatient: + practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, 'inpatient_visit_charge') else: - practitioner_charge = frappe.db.get_value("Healthcare Practitioner", practitioner, "op_consulting_charge") + practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, 'op_consulting_charge') if practitioner_charge: return practitioner_charge return False + def manage_invoice_submit_cancel(doc, method): if doc.items: for item in doc.items: - if item.get("reference_dt") and item.get("reference_dn"): - if frappe.get_meta(item.reference_dt).has_field("invoiced"): + if item.get('reference_dt') and item.get('reference_dn'): + if frappe.get_meta(item.reference_dt).has_field('invoiced'): set_invoiced(item, method, doc.name) - if method=="on_submit" and frappe.db.get_value("Healthcare Settings", None, "create_test_on_si_submit") == '1': - create_multiple("Sales Invoice", doc.name) + if method=='on_submit' and frappe.db.get_single_value('Healthcare Settings', 'create_lab_test_on_si_submit'): + create_multiple('Sales Invoice', doc.name) + def set_invoiced(item, method, ref_invoice=None): invoiced = False - if(method=="on_submit"): + if method=='on_submit': validate_invoiced_on_submit(item) invoiced = True if item.reference_dt == 'Clinical Procedure': if get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code: - frappe.db.set_value(item.reference_dt, item.reference_dn, "consumption_invoiced", invoiced) + frappe.db.set_value(item.reference_dt, item.reference_dn, 'consumption_invoiced', invoiced) else: - frappe.db.set_value(item.reference_dt, item.reference_dn, "invoiced", invoiced) + frappe.db.set_value(item.reference_dt, item.reference_dn, 'invoiced', invoiced) else: - frappe.db.set_value(item.reference_dt, item.reference_dn, "invoiced", invoiced) + frappe.db.set_value(item.reference_dt, item.reference_dn, 'invoiced', invoiced) if item.reference_dt == 'Patient Appointment': if frappe.db.get_value('Patient Appointment', item.reference_dn, 'procedure_template'): - dt_from_appointment = "Clinical Procedure" + dt_from_appointment = 'Clinical Procedure' else: - manage_fee_validity(item.reference_dn, method, ref_invoice) - dt_from_appointment = "Patient Encounter" - manage_doc_for_appoitnment(dt_from_appointment, item.reference_dn, invoiced) + dt_from_appointment = 'Patient Encounter' + manage_doc_for_appointment(dt_from_appointment, item.reference_dn, invoiced) elif item.reference_dt == 'Lab Prescription': - manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, "Lab Test", "lab_test_created") + manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, 'Lab Test', 'lab_test_created') elif item.reference_dt == 'Procedure Prescription': - manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, "Clinical Procedure", "procedure_created") + manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, 'Clinical Procedure', 'procedure_created') + def validate_invoiced_on_submit(item): if item.reference_dt == 'Clinical Procedure' and get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code: - is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, "consumption_invoiced") + is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'consumption_invoiced') else: - is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, "invoiced") - if is_invoiced == 1: - frappe.throw(_("The item referenced by {0} - {1} is already invoiced"\ + is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'invoiced') + if is_invoiced: + frappe.throw(_('The item referenced by {0} - {1} is already invoiced'\ ).format(item.reference_dt, item.reference_dn)) + def manage_prescriptions(invoiced, ref_dt, ref_dn, dt, created_check_field): created = frappe.db.get_value(ref_dt, ref_dn, created_check_field) - if created == 1: + if created: # Fetch the doc created for the prescription doc_created = frappe.db.get_value(dt, {'prescription': ref_dn}) frappe.db.set_value(dt, doc_created, 'invoiced', invoiced) -def validity_exists(practitioner, patient): - return frappe.db.exists({ - "doctype": "Fee Validity", - "practitioner": practitioner, - "patient": patient}) -def manage_fee_validity(appointment_name, method, ref_invoice=None): - appointment_doc = frappe.get_doc("Patient Appointment", appointment_name) - validity_exist = validity_exists(appointment_doc.practitioner, appointment_doc.patient) - do_not_update = False - visited = 0 - if validity_exist: - fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0]) - # Check if the validity is valid - if (fee_validity.valid_till >= appointment_doc.appointment_date): - if (method == "on_cancel" and appointment_doc.status != "Closed"): - if ref_invoice == fee_validity.ref_invoice: - visited = fee_validity.visited - 1 - if visited < 0: - visited = 0 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) - do_not_update = True - elif (method == "on_submit" and fee_validity.visited < fee_validity.max_visit): - visited = fee_validity.visited + 1 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) - do_not_update = True - else: - do_not_update = False +def check_fee_validity(appointment): + if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'): + return - if not do_not_update: - fee_validity = update_fee_validity(fee_validity, appointment_doc.appointment_date, ref_invoice) - visited = fee_validity.visited + validity = frappe.db.exists('Fee Validity', { + 'practitioner': appointment.practitioner, + 'patient': appointment.patient, + 'valid_till': ('>=', appointment.appointment_date) + }) + if not validity: + return + + validity = frappe.get_doc('Fee Validity', validity) + return validity + + +def manage_fee_validity(appointment): + fee_validity = check_fee_validity(appointment) + if fee_validity: + if appointment.status == 'Cancelled' and fee_validity.visited > 0: + fee_validity.visited -= 1 + frappe.db.delete('Fee Validity Reference', {'appointment': appointment.name}) + elif fee_validity.status == 'Completed': + return + else: + fee_validity.visited += 1 + fee_validity.append('ref_appointments', { + 'appointment': appointment.name + }) + fee_validity.save(ignore_permissions=True) else: - fee_validity = create_fee_validity(appointment_doc.practitioner, appointment_doc.patient, appointment_doc.appointment_date, ref_invoice) - visited = fee_validity.visited + fee_validity = create_fee_validity(appointment) + return fee_validity - # Mark All Patient Appointment invoiced = True in the validity range do not cross the max visit - if (method == "on_cancel"): - invoiced = True - else: - invoiced = False - patient_appointments = appointments_valid_in_fee_validity(appointment_doc, invoiced) - if patient_appointments and fee_validity: - visit = visited - for appointment in patient_appointments: - if (method == "on_cancel" and appointment.status != "Closed"): - if ref_invoice == fee_validity.ref_invoice: - visited = visited - 1 - if visited < 0: - visited = 0 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) - frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", False) - manage_doc_for_appoitnment("Patient Encounter", appointment.name, False) - elif method == "on_submit" and int(fee_validity.max_visit) > visit: - if ref_invoice == fee_validity.ref_invoice: - visited = visited + 1 - frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) - frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", True) - manage_doc_for_appoitnment("Patient Encounter", appointment.name, True) - if ref_invoice == fee_validity.ref_invoice: - visit = visit + 1 - - if method == "on_cancel": - ref_invoice_in_fee_validity = frappe.db.get_value("Fee Validity", fee_validity.name, 'ref_invoice') - if ref_invoice_in_fee_validity == ref_invoice: - frappe.delete_doc("Fee Validity", fee_validity.name) - -def appointments_valid_in_fee_validity(appointment, invoiced): - valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") - max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") - if int(max_visit) < 1: - max_visit = 1 - valid_days_date = add_days(getdate(appointment.appointment_date), int(valid_days)) - return frappe.get_list("Patient Appointment",{'patient': appointment.patient, 'invoiced': invoiced, - 'appointment_date':("<=", valid_days_date), 'appointment_date':(">=", getdate(appointment.appointment_date)), - 'practitioner': appointment.practitioner}, order_by="appointment_date", limit=int(max_visit)-1) - -def manage_doc_for_appoitnment(dt_from_appointment, appointment, invoiced): - dn_from_appointment = frappe.db.exists( +def manage_doc_for_appointment(dt_from_appointment, appointment, invoiced): + dn_from_appointment = frappe.db.get_value( dt_from_appointment, - { - "appointment": appointment - } + filters={'appointment': appointment} ) if dn_from_appointment: - frappe.db.set_value(dt_from_appointment, dn_from_appointment, "invoiced", invoiced) + frappe.db.set_value(dt_from_appointment, dn_from_appointment, 'invoiced', invoiced) + @frappe.whitelist() def get_drugs_to_invoice(encounter): - encounter = frappe.get_doc("Patient Encounter", encounter) + encounter = frappe.get_doc('Patient Encounter', encounter) if encounter: - patient = frappe.get_doc("Patient", encounter.patient) - if patient and patient.customer: - item_to_invoice = [] + patient = frappe.get_doc('Patient', encounter.patient) + if patient: + if patient.customer: + items_to_invoice = [] for drug_line in encounter.drug_prescription: if drug_line.drug_code: qty = 1 - if frappe.db.get_value("Item", drug_line.drug_code, "stock_uom") == "Nos": + if frappe.db.get_value('Item', drug_line.drug_code, 'stock_uom') == 'Nos': qty = drug_line.get_quantity() - description = False - if drug_line.dosage: - description = drug_line.dosage - if description and drug_line.period: - description += " for "+drug_line.period - if not description: - description = "" - item_to_invoice.append({'drug_code': drug_line.drug_code, 'quantity': qty, - 'description': description}) - return item_to_invoice + + description = '' + if drug_line.dosage and drug_line.period: + description = _('{0} for {1}').format(drug_line.dosage, drug_line.period) + + items_to_invoice.append({ + 'drug_code': drug_line.drug_code, + 'quantity': qty, + 'description': description + }) + return items_to_invoice + else: + validate_customer_created(patient) + @frappe.whitelist() def get_children(doctype, parent, company, is_root=False): - parent_fieldname = 'parent_' + doctype.lower().replace(' ', '_') + parent_fieldname = "parent_" + doctype.lower().replace(" ", "_") fields = [ - 'name as value', - 'is_group as expandable', - 'lft', - 'rgt' + "name as value", + "is_group as expandable", + "lft", + "rgt" ] - # fields = [ 'name', 'is_group', 'lft', 'rgt' ] - filters = [['ifnull(`{0}`,"")'.format(parent_fieldname), '=', '' if is_root else parent]] + # fields = [ "name", "is_group", "lft", "rgt" ] + filters = [["ifnull(`{0}`,'')".format(parent_fieldname), "=", "" if is_root else parent]] if is_root: - fields += ['service_unit_type'] if doctype == 'Healthcare Service Unit' else [] - filters.append(['company', '=', company]) + fields += ["service_unit_type"] if doctype == "Healthcare Service Unit" else [] + filters.append(["company", "=", company]) else: - fields += ['service_unit_type', 'allow_appointments', 'inpatient_occupancy', 'occupancy_status'] if doctype == 'Healthcare Service Unit' else [] - fields += [parent_fieldname + ' as parent'] + fields += ["service_unit_type", "allow_appointments", "inpatient_occupancy", "occupancy_status"] if doctype == "Healthcare Service Unit" else [] + fields += [parent_fieldname + " as parent"] hc_service_units = frappe.get_list(doctype, fields=fields, filters=filters) - if doctype == 'Healthcare Service Unit': + if doctype == "Healthcare Service Unit": for each in hc_service_units: occupancy_msg = "" - if each['expandable'] == 1: + if each["expandable"] == 1: occupied = False vacant = False - child_list = frappe.db.sql(""" - select name, occupancy_status from `tabHealthcare Service Unit` - where inpatient_occupancy = 1 and - lft > %s and rgt < %s""", - (each['lft'], each['rgt'])) + child_list = frappe.db.sql( + ''' + 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: if not occupied: occupied = 0 @@ -425,39 +479,44 @@ def get_children(doctype, parent, company, is_root=False): if child[1] == "Vacant": vacant += 1 if vacant and occupied: - occupancy_total = vacant+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() def get_patient_vitals(patient, from_date=None, to_date=None): if not patient: return - vitals = frappe.db.sql("""select * from `tabVital Signs` where \ - docstatus=1 and patient=%s order by signs_date, signs_time""", \ - (patient), as_dict=1) - if vitals and vitals[0]: + + vitals = frappe.db.get_all('Vital Signs', { + 'docstatus': 1, + 'patient': patient + }, order_by='signs_date, signs_time') + + if len(vitals): return vitals - else: - return False + return False + @frappe.whitelist() def render_docs_as_html(docs): # docs key value pair {doctype: docname} docs_html = "
" for doc in docs: - docs_html += render_doc_as_html(doc['doctype'], doc['docname'])['html'] + "
" + docs_html += render_doc_as_html(doc['doctype'], doc['docname'])['html'] + '
' return {'html': docs_html} + @frappe.whitelist() def render_doc_as_html(doctype, docname, exclude_fields = []): #render document as html, three column layout will break doc = frappe.get_doc(doctype, docname) meta = frappe.get_meta(doctype) doc_html = "
" - section_html = "" - section_label = "" - html = "" + section_html = '' + section_label = '' + html = '' sec_on = False col_on = 0 has_data = False @@ -476,8 +535,8 @@ def render_doc_as_html(doctype, docname, exclude_fields = []): sec_on = True has_data= False col_on = 0 - section_html = "" - html = "" + section_html = '' + html = '' if df.label: section_label = df.label continue @@ -493,19 +552,19 @@ def render_doc_as_html(doctype, docname, exclude_fields = []): d-6 col-sm-6'>" + html + "
" elif sec_on and not col_on: section_html += "
" - html = "" + html = '' col_on += 1 if df.label: html += '
' + df.label continue #on table iterate in items and create table based on in_list_view, append to section html or doc html - if df.fieldtype == "Table": + if df.fieldtype == 'Table': items = doc.get(df.fieldname) if not items: continue child_meta = frappe.get_meta(df.options) if not has_data : has_data = True - table_head = "" - table_row = "" + table_head = '' + table_row = '' create_head = True for item in items: table_row += '' @@ -521,24 +580,24 @@ def render_doc_as_html(doctype, docname, exclude_fields = []): create_head = False table_row += '' if sec_on: - section_html += '' + table_head + table_row + '
' + section_html += "" + table_head + table_row + '
' else: - html += '' \ - + table_head + table_row + '
' + html += "" \ + + table_head + table_row + "
" continue #on other field types add label and value to html if not df.hidden and not df.print_hide and doc.get(df.fieldname) and df.fieldname not in exclude_fields: - html += "
{0} : {1}".format(df.label or df.fieldname, \ + html += '
{0} : {1}'.format(df.label or df.fieldname, \ doc.get(df.fieldname)) if not has_data : has_data = True if sec_on and col_on and has_data: - doc_html += section_html + html + "
" + doc_html += section_html + html + '
' elif sec_on and not col_on and has_data: doc_html += "
" \ - + section_html + html +"
" + + section_html + html +'
' if doc_html: - doc_html = "
" %(doctype, docname) + doc_html + "
" + doc_html = "
" %(doctype, docname) + doc_html + '
' return {'html': doc_html} diff --git a/erpnext/healthcare/web_form/patient_registration/__init__.py b/erpnext/healthcare/web_form/patient_registration/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/web_form/patient_registration/patient_registration.js b/erpnext/healthcare/web_form/patient_registration/patient_registration.js new file mode 100644 index 00000000000..7da3f1fb41c --- /dev/null +++ b/erpnext/healthcare/web_form/patient_registration/patient_registration.js @@ -0,0 +1,3 @@ +frappe.ready(function() { + // bind events here +}); \ No newline at end of file diff --git a/erpnext/healthcare/web_form/patient_registration/patient_registration.json b/erpnext/healthcare/web_form/patient_registration/patient_registration.json new file mode 100644 index 00000000000..9ed92de16f5 --- /dev/null +++ b/erpnext/healthcare/web_form/patient_registration/patient_registration.json @@ -0,0 +1,397 @@ +{ + "accept_payment": 0, + "allow_comments": 0, + "allow_delete": 0, + "allow_edit": 1, + "allow_incomplete": 0, + "allow_multiple": 0, + "allow_print": 0, + "amount": 0.0, + "amount_based_on_field": 0, + "button_label": "Register", + "creation": "2020-03-03 01:01:16.250607", + "currency": "INR", + "doc_type": "Patient", + "docstatus": 0, + "doctype": "Web Form", + "idx": 0, + "introduction_text": "", + "is_standard": 1, + "login_required": 0, + "max_attachment_size": 0, + "modified": "2020-03-26 17:25:15.361918", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "patient-registration", + "owner": "Administrator", + "payment_button_label": "Buy Now", + "published": 1, + "route": "patient-registration", + "route_to_success_link": 0, + "show_attachments": 0, + "show_in_grid": 0, + "show_sidebar": 1, + "sidebar_items": [], + "success_message": "Registration Successfully. Thank You!", + "success_url": "/patient-registration", + "title": "Patient Registration", + "web_form_fields": [ + { + "allow_read_on_all_link_options": 0, + "fieldname": "basic_info", + "fieldtype": "Section Break", + "hidden": 0, + "label": "Patient Demographics", + "max_length": 0, + "max_value": 0, + "options": "fa fa-user", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "first_name", + "fieldtype": "Data", + "hidden": 0, + "label": "First Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "middle_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Middle Name (optional)", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "last_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Last Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "sex", + "fieldtype": "Link", + "hidden": 0, + "label": "Gender", + "max_length": 0, + "max_value": 0, + "options": "Gender", + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "blood_group", + "fieldtype": "Select", + "hidden": 0, + "label": "Blood Group", + "max_length": 0, + "max_value": 0, + "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "", + "fieldtype": "Column Break", + "hidden": 0, + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "dob", + "fieldtype": "Date", + "hidden": 0, + "label": "Date of birth", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "mobile", + "fieldtype": "Data", + "hidden": 0, + "label": "Mobile", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "email", + "fieldtype": "Data", + "hidden": 0, + "label": "Email", + "max_length": 0, + "max_value": 0, + "options": "Email", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "phone", + "fieldtype": "Data", + "hidden": 0, + "label": "Phone", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "", + "fieldtype": "Section Break", + "hidden": 0, + "label": "Personal Details", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "occupation", + "fieldtype": "Data", + "hidden": 0, + "label": "Occupation", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "", + "fieldtype": "Column Break", + "hidden": 0, + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "marital_status", + "fieldtype": "Select", + "hidden": 0, + "label": "Marital Status", + "max_length": 0, + "max_value": 0, + "options": "\nSingle\nMarried\nDivorced\nWidow", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "allergy_medical_and_surgical_history", + "fieldtype": "Section Break", + "hidden": 0, + "label": "Allergies, Medical and Surgical History", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "allergies", + "fieldtype": "Small Text", + "hidden": 0, + "label": "Allergies", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "medication", + "fieldtype": "Small Text", + "hidden": 0, + "label": "Medication", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "column_break_20", + "fieldtype": "Column Break", + "hidden": 0, + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "medical_history", + "fieldtype": "Small Text", + "hidden": 0, + "label": "Medical History", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "surgical_history", + "fieldtype": "Small Text", + "hidden": 0, + "label": "Surgical History", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "risk_factors", + "fieldtype": "Section Break", + "hidden": 0, + "label": "Risk Factors", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "default": "0", + "fieldname": "tobacco_past_use", + "fieldtype": "Check", + "hidden": 0, + "label": "Check if you have a history of Tobacco Consumption", + "max_length": 0, + "max_value": 0, + "options": "", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "default": "0", + "fieldname": "tobacco_current_use", + "fieldtype": "Check", + "hidden": 0, + "label": "Check if you consume Tobacco", + "max_length": 0, + "max_value": 0, + "options": "", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "default": "0", + "fieldname": "alcohol_past_use", + "fieldtype": "Check", + "hidden": 0, + "label": "Check if you have a history of Alcohol Consumption", + "max_length": 0, + "max_value": 0, + "options": "", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "default": "0", + "fieldname": "alcohol_current_use", + "fieldtype": "Check", + "hidden": 0, + "label": "Check if you consume Alcohol", + "max_length": 0, + "max_value": 0, + "options": "", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "column_break_32", + "fieldtype": "Column Break", + "hidden": 0, + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "surrounding_factors", + "fieldtype": "Small Text", + "hidden": 0, + "label": "Occupational Hazards and Environmental Factors", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "other_risk_factors", + "fieldtype": "Small Text", + "hidden": 0, + "label": "Other Risk Factors", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + } + ] +} \ No newline at end of file diff --git a/erpnext/healthcare/web_form/patient_registration/patient_registration.py b/erpnext/healthcare/web_form/patient_registration/patient_registration.py new file mode 100644 index 00000000000..1bc4d1874c8 --- /dev/null +++ b/erpnext/healthcare/web_form/patient_registration/patient_registration.py @@ -0,0 +1,5 @@ +from __future__ import unicode_literals + +def get_context(context): + # do your magic here + pass diff --git a/erpnext/hooks.py b/erpnext/hooks.py index b2dc96178d8..6b6d1e22581 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -239,7 +239,7 @@ doc_events = { "on_trash": "erpnext.regional.check_deletion_permission" }, "Payment Entry": { - "on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.make_status_as_paid"], + "on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status"], "on_trash": "erpnext.regional.check_deletion_permission" }, 'Address': { @@ -269,7 +269,8 @@ auto_cancel_exempted_doctypes= [ scheduler_events = { "all": [ - "erpnext.projects.doctype.project.project.project_status_update_reminder" + "erpnext.projects.doctype.project.project.project_status_update_reminder", + "erpnext.healthcare_healthcare.doctype.patient_appointment.patient_appointment.send_appointment_reminder" ], "hourly": [ 'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails', @@ -304,21 +305,22 @@ scheduler_events = { "erpnext.support.doctype.service_level_agreement.service_level_agreement.check_agreement_status", "erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts", "erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status", - "erpnext.selling.doctype.quotation.quotation.set_expired_status" + "erpnext.selling.doctype.quotation.quotation.set_expired_status", + "erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status" ], "daily_long": [ "erpnext.setup.doctype.email_digest.email_digest.send", "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms", "erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation", "erpnext.hr.utils.generate_leave_encashment", - "erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall.check_for_ltv_shortfall", - "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.make_accrual_interest_entry_for_term_loans" + "erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall.create_process_loan_security_shortfall", + "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_term_loans" ], "monthly_long": [ "erpnext.accounts.deferred_revenue.convert_deferred_revenue_to_income", "erpnext.accounts.deferred_revenue.convert_deferred_expense_to_expense", "erpnext.hr.utils.allocate_earned_leaves", - "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual" + "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_demand_loans" ] } @@ -345,6 +347,8 @@ get_site_info = 'erpnext.utilities.get_site_info' payment_gateway_enabled = "erpnext.accounts.utils.create_payment_gateway_account" +communication_doctypes = ["Customer", "Supplier"] + regional_overrides = { 'France': { 'erpnext.tests.test_regional.test_method': 'erpnext.regional.france.utils.test_method' diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index ec38ef078d4..743aa232391 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -1,66 +1,79 @@ { "cards": [ { - "links": "[\n {\n \"label\": \"Employee\",\n \"name\": \"Employee\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employment Type\",\n \"name\": \"Employment Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Branch\",\n \"name\": \"Branch\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Department\",\n \"name\": \"Department\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Designation\",\n \"name\": \"Designation\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Grade\",\n \"name\": \"Employee Grade\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Group\",\n \"name\": \"Employee Group\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Health Insurance\",\n \"name\": \"Employee Health Insurance\",\n \"type\": \"doctype\"\n }\n]", - "title": "Employee" + "hidden": 0, + "label": "Employee", + "links": "[\n {\n \"label\": \"Employee\",\n \"name\": \"Employee\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employment Type\",\n \"name\": \"Employment Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Branch\",\n \"name\": \"Branch\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Department\",\n \"name\": \"Department\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Designation\",\n \"name\": \"Designation\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Grade\",\n \"name\": \"Employee Grade\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Group\",\n \"name\": \"Employee Group\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Health Insurance\",\n \"name\": \"Employee Health Insurance\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"dependencies\": [\n \"Job Applicant\"\n ],\n \"label\": \"Employee Onboarding\",\n \"name\": \"Employee Onboarding\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Skill Map\",\n \"name\": \"Employee Skill Map\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Promotion\",\n \"name\": \"Employee Promotion\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Transfer\",\n \"name\": \"Employee Transfer\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Separation\",\n \"name\": \"Employee Separation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Onboarding Template\",\n \"name\": \"Employee Onboarding Template\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Separation Template\",\n \"name\": \"Employee Separation Template\",\n \"type\": \"doctype\"\n }\n]", - "title": "Employee Lifecycle" + "hidden": 0, + "label": "Employee Lifecycle", + "links": "[\n {\n \"dependencies\": [\n \"Job Applicant\"\n ],\n \"label\": \"Employee Onboarding\",\n \"name\": \"Employee Onboarding\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Skill Map\",\n \"name\": \"Employee Skill Map\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Promotion\",\n \"name\": \"Employee Promotion\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Transfer\",\n \"name\": \"Employee Transfer\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Separation\",\n \"name\": \"Employee Separation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Onboarding Template\",\n \"name\": \"Employee Onboarding Template\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Separation Template\",\n \"name\": \"Employee Separation Template\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Shift Type\",\n \"name\": \"Shift Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shift Request\",\n \"name\": \"Shift Request\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shift Assignment\",\n \"name\": \"Shift Assignment\",\n \"type\": \"doctype\"\n }\n]", - "title": "Shift Management" + "hidden": 0, + "label": "Shift Management", + "links": "[\n {\n \"label\": \"Shift Type\",\n \"name\": \"Shift Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shift Request\",\n \"name\": \"Shift Request\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shift Assignment\",\n \"name\": \"Shift Assignment\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Application\",\n \"name\": \"Leave Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Allocation\",\n \"name\": \"Leave Allocation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Type\"\n ],\n \"label\": \"Leave Policy\",\n \"name\": \"Leave Policy\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Period\",\n \"name\": \"Leave Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Type\",\n \"name\": \"Leave Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Holiday List\",\n \"name\": \"Holiday List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Compensatory Leave Request\",\n \"name\": \"Compensatory Leave Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Encashment\",\n \"name\": \"Leave Encashment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Block List\",\n \"name\": \"Leave Block List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Application\"\n ],\n \"doctype\": \"Leave Application\",\n \"is_query_report\": true,\n \"label\": \"Employee Leave Balance\",\n \"name\": \"Employee Leave Balance\",\n \"type\": \"report\"\n }\n]", - "title": "Leaves" + "hidden": 0, + "label": "Leaves", + "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Application\",\n \"name\": \"Leave Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Allocation\",\n \"name\": \"Leave Allocation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Type\"\n ],\n \"label\": \"Leave Policy\",\n \"name\": \"Leave Policy\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Period\",\n \"name\": \"Leave Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Type\",\n \"name\": \"Leave Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Holiday List\",\n \"name\": \"Holiday List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Compensatory Leave Request\",\n \"name\": \"Compensatory Leave Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Encashment\",\n \"name\": \"Leave Encashment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Block List\",\n \"name\": \"Leave Block List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Application\"\n ],\n \"doctype\": \"Leave Application\",\n \"is_query_report\": true,\n \"label\": \"Employee Leave Balance\",\n \"name\": \"Employee Leave Balance\",\n \"type\": \"report\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Structure\",\n \"Employee\"\n ],\n \"label\": \"Salary Structure Assignment\",\n \"name\": \"Salary Structure Assignment\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Entry\",\n \"name\": \"Payroll Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Slip\",\n \"name\": \"Salary Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Component\",\n \"name\": \"Salary Component\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Additional Salary\",\n \"name\": \"Additional Salary\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Retention Bonus\",\n \"name\": \"Retention Bonus\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Incentive\",\n \"name\": \"Employee Incentive\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"is_query_report\": true,\n \"label\": \"Salary Register\",\n \"name\": \"Salary Register\",\n \"type\": \"report\"\n }\n]", - "title": "Payroll" + "hidden": 0, + "label": "Payroll", + "links": "[\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Structure\",\n \"Employee\"\n ],\n \"label\": \"Salary Structure Assignment\",\n \"name\": \"Salary Structure Assignment\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Entry\",\n \"name\": \"Payroll Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Slip\",\n \"name\": \"Salary Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Component\",\n \"name\": \"Salary Component\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Additional Salary\",\n \"name\": \"Additional Salary\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Retention Bonus\",\n \"name\": \"Retention Bonus\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Incentive\",\n \"name\": \"Employee Incentive\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"is_query_report\": true,\n \"label\": \"Salary Register\",\n \"name\": \"Salary Register\",\n \"type\": \"report\"\n }\n]" }, { - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"hide_count\": true,\n \"label\": \"Employee Attendance Tool\",\n \"name\": \"Employee Attendance Tool\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Attendance\",\n \"name\": \"Attendance\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Attendance Request\",\n \"name\": \"Attendance Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"hide_count\": true,\n \"label\": \"Upload Attendance\",\n \"name\": \"Upload Attendance\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"hide_count\": true,\n \"label\": \"Employee Checkin\",\n \"name\": \"Employee Checkin\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Attendance\"\n ],\n \"doctype\": \"Attendance\",\n \"is_query_report\": true,\n \"label\": \"Monthly Attendance Sheet\",\n \"name\": \"Monthly Attendance Sheet\",\n \"type\": \"report\"\n }\n]", - "title": "Attendance" + "hidden": 0, + "label": "Attendance", + "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"hide_count\": true,\n \"label\": \"Employee Attendance Tool\",\n \"name\": \"Employee Attendance Tool\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Attendance\",\n \"name\": \"Attendance\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Attendance Request\",\n \"name\": \"Attendance Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"hide_count\": true,\n \"label\": \"Upload Attendance\",\n \"name\": \"Upload Attendance\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"hide_count\": true,\n \"label\": \"Employee Checkin\",\n \"name\": \"Employee Checkin\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Attendance\"\n ],\n \"doctype\": \"Attendance\",\n \"is_query_report\": true,\n \"label\": \"Monthly Attendance Sheet\",\n \"name\": \"Monthly Attendance Sheet\",\n \"type\": \"report\"\n }\n]" }, { - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Expense Claim\",\n \"name\": \"Expense Claim\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Advance\",\n \"name\": \"Employee Advance\",\n \"type\": \"doctype\"\n }\n]", - "title": "Expense Claims" + "hidden": 0, + "label": "Expense Claims", + "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Expense Claim\",\n \"name\": \"Expense Claim\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Advance\",\n \"name\": \"Employee Advance\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-cog", - "links": "[\n {\n \"label\": \"HR Settings\",\n \"name\": \"HR Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Daily Work Summary Group\",\n \"name\": \"Daily Work Summary Group\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Team Updates\",\n \"name\": \"team-updates\",\n \"type\": \"page\"\n }\n]", - "title": "Settings" + "hidden": 0, + "label": "Settings", + "links": "[\n {\n \"label\": \"HR Settings\",\n \"name\": \"HR Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Daily Work Summary Group\",\n \"name\": \"Daily Work Summary Group\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Team Updates\",\n \"name\": \"team-updates\",\n \"type\": \"page\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Vehicle\",\n \"name\": \"Vehicle\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Vehicle Log\",\n \"name\": \"Vehicle Log\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Vehicle\"\n ],\n \"doctype\": \"Vehicle\",\n \"is_query_report\": true,\n \"label\": \"Vehicle Expenses\",\n \"name\": \"Vehicle Expenses\",\n \"type\": \"report\"\n }\n]", - "title": "Fleet Management" + "hidden": 0, + "label": "Fleet Management", + "links": "[\n {\n \"label\": \"Vehicle\",\n \"name\": \"Vehicle\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Vehicle Log\",\n \"name\": \"Vehicle Log\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Vehicle\"\n ],\n \"doctype\": \"Vehicle\",\n \"is_query_report\": true,\n \"label\": \"Vehicle Expenses\",\n \"name\": \"Vehicle Expenses\",\n \"type\": \"report\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Job Opening\",\n \"name\": \"Job Opening\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Applicant\",\n \"name\": \"Job Applicant\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Offer\",\n \"name\": \"Job Offer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Staffing Plan\",\n \"name\": \"Staffing Plan\",\n \"type\": \"doctype\"\n }\n]", - "title": "Recruitment" + "hidden": 0, + "label": "Recruitment", + "links": "[\n {\n \"label\": \"Job Opening\",\n \"name\": \"Job Opening\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Applicant\",\n \"name\": \"Job Applicant\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Offer\",\n \"name\": \"Job Offer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Staffing Plan\",\n \"name\": \"Staffing Plan\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n }\n]", - "title": "Loans" + "hidden": 0, + "label": "Loans", + "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Training Program\",\n \"name\": \"Training Program\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Training Event\",\n \"name\": \"Training Event\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Training Result\",\n \"name\": \"Training Result\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Training Feedback\",\n \"name\": \"Training Feedback\",\n \"type\": \"doctype\"\n }\n]", - "title": "Training" + "hidden": 0, + "label": "Training", + "links": "[\n {\n \"label\": \"Training Program\",\n \"name\": \"Training Program\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Training Event\",\n \"name\": \"Training Event\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Training Result\",\n \"name\": \"Training Result\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Training Feedback\",\n \"name\": \"Training Feedback\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-list", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"doctype\": \"Employee\",\n \"is_query_report\": true,\n \"label\": \"Employee Birthday\",\n \"name\": \"Employee Birthday\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"doctype\": \"Employee\",\n \"is_query_report\": true,\n \"label\": \"Employees working on a holiday\",\n \"name\": \"Employees working on a holiday\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"doctype\": \"Employee\",\n \"is_query_report\": true,\n \"label\": \"Department Analytics\",\n \"name\": \"Department Analytics\",\n \"type\": \"report\"\n }\n]", - "title": "Reports" + "hidden": 0, + "label": "Reports", + "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"doctype\": \"Employee\",\n \"is_query_report\": true,\n \"label\": \"Employee Birthday\",\n \"name\": \"Employee Birthday\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"doctype\": \"Employee\",\n \"is_query_report\": true,\n \"label\": \"Employees working on a holiday\",\n \"name\": \"Employees working on a holiday\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"doctype\": \"Employee\",\n \"is_query_report\": true,\n \"label\": \"Department Analytics\",\n \"name\": \"Department Analytics\",\n \"type\": \"report\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Appraisal\",\n \"name\": \"Appraisal\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Appraisal Template\",\n \"name\": \"Appraisal Template\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Energy Point Rule\",\n \"name\": \"Energy Point Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Energy Point Log\",\n \"name\": \"Energy Point Log\",\n \"type\": \"doctype\"\n }\n]", - "title": "Performance" + "hidden": 0, + "label": "Performance", + "links": "[\n {\n \"label\": \"Appraisal\",\n \"name\": \"Appraisal\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Appraisal Template\",\n \"name\": \"Appraisal Template\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Energy Point Rule\",\n \"name\": \"Energy Point Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Energy Point Log\",\n \"name\": \"Energy Point Log\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Application\",\n \"name\": \"Employee Benefit Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Claim\",\n \"name\": \"Employee Benefit Claim\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n }\n]", - "title": "Employee Tax and Benefits" + "hidden": 0, + "label": "Employee Tax and Benefits", + "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Application\",\n \"name\": \"Employee Benefit Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Claim\",\n \"name\": \"Employee Benefit Claim\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n }\n]" } ], "category": "Modules", @@ -75,7 +88,7 @@ "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-03-12 16:30:35.211246", + "modified": "2020-04-01 11:28:50.860012", "modified_by": "Administrator", "module": "HR", "name": "HR", @@ -85,37 +98,37 @@ "shortcuts": [ { "format": "{} Active", - "is_query_report": 0, + "label": "Employee", "link_to": "Employee", "stats_filter": "{\"status\":\"Active\"}", "type": "DocType" }, { "format": "{} Unpaid", - "is_query_report": 0, + "label": "Expense Claim", "link_to": "Expense Claim", "stats_filter": "{\"approval_status\":\"Draft\"}", "type": "DocType" }, { "format": "{} Open", - "is_query_report": 0, + "label": "Job Applicant", "link_to": "Job Applicant", "stats_filter": "{\n \"status\": \"Open\"\n}", "type": "DocType" }, { - "is_query_report": 0, + "label": "Salary Structure", "link_to": "Salary Structure", "type": "DocType" }, { - "is_query_report": 0, + "label": "Leave Application", "link_to": "Leave Application", "type": "DocType" }, { - "is_query_report": 0, + "label": "Salary Register", "link_to": "Salary Register", "type": "Report" } diff --git a/erpnext/hr/doctype/employee/employee.js b/erpnext/hr/doctype/employee/employee.js index 7d153d432c5..c21d4b893cc 100755 --- a/erpnext/hr/doctype/employee/employee.js +++ b/erpnext/hr/doctype/employee/employee.js @@ -55,8 +55,8 @@ frappe.ui.form.on('Employee',{ }; }); }, - prefered_contact_email:function(frm){ - frm.events.update_contact(frm) + prefered_contact_email:function(frm){ + frm.events.update_contact(frm) }, personal_email:function(frm){ frm.events.update_contact(frm) diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index 3f0b9c49ac3..13c202c7759 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -259,7 +259,8 @@ "bold": 1, "fieldname": "emergency_phone_number", "fieldtype": "Data", - "label": "Emergency Phone" + "label": "Emergency Phone", + "options": "Phone" }, { "bold": 1, @@ -480,7 +481,8 @@ { "fieldname": "cell_number", "fieldtype": "Data", - "label": "Mobile" + "label": "Mobile", + "options": "Phone" }, { "fieldname": "prefered_contact_email", @@ -787,7 +789,7 @@ "idx": 24, "image_field": "image", "links": [], - "modified": "2020-01-09 04:23:55.611366", + "modified": "2020-04-08 12:25:34.306695", "modified_by": "Administrator", "module": "HR", "name": "Employee", @@ -834,6 +836,5 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1 -} + "title_field": "employee_name" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index f78e17fb189..c4417515253 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -580,19 +580,22 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date): return leave_days def skip_expiry_leaves(leave_entry, date): - ''' Checks whether the expired leaves coincide with the to_date of leave balance check ''' + ''' Checks whether the expired leaves coincide with the to_date of leave balance check. + This allows backdated leave entry creation for non carry forwarded allocation ''' end_date = frappe.db.get_value("Leave Allocation", {'name': leave_entry.transaction_name}, ['to_date']) return True if end_date == date and not leave_entry.is_carry_forward else False def get_leave_entries(employee, leave_type, from_date, to_date): - ''' Returns leave entries between from_date and to_date ''' + ''' Returns leave entries between from_date and to_date. ''' return frappe.db.sql(""" SELECT employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, is_carry_forward, is_expired FROM `tabLeave Ledger Entry` WHERE employee=%(employee)s AND leave_type=%(leave_type)s - AND docstatus=1 AND leaves<0 + AND docstatus=1 + AND (leaves<0 + OR is_expired=1) AND (from_date between %(from_date)s AND %(to_date)s OR to_date between %(from_date)s AND %(to_date)s OR (from_date < %(from_date)s AND to_date > %(to_date)s)) @@ -687,8 +690,7 @@ def add_leaves(events, start, end, filter_conditions=None): "to_date": d.to_date, "docstatus": d.docstatus, "color": d.color, - "title": cstr(d.employee_name) + \ - (d.half_day and _(" (Half Day)") or ""), + "title": cstr(d.employee_name) + (' ' + _('(Half Day)') if d.half_day else ''), } if e not in events: events.append(e) diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index ad2cc02fd7c..7d6fd422c0f 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -8,7 +8,6 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import getdate, nowdate, flt from erpnext.hr.utils import set_employee_name -from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index bfc6d95d172..9ed58c9e59b 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -141,6 +141,7 @@ def expire_allocation(allocation, expiry_date=None): leaves = get_remaining_leaves(allocation) expiry_date = expiry_date if expiry_date else allocation.to_date + # allows expired leaves entry to be created/reverted if leaves: args = dict( leaves=flt(leaves) * -1, @@ -160,6 +161,8 @@ def expire_carried_forward_allocation(allocation): from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type, allocation.from_date, allocation.to_date) leaves = flt(allocation.leaves) + flt(leaves_taken) + + # allow expired leaves entry to be created if leaves > 0: args = frappe._dict( transaction_name=allocation.name, diff --git a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py index 35f5a57a1c3..49671d5e224 100644 --- a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py +++ b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py @@ -13,7 +13,7 @@ from erpnext.hr.doctype.salary_slip.test_salary_slip import get_salary_component make_earning_salary_component, make_deduction_salary_component from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry -from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import make_accrual_interest_entry_for_term_loans +from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans class TestPayrollEntry(unittest.TestCase): def setUp(self): @@ -81,7 +81,7 @@ class TestPayrollEntry(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1)) - make_accrual_interest_entry_for_term_loans(posting_date=nowdate()) + process_loan_interest_accrual_for_term_loans(posting_date=nowdate()) dates = get_start_end_dates('Monthly', nowdate()) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json index e73d41a28ed..097d3a096b0 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.json +++ b/erpnext/hr/doctype/salary_slip/salary_slip.json @@ -372,8 +372,7 @@ "fieldtype": "Table", "label": "Employee Loan", "options": "Salary Slip Loan", - "print_hide": 1, - "read_only": 1 + "print_hide": 1 }, { "fieldname": "section_break_43", @@ -464,7 +463,7 @@ "idx": 9, "is_submittable": 1, "links": [], - "modified": "2019-12-31 17:13:45.146271", + "modified": "2020-04-09 20:02:53.159827", "modified_by": "Administrator", "module": "HR", "name": "Salary Slip", diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index d03a3dd9a36..223c4e3e3bf 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -356,13 +356,13 @@ class SalarySlip(TransactionBase): def eval_condition_and_formula(self, d, data): try: - condition = d.condition.strip() if d.condition else None + condition = d.condition.strip().replace("\n", " ") if d.condition else None if condition: if not frappe.safe_eval(condition, self.whitelisted_globals, data): return None amount = d.amount if d.amount_based_on_formula: - formula = d.formula.strip() if d.formula else None + formula = d.formula.strip().replace("\n", " ") if d.formula else None if formula: amount = flt(frappe.safe_eval(formula, self.whitelisted_globals, data), d.precision("amount")) if amount: @@ -755,30 +755,37 @@ class SalarySlip(TransactionBase): d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0] def set_loan_repayment(self): - self.set('loans', []) self.total_loan_repayment = 0 self.total_interest_amount = 0 self.total_principal_amount = 0 - for loan in self.get_loan_details(): + if not self.get('loans'): + for loan in self.get_loan_details(): - amounts = calculate_amounts(loan.name, self.posting_date, "Regular Payment") + amounts = calculate_amounts(loan.name, self.posting_date, "Regular Payment") - total_payment = amounts['interest_amount'] + amounts['payable_principal_amount'] + if amounts['interest_amount'] or amounts['payable_principal_amount']: + self.append('loans', { + 'loan': loan.name, + 'total_payment': amounts['interest_amount'] + amounts['payable_principal_amount'], + 'interest_amount': amounts['interest_amount'], + 'principal_amount': amounts['payable_principal_amount'], + 'loan_account': loan.loan_account, + 'interest_income_account': loan.interest_income_account + }) - if total_payment: - self.append('loans', { - 'loan': loan.name, - 'total_payment': total_payment, - 'interest_amount': amounts['interest_amount'], - 'principal_amount': amounts['payable_principal_amount'], - 'loan_account': loan.loan_account, - 'interest_income_account': loan.interest_income_account - }) + for payment in self.get('loans'): + amounts = calculate_amounts(payment.loan, self.posting_date, "Regular Payment") + total_amount = amounts['interest_amount'] + amounts['payable_principal_amount'] + if payment.total_payment > total_amount: + frappe.throw(_("""Row {0}: Paid amount {1} is greater than pending accrued amount {2} + against loan {3}""").format(payment.idx, frappe.bold(payment.total_payment), + frappe.bold(total_amount), frappe.bold(payment.loan))) - self.total_loan_repayment += total_payment - self.total_interest_amount += amounts['interest_amount'] - self.total_principal_amount += amounts['payable_principal_amount'] + self.total_interest_amount += payment.interest_amount + self.total_principal_amount += payment.principal_amount + + self.total_loan_repayment += payment.total_payment def get_loan_details(self): diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index 9acfd1f5c64..ecccac7d416 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -146,7 +146,7 @@ class TestSalarySlip(unittest.TestCase): def test_loan_repayment_salary_slip(self): from erpnext.loan_management.doctype.loan.test_loan import create_loan_type, create_loan, make_loan_disbursement_entry, create_loan_accounts - from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import make_accrual_interest_entry_for_term_loans + from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans applicant = make_employee("test_loanemployee@salary.com", company="_Test Company") @@ -166,7 +166,7 @@ class TestSalarySlip(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1)) - make_accrual_interest_entry_for_term_loans(posting_date=nowdate()) + process_loan_interest_accrual_for_term_loans(posting_date=nowdate()) ss = make_employee_salary_slip("test_loanemployee@salary.com", "Monthly") ss.submit() diff --git a/erpnext/loan_management/desk_page/loan_management/loan_management.json b/erpnext/loan_management/desk_page/loan_management/loan_management.json index 1d60ca4913a..691d2c1e0cb 100644 --- a/erpnext/loan_management/desk_page/loan_management/loan_management.json +++ b/erpnext/loan_management/desk_page/loan_management/loan_management.json @@ -1,24 +1,29 @@ { "cards": [ { - "links": "[\n {\n \"description\": \"Loan Type for interest and penalty rates\",\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loan Applications from customers and employees.\",\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loans provided to customers and employees.\",\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n }\n]", - "title": "Loan" + "hidden": 0, + "label": "Loan", + "links": "[\n {\n \"description\": \"Loan Type for interest and penalty rates\",\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loan Applications from customers and employees.\",\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loans provided to customers and employees.\",\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Process Loan Security Shortfall\",\n \"name\": \"Process Loan Security Shortfall\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Process Loan Interest Accrual\",\n \"name\": \"Process Loan Interest Accrual\",\n \"type\": \"doctype\"\n }\n]", - "title": "Loan Processes" + "hidden": 0, + "label": "Loan Processes", + "links": "[\n {\n \"label\": \"Process Loan Security Shortfall\",\n \"name\": \"Process Loan Security Shortfall\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Process Loan Interest Accrual\",\n \"name\": \"Process Loan Interest Accrual\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Loan Disbursement\",\n \"name\": \"Loan Disbursement\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Repayment\",\n \"name\": \"Loan Repayment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Interest Accrual\",\n \"name\": \"Loan Interest Accrual\",\n \"type\": \"doctype\"\n }\n]", - "title": "Disbursement and Repayment" + "hidden": 0, + "label": "Disbursement and Repayment", + "links": "[\n {\n \"label\": \"Loan Disbursement\",\n \"name\": \"Loan Disbursement\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Repayment\",\n \"name\": \"Loan Repayment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Interest Accrual\",\n \"name\": \"Loan Interest Accrual\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Loan Security Type\",\n \"name\": \"Loan Security Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security Price\",\n \"name\": \"Loan Security Price\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security\",\n \"name\": \"Loan Security\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security Pledge\",\n \"name\": \"Loan Security Pledge\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security Unpledge\",\n \"name\": \"Loan Security Unpledge\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security Shortfall\",\n \"name\": \"Loan Security Shortfall\",\n \"type\": \"doctype\"\n }\n]", - "title": "Loan Security" + "hidden": 0, + "label": "Loan Security", + "links": "[\n {\n \"label\": \"Loan Security Type\",\n \"name\": \"Loan Security Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security Price\",\n \"name\": \"Loan Security Price\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security\",\n \"name\": \"Loan Security\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security Pledge\",\n \"name\": \"Loan Security Pledge\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security Unpledge\",\n \"name\": \"Loan Security Unpledge\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security Shortfall\",\n \"name\": \"Loan Security Shortfall\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"dependencies\": [\n \"Loan Repayment\"\n ],\n \"doctype\": \"Loan Repayment\",\n \"incomplete_dependencies\": [\n \"Loan Repayment\"\n ],\n \"is_query_report\": true,\n \"label\": \"Loan Repayment and Closure\",\n \"name\": \"Loan Repayment and Closure\",\n \"route\": \"#query-report/Loan Repayment and Closure\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Loan Security Pledge\"\n ],\n \"doctype\": \"Loan Security Pledge\",\n \"incomplete_dependencies\": [\n \"Loan Security Pledge\"\n ],\n \"is_query_report\": true,\n \"label\": \"Loan Security Status\",\n \"name\": \"Loan Security Status\",\n \"route\": \"#query-report/Loan Security Status\",\n \"type\": \"report\"\n }\n]", - "title": "Reports" + "hidden": 0, + "label": "Reports", + "links": "[\n {\n \"dependencies\": [\n \"Loan Repayment\"\n ],\n \"doctype\": \"Loan Repayment\",\n \"incomplete_dependencies\": [\n \"Loan Repayment\"\n ],\n \"is_query_report\": true,\n \"label\": \"Loan Repayment and Closure\",\n \"name\": \"Loan Repayment and Closure\",\n \"route\": \"#query-report/Loan Repayment and Closure\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Loan Security Pledge\"\n ],\n \"doctype\": \"Loan Security Pledge\",\n \"incomplete_dependencies\": [\n \"Loan Security Pledge\"\n ],\n \"is_query_report\": true,\n \"label\": \"Loan Security Status\",\n \"name\": \"Loan Security Status\",\n \"route\": \"#query-report/Loan Security Status\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", @@ -32,7 +37,7 @@ "idx": 0, "is_standard": 1, "label": "Loan Management", - "modified": "2020-03-12 16:38:00.347959", + "modified": "2020-04-01 11:28:51.380509", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Management", @@ -41,12 +46,12 @@ "pin_to_top": 0, "shortcuts": [ { - "is_query_report": 0, + "label": "Loan Application", "link_to": "Loan Application", "type": "DocType" }, { - "is_query_report": 0, + "label": "Loan", "link_to": "Loan", "type": "DocType" } diff --git a/erpnext/loan_management/doctype/loan/loan.js b/erpnext/loan_management/doctype/loan/loan.js index 8b220171e8a..9cd8b2e90a9 100644 --- a/erpnext/loan_management/doctype/loan/loan.js +++ b/erpnext/loan_management/doctype/loan/loan.js @@ -97,6 +97,8 @@ frappe.ui.form.on('Loan', { "company": frm.doc.company, "applicant_type": frm.doc.applicant_type, "applicant": frm.doc.applicant, + "pending_amount": frm.doc.loan_amount - frm.doc.disbursed_amount > 0 ? + frm.doc.loan_amount - frm.doc.disbursed_amount : 0, "as_dict": 1 }, method: "erpnext.loan_management.doctype.loan.loan.make_loan_disbursement", @@ -149,10 +151,10 @@ frappe.ui.form.on('Loan', { return frappe.call({ method: "erpnext.loan_management.doctype.loan.loan.get_loan_application", args: { - "loan_application": frm.doc.loan_application - }, - callback: function (r) { - if (!r.exc && r.message) { + "loan_application": frm.doc.loan_application + }, + callback: function (r) { + if (!r.exc && r.message) { let loan_fields = ["loan_type", "loan_amount", "repayment_method", "monthly_repayment_amount", "repayment_periods", "rate_of_interest", "is_secured_loan"] diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json index 2834e5b655a..b04e82274e9 100644 --- a/erpnext/loan_management/doctype/loan/loan.json +++ b/erpnext/loan_management/doctype/loan/loan.json @@ -126,7 +126,7 @@ "depends_on": "eval:doc.applicant_type==\"Employee\"", "fieldname": "repay_from_salary", "fieldtype": "Check", - "label": "Repay from Salary" + "label": "Repay From Salary" }, { "fieldname": "section_break_8", @@ -178,6 +178,8 @@ }, { "depends_on": "is_term_loan", + "fetch_from": "loan_application.repayment_amount", + "fetch_if_empty": 1, "fieldname": "monthly_repayment_amount", "fieldtype": "Currency", "label": "Monthly Repayment Amount", @@ -350,7 +352,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-02-07 01:31:25.172173", + "modified": "2020-04-13 13:16:10.192624", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index 696410b7bb5..c7a2fba878e 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -19,6 +19,7 @@ class Loan(AccountsController): self.validate_loan_security_pledge() self.validate_loan_amount() self.check_sanctioned_amount_limit() + self.validate_repay_from_salary() if self.is_term_loan: validate_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, @@ -77,6 +78,10 @@ class Loan(AccountsController): if sanctioned_amount_limit and flt(self.loan_amount) + flt(total_loan_amount) > flt(sanctioned_amount_limit): frappe.throw(_("Sanctioned Amount limit crossed for {0} {1}").format(self.applicant_type, frappe.bold(self.applicant))) + def validate_repay_from_salary(self): + if not self.is_term_loan and self.repay_from_salary: + frappe.throw(_("Repay From Salary can be selected only for term loans")) + def make_repayment_schedule(self): if not self.repayment_start_date: @@ -198,7 +203,7 @@ def close_loan(loan, total_amount_paid): frappe.db.set_value("Loan", loan, "status", "Closed") @frappe.whitelist() -def make_loan_disbursement(loan, company, applicant_type, applicant, disbursed_amount=0, as_dict=0): +def make_loan_disbursement(loan, company, applicant_type, applicant, pending_amount=0, as_dict=0): disbursement_entry = frappe.new_doc("Loan Disbursement") disbursement_entry.against_loan = loan disbursement_entry.applicant_type = applicant_type @@ -206,8 +211,7 @@ def make_loan_disbursement(loan, company, applicant_type, applicant, disbursed_a disbursement_entry.company = company disbursement_entry.disbursement_date = nowdate() - if disbursed_amount: - disbursement_entry.disbursed_amount = disbursed_amount + disbursement_entry.disbursed_amount = pending_amount if as_dict: return disbursement_entry.as_dict() else: diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index 759b0d8e094..2d1ad33ed01 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -10,10 +10,10 @@ from frappe.utils import (nowdate, add_days, getdate, now_datetime, add_to_date, add_months, get_first_day, get_last_day, flt, date_diff) from erpnext.selling.doctype.customer.test_customer import get_customer_dict from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee -from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual -from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_term_loans, days_in_year) - -from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import check_for_ltv_shortfall +from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (process_loan_interest_accrual_for_demand_loans, + process_loan_interest_accrual_for_term_loans) +from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year +from erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall import create_process_loan_security_shortfall class TestLoan(unittest.TestCase): def setUp(self): @@ -145,17 +145,23 @@ class TestLoan(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) - process_loan_interest_accrual(posting_date = last_date) + process_loan_interest_accrual_for_demand_loans(posting_date = last_date) repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 10), "Regular Payment", 111118.68) repayment_entry.save() + repayment_entry.submit() penalty_amount = (accrued_interest_amount * 5 * 25) / (100 * days_in_year(get_datetime(first_date).year)) - - self.assertEquals(flt(repayment_entry.interest_payable, 2), flt(accrued_interest_amount, 2)) self.assertEquals(flt(repayment_entry.penalty_amount, 2), flt(penalty_amount, 2)) - repayment_entry.submit() + amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount', + 'paid_principal_amount']) + + loan.load_from_db() + + self.assertEquals(amounts[0], repayment_entry.interest_payable) + self.assertEquals(flt(loan.total_principal_paid, 2), flt(repayment_entry.amount_paid - + penalty_amount - amounts[0], 2)) def test_loan_closure_repayment(self): pledges = [] @@ -186,18 +192,22 @@ class TestLoan(unittest.TestCase): / (days_in_year(get_datetime(first_date).year) * 100) make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) - process_loan_interest_accrual(posting_date = last_date) + process_loan_interest_accrual_for_demand_loans(posting_date = last_date) repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5), - "Loan Closure", 13315.0681) - repayment_entry.save() + "Loan Closure", flt(loan.loan_amount + accrued_interest_amount)) + repayment_entry.submit() - repayment_entry.amount_paid = repayment_entry.payable_amount + amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount', + 'paid_principal_amount']) - self.assertEquals(flt(repayment_entry.interest_payable, 3), flt(accrued_interest_amount, 3)) + unaccrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * 6) \ + / (days_in_year(get_datetime(first_date).year) * 100) + + self.assertEquals(flt(amounts[0] + unaccrued_interest_amount, 3), + flt(accrued_interest_amount, 3)) self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0) - repayment_entry.submit() loan.load_from_db() self.assertEquals(loan.status, "Loan Closure Requested") @@ -224,60 +234,18 @@ class TestLoan(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1)) - make_accrual_interest_entry_for_term_loans(posting_date=nowdate()) + process_loan_interest_accrual_for_term_loans(posting_date=nowdate()) repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(get_last_day(nowdate()), 5), - "Regular Payment", 89768.7534247) + "Regular Payment", 89768.75) - repayment_entry.save() repayment_entry.submit() - repayment_entry.load_from_db() + amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount', + 'paid_principal_amount']) - self.assertEquals(repayment_entry.interest_payable, 11250.00) - self.assertEquals(repayment_entry.payable_principal_amount, 78303.00) - - def test_partial_loan_repayment(self): - pledges = [] - pledges.append({ - "loan_security": "Test Security 1", - "qty": 4000.00, - "haircut": 50 - }) - - loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) - - loan = create_demand_loan(self.applicant2, "Demand Loan", loan_security_pledge.name, - posting_date=get_first_day(nowdate())) - - loan.submit() - - self.assertEquals(loan.loan_amount, 1000000) - - first_date = '2019-10-01' - last_date = '2019-10-30' - - no_of_days = date_diff(last_date, first_date) + 1 - - accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \ - / (days_in_year(get_datetime().year) * 100) - - make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) - - process_loan_interest_accrual(posting_date = add_days(first_date, 15)) - process_loan_interest_accrual(posting_date = add_days(first_date, 30)) - - repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 1), "Regular Payment", 6500) - repayment_entry.save() - repayment_entry.submit() - - penalty_amount = (accrued_interest_amount * 4 * 25) / (100 * days_in_year(get_datetime(first_date).year)) - - lia1 = frappe.get_value("Loan Interest Accrual", {"loan": loan.name, "is_paid": 1}, 'name') - lia2 = frappe.get_value("Loan Interest Accrual", {"loan": loan.name, "is_paid": 0}, 'name') - - self.assertTrue(lia1) - self.assertTrue(lia2) + self.assertEquals(amounts[0], 11250.00) + self.assertEquals(amounts[1], 78303.00) def test_security_shortfall(self): pledges = [] @@ -294,10 +262,10 @@ class TestLoan(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount) - frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 100 + frappe.db.sql("""UPDATE `tabLoan Security Price` SET loan_security_price = 100 where loan_security='Test Security 2'""") - check_for_ltv_shortfall() + create_process_loan_security_shortfall() loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name}) self.assertTrue(loan_security_shortfall) diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.js b/erpnext/loan_management/doctype/loan_application/loan_application.js index 57050d86c61..aba5f4260c4 100644 --- a/erpnext/loan_management/doctype/loan_application/loan_application.js +++ b/erpnext/loan_management/doctype/loan_application/loan_application.js @@ -31,13 +31,15 @@ frappe.ui.form.on('Loan Application', { add_toolbar_buttons: function(frm) { if (frm.doc.status == "Approved") { - frappe.db.get_value("Loan Security Pledge", {"loan_application": frm.doc.name, "docstatus": 1}, "name", (r) => { - if (!r) { - frm.add_custom_button(__('Loan Security Pledge'), function() { - frm.trigger('create_loan_security_pledge') - },__('Create')) - } - }); + if (frm.doc.is_secured) { + frappe.db.get_value("Loan Security Pledge", {"loan_application": frm.doc.name, "docstatus": 1}, "name", (r) => { + if (!r) { + frm.add_custom_button(__('Loan Security Pledge'), function() { + frm.trigger('create_loan_security_pledge') + },__('Create')) + } + }); + } frappe.db.get_value("Loan", {"loan_application": frm.doc.name, "docstatus": 1}, "name", (r) => { if (!r) { @@ -61,6 +63,11 @@ frappe.ui.form.on('Loan Application', { }); }, create_loan_security_pledge: function(frm) { + + if(!frm.doc.is_secured_loan) { + frappe.throw(__("Loan Security Pledge can only be created for secured loans")); + } + frappe.call({ method: "erpnext.loan_management.doctype.loan_application.loan_application.create_pledge", args: { diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json index 72a4ddcc8fe..2d9c45d1225 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "LM-DIS-.#####", "creation": "2019-09-07 12:44:49.125452", "doctype": "DocType", @@ -13,7 +14,6 @@ "applicant_type", "applicant", "section_break_7", - "pending_amount_for_disbursal", "disbursed_amount", "accounting_dimensions_section", "cost_center", @@ -83,13 +83,6 @@ "label": "Posting Date", "read_only": 1 }, - { - "fieldname": "pending_amount_for_disbursal", - "fieldtype": "Currency", - "label": "Pending Amount For Disbursal", - "options": "Company:company:default_currency", - "read_only": 1 - }, { "fieldname": "column_break_4", "fieldtype": "Column Break" @@ -99,6 +92,7 @@ "fieldtype": "Section Break" }, { + "collapsible": 1, "fieldname": "section_break_13", "fieldtype": "Section Break" }, @@ -123,7 +117,8 @@ } ], "is_submittable": 1, - "modified": "2019-10-24 12:32:32.230881", + "links": [], + "modified": "2020-04-09 14:44:28.527271", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Disbursement", diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py index fa7db2d565a..2918486ebdd 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py @@ -4,21 +4,24 @@ from __future__ import unicode_literals import frappe, erpnext +from frappe import _ from frappe.model.document import Document from frappe.utils import nowdate, getdate, add_days, flt from erpnext.controllers.accounts_controller import AccountsController from erpnext.accounts.general_ledger import make_gl_entries -from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual +from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans class LoanDisbursement(AccountsController): def validate(self): self.set_missing_values() - self.set_pending_amount_for_disbursal() def before_submit(self): self.set_status_and_amounts() + def before_cancel(self): + self.set_status_and_amounts(cancel=1) + def on_submit(self): self.make_gl_entries() @@ -38,13 +41,7 @@ class LoanDisbursement(AccountsController): if not self.bank_account and self.applicant_type == "Customer": self.bank_account = frappe.db.get_value("Customer", self.applicant, "default_bank_account") - def set_pending_amount_for_disbursal(self): - loan_amount, disbursed_amount = frappe.db.get_value('Loan', - {'name': self.against_loan}, ['loan_amount', 'disbursed_amount']) - - self.pending_amount_for_disbursal = loan_amount - disbursed_amount - - def set_status_and_amounts(self): + def set_status_and_amounts(self, cancel=0): loan_details = frappe.get_all("Loan", fields = ["loan_amount", "disbursed_amount", "total_principal_paid", "status", "is_term_loan"], @@ -52,26 +49,32 @@ class LoanDisbursement(AccountsController): )[0] if loan_details.status == "Disbursed" and not loan_details.is_term_loan: - process_loan_interest_accrual(posting_date=add_days(self.disbursement_date, -1), + process_loan_interest_accrual_for_demand_loans(posting_date=add_days(self.disbursement_date, -1), loan=self.against_loan) - disbursed_amount = self.disbursed_amount + loan_details.disbursed_amount - - if flt(disbursed_amount) - flt(loan_details.total_principal_paid) > flt(loan_details.loan_amount): - frappe.throw(_("Disbursed Amount cannot be greater than loan amount")) - - if flt(disbursed_amount) > flt(loan_details.loan_amount): - total_principal_paid = loan_details.total_principal_paid - (disbursed_amount - loan_details.loan_amount) - frappe.db.set_value("Loan", self.against_loan, "total_principal_paid", total_principal_paid) - - if flt(loan_details.loan_amount) == flt(disbursed_amount): - frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed") + if cancel: + disbursed_amount = loan_details.disbursed_amount - self.disbursed_amount + if disbursed_amount == 0: + status = "Sanctioned" + elif disbursed_amount >= loan_details.disbursed_amount: + status = "Disbursed" + else: + status = "Partially Disbursed" else: - frappe.db.set_value("Loan", self.against_loan, "status", "Partially Disbursed") + disbursed_amount = self.disbursed_amount + loan_details.disbursed_amount + + if flt(disbursed_amount) - flt(loan_details.total_principal_paid) > flt(loan_details.loan_amount): + frappe.throw(_("Disbursed Amount cannot be greater than loan amount")) + + if flt(disbursed_amount) >= loan_details.disbursed_amount: + status = "Disbursed" + else: + status = "Partially Disbursed" frappe.db.set_value("Loan", self.against_loan, { "disbursement_date": self.disbursement_date, - "disbursed_amount": disbursed_amount + "disbursed_amount": disbursed_amount, + "status": status }) def make_gl_entries(self, cancel=0, adv_adj=0): diff --git a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py index 968e377fcc5..0c1578ffef5 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py @@ -7,8 +7,8 @@ import unittest from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_last_day, date_diff, flt, add_to_date) from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_repayment_entry, make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan, create_loan_security_price) -from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual -from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_term_loans, days_in_year) +from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans +from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year from erpnext.selling.doctype.customer.test_customer import get_customer_dict class TestLoanDisbursement(unittest.TestCase): @@ -56,20 +56,18 @@ class TestLoanDisbursement(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) - process_loan_interest_accrual(posting_date=add_days(last_date, 1)) + process_loan_interest_accrual_for_demand_loans(posting_date=add_days(last_date, 1)) + + # Should not be able to create loan disbursement entry before repayment + self.assertRaises(frappe.ValidationError, make_loan_disbursement_entry, loan.name, + 500000, first_date) - # Paid 511095.89 amount includes 5,00,000 principal amount and 11095.89 interest amount repayment_entry = create_repayment_entry(loan.name, self.applicant, add_days(get_last_day(nowdate()), 5), "Regular Payment", 611095.89) - repayment_entry.submit() + repayment_entry.submit() loan.reload() + # After repayment loan disbursement entry should go through make_loan_disbursement_entry(loan.name, 500000, disbursement_date=add_days(last_date, 16)) - total_principal_paid = loan.total_principal_paid - - loan.reload() - - # Loan Topup will result in decreasing the Total Principal Paid - self.assertEqual(flt(loan.total_principal_paid, 2), flt(total_principal_paid - 500000, 2)) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json index 33f496fc3ed..5fc3e8f4b60 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json @@ -15,14 +15,16 @@ "company", "posting_date", "is_term_loan", - "is_paid", "section_break_7", "pending_principal_amount", "payable_principal_amount", + "paid_principal_amount", "column_break_14", "interest_amount", + "paid_interest_amount", "section_break_15", "process_loan_interest_accrual", + "repayment_schedule_name", "amended_from" ], "fields": [ @@ -100,13 +102,6 @@ "label": "Company", "options": "Company" }, - { - "default": "0", - "fieldname": "is_paid", - "fieldtype": "Check", - "label": "Is Paid", - "read_only": 1 - }, { "default": "0", "fetch_from": "loan.is_term_loan", @@ -135,12 +130,31 @@ { "fieldname": "column_break_14", "fieldtype": "Column Break" + }, + { + "fieldname": "repayment_schedule_name", + "fieldtype": "Data", + "hidden": 1, + "label": "Repayment Schedule Name", + "read_only": 1 + }, + { + "fieldname": "paid_principal_amount", + "fieldtype": "Currency", + "label": "Paid Principal Amount", + "options": "Company:company:default_currency" + }, + { + "fieldname": "paid_interest_amount", + "fieldtype": "Currency", + "label": "Paid Interest Amount", + "options": "Company:company:default_currency" } ], "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2020-02-07 01:22:06.924125", + "modified": "2020-04-16 11:24:23.258404", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Interest Accrual", diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index b8e6dabba7a..094b9c698c7 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -27,8 +27,14 @@ class LoanInterestAccrual(AccountsController): self.make_gl_entries() def on_cancel(self): + if self.repayment_schedule_name: + self.update_is_accrued() + self.make_gl_entries(cancel=1) + def update_is_accrued(self): + frappe.db.set_value('Repayment Schedule', self.repayment_schedule_name, 'is_accrued', 0) + def make_gl_entries(self, cancel=0, adv_adj=0): gle_map = [] @@ -83,9 +89,19 @@ def calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_i interest_per_day = (pending_principal_amount * loan.rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100) payable_interest = interest_per_day * no_of_days - make_loan_interest_accrual_entry(loan.name, loan.applicant_type, loan.applicant,loan.interest_income_account, - loan.loan_account, pending_principal_amount, payable_interest, process_loan_interest = process_loan_interest, - posting_date=posting_date) + args = frappe._dict({ + 'loan': loan.name, + 'applicant_type': loan.applicant_type, + 'applicant': loan.applicant, + 'interest_income_account': loan.interest_income_account, + 'loan_account': loan.loan_account, + 'pending_principal_amount': pending_principal_amount, + 'interest_amount': payable_interest, + 'process_loan_interest': process_loan_interest, + 'posting_date': posting_date + }) + + make_loan_interest_accrual_entry(args) def make_accrual_interest_entry_for_demand_loans(posting_date, process_loan_interest, open_loans=None, loan_type=None): query_filters = { @@ -107,49 +123,71 @@ def make_accrual_interest_entry_for_demand_loans(posting_date, process_loan_inte for loan in open_loans: calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_interest) -def make_accrual_interest_entry_for_term_loans(posting_date=None): +def make_accrual_interest_entry_for_term_loans(posting_date, process_loan_interest, term_loan=None, loan_type=None): curr_date = posting_date or add_days(nowdate(), 1) - term_loans = frappe.db.sql("""SELECT l.name, l.total_payment, l.total_amount_paid, l.loan_account, - l.interest_income_account, l.is_term_loan, l.disbursement_date, l.applicant_type, l.applicant, - l.rate_of_interest, l.total_interest_payable, l.repayment_start_date, rs.name as payment_entry, - rs.payment_date, rs.principal_amount, rs.interest_amount, rs.is_accrued , rs.balance_loan_amount - FROM `tabLoan` l, `tabRepayment Schedule` rs - WHERE rs.parent = l.name - AND l.docstatus=1 - AND l.is_term_loan =1 - AND rs.payment_date <= %s - AND rs.is_accrued=0 - AND l.status = 'Disbursed'""", (curr_date), as_dict=1) + term_loans = get_term_loans(curr_date, term_loan, loan_type) accrued_entries = [] for loan in term_loans: accrued_entries.append(loan.payment_entry) - make_loan_interest_accrual_entry(loan.name, loan.applicant_type, loan.applicant,loan.interest_income_account, - loan.loan_account, loan.principal_amount + loan.balance_loan_amount, loan.interest_amount, - payable_principal = loan.principal_amount , posting_date=posting_date) + args = frappe._dict({ + 'loan': loan.name, + 'applicant_type': loan.applicant_type, + 'applicant': loan.applicant, + 'interest_income_account': loan.interest_income_account, + 'loan_account': loan.loan_account, + 'interest_amount': loan.interest_amount, + 'payable_principal': loan.principal_amount, + 'process_loan_interest': process_loan_interest, + 'repayment_schedule_name': loan.payment_entry, + 'posting_date': posting_date + }) + + make_loan_interest_accrual_entry(args) if accrued_entries: frappe.db.sql("""UPDATE `tabRepayment Schedule` SET is_accrued = 1 where name in (%s)""" #nosec % ", ".join(['%s']*len(accrued_entries)), tuple(accrued_entries)) -def make_loan_interest_accrual_entry(loan, applicant_type, applicant, interest_income_account, loan_account, - pending_principal_amount, interest_amount, payable_principal=None, process_loan_interest=None, posting_date=None): - loan_interest_accrual = frappe.new_doc("Loan Interest Accrual") - loan_interest_accrual.loan = loan - loan_interest_accrual.applicant_type = applicant_type - loan_interest_accrual.applicant = applicant - loan_interest_accrual.interest_income_account = interest_income_account - loan_interest_accrual.loan_account = loan_account - loan_interest_accrual.pending_principal_amount = flt(pending_principal_amount, 2) - loan_interest_accrual.interest_amount = flt(interest_amount, 2) - loan_interest_accrual.posting_date = posting_date or nowdate() - loan_interest_accrual.process_loan_interest_accrual = process_loan_interest +def get_term_loans(date, term_loan=None, loan_type=None): + condition = '' - if payable_principal: - loan_interest_accrual.payable_principal_amount = payable_principal + if term_loan: + condition +=' AND l.name = %s' % frappe.db.escape(term_loan) + + if loan_type: + condition += ' AND l.loan_type = %s' % frappe.db.escape(loan_type) + + term_loans = frappe.db.sql("""SELECT l.name, l.total_payment, l.total_amount_paid, l.loan_account, + l.interest_income_account, l.is_term_loan, l.disbursement_date, l.applicant_type, l.applicant, + l.rate_of_interest, l.total_interest_payable, l.repayment_start_date, rs.name as payment_entry, + rs.payment_date, rs.principal_amount, rs.interest_amount, rs.is_accrued , rs.balance_loan_amount + FROM `tabLoan` l, `tabRepayment Schedule` rs + WHERE rs.parent = l.name + AND l.docstatus=1 + AND l.is_term_loan =1 + AND rs.payment_date <= %s + AND rs.is_accrued=0 {0} + AND l.status = 'Disbursed'""".format(condition), (getdate(date)), as_dict=1) + + return term_loans + +def make_loan_interest_accrual_entry(args): + loan_interest_accrual = frappe.new_doc("Loan Interest Accrual") + loan_interest_accrual.loan = args.loan + loan_interest_accrual.applicant_type = args.applicant_type + loan_interest_accrual.applicant = args.applicant + loan_interest_accrual.interest_income_account = args.interest_income_account + loan_interest_accrual.loan_account = args.loan_account + loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, 2) + loan_interest_accrual.interest_amount = flt(args.interest_amount, 2) + loan_interest_accrual.posting_date = args.posting_date or nowdate() + loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest + loan_interest_accrual.repayment_schedule_name = args.repayment_schedule_name + loan_interest_accrual.payable_principal_amount = args.payable_principal loan_interest_accrual.save() loan_interest_accrual.submit() diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py index e681ae42c34..2afed08e185 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py @@ -7,8 +7,8 @@ import unittest from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_last_day, date_diff, flt, add_to_date) from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_loan_security_price, make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan) -from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual -from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_term_loans, days_in_year) +from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans +from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year from erpnext.selling.doctype.customer.test_customer import get_customer_dict class TestLoanInterestAccrual(unittest.TestCase): @@ -54,7 +54,7 @@ class TestLoanInterestAccrual(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) - process_loan_interest_accrual(posting_date=last_date) + process_loan_interest_accrual_for_demand_loans(posting_date=last_date) loan_interest_accural = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name}) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json index 4b930c50aef..789c1299463 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json @@ -30,9 +30,8 @@ "reference_number", "column_break_21", "reference_date", - "paid_accrual_entries", - "partial_paid_entry", "principal_amount_paid", + "repayment_details", "amended_from" ], "fields": [ @@ -155,13 +154,6 @@ "options": "Company:company:default_currency", "read_only": 1 }, - { - "fieldname": "paid_accrual_entries", - "fieldtype": "Text", - "hidden": 1, - "label": "Paid Accrual Entries", - "read_only": 1 - }, { "default": "0", "fetch_from": "against_loan.is_term_loan", @@ -197,13 +189,6 @@ "fieldname": "column_break_21", "fieldtype": "Column Break" }, - { - "fieldname": "partial_paid_entry", - "fieldtype": "Text", - "hidden": 1, - "label": "Partial Paid Entry", - "read_only": 1 - }, { "default": "0.0", "fieldname": "principal_amount_paid", @@ -225,11 +210,18 @@ "fieldtype": "Date", "label": "Due Date", "read_only": 1 + }, + { + "fieldname": "repayment_details", + "fieldtype": "Table", + "hidden": 1, + "label": "Repayment Details", + "options": "Loan Repayment Detail" } ], "is_submittable": 1, "links": [], - "modified": "2020-02-26 06:18:54.934538", + "modified": "2020-04-16 18:14:45.166754", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Repayment", @@ -264,7 +256,6 @@ "write": 1 } ], - "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index a70e3128808..87e8a15ab48 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -19,11 +19,11 @@ class LoanRepayment(AccountsController): def validate(self): amounts = calculate_amounts(self.against_loan, self.posting_date, self.payment_type) self.set_missing_values(amounts) - - def before_submit(self): - self.mark_as_paid() + self.validate_amount() + self.allocate_amounts(amounts['pending_accrual_entries']) def on_submit(self): + self.update_paid_amount() self.make_gl_entries() def on_cancel(self): @@ -38,32 +38,25 @@ class LoanRepayment(AccountsController): self.cost_center = erpnext.get_default_cost_center(self.company) if not self.interest_payable: - self.interest_payable = amounts['interest_amount'] + self.interest_payable = flt(amounts['interest_amount'], 2) if not self.penalty_amount: - self.penalty_amount = amounts['penalty_amount'] + self.penalty_amount = flt(amounts['penalty_amount'], 2) if not self.pending_principal_amount: - self.pending_principal_amount = amounts['pending_principal_amount'] + self.pending_principal_amount = flt(amounts['pending_principal_amount'], 2) if not self.payable_principal_amount and self.is_term_loan: - self.payable_principal_amount = amounts['payable_principal_amount'] + self.payable_principal_amount = flt(amounts['payable_principal_amount'], 2) if not self.payable_amount: - self.payable_amount = amounts['payable_amount'] - - if amounts.get('paid_accrual_entries'): - self.paid_accrual_entries = frappe.as_json(amounts.get('paid_accrual_entries')) + self.payable_amount = flt(amounts['payable_amount'], 2) if amounts.get('due_date'): self.due_date = amounts.get('due_date') - def mark_as_paid(self): - paid_entries = [] - paid_amount = self.amount_paid - interest_paid = paid_amount - - if not paid_amount: + def validate_amount(self): + if not self.amount_paid: frappe.throw(_("Amount paid cannot be zero")) if self.amount_paid < self.penalty_amount: @@ -74,37 +67,15 @@ class LoanRepayment(AccountsController): msg = _("Amount of {0} is required for Loan closure").format(self.payable_amount) frappe.throw(msg) + def update_paid_amount(self): loan = frappe.get_doc("Loan", self.against_loan) - if self.paid_accrual_entries: - paid_accrual_entries = json.loads(self.paid_accrual_entries) - - if paid_amount - self.penalty_amount > 0 and self.paid_accrual_entries: - - interest_paid = paid_amount - self.penalty_amount - - for lia, interest_amount in iteritems(paid_accrual_entries): - if interest_amount <= interest_paid: - paid_entries.append(lia) - interest_paid -= interest_amount - elif interest_paid: - self.partial_paid_entry = frappe.as_json({"name": lia, "interest_amount": interest_amount}) - frappe.db.set_value("Loan Interest Accrual", lia, "interest_amount", - interest_amount - interest_paid) - interest_paid = 0 - - if paid_entries: - self.paid_accrual_entries = frappe.as_json(paid_entries) - else: - self.paid_accrual_entries = "" - - if interest_paid: - self.principal_amount_paid = interest_paid - - if paid_entries: - frappe.db.sql("""UPDATE `tabLoan Interest Accrual` - SET is_paid = 1 where name in (%s)""" #nosec - % ", ".join(['%s']*len(paid_entries)), tuple(paid_entries)) + for payment in self.repayment_details: + frappe.db.sql(""" UPDATE `tabLoan Interest Accrual` + SET paid_principal_amount = `paid_principal_amount` + %s, + paid_interest_amount = `paid_interest_amount` + %s + WHERE name = %s""", + (flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual)) if flt(loan.total_principal_paid + self.principal_amount_paid, 2) >= flt(loan.total_payment, 2): frappe.db.set_value("Loan", self.against_loan, "status", "Loan Closure Requested") @@ -116,21 +87,14 @@ class LoanRepayment(AccountsController): update_shortfall_status(self.against_loan, self.principal_amount_paid) def mark_as_unpaid(self): - loan = frappe.get_doc("Loan", self.against_loan) - if self.paid_accrual_entries: - paid_accrual_entries = json.loads(self.paid_accrual_entries) - - if self.paid_accrual_entries: - frappe.db.sql("""UPDATE `tabLoan Interest Accrual` - SET is_paid = 0 where name in (%s)""" #nosec - % ", ".join(['%s']*len(paid_accrual_entries)), tuple(paid_accrual_entries)) - - if self.partial_paid_entry: - partial_paid_entry = json.loads(self.partial_paid_entry) - frappe.db.set_value("Loan Interest Accrual", partial_paid_entry["name"], "interest_amount", - partial_paid_entry["interest_amount"]) + for payment in self.repayment_details: + frappe.db.sql(""" UPDATE `tabLoan Interest Accrual` + SET paid_principal_amount = `paid_principal_amount` - %s, + paid_interest_amount = `paid_interest_amount` - %s + WHERE name = %s""", + (payment.paid_principal_amount, payment.paid_interest_amount, payment.loan_interest_accrual)) frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s WHERE name = %s """, (loan.total_amount_paid - self.amount_paid, @@ -139,6 +103,38 @@ class LoanRepayment(AccountsController): if loan.status == "Loan Closure Requested": frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed") + def allocate_amounts(self, paid_entries): + self.set('repayment_details', []) + self.principal_amount_paid = 0 + + if self.amount_paid - self.penalty_amount > 0 and paid_entries: + interest_paid = self.amount_paid - self.penalty_amount + for lia, amounts in iteritems(paid_entries): + if amounts['interest_amount'] + amounts['payable_principal_amount'] <= interest_paid: + interest_amount = amounts['interest_amount'] + paid_principal = amounts['payable_principal_amount'] + self.principal_amount_paid += paid_principal + interest_paid -= (interest_amount + paid_principal) + elif interest_paid: + if interest_paid >= amounts['interest_amount']: + interest_amount = amounts['interest_amount'] + paid_principal = interest_paid - interest_amount + self.principal_amount_paid += paid_principal + interest_paid = 0 + else: + interest_amount = interest_paid + interest_paid = 0 + paid_principal=0 + + self.append('repayment_details', { + 'loan_interest_accrual': lia, + 'paid_interest_amount': interest_amount, + 'paid_principal_amount': paid_principal + }) + + if interest_paid: + self.principal_amount_paid += interest_paid + def make_gl_entries(self, cancel=0, adv_adj=0): gle_map = [] loan_details = frappe.get_doc("Loan", self.against_loan) @@ -210,7 +206,7 @@ class LoanRepayment(AccountsController): ) if gle_map: - make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj) + make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False) def create_repayment_entry(loan, applicant, company, posting_date, loan_type, payment_type, interest_payable, payable_principal_amount, amount_paid, penalty_amount=None): @@ -223,7 +219,7 @@ def create_repayment_entry(loan, applicant, company, posting_date, loan_type, "posting_date": posting_date, "applicant": applicant, "penalty_amount": penalty_amount, - "interets_payable": interest_payable, + "interest_payable": interest_payable, "payable_principal_amount": payable_principal_amount, "amount_paid": amount_paid, "loan_type": loan_type @@ -232,14 +228,22 @@ def create_repayment_entry(loan, applicant, company, posting_date, loan_type, return lr def get_accrued_interest_entries(against_loan): - accrued_interest_entries = frappe.get_all("Loan Interest Accrual", - fields=["name", "interest_amount", "posting_date", "payable_principal_amount"], - filters = { - "loan": against_loan, - "is_paid": 0 - }, order_by="posting_date") - return accrued_interest_entries + unpaid_accrued_entries = frappe.db.sql( + """ + SELECT name, posting_date, interest_amount - paid_interest_amount as interest_amount, + payable_principal_amount - paid_principal_amount as payable_principal_amount + FROM + `tabLoan Interest Accrual` + WHERE + loan = %s + AND (interest_amount - paid_interest_amount > 0 OR + payable_principal_amount - paid_principal_amount > 0) + AND + docstatus = 1 + """, (against_loan), as_dict=1) + + return unpaid_accrued_entries # This function returns the amounts that are payable at the time of loan repayment based on posting date # So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable @@ -272,7 +276,11 @@ def get_amounts(amounts, against_loan, posting_date, payment_type): total_pending_interest += entry.interest_amount payable_principal_amount += entry.payable_principal_amount - pending_accrual_entries.setdefault(entry.name, entry.interest_amount) + pending_accrual_entries.setdefault(entry.name, { + 'interest_amount': flt(entry.interest_amount), + 'payable_principal_amount': flt(entry.payable_principal_amount) + }) + final_due_date = due_date pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid - against_loan_doc.total_interest_payable @@ -288,7 +296,7 @@ def get_amounts(amounts, against_loan, posting_date, payment_type): amounts["interest_amount"] = total_pending_interest amounts["penalty_amount"] = penalty_amount amounts["payable_amount"] = payable_principal_amount + total_pending_interest + penalty_amount - amounts["paid_accrual_entries"] = pending_accrual_entries + amounts["pending_accrual_entries"] = pending_accrual_entries if final_due_date: amounts["due_date"] = final_due_date diff --git a/erpnext/loan_management/doctype/loan_repayment_detail/__init__.py b/erpnext/loan_management/doctype/loan_repayment_detail/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.json b/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.json new file mode 100644 index 00000000000..cff1dbb1d29 --- /dev/null +++ b/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.json @@ -0,0 +1,43 @@ +{ + "actions": [], + "creation": "2020-04-15 18:31:54.026923", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "loan_interest_accrual", + "paid_principal_amount", + "paid_interest_amount" + ], + "fields": [ + { + "fieldname": "loan_interest_accrual", + "fieldtype": "Link", + "label": "Loan Interest Accrual", + "options": "Loan Interest Accrual" + }, + { + "fieldname": "paid_principal_amount", + "fieldtype": "Currency", + "label": "Paid Principal Amount", + "options": "Company:company:default_currency" + }, + { + "fieldname": "paid_interest_amount", + "fieldtype": "Currency", + "label": "Paid Interest Amount", + "options": "Company:company:default_currency" + } + ], + "istable": 1, + "links": [], + "modified": "2020-04-15 21:50:03.837019", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Repayment Detail", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.py b/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.py new file mode 100644 index 00000000000..a83b9b59415 --- /dev/null +++ b/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class LoanRepaymentDetail(Document): + pass diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py index b7be84f8366..ab040f1d333 100644 --- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py +++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py @@ -37,17 +37,10 @@ def add_security(loan): return loan_security_pledge.as_dict() -def check_for_ltv_shortfall(process_loan_security_shortfall=None): +def check_for_ltv_shortfall(process_loan_security_shortfall): update_time = get_datetime() - if not process_loan_security_shortfall: - process = frappe.new_doc("Process Loan Security Shortfall") - process.update_time = update_time - process.submit() - - process_loan_security_shortfall = process.name - loan_security_price_map = frappe._dict(frappe.get_all("Loan Security Price", fields=["loan_security", "loan_security_price"], filters = { diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.json b/erpnext/loan_management/doctype/loan_type/loan_type.json index a3525db9a56..51c5cb98a63 100644 --- a/erpnext/loan_management/doctype/loan_type/loan_type.json +++ b/erpnext/loan_management/doctype/loan_type/loan_type.json @@ -119,6 +119,7 @@ "label": "Penalty Interest Rate (%) Per Day" }, { + "description": "No. of days from due date until which penalty won't be charged in case of delay in loan repayment", "fieldname": "grace_period_in_days", "fieldtype": "Int", "label": "Grace Period in Days" @@ -142,7 +143,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-02-03 05:03:00.334813", + "modified": "2020-04-15 00:24:43.259963", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Type", diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.py b/erpnext/loan_management/doctype/loan_type/loan_type.py index 14b18ab57a1..208cb19c88e 100644 --- a/erpnext/loan_management/doctype/loan_type/loan_type.py +++ b/erpnext/loan_management/doctype/loan_type/loan_type.py @@ -19,3 +19,6 @@ class LoanType(Document): frappe.throw(_("Account {0} does not belong to company {1}").format(frappe.bold(self.get(fieldname)), frappe.bold(self.company))) + if self.get('loan_account') == self.get('payment_account'): + frappe.throw(_('Loan Account and Payment Account cannot be same')) + diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json index 7f79cb1fd93..0ef098f278f 100644 --- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json +++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json @@ -9,6 +9,7 @@ "posting_date", "loan_type", "loan", + "process_type", "amended_from" ], "fields": [ @@ -39,11 +40,18 @@ "fieldtype": "Link", "label": "Loan ", "options": "Loan" + }, + { + "fieldname": "process_type", + "fieldtype": "Data", + "hidden": 1, + "label": "Process Type", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-02-01 08:14:33.978636", + "modified": "2020-04-09 22:52:53.911416", "modified_by": "Administrator", "module": "Loan Management", "name": "Process Loan Interest Accrual", @@ -74,7 +82,6 @@ "write": 1 } ], - "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py index 0f33da918d2..cd3cf7ec96b 100644 --- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py @@ -6,7 +6,8 @@ from __future__ import unicode_literals import frappe from frappe.utils import nowdate from frappe.model.document import Document -from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import make_accrual_interest_entry_for_demand_loans +from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_demand_loans, + make_accrual_interest_entry_for_term_loans) class ProcessLoanInterestAccrual(Document): def on_submit(self): @@ -14,16 +15,45 @@ class ProcessLoanInterestAccrual(Document): if self.loan: loan_doc = frappe.get_doc('Loan', self.loan) - open_loans.append(loan_doc) + if loan_doc: + open_loans.append(loan_doc) - make_accrual_interest_entry_for_demand_loans(self.posting_date, self.name, - open_loans = open_loans, loan_type = self.loan_type) + if (not self.loan or not loan_doc.is_term_loan) and self.process_type != 'Term Loans': + make_accrual_interest_entry_for_demand_loans(self.posting_date, self.name, + open_loans = open_loans, loan_type = self.loan_type) -def process_loan_interest_accrual(posting_date=None, loan_type=None, loan=None): + if (not self.loan or loan_doc.is_term_loan) and self.process_type != 'Demand Loans': + make_accrual_interest_entry_for_term_loans(self.posting_date, self.name, term_loan=self.loan, + loan_type=self.loan_type) + + +def process_loan_interest_accrual_for_demand_loans(posting_date=None, loan_type=None, loan=None): loan_process = frappe.new_doc('Process Loan Interest Accrual') loan_process.posting_date = posting_date or nowdate() loan_process.loan_type = loan_type + loan_process.process_type = 'Demand Loans' loan_process.loan = loan loan_process.submit() +def process_loan_interest_accrual_for_term_loans(posting_date=None, loan_type=None, loan=None): + + if not term_loan_accrual_pending(posting_date or nowdate()): + return + + loan_process = frappe.new_doc('Process Loan Interest Accrual') + loan_process.posting_date = posting_date or nowdate() + loan_process.loan_type = loan_type + loan_process.process_type = 'Term Loans' + loan_process.loan = loan + + loan_process.submit() + +def term_loan_accrual_pending(date): + pending_accrual = frappe.db.get_value('Repayment Schedule', { + 'payment_date': ('<=', date), + 'is_accrued': 0 + }) + + return pending_accrual + diff --git a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py index 417e3678c93..b4aad25ac80 100644 --- a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py +++ b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py @@ -14,4 +14,13 @@ class ProcessLoanSecurityShortfall(Document): self.set_onload('update_time', get_datetime()) def on_submit(self): - check_for_ltv_shortfall(process_loan_security_shortfall = self.name) + check_for_ltv_shortfall(self.name) + +def create_process_loan_security_shortfall(): + if check_for_secured_loans(): + process = frappe.new_doc("Process Loan Security Shortfall") + process.update_time = get_datetime() + process.submit() + +def check_for_secured_loans(): + return frappe.db.count('Loan', {'docstatus': 1, 'is_secured_loan': 1}) diff --git a/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.json b/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.json index ce020fff07d..2f4fe249456 100644 --- a/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.json +++ b/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-08-29 18:11:36.829526", "doctype": "DocType", "editable_grid": 1, @@ -27,7 +28,6 @@ { "fieldname": "loan_account", "fieldtype": "Link", - "in_list_view": 1, "label": "Loan Account", "options": "Account", "read_only": 1 @@ -63,9 +63,9 @@ { "fieldname": "total_payment", "fieldtype": "Currency", + "in_list_view": 1, "label": "Total Payment", - "options": "Company:company:default_currency", - "read_only": 1 + "options": "Company:company:default_currency" }, { "fieldname": "loan_repayment_entry", @@ -79,11 +79,13 @@ "fieldname": "loan_type", "fieldtype": "Link", "label": "Loan Type", - "options": "Loan Type" + "options": "Loan Type", + "read_only": 1 } ], "istable": 1, - "modified": "2019-10-28 09:15:31.174244", + "links": [], + "modified": "2020-04-16 13:17:04.798335", "modified_by": "Administrator", "module": "Loan Management", "name": "Salary Slip Loan", diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index 6127ef6896c..18604e283ab 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -1,32 +1,34 @@ { "cards": [ { - "icon": "fa fa-star", - "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Activity Type\"\n ],\n \"description\": \"Time Sheet for manufacturing.\",\n \"label\": \"Timesheet\",\n \"name\": \"Timesheet\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n }\n]", - "title": "Production" + "hidden": 0, + "label": "Production", + "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Activity Type\"\n ],\n \"description\": \"Time Sheet for manufacturing.\",\n \"label\": \"Timesheet\",\n \"name\": \"Timesheet\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Bill of Materials (BOM)\",\n \"label\": \"Bill of Materials\",\n \"name\": \"BOM\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Where manufacturing operations are carried.\",\n \"label\": \"Workstation\",\n \"name\": \"Workstation\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Details of the operations carried out.\",\n \"label\": \"Operation\",\n \"name\": \"Operation\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Routing\",\n \"name\": \"Routing\",\n \"type\": \"doctype\"\n }\n]", - "title": "Bill of Materials" + "hidden": 0, + "label": "Bill of Materials", + "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Bill of Materials (BOM)\",\n \"label\": \"Bill of Materials\",\n \"name\": \"BOM\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Where manufacturing operations are carried.\",\n \"label\": \"Workstation\",\n \"name\": \"Workstation\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Details of the operations carried out.\",\n \"label\": \"Operation\",\n \"name\": \"Operation\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Routing\",\n \"name\": \"Routing\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-list", - "links": "[\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Open Work Orders\",\n \"name\": \"Open Work Orders\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Work Orders in Progress\",\n \"name\": \"Work Orders in Progress\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Issued Items Against Work Order\",\n \"name\": \"Issued Items Against Work Order\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Completed Work Orders\",\n \"name\": \"Completed Work Orders\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Production Analytics\",\n \"name\": \"Production Analytics\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Stock Report\",\n \"name\": \"BOM Stock Report\",\n \"type\": \"report\"\n }\n]", - "title": "Reports" + "hidden": 0, + "label": "Reports", + "links": "[\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Open Work Orders\",\n \"name\": \"Open Work Orders\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Work Orders in Progress\",\n \"name\": \"Work Orders in Progress\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Issued Items Against Work Order\",\n \"name\": \"Issued Items Against Work Order\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Completed Work Orders\",\n \"name\": \"Completed Work Orders\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Production Analytics\",\n \"name\": \"Production Analytics\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Stock Report\",\n \"name\": \"BOM Stock Report\",\n \"type\": \"report\"\n }\n]" }, { - "icon": "fa fa-wrench", - "links": "[\n {\n \"description\": \"Replace BOM and update latest price in all BOMs\",\n \"label\": \"BOM Update Tool\",\n \"name\": \"BOM Update Tool\",\n \"type\": \"doctype\"\n },\n {\n \"data_doctype\": \"BOM\",\n \"description\": \"Compare BOMs for changes in Raw Materials and Operations\",\n \"label\": \"BOM Comparison Tool\",\n \"name\": \"bom-comparison-tool\",\n \"type\": \"page\"\n }\n]", - "title": "Tools" + "hidden": 0, + "label": "Tools", + "links": "[\n {\n \"description\": \"Replace BOM and update latest price in all BOMs\",\n \"label\": \"BOM Update Tool\",\n \"name\": \"BOM Update Tool\",\n \"type\": \"doctype\"\n },\n {\n \"data_doctype\": \"BOM\",\n \"description\": \"Compare BOMs for changes in Raw Materials and Operations\",\n \"label\": \"BOM Comparison Tool\",\n \"name\": \"bom-comparison-tool\",\n \"type\": \"page\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Global settings for all manufacturing processes.\",\n \"label\": \"Manufacturing Settings\",\n \"name\": \"Manufacturing Settings\",\n \"type\": \"doctype\"\n }\n]", - "title": "Settings" + "hidden": 0, + "label": "Settings", + "links": "[\n {\n \"description\": \"Global settings for all manufacturing processes.\",\n \"label\": \"Manufacturing Settings\",\n \"name\": \"Manufacturing Settings\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-facetime-video", - "links": "[\n {\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"type\": \"help\",\n \"youtube_id\": \"ZotgLyp2YFY\"\n }\n]", - "title": "Help" + "hidden": 0, + "label": "Help", + "links": "[\n {\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"type\": \"help\",\n \"youtube_id\": \"ZotgLyp2YFY\"\n }\n]" } ], "category": "Domains", @@ -40,7 +42,7 @@ "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-03-12 16:30:36.389877", + "modified": "2020-04-01 11:28:50.979358", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index 8c7876d48de..bab0dfb6b43 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -82,7 +82,9 @@ frappe.ui.form.on('Job Card', { frm.set_value('current_time' , 0); } - frm.save(); + frm.save("Save", () => {}, "", () => { + frm.doc.time_logs.pop(-1); + }); }, complete_job: function(frm, completed_time, completed_qty) { @@ -105,6 +107,24 @@ frappe.ui.form.on('Job Card', { }); }, + validate: function(frm) { + if ((!frm.doc.time_logs || !frm.doc.time_logs.length) && frm.doc.started_time) { + frm.trigger("reset_timer"); + } + }, + + employee: function(frm) { + if (frm.doc.job_started && !frm.doc.current_time) { + frm.trigger("reset_timer"); + } + }, + + reset_timer: function(frm) { + frm.set_value('started_time' , ''); + frm.set_value('job_started', 0); + frm.set_value('current_time' , 0); + }, + make_dashboard: function(frm) { if(frm.doc.__islocal) return; @@ -137,12 +157,12 @@ frappe.ui.form.on('Job Card', { updateStopwatch(current); }, 1000); } - + function updateStopwatch(increment) { var hours = Math.floor(increment / 3600); var minutes = Math.floor((increment - (hours * 3600)) / 60); var seconds = increment - (hours * 3600) - (minutes * 60); - + $(section).find(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString()); $(section).find(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString()); $(section).find(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString()); @@ -205,5 +225,10 @@ frappe.ui.form.on('Job Card', { frappe.ui.form.on('Job Card Time Log', { completed_qty: function(frm) { frm.events.set_total_completed_qty(frm); + }, + + to_time: function(frm) { + frm.set_value('job_started', 0); + frm.set_value('started_time', ''); } }) \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index f8c60f2a114..e9627a55145 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -9,7 +9,7 @@ from frappe import _ from frappe.model.mapper import get_mapped_doc from frappe.model.document import Document from frappe.utils import (flt, cint, time_diff_in_hours, get_datetime, getdate, - get_time, add_to_date, time_diff, add_days, get_datetime_str) + get_time, add_to_date, time_diff, add_days, get_datetime_str, get_link_to_form) from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations @@ -189,11 +189,15 @@ class JobCard(Document): def validate_job_card(self): if not self.time_logs: - frappe.throw(_("Time logs are required for job card {0}").format(self.name)) + frappe.throw(_("Time logs are required for {0} {1}") + .format(frappe.bold("Job Card"), get_link_to_form("Job Card", self.name))) if self.for_quantity and self.total_completed_qty != self.for_quantity: - frappe.throw(_("The total completed qty({0}) must be equal to qty to manufacture({1})" - .format(frappe.bold(self.total_completed_qty),frappe.bold(self.for_quantity)))) + total_completed_qty = frappe.bold(_("Total Completed Qty")) + qty_to_manufacture = frappe.bold(_("Qty to Manufacture")) + + frappe.throw(_("The {0} ({1}) must be equal to {2} ({3})" + .format(total_completed_qty, frappe.bold(self.total_completed_qty), qty_to_manufacture,frappe.bold(self.for_quantity)))) def update_work_order(self): if not self.work_order: diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 9c8aa453a68..d541866f8b7 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -240,6 +240,8 @@ frappe.ui.form.on("Work Order", { }); }, __("Job Card"), __("Create")); + dialog.fields_dict["operations"].grid.wrapper.find('.grid-add-row').hide(); + var pending_qty = 0; frm.doc.operations.forEach(data => { if(data.completed_qty != frm.doc.qty) { diff --git a/erpnext/non_profit/desk_page/non_profit/non_profit.json b/erpnext/non_profit/desk_page/non_profit/non_profit.json index 5d759ba113e..a476857f099 100644 --- a/erpnext/non_profit/desk_page/non_profit/non_profit.json +++ b/erpnext/non_profit/desk_page/non_profit/non_profit.json @@ -1,30 +1,34 @@ { "cards": [ { - "icon": "icon-list", - "links": "[\n {\n \"description\": \"Define various loan types\",\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loan Application\",\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n }\n]", - "title": "Loan Management" + "hidden": 0, + "label": "Loan Management", + "links": "[\n {\n \"description\": \"Define various loan types\",\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loan Application\",\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Grant information.\",\n \"label\": \"Grant Application\",\n \"name\": \"Grant Application\",\n \"type\": \"doctype\"\n }\n]", - "title": "Grant Application" + "hidden": 0, + "label": "Grant Application", + "links": "[\n {\n \"description\": \"Grant information.\",\n \"label\": \"Grant Application\",\n \"name\": \"Grant Application\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Member information.\",\n \"label\": \"Member\",\n \"name\": \"Member\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Memebership Details\",\n \"label\": \"Membership\",\n \"name\": \"Membership\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Memebership Type Details\",\n \"label\": \"Membership Type\",\n \"name\": \"Membership Type\",\n \"type\": \"doctype\"\n }\n]", - "title": "Membership" + "hidden": 0, + "label": "Membership", + "links": "[\n {\n \"description\": \"Member information.\",\n \"label\": \"Member\",\n \"name\": \"Member\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Membership Details\",\n \"label\": \"Membership\",\n \"name\": \"Membership\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Membership Type Details\",\n \"label\": \"Membership Type\",\n \"name\": \"Membership Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Billing and Gateway Settings\",\n \"label\": \"Membership Settings\",\n \"name\": \"Membership Settings\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Volunteer information.\",\n \"label\": \"Volunteer\",\n \"name\": \"Volunteer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Volunteer Type information.\",\n \"label\": \"Volunteer Type\",\n \"name\": \"Volunteer Type\",\n \"type\": \"doctype\"\n }\n]", - "title": "Volunteer" + "hidden": 0, + "label": "Volunteer", + "links": "[\n {\n \"description\": \"Volunteer information.\",\n \"label\": \"Volunteer\",\n \"name\": \"Volunteer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Volunteer Type information.\",\n \"label\": \"Volunteer Type\",\n \"name\": \"Volunteer Type\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-star", - "links": "[\n {\n \"description\": \"Chapter information.\",\n \"label\": \"Chapter\",\n \"name\": \"Chapter\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]", - "title": "Chapter" + "hidden": 0, + "label": "Chapter", + "links": "[\n {\n \"description\": \"Chapter information.\",\n \"label\": \"Chapter\",\n \"name\": \"Chapter\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Donor information.\",\n \"label\": \"Donor\",\n \"name\": \"Donor\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Donor Type information.\",\n \"label\": \"Donor Type\",\n \"name\": \"Donor Type\",\n \"type\": \"doctype\"\n }\n]", - "title": "Donor" + "hidden": 0, + "label": "Donor", + "links": "[\n {\n \"description\": \"Donor information.\",\n \"label\": \"Donor\",\n \"name\": \"Donor\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Donor Type information.\",\n \"label\": \"Donor Type\",\n \"name\": \"Donor Type\",\n \"type\": \"doctype\"\n }\n]" } ], "category": "Domains", @@ -38,7 +42,7 @@ "idx": 0, "is_standard": 1, "label": "Non Profit", - "modified": "2020-03-12 16:30:36.683012", + "modified": "2020-04-01 11:28:51.430882", "modified_by": "Administrator", "module": "Non Profit", "name": "Non Profit", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ff58f40d370..86af3de06ba 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -662,4 +662,9 @@ erpnext.patches.v12_0.create_irs_1099_field_united_states erpnext.patches.v12_0.move_bank_account_swift_number_to_bank erpnext.patches.v12_0.rename_bank_reconciliation erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22 -erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom \ No newline at end of file +erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom +erpnext.patches.v12_0.recalculate_requested_qty_in_bin +erpnext.patches.v12_0.update_healthcare_refactored_changes +erpnext.patches.v12_0.set_total_batch_quantity +erpnext.patches.v12_0.rename_mws_settings_fields +erpnext.patches.v12_0.set_updated_purpose_in_pick_list diff --git a/erpnext/patches/v10_0/update_address_template_for_india.py b/erpnext/patches/v10_0/update_address_template_for_india.py index 145ed452e9c..1ddca937609 100644 --- a/erpnext/patches/v10_0/update_address_template_for_india.py +++ b/erpnext/patches/v10_0/update_address_template_for_india.py @@ -3,10 +3,10 @@ from __future__ import unicode_literals import frappe -from erpnext.regional.india.setup import update_address_template +from erpnext.regional.address_template.setup import set_up_address_templates def execute(): if frappe.db.get_value('Company', {'country': 'India'}, 'name'): address_template = frappe.db.get_value('Address Template', 'India', 'template') if not address_template or "gstin" not in address_template: - update_address_template() + set_up_address_templates(default_country='India') diff --git a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py index 31d500ea2f7..8889056e2d2 100644 --- a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py +++ b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py @@ -118,7 +118,9 @@ def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttyp account.insert() tax_type = account.name - if tax_type: + account_type = frappe.get_cached_value("Account", tax_type, "account_type") + + if tax_type and account_type in ('Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation'): item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate}) item_tax_templates.setdefault(item_tax_template.title, {}) item_tax_templates[item_tax_template.title][tax_type] = tax_rate diff --git a/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py b/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py new file mode 100644 index 00000000000..8267df95e11 --- /dev/null +++ b/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +import frappe +from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty + +def execute(): + bin_details = frappe.db.sql(""" + SELECT item_code, warehouse + FROM `tabBin`""",as_dict=1) + + for entry in bin_details: + update_bin_qty(entry.get("item_code"), entry.get("warehouse"), { + "indented_qty": get_indented_qty(entry.get("item_code"), entry.get("warehouse")) + }) \ No newline at end of file diff --git a/erpnext/patches/v12_0/rename_mws_settings_fields.py b/erpnext/patches/v12_0/rename_mws_settings_fields.py new file mode 100644 index 00000000000..e08e3769153 --- /dev/null +++ b/erpnext/patches/v12_0/rename_mws_settings_fields.py @@ -0,0 +1,11 @@ +# Copyright (c) 2020, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe + +def execute(): + count = frappe.db.sql("SELECT COUNT(*) FROM `tabSingles` WHERE doctype='Amazon MWS Settings' AND field='enable_sync';")[0][0] + if count == 0: + frappe.db.sql("UPDATE `tabSingles` SET field='enable_sync' WHERE doctype='Amazon MWS Settings' AND field='enable_synch';") + + frappe.reload_doc("ERPNext Integrations", "doctype", "Amazon MWS Settings") diff --git a/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py b/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py index 3fccbfa7003..85202bff4d6 100644 --- a/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py +++ b/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py @@ -1,10 +1,11 @@ import frappe + def execute(): - frappe.reload_doc('selling', 'doctype', frappe.scrub('Sales Order Item')) - frappe.reload_doc('buying', 'doctype', frappe.scrub('Purchase Order Item')) + frappe.reload_doc('selling', 'doctype', 'sales_order_item', force=True) + frappe.reload_doc('buying', 'doctype', 'purchase_order_item', force=True) - for doctype in ['Sales Order Item', 'Purchase Order Item']: + for doctype in ('Sales Order Item', 'Purchase Order Item'): frappe.db.sql(""" UPDATE `tab{0}` SET against_blanket_order = 1 diff --git a/erpnext/patches/v12_0/set_task_status.py b/erpnext/patches/v12_0/set_task_status.py index 70f65097dc3..dbd7e5a8181 100644 --- a/erpnext/patches/v12_0/set_task_status.py +++ b/erpnext/patches/v12_0/set_task_status.py @@ -1,16 +1,15 @@ import frappe def execute(): - frappe.reload_doctype('Task') + frappe.reload_doctype('Task') - # add "Completed" if customized - for doctype in ('Task'): - property_setter_name = frappe.db.exists('Property Setter', dict(doc_type = doctype, field_name = 'status', property = 'options')) - if property_setter_name: - property_setter = frappe.get_doc('Property Setter', property_setter_name) - if not "Completed" in property_setter.value: - property_setter.value = property_setter.value + '\nCompleted' - property_setter.save() + # add "Completed" if customized + property_setter_name = frappe.db.exists('Property Setter', dict(doc_type='Task', field_name = 'status', property = 'options')) + if property_setter_name: + property_setter = frappe.get_doc('Property Setter', property_setter_name) + if not "Completed" in property_setter.value: + property_setter.value = property_setter.value + '\nCompleted' + property_setter.save() - # renamed default status to Completed as status "Closed" is ambiguous - frappe.db.sql('update tabTask set status = "Completed" where status = "Closed"') \ No newline at end of file + # renamed default status to Completed as status "Closed" is ambiguous + frappe.db.sql('update tabTask set status = "Completed" where status = "Closed"') diff --git a/erpnext/patches/v12_0/set_total_batch_quantity.py b/erpnext/patches/v12_0/set_total_batch_quantity.py new file mode 100644 index 00000000000..d373275c253 --- /dev/null +++ b/erpnext/patches/v12_0/set_total_batch_quantity.py @@ -0,0 +1,11 @@ +import frappe + + +def execute(): + frappe.reload_doc("stock", "doctype", "batch") + + for batch in frappe.get_all("Batch", fields=["name", "batch_id"]): + batch_qty = frappe.db.get_value("Stock Ledger Entry", + {"docstatus": 1, "batch_no": batch.batch_id, "is_cancelled": "No"}, + "sum(actual_qty)") or 0.0 + frappe.db.set_value("Batch", batch.name, "batch_qty", batch_qty, update_modified=False) diff --git a/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py b/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py new file mode 100644 index 00000000000..63ca540a8e2 --- /dev/null +++ b/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py @@ -0,0 +1,11 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + +def execute(): + frappe.reload_doc("stock", "doctype", "pick_list") + frappe.db.sql("""UPDATE `tabPick List` set purpose = 'Delivery' + WHERE docstatus = 1 and purpose = 'Delivery against Sales Order' """) \ No newline at end of file diff --git a/erpnext/patches/v12_0/update_healthcare_refactored_changes.py b/erpnext/patches/v12_0/update_healthcare_refactored_changes.py new file mode 100644 index 00000000000..d06c5713d23 --- /dev/null +++ b/erpnext/patches/v12_0/update_healthcare_refactored_changes.py @@ -0,0 +1,137 @@ +from __future__ import unicode_literals +import frappe +from frappe.model.utils.rename_field import rename_field +from frappe.modules import scrub, get_doctype_module + +field_rename_map = { + 'Healthcare Settings': [ + ['patient_master_name', 'patient_name_by'], + ['max_visit', 'max_visits'], + ['reg_sms', 'send_registration_msg'], + ['reg_msg', 'registration_msg'], + ['app_con', 'send_appointment_confirmation'], + ['app_con_msg', 'appointment_confirmation_msg'], + ['no_con', 'avoid_confirmation'], + ['app_rem', 'send_appointment_reminder'], + ['app_rem_msg', 'appointment_reminder_msg'], + ['rem_before', 'remind_before'], + ['manage_customer', 'link_customer_to_patient'], + ['create_test_on_si_submit', 'create_lab_test_on_si_submit'], + ['require_sample_collection', 'create_sample_collection_for_lab_test'], + ['require_test_result_approval', 'lab_test_approval_required'], + ['manage_appointment_invoice_automatically', 'automate_appointment_invoicing'] + ], + 'Drug Prescription':[ + ['use_interval', 'usage_interval'], + ['in_every', 'interval_uom'] + ], + 'Lab Test Template':[ + ['sample_quantity', 'sample_qty'], + ['sample_collection_details', 'sample_details'] + ], + 'Sample Collection':[ + ['sample_quantity', 'sample_qty'], + ['sample_collection_details', 'sample_details'] + ], + 'Fee Validity': [ + ['max_visit', 'max_visits'] + ] +} + +def execute(): + for dn in field_rename_map: + if frappe.db.exists('DocType', dn): + if dn == 'Healthcare Settings': + frappe.reload_doctype('Healthcare Settings') + else: + frappe.reload_doc(get_doctype_module(dn), "doctype", scrub(dn)) + + for dt, field_list in field_rename_map.items(): + if frappe.db.exists('DocType', dt): + for field in field_list: + if dt == 'Healthcare Settings': + rename_field(dt, field[0], field[1]) + elif frappe.db.has_column(dt, field[0]): + rename_field(dt, field[0], field[1]) + + # first name mandatory in Patient + if frappe.db.exists('DocType', 'Patient'): + patients = frappe.db.sql("select name, patient_name from `tabPatient`", as_dict=1) + frappe.reload_doc('healthcare', 'doctype', 'patient') + for entry in patients: + name = entry.patient_name.split(' ') + frappe.db.set_value('Patient', entry.name, 'first_name', name[0]) + + # mark Healthcare Practitioner status as Disabled + if frappe.db.exists('DocType', 'Healthcare Practitioner'): + practitioners = frappe.db.sql("select name from `tabHealthcare Practitioner` where 'active'= 0", as_dict=1) + practitioners_lst = [p.name for p in practitioners] + frappe.reload_doc('healthcare', 'doctype', 'healthcare_practitioner') + if practitioners_lst: + frappe.db.sql("update `tabHealthcare Practitioner` set status = 'Disabled' where name IN %(practitioners)s""", {"practitioners": practitioners_lst}) + + # set Clinical Procedure status + if frappe.db.exists('DocType', 'Clinical Procedure'): + frappe.reload_doc('healthcare', 'doctype', 'clinical_procedure') + frappe.db.sql(""" + UPDATE + `tabClinical Procedure` + SET + docstatus = (CASE WHEN status = 'Cancelled' THEN 2 + WHEN status = 'Draft' THEN 0 + ELSE 1 + END) + """) + + # set complaints and diagnosis in table multiselect in Patient Encounter + if frappe.db.exists('DocType', 'Patient Encounter'): + field_list = [ + ['visit_department', 'medical_department'], + ['type', 'appointment_type'] + ] + encounter_details = frappe.db.sql("""select symptoms, diagnosis, name from `tabPatient Encounter`""", as_dict=True) + frappe.reload_doc('healthcare', 'doctype', 'patient_encounter') + frappe.reload_doc('healthcare', 'doctype', 'patient_encounter_symptom') + frappe.reload_doc('healthcare', 'doctype', 'patient_encounter_diagnosis') + + for field in field_list: + if frappe.db.has_column(dt, field[0]): + rename_field(dt, field[0], field[1]) + + for entry in encounter_details: + doc = frappe.get_doc('Patient Encounter', entry.name) + symptoms = entry.symptoms.split('\n') if entry.symptoms else [] + for symptom in symptoms: + if not frappe.db.exists('Complaint', symptom): + frappe.get_doc({ + 'doctype': 'Complaint', + 'complaints': symptom + }).insert() + row = doc.append('symptoms', { + 'complaint': symptom + }) + row.db_update() + + diagnosis = entry.diagnosis.split('\n') if entry.diagnosis else [] + for d in diagnosis: + if not frappe.db.exists('Diagnosis', d): + frappe.get_doc({ + 'doctype': 'Diagnosis', + 'diagnosis': d + }).insert() + row = doc.append('diagnosis', { + 'diagnosis': d + }) + row.db_update() + doc.db_update() + + if frappe.db.exists('DocType', 'Fee Validity'): + # update fee validity status + frappe.db.sql(""" + UPDATE + `tabFee Validity` + SET + status = (CASE WHEN visited >= max_visits THEN 'Completed' + ELSE 'Pending' + END) + """) \ No newline at end of file diff --git a/erpnext/patches/v8_1/gst_fixes.py b/erpnext/patches/v8_1/gst_fixes.py index 22fa53ba373..34255eb0a42 100644 --- a/erpnext/patches/v8_1/gst_fixes.py +++ b/erpnext/patches/v8_1/gst_fixes.py @@ -1,7 +1,8 @@ from __future__ import unicode_literals import frappe from frappe.custom.doctype.custom_field.custom_field import create_custom_field -from erpnext.regional.india.setup import update_address_template +from erpnext.regional.address_template.setup import set_up_address_templates + def execute(): company = frappe.get_all('Company', filters = {'country': 'India'}) @@ -10,9 +11,10 @@ def execute(): update_existing_custom_fields() add_custom_fields() - update_address_template() + set_up_address_templates(default_country='India') frappe.reload_doc("regional", "print_format", "gst_tax_invoice") + def update_existing_custom_fields(): frappe.db.sql("""update `tabCustom Field` set label = 'HSN/SAC' where fieldname='gst_hsn_code' and label='GST HSN Code' @@ -34,6 +36,7 @@ def update_existing_custom_fields(): where fieldname='gst_hsn_code' and dt in ('Sales Invoice Item', 'Purchase Invoice Item') """) + def add_custom_fields(): hsn_sac_field = dict(fieldname='gst_hsn_code', label='HSN/SAC', fieldtype='Data', options='item_code.gst_hsn_code', insert_after='description') diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index 13fd1b848f1..a07cdffcbeb 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -1,18 +1,19 @@ { "cards": [ { - "icon": "fa fa-star", - "links": "[\n {\n \"description\": \"Project master.\",\n \"label\": \"Project\",\n \"name\": \"Project\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Project activity / task.\",\n \"label\": \"Task\",\n \"name\": \"Task\",\n \"onboard\": 1,\n \"route\": \"#List/Task\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Make project from a template.\",\n \"label\": \"Project Template\",\n \"name\": \"Project Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define Project type.\",\n \"label\": \"Project Type\",\n \"name\": \"Project Type\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Project\"\n ],\n \"description\": \"Project Update.\",\n \"label\": \"Project Update\",\n \"name\": \"Project Update\",\n \"type\": \"doctype\"\n }\n]", - "title": "Projects" + "hidden": 0, + "label": "Projects", + "links": "[\n {\n \"description\": \"Project master.\",\n \"label\": \"Project\",\n \"name\": \"Project\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Project activity / task.\",\n \"label\": \"Task\",\n \"name\": \"Task\",\n \"onboard\": 1,\n \"route\": \"#List/Task\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Make project from a template.\",\n \"label\": \"Project Template\",\n \"name\": \"Project Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define Project type.\",\n \"label\": \"Project Type\",\n \"name\": \"Project Type\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Project\"\n ],\n \"description\": \"Project Update.\",\n \"label\": \"Project Update\",\n \"name\": \"Project Update\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Timesheet for tasks.\",\n \"label\": \"Timesheet\",\n \"name\": \"Timesheet\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Types of activities for Time Logs\",\n \"label\": \"Activity Type\",\n \"name\": \"Activity Type\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Activity Type\"\n ],\n \"description\": \"Cost of various activities\",\n \"label\": \"Activity Cost\",\n \"name\": \"Activity Cost\",\n \"type\": \"doctype\"\n }\n]", - "title": "Time Tracking" + "hidden": 0, + "label": "Time Tracking", + "links": "[\n {\n \"description\": \"Timesheet for tasks.\",\n \"label\": \"Timesheet\",\n \"name\": \"Timesheet\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Types of activities for Time Logs\",\n \"label\": \"Activity Type\",\n \"name\": \"Activity Type\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Activity Type\"\n ],\n \"description\": \"Cost of various activities\",\n \"label\": \"Activity Cost\",\n \"name\": \"Activity Cost\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-list", - "links": "[\n {\n \"dependencies\": [\n \"Timesheet\"\n ],\n \"doctype\": \"Timesheet\",\n \"is_query_report\": true,\n \"label\": \"Daily Timesheet Summary\",\n \"name\": \"Daily Timesheet Summary\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Project\"\n ],\n \"doctype\": \"Project\",\n \"is_query_report\": true,\n \"label\": \"Project wise Stock Tracking\",\n \"name\": \"Project wise Stock Tracking\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Project\"\n ],\n \"doctype\": \"Project\",\n \"is_query_report\": true,\n \"label\": \"Project Billing Summary\",\n \"name\": \"Project Billing Summary\",\n \"type\": \"report\"\n }\n]", - "title": "Reports" + "hidden": 0, + "label": "Reports", + "links": "[\n {\n \"dependencies\": [\n \"Timesheet\"\n ],\n \"doctype\": \"Timesheet\",\n \"is_query_report\": true,\n \"label\": \"Daily Timesheet Summary\",\n \"name\": \"Daily Timesheet Summary\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Project\"\n ],\n \"doctype\": \"Project\",\n \"is_query_report\": true,\n \"label\": \"Project wise Stock Tracking\",\n \"name\": \"Project wise Stock Tracking\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Project\"\n ],\n \"doctype\": \"Project\",\n \"is_query_report\": true,\n \"label\": \"Project Billing Summary\",\n \"name\": \"Project Billing Summary\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", @@ -27,7 +28,7 @@ "idx": 0, "is_standard": 1, "label": "Projects", - "modified": "2020-03-12 16:30:41.538685", + "modified": "2020-04-01 11:28:51.245756", "modified_by": "Administrator", "module": "Projects", "name": "Projects", @@ -37,23 +38,23 @@ "shortcuts": [ { "format": "{} Assigned", - "is_query_report": 0, + "label": "Task", "link_to": "Task", "stats_filter": "{\n \"_assign\": [\"like\", '%' + frappe.session.user + '%'],\n \"status\": \"Open\"\n}", "type": "DocType" }, { - "is_query_report": 0, + "label": "Project", "link_to": "Project", "type": "DocType" }, { - "is_query_report": 0, + "label": "Timesheet", "link_to": "Timesheet", "type": "DocType" }, { - "is_query_report": 0, + "label": "Project Billing Summary", "link_to": "Project Billing Summary", "type": "Report" } diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 7d47db371d5..f3cecd9059b 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:project_name", @@ -82,7 +83,6 @@ "oldfieldname": "status", "oldfieldtype": "Select", "options": "Open\nCompleted\nCancelled", - "reqd": 1, "search_index": 1 }, { @@ -435,16 +435,18 @@ }, { "depends_on": "collect_progress", - "description": "Message will sent to users to get their status on the project", + "description": "Message will be sent to the users to get their status on the Project", "fieldname": "message", "fieldtype": "Text", - "label": "Message" + "label": "Message", + "mandatory_depends_on": "collect_progress" } ], "icon": "fa fa-puzzle-piece", "idx": 29, + "links": [], "max_attachments": 4, - "modified": "2019-09-24 15:02:50.208301", + "modified": "2020-04-08 22:11:14.552615", "modified_by": "Administrator", "module": "Projects", "name": "Project", @@ -487,4 +489,4 @@ "sort_order": "DESC", "timeline_field": "customer", "track_seen": 1 -} \ No newline at end of file +} diff --git a/erpnext/projects/report/billing_summary.py b/erpnext/projects/report/billing_summary.py index 76379f1de2e..b808268d1b8 100644 --- a/erpnext/projects/report/billing_summary.py +++ b/erpnext/projects/report/billing_summary.py @@ -53,7 +53,7 @@ def get_columns(): def get_data(filters): data = [] if(filters.from_date > filters.to_date): - frappe.msgprint(_(" From Date can not be greater than To Date")) + frappe.msgprint(_("From Date can not be greater than To Date")) return data timesheets = get_timesheets(filters) diff --git a/erpnext/public/js/conf.js b/erpnext/public/js/conf.js index 615f6a43f9e..9870f819105 100644 --- a/erpnext/public/js/conf.js +++ b/erpnext/public/js/conf.js @@ -43,7 +43,6 @@ $.extend(frappe.breadcrumbs.preferred, { $.extend(frappe.breadcrumbs.module_map, { 'ERPNext Integrations': 'Integrations', 'Geo': 'Settings', - 'Accounts': 'Accounting', 'Portal': 'Website', 'Utilities': 'Settings', 'Shopping Cart': 'Website', diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 3d4c4a64595..afbdbc661d3 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -85,12 +85,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ filters:{ 'is_sub_contracted_item': 1 } } } - else if (me.frm.doc.material_request_type == "Customer Provided") { - return{ - query: "erpnext.controllers.queries.item_query", - filters:{ 'customer': me.frm.doc.customer } - } - } else { return{ query: "erpnext.controllers.queries.item_query", @@ -385,7 +379,31 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ } }); } - } + }, + + manufacturer_part_no: function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + + if (row.manufacturer_part_no) { + frappe.model.get_value('Item Manufacturer', + { + 'item_code': row.item_code, + 'manufacturer': row.manufacturer, + 'manufacturer_part_no': row.manufacturer_part_no + }, + 'name', + function(data) { + if (!data) { + let msg = { + message: __("Manufacturer Part Number {0} is invalid", [row.manufacturer_part_no]), + title: __("Invalid Part Number") + } + frappe.throw(msg); + } + }); + + } + } }); cur_frm.add_fetch('project', 'cost_center', 'cost_center'); diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 4397fe49c3c..f126f6a62d6 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -473,6 +473,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ item_code: item.item_code, barcode: item.barcode, serial_no: item.serial_no, + batch_no: item.batch_no, set_warehouse: me.frm.doc.set_warehouse, warehouse: item.warehouse, customer: me.frm.doc.customer || me.frm.doc.party_name, @@ -550,6 +551,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if(!d[k]) d[k] = v; }); + if (d.has_batch_no && d.has_serial_no) { + d.batch_no = undefined; + } + erpnext.show_serial_batch_selector(me.frm, d, (item) => { me.frm.script_manager.trigger('qty', item.doctype, item.name); if (!me.frm.doc.set_warehouse) @@ -558,7 +563,13 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, () => me.conversion_factor(doc, cdt, cdn, true), - () => me.remove_pricing_rule(item) + () => me.remove_pricing_rule(item), + () => { + if (item.apply_rule_on_other_items) { + let key = item.name; + me.apply_rule_on_other_items({key: item}); + } + } ]); } } @@ -637,6 +648,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ // Add the new list to the serial no. field in grid with each in new line item.serial_no = valid_serial_nos.join('\n'); + item.conversion_factor = item.conversion_factor || 1; refresh_field("serial_no", item.name, item.parentfield); if(!doc.is_return && cint(user_defaults.set_qty_in_transactions_based_on_serial_no_input)) { @@ -892,7 +904,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ shipping_rule: function() { var me = this; - if(this.frm.doc.shipping_rule) { + if(this.frm.doc.shipping_rule && this.frm.doc.shipping_address) { return this.frm.call({ doc: this.frm.doc, method: "apply_shipping_rule", @@ -1392,20 +1404,22 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ apply_rule_on_other_items: function(args) { const me = this; - const fields = ["discount_percentage", "discount_amount", "rate"]; + const fields = ["discount_percentage", "pricing_rules", "discount_amount", "rate"]; for(var k in args) { let data = args[k]; - me.frm.doc.items.forEach(d => { - if (in_list(data.apply_rule_on_other_items, d[data.apply_rule_on])) { - for(var k in data) { - if (in_list(fields, k)) { - frappe.model.set_value(d.doctype, d.name, k, data[k]); + if (data && data.apply_rule_on_other_items) { + me.frm.doc.items.forEach(d => { + if (in_list(data.apply_rule_on_other_items, d[data.apply_rule_on])) { + for(var k in data) { + if (in_list(fields, k) && data[k]) { + frappe.model.set_value(d.doctype, d.name, k, data[k]); + } } } - } - }); + }); + } } }, diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index dead309a5d0..296c6280d84 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -78,6 +78,39 @@ function get_filters(){ "fieldtype": "Link", "options": "Finance Book" }, + { + "fieldname":"filter_based_on", + "label": __("Filter Based On"), + "fieldtype": "Select", + "options": ["Fiscal Year", "Date Range"], + "default": ["Fiscal Year"], + "reqd": 1, + on_change: function() { + let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); + frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year'); + frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year'); + + frappe.query_report.refresh(); + } + }, + { + "fieldname":"period_start_date", + "label": __("Start Date"), + "fieldtype": "Date", + "default": frappe.datetime.nowdate(), + "hidden": 1, + "reqd": 1 + }, + { + "fieldname":"period_end_date", + "label": __("End Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), + "hidden": 1, + "reqd": 1 + }, { "fieldname":"from_fiscal_year", "label": __("Start Year"), diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 4d44eae086e..58969f2a9fd 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -436,6 +436,44 @@ erpnext.utils.update_child_items = function(opts) { const cannot_add_row = (typeof opts.cannot_add_row === 'undefined') ? true : opts.cannot_add_row; const child_docname = (typeof opts.cannot_add_row === 'undefined') ? "items" : opts.child_docname; this.data = []; + const fields = [{ + fieldtype:'Data', + fieldname:"docname", + read_only: 1, + hidden: 1, + }, { + fieldtype:'Link', + fieldname:"item_code", + options: 'Item', + in_list_view: 1, + read_only: 0, + disabled: 0, + label: __('Item Code') + }, { + fieldtype:'Float', + fieldname:"qty", + default: 0, + read_only: 0, + in_list_view: 1, + label: __('Qty') + }, { + fieldtype:'Currency', + fieldname:"rate", + default: 0, + read_only: 0, + in_list_view: 1, + label: __('Rate') + }]; + + if (frm.doc.doctype == 'Sales Order' || frm.doc.doctype == 'Purchase Order' ) { + fields.splice(2, 0, { + fieldtype: 'Date', + fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date", + in_list_view: 1, + label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date") + }) + } + const dialog = new frappe.ui.Dialog({ title: __("Update Items"), fields: [ @@ -450,34 +488,7 @@ erpnext.utils.update_child_items = function(opts) { get_data: () => { return this.data; }, - fields: [{ - fieldtype:'Data', - fieldname:"docname", - read_only: 1, - hidden: 1, - }, { - fieldtype:'Link', - fieldname:"item_code", - options: 'Item', - in_list_view: 1, - read_only: 0, - disabled: 0, - label: __('Item Code') - }, { - fieldtype:'Float', - fieldname:"qty", - default: 0, - read_only: 0, - in_list_view: 1, - label: __('Qty') - }, { - fieldtype:'Currency', - fieldname:"rate", - default: 0, - read_only: 0, - in_list_view: 1, - label: __('Rate') - }] + fields: fields }, ], primary_action: function() { @@ -506,6 +517,8 @@ erpnext.utils.update_child_items = function(opts) { "docname": d.name, "name": d.name, "item_code": d.item_code, + "delivery_date": d.delivery_date, + "schedule_date": d.schedule_date, "qty": d.qty, "rate": d.rate, }); diff --git a/erpnext/public/scss/website.scss b/erpnext/public/scss/website.scss index 7b9a70d232e..735b417da17 100644 --- a/erpnext/public/scss/website.scss +++ b/erpnext/public/scss/website.scss @@ -78,3 +78,7 @@ z-index: 0; } } + +.place-order-container { + text-align: right; +} \ No newline at end of file diff --git a/erpnext/quality_management/desk_page/quality/quality.json b/erpnext/quality_management/desk_page/quality/quality.json index 5193e0cfded..5ee70008dd8 100644 --- a/erpnext/quality_management/desk_page/quality/quality.json +++ b/erpnext/quality_management/desk_page/quality/quality.json @@ -1,20 +1,24 @@ { "cards": [ { - "links": "[\n {\n \"description\": \"Quality Goal.\",\n \"label\": \"Quality Goal\",\n \"name\": \"Quality Goal\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Procedure.\",\n \"label\": \"Quality Procedure\",\n \"name\": \"Quality Procedure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Quality Procedures.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Tree of Procedures\",\n \"name\": \"Quality Procedure\",\n \"route\": \"#Tree/Quality Procedure\",\n \"type\": \"doctype\"\n }\n]", - "title": "Goal and Procedure" + "hidden": 0, + "label": "Goal and Procedure", + "links": "[\n {\n \"description\": \"Quality Goal.\",\n \"label\": \"Quality Goal\",\n \"name\": \"Quality Goal\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Procedure.\",\n \"label\": \"Quality Procedure\",\n \"name\": \"Quality Procedure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Quality Procedures.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Tree of Procedures\",\n \"name\": \"Quality Procedure\",\n \"route\": \"#Tree/Quality Procedure\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Quality Feedback\",\n \"label\": \"Quality Feedback\",\n \"name\": \"Quality Feedback\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Feedback Template\",\n \"label\": \"Quality Feedback Template\",\n \"name\": \"Quality Feedback Template\",\n \"type\": \"doctype\"\n }\n]", - "title": "Feedback" + "hidden": 0, + "label": "Feedback", + "links": "[\n {\n \"description\": \"Quality Feedback\",\n \"label\": \"Quality Feedback\",\n \"name\": \"Quality Feedback\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Feedback Template\",\n \"label\": \"Quality Feedback Template\",\n \"name\": \"Quality Feedback Template\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Quality Meeting\",\n \"label\": \"Quality Meeting\",\n \"name\": \"Quality Meeting\",\n \"type\": \"doctype\"\n }\n]", - "title": "Meeting" + "hidden": 0, + "label": "Meeting", + "links": "[\n {\n \"description\": \"Quality Meeting\",\n \"label\": \"Quality Meeting\",\n \"name\": \"Quality Meeting\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Quality Review\",\n \"label\": \"Quality Review\",\n \"name\": \"Quality Review\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Action\",\n \"label\": \"Quality Action\",\n \"name\": \"Quality Action\",\n \"type\": \"doctype\"\n }\n]", - "title": "Review and Action" + "hidden": 0, + "label": "Review and Action", + "links": "[\n {\n \"description\": \"Quality Review\",\n \"label\": \"Quality Review\",\n \"name\": \"Quality Review\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Action\",\n \"label\": \"Quality Action\",\n \"name\": \"Quality Action\",\n \"type\": \"doctype\"\n }\n]" } ], "category": "Modules", @@ -29,7 +33,7 @@ "idx": 0, "is_standard": 1, "label": "Quality", - "modified": "2020-03-12 16:30:39.093081", + "modified": "2020-04-01 11:28:51.095012", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality", @@ -38,17 +42,17 @@ "pin_to_top": 0, "shortcuts": [ { - "is_query_report": 0, + "label": "Quality Goal", "link_to": "Quality Goal", "type": "DocType" }, { - "is_query_report": 0, + "label": "Quality Procedure", "link_to": "Quality Procedure", "type": "DocType" }, { - "is_query_report": 0, + "label": "Quality Inspection", "link_to": "Quality Inspection", "type": "DocType" } diff --git a/erpnext/regional/address_template/README.md b/erpnext/regional/address_template/README.md new file mode 100644 index 00000000000..991573448e1 --- /dev/null +++ b/erpnext/regional/address_template/README.md @@ -0,0 +1,18 @@ +To add an **Address Template** for your country, place a new file in this directory: + + * File name: `your_country.html` (lower case with underscores) + * File content: a [Jinja Template](http://jinja.pocoo.org/docs/templates/). + +All the fields of **Address** (including Custom Fields, if any) will be available to the template. Example: + +```jinja +{{ address_line1 }}
+{% if address_line2 %}{{ address_line2 }}
{% endif -%} +{{ city }}
+{% if state %}{{ state }}
{% endif -%} +{% if pincode %} PIN: {{ pincode }}
{% endif -%} +{{ country }}
+{% if phone %}Phone: {{ phone }}
{% endif -%} +{% if fax %}Fax: {{ fax }}
{% endif -%} +{% if email_id %}Email: {{ email_id }}
{% endif -%} +``` diff --git a/erpnext/regional/address_template/__init__.py b/erpnext/regional/address_template/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/regional/address_template/setup.py b/erpnext/regional/address_template/setup.py new file mode 100644 index 00000000000..9f318de3451 --- /dev/null +++ b/erpnext/regional/address_template/setup.py @@ -0,0 +1,53 @@ +"""Import Address Templates from ./templates directory.""" +import os +import frappe + +def set_up_address_templates(default_country=None): + for country, html in get_address_templates(): + is_default = 1 if country == default_country else 0 + update_address_template(country, html, is_default) + +def get_address_templates(): + """ + Return country and path for all HTML files in this directory. + + Returns a list of dicts. + """ + def country(file_name): + """Convert 'united_states.html' to 'United States'.""" + suffix_pos = file_name.find(".html") + country_snake_case = file_name[:suffix_pos] + country_title_case = " ".join(country_snake_case.split("_")).title() + return country_title_case + + def get_file_content(file_name): + """Convert 'united_states.html' to '/path/to/united_states.html'.""" + full_path = os.path.join(template_dir, file_name) + with open(full_path, "r") as f: + content = f.read() + return content + + dir_name = os.path.dirname(__file__) + template_dir = os.path.join(dir_name, "templates") + file_names = os.listdir(template_dir) + html_files = [file for file in file_names if file.endswith(".html")] + + return [(country(file_name), get_file_content(file_name)) for file_name in html_files] + + +def update_address_template(country, html, is_default=0): + """Update existing Address Template or create a new one.""" + if not frappe.db.exists("Country", country): + frappe.log_error("Country {} for regional Address Template does not exist.".format(country)) + return + + if frappe.db.exists("Address Template", country): + frappe.db.set_value("Address Template", country, "template", html) + frappe.db.set_value("Address Template", country, "is_default", is_default) + else: + frappe.get_doc(dict( + doctype="Address Template", + country=country, + is_default=is_default, + template=html + )).insert() diff --git a/erpnext/regional/germany/address_template.html b/erpnext/regional/address_template/templates/germany.html similarity index 100% rename from erpnext/regional/germany/address_template.html rename to erpnext/regional/address_template/templates/germany.html diff --git a/erpnext/regional/india/address_template.html b/erpnext/regional/address_template/templates/india.html similarity index 89% rename from erpnext/regional/india/address_template.html rename to erpnext/regional/address_template/templates/india.html index 55cc9af0d16..ffb9d0547e0 100644 --- a/erpnext/regional/india/address_template.html +++ b/erpnext/regional/address_template/templates/india.html @@ -6,4 +6,4 @@ {% if phone %}Phone: {{ phone }}
{% endif -%} {% if fax %}Fax: {{ fax }}
{% endif -%} {% if email_id %}Email: {{ email_id }}
{% endif -%} -{% if gstin %}GSTIN: {{ gstin }}
{% endif -%} \ No newline at end of file +{% if gstin %}GSTIN: {{ gstin }}
{% endif -%} diff --git a/erpnext/regional/united_states/address_template.html b/erpnext/regional/address_template/templates/united_states.html similarity index 100% rename from erpnext/regional/united_states/address_template.html rename to erpnext/regional/address_template/templates/united_states.html diff --git a/erpnext/regional/address_template/test_regional_address_template.py b/erpnext/regional/address_template/test_regional_address_template.py new file mode 100644 index 00000000000..8a05ea26f45 --- /dev/null +++ b/erpnext/regional/address_template/test_regional_address_template.py @@ -0,0 +1,45 @@ +from __future__ import unicode_literals +from unittest import TestCase + +import frappe +from erpnext.regional.address_template.setup import get_address_templates +from erpnext.regional.address_template.setup import update_address_template + +def ensure_country(country): + if frappe.db.exists("Country", country): + return frappe.get_doc("Country", country) + else: + c = frappe.get_doc({ + "doctype": "Country", + "country_name": country + }) + c.insert() + return c + +class TestRegionalAddressTemplate(TestCase): + def test_get_address_templates(self): + """Get the countries and paths from the templates directory.""" + templates = get_address_templates() + self.assertIsInstance(templates, list) + self.assertIsInstance(templates[0], tuple) + + def test_create_address_template(self): + """Create a new Address Template.""" + country = ensure_country("Germany") + update_address_template(country.name, "TEST") + doc = frappe.get_doc("Address Template", country.name) + self.assertEqual(doc.template, "TEST") + + def test_update_address_template(self): + """Update an existing Address Template.""" + country = ensure_country("Germany") + if not frappe.db.exists("Address Template", country.name): + template = frappe.get_doc({ + "doctype": "Address Template", + "country": country.name, + "template": "EXISTING" + }).insert() + + update_address_template(country.name, "NEW") + doc = frappe.get_doc("Address Template", country.name) + self.assertEqual(doc.template, "NEW") diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index a2b32fec781..9e7a023926d 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -277,7 +277,7 @@ class GSTR3BReport(Document): def get_itc_details(self, reverse_charge='N'): itc_amount = frappe.db.sql(""" - select s.gst_category, sum(t.tax_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge + select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s @@ -312,7 +312,7 @@ class GSTR3BReport(Document): and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders') group by s.gst_category, s.place_of_supply""", (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1) - inter_state_supply_tax = frappe.db.sql(""" select sum(t.tax_amount) as tax_amount, s.place_of_supply, s.gst_category + inter_state_supply_tax = frappe.db.sql(""" select sum(t.tax_amount_after_discount_amount) as tax_amount, s.place_of_supply, s.gst_category from `tabSales Invoice` s, `tabSales Taxes and Charges` t where t.parent = s.name and s.docstatus = 1 and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders') @@ -385,7 +385,7 @@ class GSTR3BReport(Document): tax_template = 'Purchase Taxes and Charges' tax_amounts = frappe.db.sql(""" - select s.gst_category, sum(t.tax_amount) as tax_amount, t.account_head + select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head from `tab{doctype}` s , `tab{template}` t where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s diff --git a/erpnext/regional/germany/setup.py b/erpnext/regional/germany/setup.py index a5471366ca2..d6047e863ce 100644 --- a/erpnext/regional/germany/setup.py +++ b/erpnext/regional/germany/setup.py @@ -3,29 +3,4 @@ import frappe def setup(company=None, patch=True): - if not patch: - update_address_template() - - -def update_address_template(): - """ - Read address template from file. Update existing Address Template or create a - new one. - """ - dir_name = os.path.dirname(__file__) - template_path = os.path.join(dir_name, 'address_template.html') - - with open(template_path, 'r') as template_file: - template_html = template_file.read() - - address_template = frappe.db.get_value('Address Template', 'Germany') - - if address_template: - frappe.db.set_value('Address Template', 'Germany', 'template', template_html) - else: - # make new html template for Germany - frappe.get_doc(dict( - doctype='Address Template', - country='Germany', - template=template_html - )).insert() + pass diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 75f29b8380b..28b1f8ffb8b 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -13,7 +13,6 @@ from frappe.utils import today def setup(company=None, patch=True): setup_company_independent_fixtures() if not patch: - update_address_template() make_fixtures(company) # TODO: for all countries @@ -24,21 +23,6 @@ def setup_company_independent_fixtures(): frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes', now=frappe.flags.in_test) add_print_formats() -def update_address_template(): - with open(os.path.join(os.path.dirname(__file__), 'address_template.html'), 'r') as f: - html = f.read() - - address_template = frappe.db.get_value('Address Template', 'India') - if address_template: - frappe.db.set_value('Address Template', 'India', 'template', html) - else: - # make new html template for India - frappe.get_doc(dict( - doctype='Address Template', - country='India', - template=html - )).insert() - def add_hsn_sac_codes(): # HSN codes with open(os.path.join(os.path.dirname(__file__), 'hsn_code_data.json'), 'r') as f: diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index a6579121cbf..a8e40cc4930 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -13,6 +13,7 @@ import json import zlib import zipfile import six +from csv import QUOTE_NONNUMERIC from six import BytesIO from six import string_types import frappe @@ -80,7 +81,7 @@ def get_transactions(filters, as_dict=1): gl.posting_date as 'Belegdatum', gl.voucher_no as 'Belegfeld 1', - gl.remarks as 'Buchungstext', + LEFT(gl.remarks, 60) as 'Buchungstext', gl.voucher_type as 'Beleginfo - Art 1', gl.voucher_no as 'Beleginfo - Inhalt 1', gl.against_voucher_type as 'Beleginfo - Art 2', @@ -268,7 +269,9 @@ def get_datev_csv(data, filters, csv_class): # Do not number rows index=False, # Use all columns defined above - columns=csv_class.COLUMNS + columns=csv_class.COLUMNS, + # Quote most fields, even currency values with "," separator + quoting=QUOTE_NONNUMERIC ) if not six.PY2: @@ -285,6 +288,7 @@ def get_datev_csv(data, filters, csv_class): def get_header(filters, csv_class): coa = frappe.get_value("Company", filters.get("company"), "chart_of_accounts") + description = filters.get("voucher_type", csv_class.FORMAT_NAME) coa_used = "04" if "SKR04" in coa else ("03" if "SKR03" in coa else "") header = [ @@ -323,12 +327,8 @@ def get_header(filters, csv_class): frappe.utils.formatdate(filters.get('from_date'), "yyyyMMdd"), # P = Transaction batch end date (YYYYMMDD) frappe.utils.formatdate(filters.get('to_date'), "yyyyMMdd"), - # Q = Description (for example, "January - February 2019 Transactions") - '"{} - {} {}"'.format( - frappe.utils.formatdate(filters.get('from_date'), "MMMM yyyy"), - frappe.utils.formatdate(filters.get('to_date'), "MMMM yyyy"), - csv_class.FORMAT_NAME - ), + # Q = Description (for example, "Sales Invoice") Max. 30 chars + '"{}"'.format(_(description)), # R = Diktatkürzel '', # S = Buchungstyp diff --git a/erpnext/regional/united_states/setup.py b/erpnext/regional/united_states/setup.py index cb82b639ba8..6d344025d26 100644 --- a/erpnext/regional/united_states/setup.py +++ b/erpnext/regional/united_states/setup.py @@ -5,12 +5,9 @@ from __future__ import unicode_literals import frappe from frappe.custom.doctype.custom_field.custom_field import create_custom_fields - def setup(company=None, patch=True): make_custom_fields() add_print_formats() - update_address_template() - def make_custom_fields(): custom_fields = { @@ -21,22 +18,7 @@ def make_custom_fields(): } create_custom_fields(custom_fields) - def add_print_formats(): frappe.reload_doc("regional", "print_format", "irs_1099_form") frappe.db.sql(""" update `tabPrint Format` set disabled = 0 where name in('IRS 1099 Form') """) - - -def update_address_template(): - html = """{{ address_line1 }}
- {% if address_line2 %}{{ address_line2 }}
{% endif -%} - {{ city }}, {% if state %}{{ state }}{% endif -%}{% if pincode %} {{ pincode }}
{% endif -%} - {% if country != "United States" %}{{ country|upper }}{% endif -%} - """ - - address_template = frappe.db.get_value('Address Template', 'United States') - if address_template: - frappe.db.set_value('Address Template', 'United States', 'template', html) - else: - frappe.get_doc(dict(doctype='Address Template', country='United States', template=html)).insert() diff --git a/erpnext/selling/desk_page/retail/retail.json b/erpnext/selling/desk_page/retail/retail.json index 9838462f684..9f3912db4c2 100644 --- a/erpnext/selling/desk_page/retail/retail.json +++ b/erpnext/selling/desk_page/retail/retail.json @@ -1,8 +1,9 @@ { "cards": [ { - "links": "[\n {\n \"description\": \"Setup default values for POS Invoices\",\n \"label\": \"Point-of-Sale Profile\",\n \"name\": \"POS Profile\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"POS Profile\"\n ],\n \"description\": \"Point of Sale\",\n \"label\": \"POS\",\n \"name\": \"pos\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"description\": \"Cashier Closing\",\n \"label\": \"Cashier Closing\",\n \"name\": \"Cashier Closing\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup mode of POS (Online / Offline)\",\n \"label\": \"POS Settings\",\n \"name\": \"POS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To make Customer based incentive schemes.\",\n \"label\": \"Loyalty Program\",\n \"name\": \"Loyalty Program\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To view logs of Loyalty Points assigned to a Customer.\",\n \"label\": \"Loyalty Point Entry\",\n \"name\": \"Loyalty Point Entry\",\n \"type\": \"doctype\"\n }\n]", - "title": "Retail Operations" + "hidden": 0, + "label": "Retail Operations", + "links": "[\n {\n \"description\": \"Setup default values for POS Invoices\",\n \"label\": \"Point-of-Sale Profile\",\n \"name\": \"POS Profile\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"POS Profile\"\n ],\n \"description\": \"Point of Sale\",\n \"label\": \"POS\",\n \"name\": \"pos\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"description\": \"Cashier Closing\",\n \"label\": \"Cashier Closing\",\n \"name\": \"Cashier Closing\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup mode of POS (Online / Offline)\",\n \"label\": \"POS Settings\",\n \"name\": \"POS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To make Customer based incentive schemes.\",\n \"label\": \"Loyalty Program\",\n \"name\": \"Loyalty Program\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To view logs of Loyalty Points assigned to a Customer.\",\n \"label\": \"Loyalty Point Entry\",\n \"name\": \"Loyalty Point Entry\",\n \"type\": \"doctype\"\n }\n]" } ], "category": "Domains", @@ -16,7 +17,7 @@ "idx": 0, "is_standard": 1, "label": "Retail", - "modified": "2020-03-12 16:30:36.154297", + "modified": "2020-04-01 11:28:50.966145", "modified_by": "Administrator", "module": "Selling", "name": "Retail", diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json index 7a0111c8f99..a20806b264e 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -1,36 +1,36 @@ { "cards": [ { - "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Price List\"\n ],\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for adding shipping costs.\",\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define coupon codes.\",\n \"label\": \"Coupon Code\",\n \"name\": \"Coupon Code\",\n \"type\": \"doctype\"\n }\n]", - "title": "Items and Pricing" + "hidden": 0, + "label": "Items and Pricing", + "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Price List\"\n ],\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for adding shipping costs.\",\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define coupon codes.\",\n \"label\": \"Coupon Code\",\n \"name\": \"Coupon Code\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-cog", - "links": "[\n {\n \"description\": \"Default settings for selling transactions.\",\n \"label\": \"Selling Settings\",\n \"name\": \"Selling Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Track Leads by Lead Source.\",\n \"label\": \"Lead Source\",\n \"name\": \"Lead Source\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n }\n]", - "title": "Settings" + "hidden": 0, + "label": "Settings", + "links": "[\n {\n \"description\": \"Default settings for selling transactions.\",\n \"label\": \"Selling Settings\",\n \"name\": \"Selling Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Track Leads by Lead Source.\",\n \"label\": \"Lead Source\",\n \"name\": \"Lead Source\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-list", - "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]", - "title": "Other Reports" + "hidden": 0, + "label": "Other Reports", + "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]" }, { - "icon": "fa fa-star", - "links": "[\n {\n \"description\": \"Customer Database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Quotes to Leads or Customers.\",\n \"label\": \"Quotation\",\n \"name\": \"Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Confirmed orders from Customers.\",\n \"label\": \"Sales Order\",\n \"name\": \"Sales Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Invoices for Costumers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Blanket Orders from Costumers.\",\n \"label\": \"Blanket Order\",\n \"name\": \"Blanket Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Manage Sales Partners.\",\n \"label\": \"Sales Partner\",\n \"name\": \"Sales Partner\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"type\": \"doctype\"\n }\n]", - "title": "Sales" + "hidden": 0, + "label": "Sales", + "links": "[\n {\n \"description\": \"Customer Database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Quotes to Leads or Customers.\",\n \"label\": \"Quotation\",\n \"name\": \"Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Confirmed orders from Customers.\",\n \"label\": \"Sales Order\",\n \"name\": \"Sales Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Invoices for Costumers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Blanket Orders from Costumers.\",\n \"label\": \"Blanket Order\",\n \"name\": \"Blanket Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Manage Sales Partners.\",\n \"label\": \"Sales Partner\",\n \"name\": \"Sales Partner\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-table", - "links": "[\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Analytics\",\n \"name\": \"Sales Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"icon\": \"fa fa-bar-chart\",\n \"is_query_report\": true,\n \"label\": \"Customer Acquisition and Loyalty\",\n \"name\": \"Customer Acquisition and Loyalty\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person-wise Transaction Summary\",\n \"name\": \"Sales Person-wise Transaction Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales History\",\n \"name\": \"Item-wise Sales History\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quotation\"\n ],\n \"doctype\": \"Quotation\",\n \"is_query_report\": true,\n \"label\": \"Quotation Trends\",\n \"name\": \"Quotation Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Trends\",\n \"name\": \"Sales Order Trends\",\n \"type\": \"report\"\n }\n]", - "title": "Key Reports" + "hidden": 0, + "label": "Key Reports", + "links": "[\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Analytics\",\n \"name\": \"Sales Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"icon\": \"fa fa-bar-chart\",\n \"is_query_report\": true,\n \"label\": \"Customer Acquisition and Loyalty\",\n \"name\": \"Customer Acquisition and Loyalty\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person-wise Transaction Summary\",\n \"name\": \"Sales Person-wise Transaction Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales History\",\n \"name\": \"Item-wise Sales History\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quotation\"\n ],\n \"doctype\": \"Quotation\",\n \"is_query_report\": true,\n \"label\": \"Quotation Trends\",\n \"name\": \"Quotation Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Trends\",\n \"name\": \"Sales Order Trends\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", "charts": [ { "chart_name": "Income", - "label": "Income", - "size": "Full" + "label": "Income" } ], "creation": "2020-01-28 11:49:12.092882", @@ -43,7 +43,7 @@ "idx": 0, "is_standard": 1, "label": "Selling", - "modified": "2020-03-12 16:30:38.207013", + "modified": "2020-04-01 11:28:51.047373", "modified_by": "Administrator", "module": "Selling", "name": "Selling", @@ -52,32 +52,32 @@ "pin_to_top": 0, "shortcuts": [ { - "is_query_report": 0, + "label": "Sales Invoice", "link_to": "Sales Invoice", "type": "DocType" }, { - "is_query_report": 0, + "label": "Sales Order", "link_to": "Sales Order", "type": "DocType" }, { - "is_query_report": 0, + "label": "Quotation", "link_to": "Quotation", "type": "DocType" }, { - "is_query_report": 0, + "label": "Delivery Note", "link_to": "Delivery Note", "type": "DocType" }, { - "is_query_report": 0, + "label": "Accounts Receivable", "link_to": "Accounts Receivable", "type": "Report" }, { - "is_query_report": 0, + "label": "Sales Register", "link_to": "Sales Register", "type": "Report" } diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 02667e8c233..50e719f02e8 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -150,7 +150,7 @@ class Customer(TransactionBase): contact.save() else: - lead.lead_name = lead.lead_name.split(" ") + lead.lead_name = lead.lead_name.lstrip().split(" ") lead.first_name = lead.lead_name[0] lead.last_name = " ".join(lead.lead_name[1:]) diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 54e87f7a3b5..6462d3bc8c3 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -60,9 +60,9 @@ "base_total", "base_net_total", "column_break_33", + "total_net_weight", "total", "net_total", - "total_net_weight", "taxes_section", "tax_category", "column_break_38", @@ -1196,7 +1196,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2019-12-30 19:15:28.605085", + "modified": "2020-04-17 12:50:39.640534", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index ef2d19ac546..05e4aa892b0 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1036,7 +1036,7 @@ def create_pick_list(source_name, target_doc=None): }, }, target_doc) - doc.purpose = 'Delivery against Sales Order' + doc.purpose = 'Delivery' doc.set_item_locations() diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index df487833a8c..7011cf9804b 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -287,7 +287,7 @@ erpnext.pos.PointOfSale = class PointOfSale { if (in_list(['serial_no', 'batch_no'], field)) { args[field] = value; } - + // add to cur_frm const item = this.frm.add_child('items', args); frappe.flags.hide_serial_batch_dialog = true; @@ -1176,7 +1176,7 @@ class POSCart { return `
+ data-batch-no="${batch_no}" title="Item: ${item.item_name} Available Qty: ${item.actual_qty} ${item.stock_uom}">
${item.item_name}
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index 17136e04723..dfa0f7f2dbf 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -37,20 +37,33 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt']) # locate function is used to sort by closest match from the beginning of the value - result = [] - items_data = frappe.db.sql(""" SELECT name as item_code, - item_name, image as item_image, idx as idx,is_stock_item + items_data = frappe.db.sql(""" + SELECT + name AS item_code, + item_name, + stock_uom, + image AS item_image, + idx AS idx, + is_stock_item FROM `tabItem` WHERE - disabled = 0 and has_variants = 0 and is_sales_item = 1 - and item_group in (select name from `tabItem Group` where lft >= {lft} and rgt <= {rgt}) - and {condition} order by idx desc limit {start}, {page_length}""" + disabled = 0 + AND has_variants = 0 + AND is_sales_item = 1 + AND item_group in (SELECT name FROM `tabItem Group` WHERE lft >= {lft} AND rgt <= {rgt}) + AND {condition} + ORDER BY + idx desc + LIMIT + {start}, {page_length}""" .format( - start=start, page_length=page_length, - lft=lft, rgt=rgt, + start=start, + page_length=page_length, + lft=lft, + rgt=rgt, condition=condition ), as_dict=1) diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index af100692c6f..095b7c3dffa 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -228,9 +228,15 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ warehouse: function(doc, cdt, cdn) { var me = this; var item = frappe.get_doc(cdt, cdn); + + if (item.serial_no && item.qty === item.serial_no.split(`\n`).length) { + return; + } + if (item.serial_no && !item.batch_no) { item.serial_no = null; } + var has_batch_no; frappe.db.get_value('Item', {'item_code': item.item_code}, 'has_batch_no', (r) => { has_batch_no = r && r.has_batch_no; diff --git a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json index ac0a4b21fb3..253d711b322 100644 --- a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json +++ b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json @@ -12,7 +12,7 @@ "idx": 0, "is_standard": 1, "label": "ERPNext Settings", - "modified": "2020-03-12 18:03:17.126782", + "modified": "2020-04-01 11:28:51.400851", "modified_by": "Administrator", "module": "Setup", "name": "ERPNext Settings", @@ -22,89 +22,89 @@ "shortcuts": [ { "icon": "octicon octicon-rocket", - "is_query_report": 0, + "label": "Projects Settings", "link_to": "Projects Settings", "type": "DocType" }, { "icon": "octicon octicon-repo", - "is_query_report": 0, + "label": "Accounts Settings", "link_to": "Accounts Settings", "type": "DocType" }, { "icon": "octicon octicon-package", - "is_query_report": 0, + "label": "Stock Settings", "link_to": "Stock Settings", "type": "DocType" }, { "icon": "octicon octicon-organization", - "is_query_report": 0, + "label": "HR Settings", "link_to": "HR Settings", "type": "DocType" }, { "icon": "octicon octicon-tag", - "is_query_report": 0, + "label": "Selling Settings", "link_to": "Selling Settings", "type": "DocType" }, { "icon": "octicon octicon-briefcase", - "is_query_report": 0, + "label": "Buying Settings", "link_to": "Buying Settings", "type": "DocType" }, { "icon": "fa fa-life-ring", - "is_query_report": 0, + "label": "Support Settings", "link_to": "Support Settings", "type": "DocType" }, { "icon": "fa fa-shopping-cart", - "is_query_report": 0, + "label": "Shopping Cart Settings", "link_to": "Shopping Cart Settings", "type": "DocType" }, { "icon": "fa fa-globe", - "is_query_report": 0, + "label": "Portal Settings", "link_to": "Portal Settings", "type": "DocType" }, { "icon": "octicon octicon-tools", - "is_query_report": 0, + "label": "Manufacturing Settings", "link_to": "Manufacturing Settings", "restrict_to_domain": "Manufacturing", "type": "DocType" }, { "icon": "octicon octicon-mortar-board", - "is_query_report": 0, + "label": "Education Settings", "link_to": "Education Settings", "restrict_to_domain": "Education", "type": "DocType" }, { "icon": "fa fa-bed", - "is_query_report": 0, + "label": "Hotel Settings", "link_to": "Hotel Settings", "restrict_to_domain": "Hospitality", "type": "DocType" }, { "icon": "fa fa-heartbeat", - "is_query_report": 0, + "label": "Healthcare Settings", "link_to": "Healthcare Settings", "restrict_to_domain": "Healthcare", "type": "DocType" }, { "icon": "fa fa-cog", - "is_query_report": 0, + "label": "Domain Settings", "link_to": "Domain Settings", "type": "DocType" } diff --git a/erpnext/setup/desk_page/getting_started/getting_started.json b/erpnext/setup/desk_page/getting_started/getting_started.json index b045b5d083d..63d8984c405 100644 --- a/erpnext/setup/desk_page/getting_started/getting_started.json +++ b/erpnext/setup/desk_page/getting_started/getting_started.json @@ -1,40 +1,49 @@ { "cards": [ { - "links": "[\n {\n \"label\": \"Patient\",\n \"name\": \"Patient\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Diagnosis\",\n \"name\": \"Diagnosis\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]", - "title": "Healthcare" + "hidden": 0, + "label": "Healthcare", + "links": "[\n {\n \"label\": \"Patient\",\n \"name\": \"Patient\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Diagnosis\",\n \"name\": \"Diagnosis\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Crop\",\n \"name\": \"Crop\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Crop Cycle\",\n \"name\": \"Crop Cycle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Location\",\n \"name\": \"Location\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fertilizer\",\n \"name\": \"Fertilizer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]", - "title": "Agriculture" + "hidden": 0, + "label": "Agriculture", + "links": "[\n {\n \"label\": \"Crop\",\n \"name\": \"Crop\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Crop Cycle\",\n \"name\": \"Crop Cycle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Location\",\n \"name\": \"Location\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fertilizer\",\n \"name\": \"Fertilizer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Warehouse\",\n \"name\": \"Warehouse\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Brand\",\n \"name\": \"Brand\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Unit of Measure (UOM)\",\n \"name\": \"UOM\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Stock Reconciliation\",\n \"name\": \"Stock Reconciliation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]", - "title": "Stock" + "hidden": 0, + "label": "Education", + "links": "[\n {\n \"label\": \"Student\",\n \"name\": \"Student\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course\",\n \"name\": \"Course\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Instructor\",\n \"name\": \"Instructor\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Room\",\n \"name\": \"Room\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Employee\",\n \"name\": \"Employee\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"hide_count\": true,\n \"label\": \"Employee Attendance Tool\",\n \"name\": \"Employee Attendance Tool\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]", - "title": "Human Resources" + "hidden": 0, + "label": "Non Profit", + "links": "[\n {\n \"description\": \"Member information.\",\n \"label\": \"Member\",\n \"name\": \"Member\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Volunteer information.\",\n \"label\": \"Volunteer\",\n \"name\": \"Volunteer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Chapter information.\",\n \"label\": \"Chapter\",\n \"name\": \"Chapter\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Donor information.\",\n \"label\": \"Donor\",\n \"name\": \"Donor\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Database of potential customers.\",\n \"label\": \"Lead\",\n \"name\": \"Lead\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]", - "title": "CRM" + "hidden": 0, + "label": "Stock", + "links": "[\n {\n \"label\": \"Warehouse\",\n \"name\": \"Warehouse\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Brand\",\n \"name\": \"Brand\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Unit of Measure (UOM)\",\n \"name\": \"UOM\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Stock Reconciliation\",\n \"name\": \"Stock Reconciliation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Company (not Customer or Supplier) master.\",\n \"label\": \"Company\",\n \"name\": \"Company\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of financial accounts.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Chart of Accounts\",\n \"name\": \"Account\",\n \"onboard\": 1,\n \"route\": \"#Tree/Account\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Create Opening Sales and Purchase Invoices\",\n \"label\": \"Opening Invoice Creation Tool\",\n \"name\": \"Opening Invoice Creation Tool\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]", - "title": "Accounting" + "hidden": 0, + "label": "Human Resources", + "links": "[\n {\n \"label\": \"Employee\",\n \"name\": \"Employee\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"hide_count\": true,\n \"label\": \"Employee Attendance Tool\",\n \"name\": \"Employee Attendance Tool\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Student\",\n \"name\": \"Student\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course\",\n \"name\": \"Course\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Instructor\",\n \"name\": \"Instructor\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Room\",\n \"name\": \"Room\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]", - "title": "Education" + "hidden": 0, + "label": "CRM", + "links": "[\n {\n \"description\": \"Database of potential customers.\",\n \"label\": \"Lead\",\n \"name\": \"Lead\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Import Data from CSV / Excel files.\",\n \"icon\": \"octicon octicon-cloud-upload\",\n \"label\": \"Import Data\",\n \"name\": \"Data Import\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Import Chart Of Accounts from CSV / Excel files\",\n \"labe\": \"Chart Of Accounts Importer\",\n \"label\": \"Chart of Accounts Importer\",\n \"name\": \"Chart of Accounts Importer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Letter Heads for print templates.\",\n \"label\": \"Letter Head\",\n \"name\": \"Letter Head\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add / Manage Email Accounts.\",\n \"label\": \"Email Account\",\n \"name\": \"Email Account\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]", - "title": "Data Import and Settings" + "hidden": 0, + "label": "Accounting", + "links": "[\n {\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Company (not Customer or Supplier) master.\",\n \"label\": \"Company\",\n \"name\": \"Company\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of financial accounts.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Chart of Accounts\",\n \"name\": \"Account\",\n \"onboard\": 1,\n \"route\": \"#Tree/Account\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Create Opening Sales and Purchase Invoices\",\n \"label\": \"Opening Invoice Creation Tool\",\n \"name\": \"Opening Invoice Creation Tool\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Member information.\",\n \"label\": \"Member\",\n \"name\": \"Member\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Volunteer information.\",\n \"label\": \"Volunteer\",\n \"name\": \"Volunteer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Chapter information.\",\n \"label\": \"Chapter\",\n \"name\": \"Chapter\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Donor information.\",\n \"label\": \"Donor\",\n \"name\": \"Donor\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]", - "title": "Non Profit" + "hidden": 0, + "label": "Data Import and Settings", + "links": "[\n {\n \"description\": \"Import Data from CSV / Excel files.\",\n \"icon\": \"octicon octicon-cloud-upload\",\n \"label\": \"Import Data\",\n \"name\": \"Data Import\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Import Chart Of Accounts from CSV / Excel files\",\n \"labe\": \"Chart Of Accounts Importer\",\n \"label\": \"Chart of Accounts Importer\",\n \"name\": \"Chart of Accounts Importer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Letter Heads for print templates.\",\n \"label\": \"Letter Head\",\n \"name\": \"Letter Head\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add / Manage Email Accounts.\",\n \"label\": \"Email Account\",\n \"name\": \"Email Account\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" } ], "category": "Modules", @@ -54,7 +63,7 @@ "idx": 0, "is_standard": 1, "label": "Getting Started", - "modified": "2020-03-23 11:20:49.161823", + "modified": "2020-04-01 11:30:19.763099", "modified_by": "Administrator", "module": "Setup", "name": "Getting Started", @@ -63,32 +72,32 @@ "pin_to_top": 1, "shortcuts": [ { - "is_query_report": 0, + "label": "Item", "link_to": "Item", "type": "DocType" }, { - "is_query_report": 0, + "label": "Customer", "link_to": "Customer", "type": "DocType" }, { - "is_query_report": 0, + "label": "Supplier", "link_to": "Supplier", "type": "DocType" }, { - "is_query_report": 0, + "label": "Sales Invoice", "link_to": "Sales Invoice", "type": "DocType" }, { - "is_query_report": 0, + "label": "Dashboard", "link_to": "dashboard", "type": "Page" }, { - "is_query_report": 0, + "label": "Leaderboard", "link_to": "leaderboard", "type": "Page" } diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index ebd7b509396..e4986e36b7b 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -8,9 +8,11 @@ import frappe, os, json from frappe import _ from frappe.desk.page.setup_wizard.setup_wizard import make_records from frappe.utils import cstr, getdate -from erpnext.accounts.doctype.account.account import RootNotEditable from frappe.desk.doctype.global_search_settings.global_search_settings import update_global_search_doctypes +from erpnext.accounts.doctype.account.account import RootNotEditable +from erpnext.regional.address_template.setup import set_up_address_templates + default_lead_sources = ["Existing Customer", "Reference", "Advertisement", "Cold Calling", "Exhibition", "Supplier Reference", "Mass Mailing", "Customer's Vendor", "Campaign", "Walk In"] @@ -30,7 +32,7 @@ def install(country=None): { 'doctype': 'Domain', 'domain': 'Agriculture'}, { 'doctype': 'Domain', 'domain': 'Non Profit'}, - # address template + # ensure at least an empty Address Template exists for this Country {'doctype':"Address Template", "country": country}, # item group @@ -269,12 +271,11 @@ def install(country=None): # Records for the Supplier Scorecard from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import make_default_records + make_default_records() - make_records(records) - + set_up_address_templates(default_country=country) set_more_defaults() - update_global_search_doctypes() # path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country)) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 1dac9bd162b..e11e1bb5dcd 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -38,14 +38,14 @@ def get_cart_quotation(doc=None): addresses = get_address_docs(party=party) if not doc.customer_address and addresses: - update_cart_address("customer_address", addresses[0].name) + update_cart_address("billing", addresses[0].name) return { "doc": decorate_quotation_doc(doc), "shipping_addresses": [{"name": address.name, "display": address.display} - for address in addresses], + for address in addresses if address.address_type == "Shipping"], "billing_addresses": [{"name": address.name, "display": address.display} - for address in addresses], + for address in addresses if address.address_type == "Billing"], "shipping_rules": get_applicable_shipping_rules(party), "cart_settings": frappe.get_cached_doc("Shopping Cart Settings") } @@ -64,6 +64,9 @@ def place_order(): # company used to create customer accounts frappe.defaults.set_user_default("company", quotation.company) + if not (quotation.shipping_address_name or quotation.customer_address): + frappe.throw(_("Set Shipping Address or Billing Address")) + from erpnext.selling.doctype.quotation.quotation import _make_sales_order sales_order = frappe.get_doc(_make_sales_order(quotation.name, ignore_permissions=True)) sales_order.payment_schedule = [] @@ -194,21 +197,18 @@ def get_terms_and_conditions(terms_name): return frappe.db.get_value('Terms and Conditions', terms_name, 'terms') @frappe.whitelist() -def update_cart_address(address_fieldname, address_name): +def update_cart_address(address_type, address_name): quotation = _get_cart_quotation() address_display = get_address_display(frappe.get_doc("Address", address_name).as_dict()) - if address_fieldname == "shipping_address_name": - quotation.shipping_address_name = address_name - quotation.shipping_address = address_display - - if not quotation.customer_address: - address_fieldname == "customer_address" - - if address_fieldname == "customer_address": + if address_type.lower() == "billing": quotation.customer_address = address_name quotation.address_display = address_display - + quotation.shipping_address_name == quotation.shipping_address_name or address_name + elif address_type.lower() == "shipping": + quotation.shipping_address_name = address_name + quotation.shipping_address = address_display + quotation.customer_address == quotation.customer_address or address_name apply_cart_settings(quotation=quotation) diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index 1bfa2cf56c6..9bd03d45cbb 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -108,8 +108,8 @@ erpnext.stock.ItemDashboard = Class.extend({ if (context.data.length > 0) { $(frappe.render_template('item_dashboard_list', context)).appendTo(this.result); } else { - var message = __(" Currently no stock available in any warehouse") - $(""+message+"").appendTo(this.result); + var message = __("Currently no stock available in any warehouse"); + $(` ${message} `).appendTo(this.result); } }, get_item_dashboard_data: function(data, max_count, show_item) { diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 779e61f81a5..38475a6f269 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -1,40 +1,44 @@ { "cards": [ { - "links": "[\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"label\": \"Delivery Note\",\n \"name\": \"Delivery Note\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Receipt\",\n \"name\": \"Purchase Receipt\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Pick List\",\n \"name\": \"Pick List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Delivery Trip\",\n \"name\": \"Delivery Trip\",\n \"type\": \"doctype\"\n }\n]", - "title": "Stock Transactions" + "hidden": 0, + "label": "Stock Transactions", + "links": "[\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"label\": \"Delivery Note\",\n \"name\": \"Delivery Note\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Receipt\",\n \"name\": \"Purchase Receipt\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Pick List\",\n \"name\": \"Pick List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Delivery Trip\",\n \"name\": \"Delivery Trip\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Ledger\",\n \"name\": \"Stock Ledger\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Balance\",\n \"name\": \"Stock Balance\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Stock Projected Qty\",\n \"name\": \"Stock Projected Qty\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Summary\",\n \"name\": \"stock-balance\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Stock Ageing\",\n \"name\": \"Stock Ageing\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item Price Stock\",\n \"name\": \"Item Price Stock\",\n \"type\": \"report\"\n }\n]", - "title": "Stock Reports" + "hidden": 0, + "label": "Stock Reports", + "links": "[\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Ledger\",\n \"name\": \"Stock Ledger\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Balance\",\n \"name\": \"Stock Balance\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Stock Projected Qty\",\n \"name\": \"Stock Projected Qty\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Summary\",\n \"name\": \"stock-balance\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Stock Ageing\",\n \"name\": \"Stock Ageing\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item Price Stock\",\n \"name\": \"Item Price Stock\",\n \"type\": \"report\"\n }\n]" }, { - "icon": "fa fa-cog", - "links": "[\n {\n \"label\": \"Stock Settings\",\n \"name\": \"Stock Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Warehouse\",\n \"name\": \"Warehouse\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Unit of Measure (UOM)\",\n \"name\": \"UOM\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Brand\",\n \"name\": \"Brand\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Attribute\",\n \"name\": \"Item Attribute\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Variant Settings\",\n \"name\": \"Item Variant Settings\",\n \"type\": \"doctype\"\n }\n]", - "title": "Settings" + "hidden": 0, + "label": "Settings", + "links": "[\n {\n \"label\": \"Stock Settings\",\n \"name\": \"Stock Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Warehouse\",\n \"name\": \"Warehouse\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Unit of Measure (UOM)\",\n \"name\": \"UOM\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Brand\",\n \"name\": \"Brand\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Attribute\",\n \"name\": \"Item Attribute\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Variant Settings\",\n \"name\": \"Item Variant Settings\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Alternative\",\n \"name\": \"Item Alternative\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Manufacturer\",\n \"name\": \"Item Manufacturer\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Variant Settings\",\n \"name\": \"Item Variant Settings\",\n \"type\": \"doctype\"\n }\n]", - "title": "Items and Pricing" + "hidden": 0, + "label": "Items and Pricing", + "links": "[\n {\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Alternative\",\n \"name\": \"Item Alternative\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Manufacturer\",\n \"name\": \"Item Manufacturer\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Variant Settings\",\n \"name\": \"Item Variant Settings\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Serial No\",\n \"name\": \"Serial No\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Batch\",\n \"name\": \"Batch\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Installation Note\",\n \"name\": \"Installation Note\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Serial No\"\n ],\n \"doctype\": \"Serial No\",\n \"label\": \"Serial No Service Contract Expiry\",\n \"name\": \"Serial No Service Contract Expiry\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Serial No\"\n ],\n \"doctype\": \"Serial No\",\n \"label\": \"Serial No Status\",\n \"name\": \"Serial No Status\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Serial No\"\n ],\n \"doctype\": \"Serial No\",\n \"label\": \"Serial No Warranty Expiry\",\n \"name\": \"Serial No Warranty Expiry\",\n \"type\": \"report\"\n }\n]", - "title": "Serial No and Batch" + "hidden": 0, + "label": "Serial No and Batch", + "links": "[\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Serial No\",\n \"name\": \"Serial No\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Batch\",\n \"name\": \"Batch\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Installation Note\",\n \"name\": \"Installation Note\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Serial No\"\n ],\n \"doctype\": \"Serial No\",\n \"label\": \"Serial No Service Contract Expiry\",\n \"name\": \"Serial No Service Contract Expiry\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Serial No\"\n ],\n \"doctype\": \"Serial No\",\n \"label\": \"Serial No Status\",\n \"name\": \"Serial No Status\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Serial No\"\n ],\n \"doctype\": \"Serial No\",\n \"label\": \"Serial No Warranty Expiry\",\n \"name\": \"Serial No Warranty Expiry\",\n \"type\": \"report\"\n }\n]" }, { - "icon": "fa fa-wrench", - "links": "[\n {\n \"label\": \"Stock Reconciliation\",\n \"name\": \"Stock Reconciliation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Landed Cost Voucher\",\n \"name\": \"Landed Cost Voucher\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Packing Slip\",\n \"name\": \"Packing Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quality Inspection\",\n \"name\": \"Quality Inspection\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quality Inspection Template\",\n \"name\": \"Quality Inspection Template\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quick Stock Balance\",\n \"name\": \"Quick Stock Balance\",\n \"type\": \"doctype\"\n }\n]", - "title": "Tools" + "hidden": 0, + "label": "Tools", + "links": "[\n {\n \"label\": \"Stock Reconciliation\",\n \"name\": \"Stock Reconciliation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Landed Cost Voucher\",\n \"name\": \"Landed Cost Voucher\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Packing Slip\",\n \"name\": \"Packing Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quality Inspection\",\n \"name\": \"Quality Inspection\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quality Inspection Template\",\n \"name\": \"Quality Inspection Template\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quick Stock Balance\",\n \"name\": \"Quick Stock Balance\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-table", - "links": "[\n {\n \"dependencies\": [\n \"Item Price\"\n ],\n \"doctype\": \"Item Price\",\n \"is_query_report\": false,\n \"label\": \"Item-wise Price List Rate\",\n \"name\": \"Item-wise Price List Rate\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Entry\"\n ],\n \"doctype\": \"Stock Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Analytics\",\n \"name\": \"Stock Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Items To Be Received\",\n \"name\": \"Purchase Order Items To Be Received\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Bin\"\n ],\n \"doctype\": \"Bin\",\n \"is_query_report\": true,\n \"label\": \"Item Shortage Report\",\n \"name\": \"Item Shortage Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Batch\"\n ],\n \"doctype\": \"Batch\",\n \"is_query_report\": true,\n \"label\": \"Batch-Wise Balance History\",\n \"name\": \"Batch-Wise Balance History\",\n \"type\": \"report\"\n }\n]", - "title": "Key Reports" + "hidden": 0, + "label": "Key Reports", + "links": "[\n {\n \"dependencies\": [\n \"Item Price\"\n ],\n \"doctype\": \"Item Price\",\n \"is_query_report\": false,\n \"label\": \"Item-wise Price List Rate\",\n \"name\": \"Item-wise Price List Rate\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Entry\"\n ],\n \"doctype\": \"Stock Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Analytics\",\n \"name\": \"Stock Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Items To Be Received\",\n \"name\": \"Purchase Order Items To Be Received\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Bin\"\n ],\n \"doctype\": \"Bin\",\n \"is_query_report\": true,\n \"label\": \"Item Shortage Report\",\n \"name\": \"Item Shortage Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Batch\"\n ],\n \"doctype\": \"Batch\",\n \"is_query_report\": true,\n \"label\": \"Batch-Wise Balance History\",\n \"name\": \"Batch-Wise Balance History\",\n \"type\": \"report\"\n }\n]" }, { - "icon": "fa fa-list", - "links": "[\n {\n \"dependencies\": [\n \"Material Request\"\n ],\n \"doctype\": \"Material Request\",\n \"is_query_report\": true,\n \"label\": \"Requested Items To Be Transferred\",\n \"name\": \"Requested Items To Be Transferred\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Ledger Entry\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Batch Item Expiry Status\",\n \"name\": \"Batch Item Expiry Status\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Price List\"\n ],\n \"doctype\": \"Price List\",\n \"is_query_report\": true,\n \"label\": \"Item Prices\",\n \"name\": \"Item Prices\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Itemwise Recommended Reorder Level\",\n \"name\": \"Itemwise Recommended Reorder Level\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item Variant Details\",\n \"name\": \"Item Variant Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Ledger Entry\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock and Account Value Comparison\",\n \"name\": \"Stock and Account Value Comparison\",\n \"type\": \"report\"\n }\n]", - "title": "Other Reports" + "hidden": 0, + "label": "Other Reports", + "links": "[\n {\n \"dependencies\": [\n \"Material Request\"\n ],\n \"doctype\": \"Material Request\",\n \"is_query_report\": true,\n \"label\": \"Requested Items To Be Transferred\",\n \"name\": \"Requested Items To Be Transferred\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Ledger Entry\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Batch Item Expiry Status\",\n \"name\": \"Batch Item Expiry Status\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Price List\"\n ],\n \"doctype\": \"Price List\",\n \"is_query_report\": true,\n \"label\": \"Item Prices\",\n \"name\": \"Item Prices\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Itemwise Recommended Reorder Level\",\n \"name\": \"Itemwise Recommended Reorder Level\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item Variant Details\",\n \"name\": \"Item Variant Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Ledger Entry\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock and Account Value Comparison\",\n \"name\": \"Stock and Account Value Comparison\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", @@ -49,7 +53,7 @@ "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-03-12 16:30:39.881667", + "modified": "2020-04-01 11:28:51.148421", "modified_by": "Administrator", "module": "Stock", "name": "Stock", @@ -58,27 +62,27 @@ "pin_to_top": 0, "shortcuts": [ { - "is_query_report": 0, + "label": "Item", "link_to": "Item", "type": "DocType" }, { - "is_query_report": 0, + "label": "Pricing Rule", "link_to": "Pricing Rule", "type": "DocType" }, { - "is_query_report": 0, + "label": "Stock Entry", "link_to": "Stock Entry", "type": "DocType" }, { - "is_query_report": 0, + "label": "Stock Ledger", "link_to": "Stock Ledger", "type": "Report" }, { - "is_query_report": 0, + "label": "Stock Balance", "link_to": "Stock Balance", "type": "Report" } diff --git a/erpnext/stock/doctype/batch/batch.json b/erpnext/stock/doctype/batch/batch.json index e6d4e9d8b1b..1eb457734e0 100644 --- a/erpnext/stock/doctype/batch/batch.json +++ b/erpnext/stock/doctype/batch/batch.json @@ -1,554 +1,194 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, "allow_import": 1, - "allow_rename": 0, "autoname": "field:batch_id", - "beta": 0, "creation": "2013-03-05 14:50:38", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "sb_disabled", + "disabled", + "sb_batch", + "batch_id", + "item", + "item_name", + "image", + "parent_batch", + "manufacturing_date", + "column_break_3", + "batch_qty", + "stock_uom", + "expiry_date", + "source", + "supplier", + "column_break_9", + "reference_doctype", + "reference_name", + "section_break_7", + "description" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { "depends_on": "eval:doc.__islocal", "fieldname": "batch_id", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Batch ID", - "length": 0, "no_copy": 1, "oldfieldname": "batch_id", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, "unique": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "item", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, "in_standard_filter": 1, "label": "Item", - "length": 0, - "no_copy": 0, "oldfieldname": "item", "oldfieldtype": "Link", "options": "Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "image", "fieldtype": "Attach Image", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "image", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "image" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.parent_batch", "fieldname": "parent_batch", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Parent Batch", - "length": 0, - "no_copy": 0, "options": "Batch", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "disabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Disabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Today", "fieldname": "manufacturing_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Manufacturing Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Manufacturing Date" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "expiry_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Expiry Date", - "length": 0, - "no_copy": 0, "oldfieldname": "expiry_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "source", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Source", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Source" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "supplier", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Supplier", - "length": 0, - "no_copy": 0, "options": "Supplier", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_9", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "reference_doctype", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Source Document Type", - "length": 0, - "no_copy": 0, "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "reference_name", "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Source Document Name", - "length": 0, - "no_copy": 0, "options": "reference_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_7", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "description", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Batch Description", - "length": 0, - "no_copy": 0, "oldfieldname": "description", "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "300px" + }, + { + "fieldname": "sb_disabled", + "fieldtype": "Section Break" + }, + { + "fieldname": "sb_batch", + "fieldtype": "Section Break", + "label": "Batch Details" + }, + { + "fetch_from": "item.item_name", + "fieldname": "item_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Name", + "read_only": 1 + }, + { + "fieldname": "batch_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Batch Quantity", + "read_only": 1 + }, + { + "fetch_from": "item.stock_uom", + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Batch UOM", + "options": "UOM", + "read_only": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-archive", "idx": 1, "image_field": "image", - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, "max_attachments": 5, - "modified": "2018-08-29 06:28:57.985997", + "modified": "2019-10-03 22:38:45.104056", "modified_by": "Administrator", "module": "Stock", "name": "Batch", "owner": "harshada@webnotestech.com", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, "import": 1, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Item Manager", "set_user_permissions": 1, "share": 1, - "submit": 0, "write": 1 } ], "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, + "sort_field": "modified", "sort_order": "DESC", - "title_field": "item", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "title_field": "batch_id" } \ No newline at end of file diff --git a/erpnext/stock/doctype/batch/batch_dashboard.py b/erpnext/stock/doctype/batch/batch_dashboard.py new file mode 100644 index 00000000000..eb6a97ecfd8 --- /dev/null +++ b/erpnext/stock/doctype/batch/batch_dashboard.py @@ -0,0 +1,27 @@ +from __future__ import unicode_literals + +from frappe import _ + + +def get_data(): + return { + 'fieldname': 'batch_no', + 'transactions': [ + { + 'label': _('Buy'), + 'items': ['Purchase Invoice', 'Purchase Receipt'] + }, + { + 'label': _('Sell'), + 'items': ['Sales Invoice', 'Delivery Note'] + }, + { + 'label': _('Move'), + 'items': ['Stock Entry'] + }, + { + 'label': _('Quality'), + 'items': ['Quality Inspection'] + } + ] + } diff --git a/erpnext/stock/doctype/batch/batch_list.js b/erpnext/stock/doctype/batch/batch_list.js index 7ee81aa7e9f..d4f74c3a217 100644 --- a/erpnext/stock/doctype/batch/batch_list.js +++ b/erpnext/stock/doctype/batch/batch_list.js @@ -1,12 +1,14 @@ frappe.listview_settings['Batch'] = { - add_fields: ["item", "expiry_date"], - get_indicator: function(doc) { - if(doc.expiry_date && frappe.datetime.get_diff(doc.expiry_date, frappe.datetime.nowdate()) <= 0) { - return [__("Expired"), "red", "expiry_date,>=,Today"] - } else if(doc.expiry_date) { - return [__("Not Expired"), "green", "expiry_date,<,Today"] + add_fields: ["item", "expiry_date", "batch_qty", "disabled"], + get_indicator: (doc) => { + if (doc.disabled) { + return [__("Disabled"), "darkgrey", "disabled,=,1"]; + } else if (!doc.batch_qty) { + return [__("Empty"), "darkgrey", "batch_qty,=,0|disabled,=,0"]; + } else if (doc.expiry_date && frappe.datetime.get_diff(doc.expiry_date, frappe.datetime.nowdate()) <= 0) { + return [__("Expired"), "red", "expiry_date,not in,|expiry_date,<=,Today|batch_qty,>,0|disabled,=,0"] } else { - return ["Not Set", "darkgrey", ""]; - } + return [__("Active"), "green", "batch_qty,>,0|disabled,=,0"]; + }; } }; diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py index 32445a618d1..1fce5046f91 100644 --- a/erpnext/stock/doctype/batch/test_batch.py +++ b/erpnext/stock/doctype/batch/test_batch.py @@ -7,7 +7,7 @@ from frappe.exceptions import ValidationError import unittest from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no -from frappe.utils import cint +from frappe.utils import cint, flt from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory class TestBatch(unittest.TestCase): @@ -35,12 +35,13 @@ class TestBatch(unittest.TestCase): receipt = frappe.get_doc(dict( doctype='Purchase Receipt', supplier='_Test Supplier', + company='_Test Company', items=[ dict( item_code='ITEM-BATCH-1', qty=batch_qty, rate=10, - warehouse= 'Stores - WP' + warehouse= 'Stores - _TC' ) ] )).insert() @@ -175,6 +176,18 @@ class TestBatch(unittest.TestCase): self.assertEqual(get_batch_qty('batch a', '_Test Warehouse - _TC'), 90) + def test_total_batch_qty(self): + self.make_batch_item('ITEM-BATCH-3') + existing_batch_qty = flt(frappe.db.get_value("Batch", "B100", "batch_qty")) + stock_entry = self.make_new_batch_and_entry('ITEM-BATCH-3', 'B100', '_Test Warehouse - _TC') + + current_batch_qty = flt(frappe.db.get_value("Batch", "B100", "batch_qty")) + self.assertEqual(current_batch_qty, existing_batch_qty + 90) + + stock_entry.cancel() + current_batch_qty = flt(frappe.db.get_value("Batch", "B100", "batch_qty")) + self.assertEqual(current_batch_qty, existing_batch_qty) + @classmethod def make_new_batch_and_entry(cls, item_name, batch_name, warehouse): '''Make a new stock entry for given target warehouse and batch name of item''' @@ -208,6 +221,8 @@ class TestBatch(unittest.TestCase): stock_entry.insert() stock_entry.submit() + return stock_entry + def test_batch_name_with_naming_series(self): stock_settings = frappe.get_single('Stock Settings') use_naming_series = cint(stock_settings.use_naming_series) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 6f9d83d6749..9f5dee901ce 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -66,9 +66,9 @@ "base_total", "base_net_total", "column_break_33", + "total_net_weight", "total", "net_total", - "total_net_weight", "taxes_section", "tax_category", "column_break_39", @@ -1256,7 +1256,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2019-12-31 19:17:13.122644", + "modified": "2020-04-17 12:51:41.288600", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 30d82ca415d..dc96e7bd493 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -112,7 +112,6 @@ class DeliveryNote(SellingController): self.so_required() self.validate_proj_cust() self.check_sales_order_on_hold_or_close("against_sales_order") - self.validate_for_items() self.validate_warehouse() self.validate_uom_is_integer("stock_uom", "stock_qty") self.validate_uom_is_integer("uom", "qty") @@ -166,12 +165,6 @@ class DeliveryNote(SellingController): if not res: frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project)) - def validate_for_items(self): - for d in self.get('items'): - #Customer Provided parts will have zero valuation rate - if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): - d.allow_zero_valuation_rate = 1 - def validate_warehouse(self): super(DeliveryNote, self).validate_warehouse() diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index dc92c5c9ff2..d7a93fb6917 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -21,6 +21,7 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation \ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order, create_dn_against_so from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse +from erpnext.stock.doctype.item.test_item import create_item class TestDeliveryNote(unittest.TestCase): def setUp(self): @@ -671,7 +672,7 @@ def create_delivery_note(**args): "item_code": args.item or args.item_code or "_Test Item", "warehouse": args.warehouse or "_Test Warehouse - _TC", "qty": args.qty or 1, - "rate": args.rate or 100, + "rate": args.rate if args.get("rate") is not None else 100, "conversion_factor": 1.0, "allow_zero_valuation_rate": args.allow_zero_valuation_rate or 1, "expense_account": args.expense_account or "Cost of Goods Sold - _TC", diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index aa6b2fedd7c..7d2e3112fb3 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -114,6 +114,8 @@ "is_sub_contracted_item", "column_break_74", "customer_code", + "default_item_manufacturer", + "default_manufacturer_part_no", "website_section", "show_in_website", "show_variant_in_website", @@ -1038,6 +1040,18 @@ "fieldname": "auto_create_assets", "fieldtype": "Check", "label": "Auto Create Assets on Purchase" + }, + { + "fieldname": "default_item_manufacturer", + "fieldtype": "Data", + "label": "Default Item Manufacturer", + "read_only": 1 + }, + { + "fieldname": "default_manufacturer_part_no", + "fieldtype": "Data", + "label": "Default Manufacturer Part No", + "read_only": 1 } ], "has_web_view": 1, @@ -1046,7 +1060,7 @@ "image_field": "image", "links": [], "max_attachments": 1, - "modified": "2020-03-24 16:14:36.950677", + "modified": "2020-04-07 15:56:06.195722", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index e2e84c47473..c62b3ab583d 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -101,6 +101,7 @@ class Item(WebsiteGenerator): self.add_default_uom_in_conversion_factor_table() self.validate_conversion_factor() self.validate_item_type() + self.validate_naming_series() self.check_for_active_boms() self.fill_customer_code() self.check_item_tax() @@ -186,7 +187,7 @@ class Item(WebsiteGenerator): or frappe.db.get_single_value('Stock Settings', 'default_warehouse')) if default_warehouse: warehouse_company = frappe.db.get_value("Warehouse", default_warehouse, "company") - + if not default_warehouse or warehouse_company != default.company: default_warehouse = frappe.db.get_value('Warehouse', {'warehouse_name': _('Stores'), 'company': default.company}) @@ -307,7 +308,7 @@ class Item(WebsiteGenerator): if self.retain_sample and not frappe.db.get_single_value('Stock Settings', 'sample_retention_warehouse'): frappe.throw(_("Please select Sample Retention Warehouse in Stock Settings first")) if self.retain_sample and not self.has_batch_no: - frappe.throw(_(" {0} Retain Sample is based on batch, please check Has Batch No to retain sample of item").format( + frappe.throw(_("{0} Retain Sample is based on batch, please check Has Batch No to retain sample of item").format( self.item_code)) def get_context(self, context): @@ -522,6 +523,13 @@ class Item(WebsiteGenerator): if self.has_serial_no == 0 and self.serial_no_series: self.serial_no_series = None + def validate_naming_series(self): + for field in ["serial_no_series", "batch_number_series"]: + series = self.get(field) + if series and "#" in series and "." not in series: + frappe.throw(_("Invalid naming series (. missing) for {0}") + .format(frappe.bold(self.meta.get_field(field).label))) + def check_for_active_boms(self): if self.default_bom: bom_item = frappe.db.get_value("BOM", self.default_bom, "item") @@ -1007,8 +1015,6 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0): order by pr.posting_date desc, pr.posting_time desc, pr.name desc limit 1""", (item_code, cstr(doc_name)), as_dict=1) - - purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date or "1900-01-01") purchase_receipt_date = getdate(last_purchase_receipt and @@ -1016,7 +1022,7 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0): if last_purchase_order and (purchase_order_date >= purchase_receipt_date or not last_purchase_receipt): # use purchase order - + last_purchase = last_purchase_order[0] purchase_date = purchase_order_date @@ -1036,7 +1042,7 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0): "discount_percentage": flt(last_purchase.discount_percentage), "purchase_date": purchase_date }) - + conversion_rate = flt(conversion_rate) or 1.0 out.update({ diff --git a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.json b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.json index 956c92e6738..0cef6eafaef 100644 --- a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.json +++ b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "creation": "2019-06-02 04:41:37.332911", "doctype": "DocType", @@ -10,7 +11,8 @@ "manufacturer_part_no", "column_break_3", "item_name", - "description" + "description", + "is_default" ], "fields": [ { @@ -52,9 +54,17 @@ "fieldtype": "Small Text", "label": "Description", "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_default", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Default" } ], - "modified": "2019-06-06 19:07:31.175919", + "links": [], + "modified": "2020-04-07 20:25:55.507905", "modified_by": "Administrator", "module": "Stock", "name": "Item Manufacturer", diff --git a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py index 67eab82d970..c27d1be7892 100644 --- a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py +++ b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py @@ -11,6 +11,10 @@ from frappe.model.document import Document class ItemManufacturer(Document): def validate(self): self.validate_duplicate_entry() + self.manage_default_item_manufacturer() + + def on_trash(self): + self.manage_default_item_manufacturer(delete=True) def validate_duplicate_entry(self): if self.is_new(): @@ -24,6 +28,40 @@ class ItemManufacturer(Document): frappe.throw(_("Duplicate entry against the item code {0} and manufacturer {1}") .format(self.item_code, self.manufacturer)) + def manage_default_item_manufacturer(self, delete=False): + from frappe.model.utils import set_default + + item = frappe.get_doc("Item", self.item_code) + default_manufacturer = item.default_item_manufacturer + default_part_no = item.default_manufacturer_part_no + + if not self.is_default: + # if unchecked and default in Item master, clear it. + if default_manufacturer == self.manufacturer and default_part_no == self.manufacturer_part_no: + frappe.db.set_value("Item", item.name, + { + "default_item_manufacturer": None, + "default_manufacturer_part_no": None + }) + + elif self.is_default: + set_default(self, "item_code") + manufacturer, manufacturer_part_no = default_manufacturer, default_part_no + + if delete: + manufacturer, manufacturer_part_no = None, None + + elif (default_manufacturer != self.manufacturer) or \ + (default_manufacturer == self.manufacturer and default_part_no != self.manufacturer_part_no): + manufacturer = self.manufacturer + manufacturer_part_no = self.manufacturer_part_no + + frappe.db.set_value("Item", item.name, + { + "default_item_manufacturer": manufacturer, + "default_manufacturer_part_no": manufacturer_part_no + }) + @frappe.whitelist() def get_item_manufacturer_part_no(item_code, manufacturer): return frappe.db.get_value("Item Manufacturer", diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 6b26d383773..6110ea822cf 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -19,11 +19,6 @@ frappe.ui.form.on('Material Request', { frm.set_indicator_formatter('item_code', function(doc) { return (doc.qty<=doc.ordered_qty) ? "green" : "orange"; }); - frm.set_query("item_code", "items", function() { - return { - query: "erpnext.controllers.queries.item_query" - }; - }); }, onload: function(frm) { @@ -145,7 +140,8 @@ frappe.ui.form.on('Material Request', { }, get_item_data: function(frm, item) { - if (!item.item_code) return; + if (item && !item.item_code) { return; } + frm.call({ method: "erpnext.stock.get_item_details.get_item_details", child: item, @@ -360,6 +356,22 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten set_schedule_date(this.frm); }, + onload: function(doc, cdt, cdn) { + this.frm.set_query("item_code", "items", function() { + if (doc.material_request_type == "Customer Provided") { + return{ + query: "erpnext.controllers.queries.item_query", + filters:{ 'customer': me.frm.doc.customer } + } + } else if (doc.material_request_type != "Manufacture") { + return{ + query: "erpnext.controllers.queries.item_query", + filters: {'is_purchase_item': 1} + } + } + }); + }, + items_add: function(doc, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); if(doc.schedule_date) { diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 285643d712c..2d9855713c3 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -175,12 +175,11 @@ class MaterialRequest(BuyingController): frappe.db.set_value(d.doctype, d.name, "ordered_qty", d.ordered_qty) - target_ref_field = 'qty' if self.material_request_type == "Manufacture" else 'stock_qty' self._update_percent_field({ "target_dt": "Material Request Item", "target_parent_dt": self.doctype, "target_parent_field": "per_ordered", - "target_ref_field": target_ref_field, + "target_ref_field": "stock_qty", "target_field": "ordered_qty", "name": self.name, }, update_modified) @@ -454,6 +453,9 @@ def make_stock_entry(source_name, target_doc=None): else: target.s_warehouse = obj.warehouse + if source_parent.material_request_type == "Customer Provided": + target.allow_zero_valuation_rate = 1 + def set_missing_values(source, target): target.purpose = source.material_request_type if source.job_card: @@ -471,7 +473,7 @@ def make_stock_entry(source_name, target_doc=None): "doctype": "Stock Entry", "validation": { "docstatus": ["=", 1], - "material_request_type": ["in", ["Material Transfer", "Material Issue"]] + "material_request_type": ["in", ["Material Transfer", "Material Issue", "Customer Provided"]] } }, "Material Request Item": { @@ -496,7 +498,7 @@ def raise_work_orders(material_request): default_wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse") for d in mr.items: - if (d.qty - d.ordered_qty) >0: + if (d.stock_qty - d.ordered_qty) > 0: if frappe.db.exists("BOM", {"item": d.item_code, "is_default": 1}): wo_order = frappe.new_doc("Work Order") wo_order.update({ @@ -528,7 +530,7 @@ def raise_work_orders(material_request): msgprint(_("The following Work Orders were created:") + '\n' + new_line_sep(message)) if errors: - frappe.throw(_("Productions Orders cannot be raised for:") + '\n' + new_line_sep(errors)) + frappe.throw(_("Work Order cannot be created for following reason:") + '\n' + new_line_sep(errors)) return work_orders diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 79cdc1ad18a..19924b16363 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -7,7 +7,8 @@ from __future__ import unicode_literals import frappe, unittest, erpnext from frappe.utils import flt, today -from erpnext.stock.doctype.material_request.material_request import raise_work_orders +from erpnext.stock.doctype.material_request.material_request \ + import raise_work_orders, make_stock_entry, make_purchase_order, make_supplier_quotation from erpnext.stock.doctype.item.test_item import create_item class TestMaterialRequest(unittest.TestCase): @@ -15,8 +16,6 @@ class TestMaterialRequest(unittest.TestCase): erpnext.set_perpetual_inventory(0) def test_make_purchase_order(self): - from erpnext.stock.doctype.material_request.material_request import make_purchase_order - mr = frappe.copy_doc(test_records[0]).insert() self.assertRaises(frappe.ValidationError, make_purchase_order, @@ -30,8 +29,6 @@ class TestMaterialRequest(unittest.TestCase): self.assertEqual(len(po.get("items")), len(mr.get("items"))) def test_make_supplier_quotation(self): - from erpnext.stock.doctype.material_request.material_request import make_supplier_quotation - mr = frappe.copy_doc(test_records[0]).insert() self.assertRaises(frappe.ValidationError, make_supplier_quotation, mr.name) @@ -45,12 +42,9 @@ class TestMaterialRequest(unittest.TestCase): def test_make_stock_entry(self): - from erpnext.stock.doctype.material_request.material_request import make_stock_entry - mr = frappe.copy_doc(test_records[0]).insert() - self.assertRaises(frappe.ValidationError, make_stock_entry, - mr.name) + self.assertRaises(frappe.ValidationError, make_stock_entry, mr.name) mr = frappe.get_doc("Material Request", mr.name) mr.material_request_type = "Material Transfer" @@ -62,40 +56,40 @@ class TestMaterialRequest(unittest.TestCase): def _insert_stock_entry(self, qty1, qty2, warehouse = None ): se = frappe.get_doc({ - "company": "_Test Company", - "doctype": "Stock Entry", - "posting_date": "2013-03-01", - "posting_time": "00:00:00", - "purpose": "Material Receipt", - "items": [ - { - "conversion_factor": 1.0, - "doctype": "Stock Entry Detail", - "item_code": "_Test Item Home Desktop 100", - "parentfield": "items", - "basic_rate": 100, - "qty": qty1, - "stock_uom": "_Test UOM 1", - "transfer_qty": qty1, - "uom": "_Test UOM 1", - "t_warehouse": warehouse or "_Test Warehouse 1 - _TC", - "cost_center": "_Test Cost Center - _TC" - }, - { - "conversion_factor": 1.0, - "doctype": "Stock Entry Detail", - "item_code": "_Test Item Home Desktop 200", - "parentfield": "items", - "basic_rate": 100, - "qty": qty2, - "stock_uom": "_Test UOM 1", - "transfer_qty": qty2, - "uom": "_Test UOM 1", - "t_warehouse": warehouse or "_Test Warehouse 1 - _TC", - "cost_center": "_Test Cost Center - _TC" - } - ] - }) + "company": "_Test Company", + "doctype": "Stock Entry", + "posting_date": "2013-03-01", + "posting_time": "00:00:00", + "purpose": "Material Receipt", + "items": [ + { + "conversion_factor": 1.0, + "doctype": "Stock Entry Detail", + "item_code": "_Test Item Home Desktop 100", + "parentfield": "items", + "basic_rate": 100, + "qty": qty1, + "stock_uom": "_Test UOM 1", + "transfer_qty": qty1, + "uom": "_Test UOM 1", + "t_warehouse": warehouse or "_Test Warehouse 1 - _TC", + "cost_center": "_Test Cost Center - _TC" + }, + { + "conversion_factor": 1.0, + "doctype": "Stock Entry Detail", + "item_code": "_Test Item Home Desktop 200", + "parentfield": "items", + "basic_rate": 100, + "qty": qty2, + "stock_uom": "_Test UOM 1", + "transfer_qty": qty2, + "uom": "_Test UOM 1", + "t_warehouse": warehouse or "_Test Warehouse 1 - _TC", + "cost_center": "_Test Cost Center - _TC" + } + ] + }) se.set_stock_entry_type() se.insert() @@ -198,14 +192,7 @@ class TestMaterialRequest(unittest.TestCase): mr.insert() mr.submit() - # check if per complete is None - mr.load_from_db() - self.assertEqual(mr.per_ordered, 0) - self.assertEqual(mr.get("items")[0].ordered_qty, 0) - self.assertEqual(mr.get("items")[1].ordered_qty, 0) - # map a purchase order - from erpnext.stock.doctype.material_request.material_request import make_purchase_order po_doc = make_purchase_order(mr.name) po_doc.supplier = "_Test Supplier" po_doc.transaction_date = "2013-07-07" @@ -279,8 +266,6 @@ class TestMaterialRequest(unittest.TestCase): self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0) self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0) - from erpnext.stock.doctype.material_request.material_request import make_stock_entry - # map a stock entry se_doc = make_stock_entry(mr.name) se_doc.update({ @@ -357,14 +342,7 @@ class TestMaterialRequest(unittest.TestCase): mr.insert() mr.submit() - # check if per complete is None - mr.load_from_db() - self.assertEqual(mr.per_ordered, 0) - self.assertEqual(mr.get("items")[0].ordered_qty, 0) - self.assertEqual(mr.get("items")[1].ordered_qty, 0) - # map a stock entry - from erpnext.stock.doctype.material_request.material_request import make_stock_entry se_doc = make_stock_entry(mr.name) se_doc.update({ @@ -435,9 +413,6 @@ class TestMaterialRequest(unittest.TestCase): mr.insert() mr.submit() - # map a stock entry - from erpnext.stock.doctype.material_request.material_request import make_stock_entry - se_doc = make_stock_entry(mr.name) se_doc.update({ "posting_date": "2013-03-01", @@ -468,8 +443,6 @@ class TestMaterialRequest(unittest.TestCase): mr.insert() mr.submit() - # map a stock entry - from erpnext.stock.doctype.material_request.material_request import make_stock_entry se_doc = make_stock_entry(mr.name) self.assertEqual(se_doc.get("items")[0].s_warehouse, "_Test Warehouse - _TC") @@ -483,8 +456,6 @@ class TestMaterialRequest(unittest.TestCase): return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "indented_qty")) def test_make_stock_entry_for_material_issue(self): - from erpnext.stock.doctype.material_request.material_request import make_stock_entry - mr = frappe.copy_doc(test_records[0]).insert() self.assertRaises(frappe.ValidationError, make_stock_entry, @@ -503,8 +474,6 @@ class TestMaterialRequest(unittest.TestCase): return flt(frappe.db.get_value("Bin", {"item_code": "_Test Item Home Desktop 100", "warehouse": "_Test Warehouse - _TC"}, "indented_qty")) - from erpnext.stock.doctype.material_request.material_request import make_stock_entry - existing_requested_qty = _get_requested_qty() mr = frappe.copy_doc(test_records[0]) @@ -512,7 +481,7 @@ class TestMaterialRequest(unittest.TestCase): mr.submit() #testing bin value after material request is submitted - self.assertEqual(_get_requested_qty(), existing_requested_qty + 54.0) + self.assertEqual(_get_requested_qty(), existing_requested_qty - 54.0) # receive items to allow issue self._insert_stock_entry(60, 6, "_Test Warehouse - _TC") @@ -563,9 +532,37 @@ class TestMaterialRequest(unittest.TestCase): item_code= %s and warehouse= %s """, (mr.items[0].item_code, mr.items[0].warehouse))[0][0] self.assertEqual(requested_qty, new_requested_qty) - def test_multi_uom_for_purchase(self): - from erpnext.stock.doctype.material_request.material_request import make_purchase_order + def test_requested_qty_multi_uom(self): + existing_requested_qty = self._get_requested_qty('_Test FG Item', '_Test Warehouse - _TC') + mr = make_material_request(item_code='_Test FG Item', material_request_type='Manufacture', + uom="_Test UOM 1", conversion_factor=12) + + requested_qty = self._get_requested_qty('_Test FG Item', '_Test Warehouse - _TC') + + self.assertEqual(requested_qty, existing_requested_qty + 120) + + work_order = raise_work_orders(mr.name) + wo = frappe.get_doc("Work Order", work_order[0]) + wo.qty = 50 + wo.wip_warehouse = "_Test Warehouse 1 - _TC" + wo.submit() + + requested_qty = self._get_requested_qty('_Test FG Item', '_Test Warehouse - _TC') + self.assertEqual(requested_qty, existing_requested_qty + 70) + + wo.cancel() + + requested_qty = self._get_requested_qty('_Test FG Item', '_Test Warehouse - _TC') + self.assertEqual(requested_qty, existing_requested_qty + 120) + + mr.reload() + mr.cancel() + requested_qty = self._get_requested_qty('_Test FG Item', '_Test Warehouse - _TC') + self.assertEqual(requested_qty, existing_requested_qty) + + + def test_multi_uom_for_purchase(self): mr = frappe.copy_doc(test_records[0]) mr.material_request_type = 'Purchase' item = mr.items[0] @@ -607,8 +604,9 @@ class TestMaterialRequest(unittest.TestCase): self.assertEqual(mr.per_ordered, 100) def test_customer_provided_parts_mr(self): - from erpnext.stock.doctype.material_request.material_request import make_stock_entry create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0) + existing_requested_qty = self._get_requested_qty("_Test Customer", "_Test Warehouse - _TC") + mr = make_material_request(item_code='CUST-0987', material_request_type='Customer Provided') se = make_stock_entry(mr.name) se.insert() @@ -617,7 +615,10 @@ class TestMaterialRequest(unittest.TestCase): self.assertEqual(se.get("items")[0].material_request, mr.name) mr = frappe.get_doc("Material Request", mr.name) mr.submit() + current_requested_qty = self._get_requested_qty("_Test Customer", "_Test Warehouse - _TC") + self.assertEqual(mr.per_ordered, 100) + self.assertEqual(existing_requested_qty, current_requested_qty) def make_material_request(**args): args = frappe._dict(args) @@ -628,6 +629,8 @@ def make_material_request(**args): mr.append("items", { "item_code": args.item_code or "_Test Item", "qty": args.qty or 10, + "uom": args.uom or "_Test UOM", + "conversion_factor": args.conversion_factor or 1, "schedule_date": args.schedule_date or today(), "warehouse": args.warehouse or "_Test Warehouse - _TC", "cost_center": args.cost_center or "_Test Cost Center - _TC" diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index 9d1dafb136b..56049131bb4 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -1,5 +1,4 @@ { - "actions": [], "autoname": "hash", "creation": "2013-02-22 01:28:02", "doctype": "DocType", @@ -374,7 +373,10 @@ { "fieldname": "received_qty", "fieldtype": "Float", - "label": "Received Quantity" + "label": "Received Quantity", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 }, { "collapsible": 1, @@ -404,14 +406,13 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-02-25 03:09:10.698967", + "modified": "2020-04-16 09:00:00.992835", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 278971125f0..d46b98b461b 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -38,13 +38,17 @@ frappe.ui.form.on('Pick List', { }; }); }, - get_item_locations: (frm) => { - if (!frm.doc.locations || !frm.doc.locations.length) { - frappe.msgprint(__('First add items in the Item Locations table')); + set_item_locations:(frm, save) => { + if (!(frm.doc.locations && frm.doc.locations.length)) { + frappe.msgprint(__('Add items in the Item Locations table')); } else { - frm.call('set_item_locations'); + frm.call('set_item_locations', {save: save}); } }, + get_item_locations: (frm) => { + // Button on the form + frm.events.set_item_locations(frm, false); + }, refresh: (frm) => { frm.trigger('add_get_items_button'); if (frm.doc.docstatus === 1) { @@ -52,8 +56,13 @@ frappe.ui.form.on('Pick List', { 'pick_list_name': frm.doc.name, 'purpose': frm.doc.purpose }).then(target_document_exists => { + frm.set_df_property("locations", "allow_on_submit", target_document_exists ? 0 : 1); + if (target_document_exists) return; - if (frm.doc.purpose === 'Delivery against Sales Order') { + + frm.add_custom_button(__('Update Current Stock'), () => frm.trigger('update_pick_list_stock')); + + if (frm.doc.purpose === 'Delivery') { frm.add_custom_button(__('Delivery Note'), () => frm.trigger('create_delivery_note'), __('Create')); } else { frm.add_custom_button(__('Stock Entry'), () => frm.trigger('create_stock_entry'), __('Create')); @@ -105,6 +114,7 @@ frappe.ui.form.on('Pick List', { method: 'erpnext.stock.doctype.pick_list.pick_list.create_delivery_note', frm: frm }); + }, create_stock_entry: (frm) => { frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.create_stock_entry', { @@ -114,9 +124,12 @@ frappe.ui.form.on('Pick List', { frappe.set_route("Form", 'Stock Entry', stock_entry.name); }); }, + update_pick_list_stock: (frm) => { + frm.events.set_item_locations(frm, true); + }, add_get_items_button: (frm) => { let purpose = frm.doc.purpose; - if (purpose != 'Delivery against Sales Order' || frm.doc.docstatus !== 0) return; + if (purpose != 'Delivery' || frm.doc.docstatus !== 0) return; let get_query_filters = { docstatus: 1, per_delivered: ['<', 100], diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index 8d5ef3d12ab..c01388dcd21 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "naming_series:", "creation": "2019-07-11 16:03:13.681045", "doctype": "DocType", @@ -44,7 +45,7 @@ "options": "Warehouse" }, { - "depends_on": "eval:doc.purpose==='Delivery against Sales Order'", + "depends_on": "eval:doc.purpose==='Delivery'", "fieldname": "customer", "fieldtype": "Link", "in_list_view": 1, @@ -59,6 +60,7 @@ "options": "Work Order" }, { + "allow_on_submit": 1, "fieldname": "locations", "fieldtype": "Table", "label": "Item Locations", @@ -86,7 +88,7 @@ "fieldname": "purpose", "fieldtype": "Select", "label": "Purpose", - "options": "Material Transfer for Manufacture\nMaterial Transfer\nDelivery against Sales Order" + "options": "Material Transfer for Manufacture\nMaterial Transfer\nDelivery" }, { "depends_on": "eval:['Material Transfer', 'Material Issue'].includes(doc.purpose)", @@ -111,7 +113,8 @@ } ], "is_submittable": 1, - "modified": "2019-08-29 21:10:11.572387", + "links": [], + "modified": "2020-03-17 11:38:41.932875", "modified_by": "Administrator", "module": "Stock", "name": "Pick List", diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index c4d8c41ebb7..616de5e00a9 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -29,7 +29,7 @@ class PickList(Document): frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity') .format(frappe.bold(item.item_code), frappe.bold(item.idx))) - def set_item_locations(self): + def set_item_locations(self, save=False): items = self.aggregate_item_qty() self.item_location_map = frappe._dict() @@ -43,7 +43,7 @@ class PickList(Document): item_code = item_doc.item_code self.item_location_map.setdefault(item_code, - get_available_item_locations(item_code, from_warehouses, self.item_count_map.get(item_code))) + get_available_item_locations(item_code, from_warehouses, self.item_count_map.get(item_code), self.company)) locations = get_items_with_location_and_quantity(item_doc, self.item_location_map) @@ -59,12 +59,17 @@ class PickList(Document): location.update(row) self.append('locations', location) + if save: + self.save() + def aggregate_item_qty(self): locations = self.get('locations') self.item_count_map = {} # aggregate qty for same item item_map = OrderedDict() for item in locations: + if not item.item_code: + frappe.throw("Row #{0}: Item Code is Mandatory".format(item.idx)) item_code = item.item_code reference = item.sales_order_item or item.material_request_item key = (item_code, item.uom, reference) @@ -85,6 +90,10 @@ class PickList(Document): return item_map.values() +def validate_item_locations(pick_list): + if not pick_list.locations: + frappe.throw(_("Add items in the Item Locations table")) + def get_items_with_location_and_quantity(item_doc, item_location_map): available_locations = item_location_map.get(item_doc.item_code) locations = [] @@ -130,14 +139,14 @@ def get_items_with_location_and_quantity(item_doc, item_location_map): item_location_map[item_doc.item_code] = available_locations return locations -def get_available_item_locations(item_code, from_warehouses, required_qty): +def get_available_item_locations(item_code, from_warehouses, required_qty, company): locations = [] if frappe.get_cached_value('Item', item_code, 'has_serial_no'): - locations = get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty) + locations = get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty, company) elif frappe.get_cached_value('Item', item_code, 'has_batch_no'): - locations = get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty) + locations = get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty, company) else: - locations = get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty) + locations = get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty, company) total_qty_available = sum(location.get('qty') for location in locations) @@ -150,9 +159,10 @@ def get_available_item_locations(item_code, from_warehouses, required_qty): return locations -def get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty): +def get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty, company): filters = frappe._dict({ 'item_code': item_code, + 'company': company, 'warehouse': ['!=', ''] }) @@ -180,7 +190,7 @@ def get_available_item_locations_for_serialized_item(item_code, from_warehouses, return locations -def get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty): +def get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty, company): warehouse_condition = 'and warehouse in %(warehouses)s' if from_warehouses else '' batch_locations = frappe.db.sql(""" SELECT @@ -192,6 +202,7 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re WHERE sle.batch_no = batch.name and sle.`item_code`=%(item_code)s + and sle.`company` = %(company)s and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s {warehouse_condition} GROUP BY @@ -202,16 +213,20 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re ORDER BY IFNULL(batch.`expiry_date`, '2200-01-01'), batch.`creation` """.format(warehouse_condition=warehouse_condition), { #nosec 'item_code': item_code, + 'company': company, 'today': today(), 'warehouses': from_warehouses }, as_dict=1) return batch_locations -def get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty): +def get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty, company): # gets all items available in different warehouses + warehouses = [x.get('name') for x in frappe.get_list("Warehouse", {'company': company}, "name")] + filters = frappe._dict({ 'item_code': item_code, + 'warehouse': ['in', warehouses], 'actual_qty': ['>', 0] }) @@ -230,7 +245,9 @@ def get_available_item_locations_for_other_item(item_code, from_warehouses, requ @frappe.whitelist() def create_delivery_note(source_name, target_doc=None): pick_list = frappe.get_doc('Pick List', source_name) - sales_orders = [d.sales_order for d in pick_list.locations] + validate_item_locations(pick_list) + + sales_orders = [d.sales_order for d in pick_list.locations if d.sales_order] sales_orders = set(sales_orders) delivery_note = None @@ -238,6 +255,10 @@ def create_delivery_note(source_name, target_doc=None): delivery_note = create_delivery_note_from_sales_order(sales_order, delivery_note, skip_item_mapping=True) + # map rows without sales orders as well + if not delivery_note: + delivery_note = frappe.new_doc("Delivery Note") + item_table_mapper = { 'doctype': 'Delivery Note Item', 'field_map': { @@ -248,9 +269,25 @@ def create_delivery_note(source_name, target_doc=None): 'condition': lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 } + item_table_mapper_without_so = { + 'doctype': 'Delivery Note Item', + 'field_map': { + 'rate': 'rate', + 'name': 'name', + 'parent': '', + } + } + for location in pick_list.locations: - sales_order_item = frappe.get_cached_doc('Sales Order Item', location.sales_order_item) - dn_item = map_child_doc(sales_order_item, delivery_note, item_table_mapper) + if location.sales_order_item: + sales_order_item = frappe.get_cached_doc('Sales Order Item', {'name':location.sales_order_item}) + else: + sales_order_item = None + + source_doc, table_mapper = [sales_order_item, item_table_mapper] if sales_order_item \ + else [location, item_table_mapper_without_so] + + dn_item = map_child_doc(source_doc, delivery_note, table_mapper) if dn_item: dn_item.warehouse = location.warehouse @@ -258,7 +295,7 @@ def create_delivery_note(source_name, target_doc=None): dn_item.batch_no = location.batch_no dn_item.serial_no = location.serial_no - update_delivery_note_item(sales_order_item, dn_item, delivery_note) + update_delivery_note_item(source_doc, dn_item, delivery_note) set_delivery_note_missing_values(delivery_note) @@ -269,6 +306,7 @@ def create_delivery_note(source_name, target_doc=None): @frappe.whitelist() def create_stock_entry(pick_list): pick_list = frappe.get_doc(json.loads(pick_list)) + validate_item_locations(pick_list) if stock_entry_exists(pick_list.get('name')): return frappe.msgprint(_('Stock Entry has been already created against this Pick List')) @@ -318,7 +356,7 @@ def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filte @frappe.whitelist() def target_document_exists(pick_list_name, purpose): - if purpose == 'Delivery against Sales Order': + if purpose == 'Delivery': return frappe.db.exists('Delivery Note', { 'pick_list': pick_list_name }) diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index 6b4f73b140a..1b9ff41cc33 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -111,6 +111,7 @@ class TestPickList(unittest.TestCase): stock_reconciliation = frappe.get_doc({ 'doctype': 'Stock Reconciliation', + 'purpose': 'Stock Reconciliation', 'company': '_Test Company', 'items': [{ 'item_code': '_Test Serialized Item', diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.json b/erpnext/stock/doctype/pick_list_item/pick_list_item.json index c7a35df51f5..71fbf9a866a 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-07-11 16:01:22.832885", "doctype": "DocType", "editable_grid": 1, @@ -8,6 +9,7 @@ "item_name", "column_break_2", "description", + "item_group", "section_break_5", "warehouse", "quantity_section", @@ -120,7 +122,8 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Item", - "options": "Item" + "options": "Item", + "reqd": 1 }, { "fieldname": "quantity_section", @@ -166,10 +169,18 @@ "fieldtype": "Data", "label": "Material Request Item", "read_only": 1 + }, + { + "fetch_from": "item_code.item_group", + "fieldname": "item_group", + "fieldtype": "Data", + "label": "Item Group", + "read_only": 1 } ], "istable": 1, - "modified": "2019-08-29 21:28:39.539007", + "links": [], + "modified": "2020-03-13 19:08:21.995986", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 35446ecb1fc..eed5749b063 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -47,6 +47,7 @@ "is_subcontracted", "supplier_warehouse", "items_section", + "scan_barcode", "items", "pricing_rule_details", "pricing_rules", @@ -58,9 +59,9 @@ "base_total", "base_net_total", "column_break_27", + "total_net_weight", "total", "net_total", - "total_net_weight", "taxes_charges_section", "tax_category", "shipping_col", @@ -1070,13 +1071,18 @@ "label": "Inter Company Reference", "options": "Delivery Note", "read_only": 1 + }, + { + "fieldname": "scan_barcode", + "fieldtype": "Data", + "label": "Scan Barcode" } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2019-12-30 19:12:49.709711", + "modified": "2020-04-17 13:06:26.970288", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index cba7f201536..40d7cc2537c 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -356,8 +356,8 @@ class TestPurchaseReceipt(unittest.TestCase): 'accounts': [{ 'company_name': '_Test Company', 'fixed_asset_account': '_Test Fixed Asset - _TC', - 'accumulated_depreciation_account': 'Depreciation - _TC', - 'depreciation_expense_account': 'Depreciation - _TC' + 'accumulated_depreciation_account': '_Test Accumulated Depreciations - _TC', + 'depreciation_expense_account': '_Test Depreciation - _TC' }] }).insert() @@ -375,6 +375,33 @@ class TestPurchaseReceipt(unittest.TestCase): location = frappe.db.get_value('Asset', assets[0].name, 'location') self.assertEquals(location, "Test Location") + + def test_purchase_return_with_submitted_asset(self): + from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return + + pr = make_purchase_receipt(item_code="Test Asset Item", qty=1) + + asset = frappe.get_doc("Asset", { + 'purchase_receipt': pr.name + }) + asset.available_for_use_date = frappe.utils.nowdate() + asset.gross_purchase_amount = 50.0 + asset.append("finance_books", { + "expected_value_after_useful_life": 10, + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 1, + "depreciation_start_date": frappe.utils.nowdate() + }) + asset.submit() + + pr_return = make_purchase_return(pr.name) + self.assertRaises(frappe.exceptions.ValidationError, pr_return.submit) + + asset.load_from_db() + asset.cancel() + + pr_return.submit() def test_purchase_receipt_for_enable_allow_cost_center_in_entry_of_bs_account(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index a8b9c81ff0e..b15f23c3038 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -801,8 +801,7 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" }, { "depends_on": "is_fixed_asset", @@ -832,7 +831,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-11 14:19:48.799370", + "modified": "2020-04-07 18:38:21.141558", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json index fb28b5c079d..731a7302797 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.json +++ b/erpnext/stock/doctype/serial_no/serial_no.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:serial_no", @@ -41,7 +42,6 @@ "delivery_document_no", "delivery_date", "delivery_time", - "is_cancelled", "column_break5", "customer", "customer_name", @@ -56,7 +56,8 @@ "warranty_period", "more_info", "serial_no_details", - "company" + "company", + "status" ], "fields": [ { @@ -306,16 +307,6 @@ "no_copy": 1, "read_only": 1 }, - { - "fieldname": "is_cancelled", - "fieldtype": "Select", - "hidden": 1, - "label": "Is Cancelled", - "oldfieldname": "is_cancelled", - "oldfieldtype": "Select", - "options": "\nYes\nNo", - "report_hide": 1 - }, { "fieldname": "column_break5", "fieldtype": "Column Break", @@ -423,11 +414,20 @@ "remember_last_selected_value": 1, "reqd": 1, "search_index": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "options": "\nActive\nDelivered\nExpired", + "read_only": 1 } ], "icon": "fa fa-barcode", "idx": 1, - "modified": "2020-02-28 19:31:09.357323", + "links": [], + "modified": "2020-04-08 13:29:58.517772", "modified_by": "Administrator", "module": "Stock", "name": "Serial No", diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 772ac58af6f..b32c709be36 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -35,6 +35,15 @@ class SerialNo(StockController): self.set_maintenance_status() self.validate_warehouse() self.validate_item() + self.set_status() + + def set_status(self): + if self.delivery_document_type: + self.status = "Delivered" + elif self.warranty_expiry_date and getdate(self.warranty_expiry_date) <= getdate(nowdate()): + self.status = "Expired" + else: + self.status = "Active" def set_maintenance_status(self): if not self.warranty_expiry_date and not self.amc_expiry_date: @@ -197,6 +206,7 @@ class SerialNo(StockController): self.set_purchase_details(last_sle.get("purchase_sle")) self.set_sales_details(last_sle.get("delivery_sle")) self.set_maintenance_status() + self.set_status() def process_serial_no(sle): item_det = get_item_details(sle.item_code) diff --git a/erpnext/stock/doctype/serial_no/serial_no_list.js b/erpnext/stock/doctype/serial_no/serial_no_list.js index 5b1e312f68f..651f790583d 100644 --- a/erpnext/stock/doctype/serial_no/serial_no_list.js +++ b/erpnext/stock/doctype/serial_no/serial_no_list.js @@ -1,14 +1,12 @@ frappe.listview_settings['Serial No'] = { - add_fields: ["is_cancelled", "item_code", "warehouse", "warranty_expiry_date", "delivery_document_type"], + add_fields: ["item_code", "warehouse", "warranty_expiry_date", "delivery_document_type"], get_indicator: (doc) => { - if (doc.is_cancelled) { - return [__("Cancelled"), "red", "is_cancelled,=,Yes"]; - } else if (doc.delivery_document_type) { - return [__("Delivered"), "green", "delivery_document_type,is,set|is_cancelled,=,No"]; + if (doc.delivery_document_type) { + return [__("Delivered"), "green", "delivery_document_type,is,set"]; } else if (doc.warranty_expiry_date && frappe.datetime.get_diff(doc.warranty_expiry_date, frappe.datetime.nowdate()) <= 0) { - return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set|is_cancelled,=,No"]; + return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set"]; } else { - return [__("Active"), "green", "delivery_document_type,is,not set|is_cancelled,=,No"]; + return [__("Active"), "green", "delivery_document_type,is,not set"]; } } }; diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 3bb941573b6..d1048fc195f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -220,8 +220,8 @@ frappe.ui.form.on('Stock Entry', { }, get_query_filters: { docstatus: 1, - material_request_type: "Material Transfer", - status: ['!=', 'Transferred'] + material_request_type: ["in", ["Material Transfer", "Material Issue"]], + status: ["not in", ["Transferred", "Issued"]] } }) }, __("Get items from")); diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index be4c78b1ebd..7cf822bf49f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -50,6 +50,7 @@ class StockEntry(StockController): self.validate_posting_time() self.validate_purpose() self.validate_item() + self.validate_customer_provided_item() self.validate_qty() self.set_transfer_qty() self.validate_uom_is_integer("uom", "qty") @@ -203,10 +204,6 @@ class StockEntry(StockController): frappe.throw(_("Row #{0}: Please specify Serial No for Item {1}").format(item.idx, item.item_code), frappe.MandatoryError) - #Customer Provided parts will have zero valuation rate - if frappe.db.get_value('Item', item.item_code, 'is_customer_provided_item'): - item.allow_zero_valuation_rate = 1 - def validate_qty(self): manufacture_purpose = ["Manufacture", "Material Consumption for Manufacture"] diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index ee5f2370987..2afabe1480d 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -744,7 +744,7 @@ class TestStockEntry(unittest.TestCase): def test_customer_provided_parts_se(self): create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0) - se = make_stock_entry(item_code='CUST-0987', purporse = 'Material Receipt', qty=4, to_warehouse = "_Test Warehouse - _TC") + se = make_stock_entry(item_code='CUST-0987', purpose = 'Material Receipt', qty=4, to_warehouse = "_Test Warehouse - _TC") self.assertEqual(se.get("items")[0].allow_zero_valuation_rate, 1) self.assertEqual(se.get("items")[0].amount, 0) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 5fe89d6e227..dab5a7beb87 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -37,11 +37,19 @@ class StockLedgerEntry(Document): def on_submit(self): self.check_stock_frozen_date() self.actual_amt_check() + self.calculate_batch_qty() if not self.get("via_landed_cost_voucher"): from erpnext.stock.doctype.serial_no.serial_no import process_serial_no process_serial_no(self) + def calculate_batch_qty(self): + if self.batch_no: + batch_qty = frappe.db.get_value("Stock Ledger Entry", + {"docstatus": 1, "batch_no": self.batch_no, "is_cancelled": "No"}, + "sum(actual_qty)") or 0 + frappe.db.set_value("Batch", self.batch_no, "batch_qty", batch_qty) + #check for item quantity available in stock def actual_amt_check(self): if self.batch_no and not self.get("allow_negative_stock"): @@ -64,7 +72,7 @@ class StockLedgerEntry(Document): frappe.throw(_("Actual Qty is mandatory")) def validate_item(self): - item_det = frappe.db.sql("""select name, has_batch_no, docstatus, + item_det = frappe.db.sql("""select name, item_name, has_batch_no, docstatus, is_stock_item, has_variants, stock_uom, create_new_batch from tabItem where name=%s""", self.item_code, as_dict=True) @@ -79,10 +87,11 @@ class StockLedgerEntry(Document): # check if batch number is required if self.voucher_type != 'Stock Reconciliation': if item_det.has_batch_no ==1: + batch_item = self.item_code if self.item_code == item_det.item_name else self.item_code + ":" + item_det.item_name if not self.batch_no: - frappe.throw(_("Batch number is mandatory for Item {0}").format(self.item_code)) + frappe.throw(_("Batch number is mandatory for Item {0}").format(batch_item)) elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}): - frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, self.item_code)) + frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, batch_item)) elif item_det.has_batch_no ==0 and self.batch_no and self.is_cancelled == "No": frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code)) @@ -139,4 +148,3 @@ def on_doctype_update(): frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"]) frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"]) - diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json index 7f4efba33f8..b7d1497319f 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json @@ -4,6 +4,7 @@ "description": "This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.", "doctype": "DocType", "document_type": "Document", + "engine": "InnoDB", "field_order": [ "naming_series", "company", @@ -44,11 +45,11 @@ "reqd": 1 }, { - "default": "Stock Reconciliation", "fieldname": "purpose", "fieldtype": "Select", "label": "Purpose", - "options": "Opening Stock\nStock Reconciliation" + "options": "\nOpening Stock\nStock Reconciliation", + "reqd": 1 }, { "fieldname": "col1", @@ -153,7 +154,7 @@ "idx": 1, "is_submittable": 1, "max_attachments": 1, - "modified": "2019-05-26 09:03:09.542141", + "modified": "2020-04-08 17:02:47.196206", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reconciliation", diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index afa239466b5..0a49c26b629 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -488,12 +488,14 @@ def get_stock_balance_for(item_code, warehouse, ["has_serial_no", "has_batch_no"], as_dict=1) serial_nos = "" - if item_dict.get("has_serial_no"): - qty, rate, serial_nos = get_qty_rate_for_serial_nos(item_code, - warehouse, posting_date, posting_time, item_dict) + with_serial_no = True if item_dict.get("has_serial_no") else False + data = get_stock_balance(item_code, warehouse, posting_date, posting_time, + with_valuation_rate=with_valuation_rate, with_serial_no=with_serial_no) + + if with_serial_no: + qty, rate, serial_nos = data else: - qty, rate = get_stock_balance(item_code, warehouse, - posting_date, posting_time, with_valuation_rate=with_valuation_rate) + qty, rate = data if item_dict.get("has_batch_no"): qty = get_batch_qty(batch_no, warehouse) or 0 @@ -504,28 +506,6 @@ def get_stock_balance_for(item_code, warehouse, 'serial_nos': serial_nos } -def get_qty_rate_for_serial_nos(item_code, warehouse, posting_date, posting_time, item_dict): - args = { - "item_code": item_code, - "warehouse": warehouse, - "posting_date": posting_date, - "posting_time": posting_time, - } - - serial_nos_list = [serial_no.get("name") - for serial_no in get_available_serial_nos(args)] - - qty = len(serial_nos_list) - serial_nos = '\n'.join(serial_nos_list) - args.update({ - 'qty': qty, - "serial_nos": serial_nos - }) - - rate = get_incoming_rate(args, raise_error_if_no_rate=False) or 0 - - return qty, rate, serial_nos - @frappe.whitelist() def get_difference_account(purpose, company): if purpose == 'Stock Reconciliation': diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index e6d7e3fea7d..51d027f22ef 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -240,6 +240,7 @@ def create_batch_or_serial_no_items(): def create_stock_reconciliation(**args): args = frappe._dict(args) sr = frappe.new_doc("Stock Reconciliation") + sr.purpose = args.purpose or "Stock Reconciliation" sr.posting_date = args.posting_date or nowdate() sr.posting_time = args.posting_time or nowtime() sr.set_posting_time = 1 diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 9c5a8e1be81..61429cc1c11 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -245,7 +245,7 @@ def get_basic_details(args, item, overwrite_warehouse=True): 'item_group_defaults': item_group_defaults, 'brand_defaults': brand_defaults }) - + warehouse = get_item_warehouse(item, args, overwrite_warehouse, defaults) if args.get('doctype') == "Material Request" and not args.get('material_request_type'): @@ -279,7 +279,7 @@ def get_basic_details(args, item, overwrite_warehouse=True): "cost_center": get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults), 'has_serial_no': item.has_serial_no, 'has_batch_no': item.has_batch_no, - "batch_no": None, + "batch_no": args.get("batch_no"), "uom": args.uom, "min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "", "qty": flt(args.qty) or 1.0, @@ -341,6 +341,9 @@ def get_basic_details(args, item, overwrite_warehouse=True): else: out["manufacturer_part_no"] = None out["manufacturer"] = None + else: + out["manufacturer"], out["manufacturer_part_no"] = frappe.get_value("Item", item.name, + ["default_item_manufacturer", "default_manufacturer_part_no"] ) child_doctype = args.doctype + ' Item' meta = frappe.get_meta(child_doctype) @@ -377,7 +380,7 @@ def get_item_warehouse(item, args, overwrite_warehouse, defaults={}): else: warehouse = args.get('warehouse') - + return warehouse def update_barcode_value(out): diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py index 94ec314e8c9..1af68dd7f22 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py @@ -35,7 +35,7 @@ def get_data(report_filters): gl_data = voucher_wise_gl_data.get(key) or {} d.account_value = gl_data.get("account_value", 0) d.difference_value = (d.stock_value - d.account_value) - if abs(d.difference_value) > 1.0/10 ** currency_precision: + if abs(d.difference_value) > 0.1: data.append(d) return data diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py index e5dc6b12df7..56973153609 100644 --- a/erpnext/stock/stock_balance.py +++ b/erpnext/stock/stock_balance.py @@ -113,13 +113,30 @@ def get_reserved_qty(item_code, warehouse): return flt(reserved_qty[0][0]) if reserved_qty else 0 def get_indented_qty(item_code, warehouse): - indented_qty = frappe.db.sql("""select sum((mr_item.qty - mr_item.ordered_qty) * mr_item.conversion_factor) + # Ordered Qty is always maintained in stock UOM + inward_qty = frappe.db.sql(""" + select sum(mr_item.stock_qty - mr_item.ordered_qty) from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr where mr_item.item_code=%s and mr_item.warehouse=%s - and mr_item.qty > mr_item.ordered_qty and mr_item.parent=mr.name - and mr.status!='Stopped' and mr.docstatus=1""", (item_code, warehouse)) + and mr.material_request_type in ('Purchase', 'Manufacture', 'Customer Provided', 'Material Transfer') + and mr_item.stock_qty > mr_item.ordered_qty and mr_item.parent=mr.name + and mr.status!='Stopped' and mr.docstatus=1 + """, (item_code, warehouse)) + inward_qty = flt(inward_qty[0][0]) if inward_qty else 0 - return flt(indented_qty[0][0]) if indented_qty else 0 + outward_qty = frappe.db.sql(""" + select sum(mr_item.stock_qty - mr_item.ordered_qty) + from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr + where mr_item.item_code=%s and mr_item.warehouse=%s + and mr.material_request_type = 'Material Issue' + and mr_item.stock_qty > mr_item.ordered_qty and mr_item.parent=mr.name + and mr.status!='Stopped' and mr.docstatus=1 + """, (item_code, warehouse)) + outward_qty = flt(outward_qty[0][0]) if outward_qty else 0 + + requested_qty = inward_qty - outward_qty + + return requested_qty def get_ordered_qty(item_code, warehouse): ordered_qty = frappe.db.sql(""" @@ -145,9 +162,9 @@ def update_bin_qty(item_code, warehouse, qty_dict=None): from erpnext.stock.utils import get_bin bin = get_bin(item_code, warehouse) mismatch = False - for fld, val in qty_dict.items(): - if flt(bin.get(fld)) != flt(val): - bin.set(fld, flt(val)) + for field, value in qty_dict.items(): + if flt(bin.get(field)) != flt(value): + bin.set(field, flt(value)) mismatch = True if mismatch: diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index f3381c76091..7f32b8d8bb8 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -74,7 +74,8 @@ def get_stock_value_on(warehouse=None, posting_date=None, item_code=None): return sum(sle_map.values()) @frappe.whitelist() -def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None, with_valuation_rate=False): +def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None, + with_valuation_rate=False, with_serial_no=False): """Returns stock balance quantity at given warehouse on given posting date or current date. If `with_valuation_rate` is True, will return tuple (qty, rate)""" @@ -84,17 +85,51 @@ def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None if not posting_date: posting_date = nowdate() if not posting_time: posting_time = nowtime() - last_entry = get_previous_sle({ + args = { "item_code": item_code, "warehouse":warehouse, "posting_date": posting_date, - "posting_time": posting_time }) + "posting_time": posting_time + } + + last_entry = get_previous_sle(args) if with_valuation_rate: - return (last_entry.qty_after_transaction, last_entry.valuation_rate) if last_entry else (0.0, 0.0) + if with_serial_no: + serial_nos = last_entry.get("serial_no") + + if (serial_nos and + len(get_serial_nos_data(serial_nos)) < last_entry.qty_after_transaction): + serial_nos = get_serial_nos_data_after_transactions(args) + + return ((last_entry.qty_after_transaction, last_entry.valuation_rate, serial_nos) + if last_entry else (0.0, 0.0, 0.0)) + else: + return (last_entry.qty_after_transaction, last_entry.valuation_rate) if last_entry else (0.0, 0.0) else: return last_entry.qty_after_transaction if last_entry else 0.0 +def get_serial_nos_data_after_transactions(args): + serial_nos = [] + data = frappe.db.sql(""" SELECT serial_no, actual_qty + FROM `tabStock Ledger Entry` + WHERE + item_code = %(item_code)s and warehouse = %(warehouse)s + and timestamp(posting_date, posting_time) < timestamp(%(posting_date)s, %(posting_time)s) + order by posting_date, posting_time asc """, args, as_dict=1) + + for d in data: + if d.actual_qty > 0: + serial_nos.extend(get_serial_nos_data(d.serial_no)) + else: + serial_nos = list(set(serial_nos) - set(get_serial_nos_data(d.serial_no))) + + return '\n'.join(serial_nos) + +def get_serial_nos_data(serial_nos): + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + return get_serial_nos(serial_nos) + @frappe.whitelist() def get_latest_stock_qty(item_code, warehouse=None): values, condition = [item_code], "" diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json index 4a7885e46f9..596987f46a5 100644 --- a/erpnext/support/desk_page/support/support.json +++ b/erpnext/support/desk_page/support/support.json @@ -1,30 +1,34 @@ { "cards": [ { - "links": "[\n {\n \"description\": \"Service Level.\",\n \"label\": \"Service Level\",\n \"name\": \"Service Level\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]", - "title": "Service Level Agreement" + "hidden": 0, + "label": "Service Level Agreement", + "links": "[\n {\n \"description\": \"Service Level.\",\n \"label\": \"Service Level\",\n \"name\": \"Service Level\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"label\": \"Maintenance Schedule\",\n \"name\": \"Maintenance Schedule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Maintenance Visit\",\n \"name\": \"Maintenance Visit\",\n \"type\": \"doctype\"\n }\n]", - "title": "Maintenance" + "hidden": 0, + "label": "Maintenance", + "links": "[\n {\n \"label\": \"Maintenance Schedule\",\n \"name\": \"Maintenance Schedule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Maintenance Visit\",\n \"name\": \"Maintenance Visit\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Support queries from customers.\",\n \"label\": \"Issue\",\n \"name\": \"Issue\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Issue Type.\",\n \"label\": \"Issue Type\",\n \"name\": \"Issue Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Issue Priority.\",\n \"label\": \"Issue Priority\",\n \"name\": \"Issue Priority\",\n \"type\": \"doctype\"\n }\n]", - "title": "Issues" + "hidden": 0, + "label": "Issues", + "links": "[\n {\n \"description\": \"Support queries from customers.\",\n \"label\": \"Issue\",\n \"name\": \"Issue\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Issue Type.\",\n \"label\": \"Issue Type\",\n \"name\": \"Issue Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Issue Priority.\",\n \"label\": \"Issue Priority\",\n \"name\": \"Issue Priority\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Warranty Claim against Serial No.\",\n \"label\": \"Warranty Claim\",\n \"name\": \"Warranty Claim\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Single unit of an Item.\",\n \"label\": \"Serial No\",\n \"name\": \"Serial No\",\n \"type\": \"doctype\"\n }\n]", - "title": "Warranty" + "hidden": 0, + "label": "Warranty", + "links": "[\n {\n \"description\": \"Warranty Claim against Serial No.\",\n \"label\": \"Warranty Claim\",\n \"name\": \"Warranty Claim\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Single unit of an Item.\",\n \"label\": \"Serial No\",\n \"name\": \"Serial No\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-list", - "links": "[\n {\n \"label\": \"Support Settings\",\n \"name\": \"Support Settings\",\n \"type\": \"doctype\"\n }\n]", - "title": "Settings" + "hidden": 0, + "label": "Settings", + "links": "[\n {\n \"label\": \"Support Settings\",\n \"name\": \"Support Settings\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-list", - "links": "[\n {\n \"dependencies\": [\n \"Issue\"\n ],\n \"doctype\": \"Issue\",\n \"is_query_report\": true,\n \"label\": \"Minutes to First Response for Issues\",\n \"name\": \"Minutes to First Response for Issues\",\n \"type\": \"report\"\n }\n]", - "title": "Reports" + "hidden": 0, + "label": "Reports", + "links": "[\n {\n \"dependencies\": [\n \"Issue\"\n ],\n \"doctype\": \"Issue\",\n \"is_query_report\": true,\n \"label\": \"Minutes to First Response for Issues\",\n \"name\": \"Minutes to First Response for Issues\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", @@ -39,7 +43,7 @@ "idx": 0, "is_standard": 1, "label": "Support", - "modified": "2020-03-12 16:30:39.482621", + "modified": "2020-04-01 11:28:51.120583", "modified_by": "Administrator", "module": "Support", "name": "Support", @@ -48,17 +52,17 @@ "pin_to_top": 0, "shortcuts": [ { - "is_query_report": 0, + "label": "Issue", "link_to": "Issue", "type": "DocType" }, { - "is_query_report": 0, + "label": "Service Level", "link_to": "Service Level", "type": "DocType" }, { - "is_query_report": 0, + "label": "Maintenance Visit", "link_to": "Maintenance Visit", "type": "DocType" } diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index fd72807418f..117267f1a42 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -335,8 +335,13 @@ def get_issue_list(doctype, txt, filters, limit_start, limit_page_length=20, ord ignore_permissions = False if is_website_user(): - if not filters: filters = [] - filters.append(("Issue", "customer", "=", customer)) if customer else filters.append(("Issue", "raised_by", "=", user)) + if not filters: filters = {} + + if customer: + filters["customer"] = customer + else: + filters["raised_by"] = user + ignore_permissions = True return get_list(doctype, txt, filters, limit_start, limit_page_length, ignore_permissions=ignore_permissions) diff --git a/erpnext/templates/includes/cart.js b/erpnext/templates/includes/cart.js index 456bc7e4136..c6dfd35e29e 100644 --- a/erpnext/templates/includes/cart.js +++ b/erpnext/templates/includes/cart.js @@ -26,15 +26,14 @@ $.extend(shopping_cart, { bind_address_select: function() { $(".cart-addresses").on('click', '.address-card', function(e) { const $card = $(e.currentTarget); - const address_fieldname = $card.closest('[data-fieldname]').attr('data-fieldname'); + const address_type = $card.closest('[data-address-type]').attr('data-address-type'); const address_name = $card.closest('[data-address-name]').attr('data-address-name'); - return frappe.call({ type: "POST", method: "erpnext.shopping_cart.cart.update_cart_address", freeze: true, args: { - address_fieldname, + address_type, address_name }, callback: function(r) { diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html index f7f35483208..60de3af17bf 100644 --- a/erpnext/templates/includes/cart/cart_address.html +++ b/erpnext/templates/includes/cart/cart_address.html @@ -18,7 +18,7 @@
{{ _("Shipping Address") }}
{% for address in shipping_addresses %} -
+
{% include "templates/includes/cart/address_card.html" %}
{% endfor %} @@ -28,7 +28,7 @@
{{ _("Billing Address") }}
{% for address in billing_addresses %} -
+
{% include "templates/includes/cart/address_card.html" %}
{% endfor %} @@ -123,9 +123,19 @@ frappe.ready(() => { primary_action: (values) => { frappe.call('erpnext.shopping_cart.cart.add_new_address', { doc: values }) .then(r => { - d.hide(); - window.location.reload(); + frappe.call({ + method: "erpnext.shopping_cart.cart.update_cart_address", + args: { + address_type: r.message.address_type, + address_name: r.message.name + }, + callback: function (r) { + d.hide(); + window.location.reload(); + } + }); }); + } }) diff --git a/erpnext/templates/includes/itemised_tax_breakup.html b/erpnext/templates/includes/itemised_tax_breakup.html index c27e4cede0d..c2f13539cdf 100644 --- a/erpnext/templates/includes/itemised_tax_breakup.html +++ b/erpnext/templates/includes/itemised_tax_breakup.html @@ -16,7 +16,11 @@ {{ item }} - {{ frappe.utils.fmt_money(itemised_taxable_amount.get(item, 0), None, currency) }} + {% if doc.get('is_return') %} + {{ frappe.utils.fmt_money((itemised_taxable_amount.get(item, 0))|abs, None, doc.currency) }} + {% else %} + {{ frappe.utils.fmt_money(itemised_taxable_amount.get(item, 0), None, doc.currency) }} + {% endif %} {% for tax_account in tax_accounts %} {% set tax_details = taxes.get(tax_account) %} @@ -25,7 +29,11 @@ {% if tax_details.tax_rate or not tax_details.tax_amount %} ({{ tax_details.tax_rate }}%) {% endif %} - {{ frappe.utils.fmt_money(tax_details.tax_amount / conversion_rate, None, currency) }} + {% if doc.get('is_return') %} + {{ frappe.utils.fmt_money((tax_details.tax_amount / doc.conversion_rate)|abs, None, doc.currency) }} + {% else %} + {{ frappe.utils.fmt_money(tax_details.tax_amount / doc.conversion_rate, None, doc.currency) }} + {% endif %} {% else %} diff --git a/erpnext/templates/includes/order/order_taxes.html b/erpnext/templates/includes/order/order_taxes.html index 4a32aa47446..ebec838d03e 100644 --- a/erpnext/templates/includes/order/order_taxes.html +++ b/erpnext/templates/includes/order/order_taxes.html @@ -10,16 +10,16 @@ {% endif %} {% for d in doc.taxes %} -{% if d.base_tax_amount > 0 %} - - - {{ d.description }} - - - {{ d.get_formatted("base_tax_amount") }} - - -{% endif %} + {% if d.base_tax_amount %} + + + {{ d.description }} + + + {{ d.get_formatted("base_tax_amount") }} + + + {% endif %} {% endfor %} {% if doc.doctype == 'Quotation' %} diff --git a/erpnext/templates/pages/cart.html b/erpnext/templates/pages/cart.html index 912702e386d..3033d1587d6 100644 --- a/erpnext/templates/pages/cart.html +++ b/erpnext/templates/pages/cart.html @@ -12,16 +12,6 @@ {% block header_actions %} -{% if doc.items and cart_settings.enable_checkout %} - -{% endif %} -{% if doc.items and not cart_settings.enable_checkout %} - -{% endif %} {% endblock %} {% block page_content %} @@ -55,6 +45,20 @@

{{ _('Your cart is Empty') }}

{% endif %} + {% if doc.items %} +
+ {% if cart_settings.enable_checkout %} + + {% else %} + + {% endif %} +
+ {% endif %} + {% if doc.items %} {% if doc.tc_name %}