diff --git a/.travis.yml b/.travis.yml index a70062fea3a..8681c03f55d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,11 +51,17 @@ before_script: - bench start & - sleep 10 -script: - - set -e - - bench run-tests - - sleep 5 - - bench reinstall --yes - - bench execute erpnext.setup.setup_wizard.utils.complete - - bench execute erpnext.setup.utils.enable_all_roles_and_domains - - bench run-ui-tests --app erpnext +jobs: + include: + - stage: test + script: + - set -e + - bench run-tests + env: Server Side Test + - # stage + script: + - bench --verbose run-setup-wizard-ui-test + - bench execute erpnext.setup.utils.enable_all_roles_and_domains + - bench run-ui-tests --app erpnext + env: Client Side Test + diff --git a/erpnext/__init__.py b/erpnext/__init__.py index f327404c5b6..514e5b3df5a 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from erpnext.hooks import regional_overrides -__version__ = '8.11.6' +__version__ = '9.0.0' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/account/test_account.js b/erpnext/accounts/doctype/account/test_account.js index 7b23ef01dd6..039e33e011d 100644 --- a/erpnext/accounts/doctype/account/test_account.js +++ b/erpnext/accounts/doctype/account/test_account.js @@ -5,7 +5,9 @@ QUnit.test("test account", function(assert) { let done = assert.async(); frappe.run_serially([ () => frappe.set_route('Tree', 'Account'), + () => frappe.timeout(3), () => frappe.click_button('Expand All'), + () => frappe.timeout(1), () => frappe.click_link('Debtors'), () => frappe.click_button('Edit'), () => frappe.timeout(1), diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 304af373081..47e214e1b48 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -36,7 +36,7 @@ class GLEntry(Document): validate_balance_type(self.account, adv_adj) # Update outstanding amt on against voucher - if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice'] \ + if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] \ and self.against_voucher and update_outstanding == 'Yes' and not from_repost: update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type, self.against_voucher) @@ -196,7 +196,7 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal))) # Update outstanding amt on against voucher - if against_voucher_type in ["Sales Invoice", "Purchase Invoice"]: + if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]: ref_doc = frappe.get_doc(against_voucher_type, against_voucher) ref_doc.db_set('outstanding_amount', bal) ref_doc.set_status(update=True) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index 57e83b0b7af..19f4b569283 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -1337,6 +1337,67 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription", + "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": "Subscription", + "length": 0, + "no_copy": 1, + "options": "Subscription", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1382,7 +1443,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-06-13 14:29:09.794076", + "modified": "2017-08-31 11:21:09.442695", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 12e46c42d3c..61ede97122c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -12,7 +12,8 @@ frappe.ui.form.on('Payment Entry', { setup: function(frm) { frm.set_query("paid_from", function() { - var party_account_type = frm.doc.party_type=="Customer" ? "Receivable" : "Payable"; + var party_account_type = in_list(["Customer", "Student"], frm.doc.party_type) ? + "Receivable" : "Payable"; var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ? ["Bank", "Cash"] : party_account_type; @@ -28,13 +29,14 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("party_type", function() { return{ "filters": { - "name": ["in",["Customer","Supplier", "Employee"]], + "name": ["in",["Customer","Supplier", "Employee", "Student"]], } } }); frm.set_query("paid_to", function() { - var party_account_type = frm.doc.party_type=="Customer" ? "Receivable" : "Payable"; + var party_account_type = in_list(["Customer", "Student"], frm.doc.party_type) ? + "Receivable" : "Payable"; var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ? ["Bank", "Cash"] : party_account_type; @@ -72,6 +74,8 @@ frappe.ui.form.on('Payment Entry', { var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"]; } else if (frm.doc.party_type=="Employee") { var doctypes = ["Expense Claim", "Journal Entry"]; + } else if (frm.doc.party_type=="Student") { + var doctypes = ["Fees"]; } else { var doctypes = ["Journal Entry"]; } @@ -85,7 +89,7 @@ frappe.ui.form.on('Payment Entry', { child = locals[cdt][cdn]; filters = {"docstatus": 1, "company": doc.company}; party_type_doctypes = ['Sales Invoice', 'Sales Order', 'Purchase Invoice', - 'Purchase Order', 'Expense Claim']; + 'Purchase Order', 'Expense Claim', 'Fees']; if (in_list(party_type_doctypes, child.reference_doctype)) { filters[doc.party_type.toLowerCase()] = doc.party; @@ -207,19 +211,13 @@ frappe.ui.form.on('Payment Entry', { frm.set_value(field, null); }); } else { - if(!frm.doc.party) - { - if (frm.doc.payment_type=="Receive"){ - frm.set_value("party_type", "Customer"); - } - } - else - { - frm.events.party(frm); + if(frm.doc.party) { + frm.events.party(frm); } - if(frm.doc.mode_of_payment) + if(frm.doc.mode_of_payment) { frm.events.mode_of_payment(frm); + } } }, @@ -254,6 +252,7 @@ frappe.ui.form.on('Payment Entry', { date: frm.doc.posting_date }, callback: function(r, rt) { + console.log(r, rt); if(r.message) { if(frm.doc.payment_type == "Receive") { frm.set_value("paid_from", r.message.party_account); @@ -502,6 +501,8 @@ frappe.ui.form.on('Payment Entry', { c.due_date = d.due_date c.total_amount = d.invoice_amount; c.outstanding_amount = d.outstanding_amount; + c.bill_no = d.bill_no; + if(!in_list(["Sales Order", "Purchase Order", "Expense Claim"], d.voucher_type)) { if(flt(d.outstanding_amount) > 0) total_positive_outstanding += flt(d.outstanding_amount); @@ -644,16 +645,9 @@ frappe.ui.form.on('Payment Entry', { if(frm.doc.party) { var party_amount = frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount; - - var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [], - function(d) { return flt(d.amount) })); if(frm.doc.total_allocated_amount < party_amount) { - if(frm.doc.payment_type == "Receive") { - unallocated_amount = party_amount - (frm.doc.total_allocated_amount - total_deductions); - } else { - unallocated_amount = party_amount - (frm.doc.total_allocated_amount + total_deductions); - } + unallocated_amount = party_amount - frm.doc.total_allocated_amount; } } frm.set_value("unallocated_amount", unallocated_amount); @@ -672,11 +666,10 @@ frappe.ui.form.on('Payment Entry', { difference_amount = flt(frm.doc.base_paid_amount) - flt(frm.doc.base_received_amount); } - $.each(frm.doc.deductions || [], function(i, d) { - if(d.amount) difference_amount -= flt(d.amount); - }) + var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [], + function(d) { return flt(d.amount) })); - frm.set_value("difference_amount", difference_amount); + frm.set_value("difference_amount", difference_amount - total_deductions); frm.events.hide_unhide_fields(frm); }, diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index a59223d3a8c..abf4ac9b15b 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -1659,6 +1659,67 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription", + "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": "Subscription", + "length": 0, + "no_copy": 1, + "options": "Subscription", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1730,7 +1791,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-06-13 14:29:04.244537", + "modified": "2017-08-31 11:20:37.578469", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 9832c0527a0..9d24261038b 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -100,8 +100,8 @@ class PaymentEntry(AccountsController): if not self.party: frappe.throw(_("Party is mandatory")) - self.party_name = frappe.db.get_value(self.party_type, self.party, - self.party_type.lower() + "_name") + _party_name = "title" if self.party_type == "Student" else self.party_type.lower() + "_name" + self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name) if self.party: if not self.party_balance: @@ -149,7 +149,7 @@ class PaymentEntry(AccountsController): frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party)) if self.party_account: - party_account_type = "Receivable" if self.party_type=="Customer" else "Payable" + party_account_type = "Receivable" if self.party_type in ("Customer", "Student") else "Payable" self.validate_account_type(self.party_account, [party_account_type]) def validate_bank_accounts(self): @@ -182,7 +182,9 @@ class PaymentEntry(AccountsController): frappe.throw(_("{0} is mandatory").format(self.meta.get_label(field))) def validate_reference_documents(self): - if self.party_type == "Customer": + if self.party_type == "Student": + valid_reference_doctypes = ("Fees") + elif self.party_type == "Customer": valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry") elif self.party_type == "Supplier": valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry") @@ -209,17 +211,19 @@ class PaymentEntry(AccountsController): else: self.validate_journal_entry() - if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim"): - if self.party_type=="Customer": + if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim", "Fees"): + if self.party_type == "Customer": ref_party_account = ref_doc.debit_to + elif self.party_type == "Student": + ref_party_account = ref_doc.receivable_account elif self.party_type=="Supplier": ref_party_account = ref_doc.credit_to elif self.party_type=="Employee": ref_party_account = ref_doc.payable_account if ref_party_account != self.party_account: - frappe.throw(_("{0} {1} is associated with {2}, but Party Account is {3}") - .format(d.reference_doctype, d.reference_name, ref_party_account, self.party_account)) + frappe.throw(_("{0} {1} is associated with {2}, but Party Account is {3}") + .format(d.reference_doctype, d.reference_name, ref_party_account, self.party_account)) if ref_doc.docstatus != 1: frappe.throw(_("{0} {1} must be submitted") @@ -281,13 +285,8 @@ class PaymentEntry(AccountsController): if self.party: party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount - total_deductions = sum([flt(d.amount) for d in self.get("deductions")]) - if self.total_allocated_amount < party_amount: - if self.payment_type == "Receive": - self.unallocated_amount = party_amount - (self.total_allocated_amount - total_deductions) - else: - self.unallocated_amount = party_amount - (self.total_allocated_amount + total_deductions) + self.unallocated_amount = party_amount - self.total_allocated_amount def set_difference_amount(self): base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate) @@ -302,11 +301,10 @@ class PaymentEntry(AccountsController): else: self.difference_amount = self.base_paid_amount - flt(self.base_received_amount) - for d in self.get("deductions"): - if d.amount: - self.difference_amount -= flt(d.amount) + total_deductions = sum([flt(d.amount) for d in self.get("deductions")]) - self.difference_amount = flt(self.difference_amount, self.precision("difference_amount")) + self.difference_amount = flt(self.difference_amount - total_deductions, + self.precision("difference_amount")) def clear_unallocated_reference_document_rows(self): self.set("references", self.get("references", {"allocated_amount": ["not in", [0, None, ""]]})) @@ -404,7 +402,7 @@ class PaymentEntry(AccountsController): "account_currency": self.party_account_currency }) - dr_or_cr = "credit" if self.party_type == "Customer" else "debit" + dr_or_cr = "credit" if self.party_type in ["Customer", "Student"] else "debit" for d in self.get("references"): gle = party_gl_dict.copy() @@ -489,9 +487,14 @@ class PaymentEntry(AccountsController): doc = frappe.get_doc("Expense Claim", d.reference_name) update_reimbursed_amount(doc) + def on_recurring(self, reference_doc, subscription_doc): + self.reference_no = reference_doc.name + self.reference_date = nowdate() + @frappe.whitelist() def get_outstanding_reference_documents(args): - args = json.loads(args) + if isinstance(args, basestring): + args = json.loads(args) party_account_currency = get_account_currency(args.get("party_account")) company_currency = frappe.db.get_value("Company", args.get("company"), "default_currency") @@ -499,10 +502,12 @@ def get_outstanding_reference_documents(args): # Get negative outstanding sales /purchase invoices total_field = "base_grand_total" if party_account_currency == company_currency else "grand_total" - negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), - args.get("party"), args.get("party_account"), total_field) + negative_outstanding_invoices = [] + if (args.get("party_type") != "Student"): + negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), + args.get("party"), args.get("party_account"), total_field) - # Get positive outstanding sales /purchase invoices + # Get positive outstanding sales /purchase invoices/ Fees outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"), args.get("party_account")) @@ -515,10 +520,14 @@ def get_outstanding_reference_documents(args): d["exchange_rate"] = get_exchange_rate( party_account_currency, company_currency, d.posting_date ) + if d.voucher_type in ("Purchase Invoice"): + d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no") # Get all SO / PO which are not fully billed or aginst which full advance not paid - orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), args.get("party"), - party_account_currency, company_currency) + orders_to_be_billed = [] + if (args.get("party_type") != "Student"): + orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), + args.get("party"), party_account_currency, company_currency) return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed @@ -633,7 +642,11 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre total_amount = outstanding_amount = exchange_rate = None ref_doc = frappe.get_doc(reference_doctype, reference_name) - if reference_doctype != "Journal Entry": + if reference_doctype == "Fees": + total_amount = ref_doc.get("grand_total") + exchange_rate = 1 + outstanding_amount = ref_doc.get("outstanding_amount") + elif reference_doctype != "Journal Entry": if party_account_currency == ref_doc.company_currency: if ref_doc.doctype == "Expense Claim": total_amount = ref_doc.total_sanctioned_amount @@ -676,19 +689,23 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= party_type = "Supplier" elif dt in ("Expense Claim"): party_type = "Employee" + elif dt in ("Fees"): + party_type = "Student" # party account if dt == "Sales Invoice": party_account = doc.debit_to elif dt == "Purchase Invoice": party_account = doc.credit_to + elif dt == "Fees": + party_account = doc.receivable_account else: party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company) party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account) # payment type - if (dt == "Sales Order" or (dt=="Sales Invoice" and doc.outstanding_amount > 0)) \ + if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees") and doc.outstanding_amount > 0)) \ or (dt=="Purchase Invoice" and doc.outstanding_amount < 0): payment_type = "Receive" else: @@ -704,6 +721,9 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= elif dt in ("Expense Claim"): grand_total = doc.total_sanctioned_amount outstanding_amount = doc.total_sanctioned_amount - doc.total_amount_reimbursed + elif dt == "Fees": + grand_total = doc.grand_total + outstanding_amount = doc.outstanding_amount else: total_field = "base_grand_total" if party_account_currency == doc.company_currency else "grand_total" grand_total = flt(doc.get(total_field)) @@ -745,6 +765,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= pe.append("references", { "reference_doctype": dt, "reference_name": dn, + "bill_no": doc.get("bill_no"), "due_date": doc.get("due_date"), "total_amount": grand_total, "outstanding_amount": outstanding_amount, diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 0316ccafe24..60be20dd89a 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -267,3 +267,65 @@ class TestPaymentEntry(unittest.TestCase): return frappe.db.sql("""select account, debit, credit, against_voucher from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s order by account asc""", voucher_no, as_dict=1) + + def test_payment_entry_write_off_difference(self): + si = create_sales_invoice() + pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC") + pe.reference_no = "1" + pe.reference_date = "2016-01-01" + pe.received_amount = pe.paid_amount = 110 + pe.insert() + + self.assertEqual(pe.unallocated_amount, 10) + + pe.received_amount = pe.paid_amount = 95 + pe.append("deductions", { + "account": "_Test Write Off - _TC", + "cost_center": "_Test Cost Center - _TC", + "amount": 5 + }) + pe.save() + + self.assertEqual(pe.unallocated_amount, 0) + self.assertEqual(pe.difference_amount, 0) + + pe.submit() + + expected_gle = dict((d[0], d) for d in [ + ["Debtors - _TC", 0, 100, si.name], + ["_Test Cash - _TC", 95, 0, None], + ["_Test Write Off - _TC", 5, 0, None] + ]) + + self.validate_gl_entries(pe.name, expected_gle) + + def test_payment_entry_exchange_gain_loss(self): + si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", + currency="USD", conversion_rate=50) + pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") + pe.reference_no = "1" + pe.reference_date = "2016-01-01" + pe.target_exchange_rate = 55 + + pe.append("deductions", { + "account": "_Test Exchange Gain/Loss - _TC", + "cost_center": "_Test Cost Center - _TC", + "amount": -500 + }) + pe.save() + + self.assertEqual(pe.unallocated_amount, 0) + self.assertEqual(pe.difference_amount, 0) + + pe.submit() + + expected_gle = dict((d[0], d) for d in [ + ["_Test Receivable USD - _TC", 0, 5000, si.name], + ["_Test Bank USD - _TC", 5500, 0, None], + ["_Test Exchange Gain/Loss - _TC", 0, 500, None], + ]) + + self.validate_gl_entries(pe.name, expected_gle) + + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 0) diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js new file mode 100644 index 00000000000..4f27b74d4b8 --- /dev/null +++ b/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js @@ -0,0 +1,55 @@ +QUnit.module('Payment Entry'); + +QUnit.test("test payment entry", function(assert) { + assert.expect(6); + let done = assert.async(); + frappe.run_serially([ + () => { + return frappe.tests.make('Sales Invoice', [ + {customer: 'Test Customer 1'}, + {items: [ + [ + {'item_code': 'Test Product 1'}, + {'qty': 1}, + {'rate': 101}, + ] + ]} + ]); + }, + () => cur_frm.save(), + () => frappe.tests.click_button('Submit'), + () => frappe.tests.click_button('Yes'), + () => frappe.timeout(1), + () => frappe.tests.click_button('Close'), + () => frappe.timeout(1), + () => frappe.click_button('Make'), + () => frappe.timeout(1), + () => frappe.click_link('Payment'), + () => frappe.timeout(2), + () => { + assert.equal(frappe.get_route()[1], 'Payment Entry', + 'made payment entry'); + assert.equal(cur_frm.doc.party, 'Test Customer 1', + 'customer set in payment entry'); + assert.equal(cur_frm.doc.paid_amount, 101, + 'paid amount set in payment entry'); + assert.equal(cur_frm.doc.references[0].allocated_amount, 101, + 'amount allocated against sales invoice'); + }, + () => frappe.timeout(1), + () => cur_frm.set_value('paid_amount', 100), + () => frappe.timeout(1), + () => { + frappe.model.set_value("Payment Entry Reference", cur_frm.doc.references[0].name, + "allocated_amount", 101); + }, + () => frappe.timeout(1), + () => frappe.click_button('Write Off Difference Amount'), + () => frappe.timeout(1), + () => { + assert.equal(cur_frm.doc.difference_amount, 0, 'difference amount is zero'); + assert.equal(cur_frm.doc.deductions[0].amount, 1, 'Write off amount = 1'); + }, + () => done() + ]); +}); diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js index a4ef0ca4eb3..0c76343fa90 100644 --- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js @@ -25,5 +25,4 @@ QUnit.test("test payment entry", function(assert) { () => frappe.timeout(0.3), () => done() ]); -}); - +}); \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js new file mode 100644 index 00000000000..9849d767271 --- /dev/null +++ b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js @@ -0,0 +1,67 @@ +QUnit.module('Payment Entry'); + +QUnit.test("test payment entry", function(assert) { + assert.expect(8); + let done = assert.async(); + frappe.run_serially([ + () => { + return frappe.tests.make('Sales Invoice', [ + {customer: 'Test Customer 1'}, + {company: 'For Testing'}, + {currency: 'INR'}, + {selling_price_list: '_Test Price List'}, + {items: [ + [ + {'qty': 1}, + {'item_code': 'Test Product 1'}, + ] + ]} + ]); + }, + () => frappe.timeout(1), + () => cur_frm.save(), + () => frappe.tests.click_button('Submit'), + () => frappe.tests.click_button('Yes'), + () => frappe.timeout(1.5), + () => frappe.click_button('Close'), + () => frappe.timeout(0.5), + () => frappe.click_button('Make'), + () => frappe.timeout(1), + () => frappe.click_link('Payment'), + () => frappe.timeout(2), + () => cur_frm.set_value("paid_to", "_Test Cash - FT"), + () => frappe.timeout(0.5), + () => { + assert.equal(frappe.get_route()[1], 'Payment Entry', 'made payment entry'); + assert.equal(cur_frm.doc.party, 'Test Customer 1', 'customer set in payment entry'); + assert.equal(cur_frm.doc.paid_from, 'Debtors - FT', 'customer account set in payment entry'); + assert.equal(cur_frm.doc.paid_amount, 100, 'paid amount set in payment entry'); + assert.equal(cur_frm.doc.references[0].allocated_amount, 100, + 'amount allocated against sales invoice'); + }, + () => cur_frm.set_value('paid_amount', 95), + () => frappe.timeout(1), + () => { + frappe.model.set_value("Payment Entry Reference", + cur_frm.doc.references[0].name, "allocated_amount", 100); + }, + () => frappe.timeout(.5), + () => { + assert.equal(cur_frm.doc.difference_amount, 5, 'difference amount is 5'); + }, + () => { + frappe.db.set_value("Company", "For Testing", "write_off_account", "_Test Write Off - FT"); + frappe.timeout(1); + frappe.db.set_value("Company", "For Testing", + "exchange_gain_loss_account", "_Test Exchange Gain/Loss - FT"); + }, + () => frappe.timeout(1), + () => frappe.click_button('Write Off Difference Amount'), + () => frappe.timeout(2), + () => { + assert.equal(cur_frm.doc.difference_amount, 0, 'difference amount is zero'); + assert.equal(cur_frm.doc.deductions[0].amount, 5, 'Write off amount = 5'); + }, + () => done() + ]); +}); \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json index 8104e9b3fc6..da17bb3fc85 100644 --- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json +++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "beta": 0, @@ -12,6 +13,7 @@ "engine": "InnoDB", "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -42,6 +44,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -72,6 +75,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -101,6 +105,38 @@ "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fieldname": "bill_no", + "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": "Supplier Invoice No", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -129,6 +165,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -158,6 +195,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -187,6 +225,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -216,10 +255,12 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "depends_on": "eval:(doc.reference_doctype=='Purchase Invoice')", "fieldname": "exchange_rate", "fieldtype": "Float", "hidden": 0, @@ -245,17 +286,17 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 0, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-02-17 16:47:17.156256", + "modified": "2017-09-04 17:37:01.192312", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry Reference", diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 44a3644a0cf..807ad280dbe 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -35,7 +35,6 @@ class PaymentRequest(Document): def on_submit(self): send_mail = True - self.make_communication_entry() ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \ @@ -45,6 +44,7 @@ class PaymentRequest(Document): if send_mail: self.set_payment_request_url() self.send_email() + self.make_communication_entry() def on_cancel(self): self.check_if_payment_entry_exists() @@ -69,8 +69,11 @@ class PaymentRequest(Document): self.db_set('status', 'Initiated') def get_payment_url(self): - data = frappe.db.get_value(self.reference_doctype, self.reference_name, - ["company", "customer_name"], as_dict=1) + if self.reference_doctype != "Fees": + data = frappe.db.get_value(self.reference_doctype, self.reference_name, ["company", "customer_name"], as_dict=1) + else: + data = frappe.db.get_value(self.reference_doctype, self.reference_name, ["student_name"], as_dict=1) + data.update({"company": frappe.defaults.get_defaults().company}) controller = get_payment_gateway_controller(self.payment_gateway) controller.validate_transaction_currency(self.currency) @@ -277,6 +280,9 @@ def get_amount(ref_doc, dt): else: grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate + if dt == "Fees": + grand_total = ref_doc.outstanding_amount + if grand_total > 0 : return grand_total diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js index 03d84e9ad6f..97bbc1227f1 100755 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.js +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js @@ -8,10 +8,6 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) { return { filters: { selling: 1 } }; }); - frm.set_query("print_format", function() { - return { filters: { doc_type: "Sales Invoice", print_format_type: "Js"} }; - }); - erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); }); @@ -27,6 +23,27 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) { }); frappe.ui.form.on('POS Profile', { + setup: function(frm) { + frm.set_query("online_print_format", function() { + return { + filters: [ + ['Print Format', 'doc_type', '=', 'Sales Invoice'], + ['Print Format', 'print_format_type', '!=', 'Js'], + ] + }; + }); + + frm.set_query("print_format", function() { + return { filters: { doc_type: "Sales Invoice", print_format_type: "Js"} }; + }); + + frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'is_online', (r) => { + is_online = r && cint(r.is_online) + frm.toggle_display('offline_pos_section', !is_online); + frm.toggle_display('print_format_for_online', is_online); + }); + }, + refresh: function(frm) { if(frm.doc.company) { frm.trigger("toggle_display_account_head"); diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index 6991da2888a..187454ef332 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -631,8 +631,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "default": "Point of Sale", - "fieldname": "print_format", + "fieldname": "print_format_for_online", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -641,7 +640,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Print Format", + "label": "Print Format for Online", "length": 0, "no_copy": 0, "options": "Print Format", @@ -822,7 +821,7 @@ "columns": 0, "fieldname": "apply_discount", "fieldtype": "Check", - "hidden": 0, + "hidden": 1, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -836,7 +835,7 @@ "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, @@ -851,7 +850,7 @@ "collapsible": 0, "columns": 0, "default": "Grand Total", - "depends_on": "apply_discount", + "depends_on": "", "fieldname": "apply_discount_on", "fieldtype": "Select", "hidden": 0, @@ -883,7 +882,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "customer_details", + "fieldname": "offline_pos_section", "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, @@ -892,7 +891,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "New Customer Details", + "label": "Offline POS Section", "length": 0, "no_copy": 0, "permlevel": 0, @@ -969,6 +968,38 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Point of Sale", + "fieldname": "print_format", + "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": "Print Format", + "length": 0, + "no_copy": 0, + "options": "Print Format", + "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 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1291,7 +1322,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-07-28 03:40:03.253088", + "modified": "2017-09-01 15:55:14.890452", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", diff --git a/erpnext/docs/user/manual/en/setting-up/setup-wizard/__init__.py b/erpnext/accounts/doctype/pos_settings/__init__.py similarity index 100% rename from erpnext/docs/user/manual/en/setting-up/setup-wizard/__init__.py rename to erpnext/accounts/doctype/pos_settings/__init__.py diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.js b/erpnext/accounts/doctype/pos_settings/pos_settings.js new file mode 100644 index 00000000000..1a146185139 --- /dev/null +++ b/erpnext/accounts/doctype/pos_settings/pos_settings.js @@ -0,0 +1,8 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('POS Settings', { + refresh: function() { + + } +}); diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.json b/erpnext/accounts/doctype/pos_settings/pos_settings.json new file mode 100644 index 00000000000..8f5b631c89a --- /dev/null +++ b/erpnext/accounts/doctype/pos_settings/pos_settings.json @@ -0,0 +1,133 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2017-08-28 16:46:41.732676", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fieldname": "use_pos_in_offline_mode", + "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": "Use POS in Offline Mode", + "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 + } + ], + "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": "2017-09-11 13:57:28.787023", + "modified_by": "Administrator", + "module": "Accounts", + "name": "POS Settings", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "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, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Accounts User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Sales User", + "set_user_permissions": 0, + "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", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.py b/erpnext/accounts/doctype/pos_settings/pos_settings.py new file mode 100644 index 00000000000..736d36eea96 --- /dev/null +++ b/erpnext/accounts/doctype/pos_settings/pos_settings.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class POSSettings(Document): + pass \ No newline at end of file diff --git a/erpnext/accounts/doctype/pos_settings/test_pos_settings.js b/erpnext/accounts/doctype/pos_settings/test_pos_settings.js new file mode 100644 index 00000000000..639c94ed10d --- /dev/null +++ b/erpnext/accounts/doctype/pos_settings/test_pos_settings.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: POS Settings", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new POS Settings + () => frappe.tests.make('POS Settings', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/accounts/doctype/pos_settings/test_pos_settings.py b/erpnext/accounts/doctype/pos_settings/test_pos_settings.py new file mode 100644 index 00000000000..a3df10803c0 --- /dev/null +++ b/erpnext/accounts/doctype/pos_settings/test_pos_settings.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import unittest + +class TestPOSSettings(unittest.TestCase): + pass diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index 49af59b3880..8860b092e0b 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, "autoname": "field:title", @@ -12,6 +13,7 @@ "editable_grid": 0, "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -40,6 +42,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -69,6 +72,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -99,6 +103,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -129,6 +134,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -159,6 +165,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -189,6 +196,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -217,6 +225,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -247,6 +256,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -275,6 +285,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -303,6 +314,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -331,6 +343,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -359,6 +372,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -387,6 +401,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -417,6 +432,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -447,6 +463,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -477,6 +494,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -507,6 +525,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -537,6 +556,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -567,6 +587,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -597,6 +618,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -627,6 +649,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -655,6 +678,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -683,6 +707,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -711,6 +736,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -739,6 +765,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -767,6 +794,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -796,6 +824,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -824,6 +853,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -851,6 +881,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -880,6 +911,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -910,6 +942,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -941,6 +974,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -969,6 +1003,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1000,6 +1035,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1028,6 +1064,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1058,6 +1095,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1085,13 +1123,14 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, "depends_on": "eval:doc.price_or_discount==\"Price\"", "fieldname": "price", - "fieldtype": "Float", + "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -1114,6 +1153,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1143,6 +1183,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1173,6 +1214,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1202,6 +1244,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1230,18 +1273,18 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-gift", "idx": 1, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-02-17 16:21:28.446208", + "modified": "2017-08-31 16:34:41.614743", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index ac5f5dd3214..b9a7dae55f5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -46,6 +46,12 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ cur_frm.add_custom_button(__('Return / Debit Note'), this.make_debit_note, __("Make")); } + + if(!doc.subscription) { + cur_frm.add_custom_button(__('Subscription'), function() { + erpnext.utils.make_subscription(doc.doctype, doc.name) + }, __("Make")) + } } if(doc.docstatus===0) { @@ -343,6 +349,7 @@ frappe.ui.form.on("Purchase Invoice", { 'Payment Entry': 'Payment' } }, + onload: function(frm) { $.each(["warehouse", "rejected_warehouse"], function(i, field) { frm.set_query(field, "items", function() { @@ -370,5 +377,5 @@ frappe.ui.form.on("Purchase Invoice", { erpnext.buying.get_default_bom(frm); } frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes"); - } -}) + }, +}) \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 8ea48f65aa8..e3c5fb49424 100755 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -2072,6 +2072,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "base_rounding_adjustment", + "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": "Rounding Adjustment (Company Currency)", + "length": 0, + "no_copy": 1, + "options": "Company:company:default_currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2166,6 +2197,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "rounding_adjustment", + "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": "Rounding Adjustment", + "length": 0, + "no_copy": 1, + "options": "currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -3348,6 +3410,67 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription", + "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": "Subscription", + "length": 0, + "no_copy": 1, + "options": "Subscription", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -3358,7 +3481,7 @@ "depends_on": "eval:doc.docstatus<2 && !doc.__islocal", "fieldname": "recurring_invoice", "fieldtype": "Section Break", - "hidden": 0, + "hidden": 1, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -3797,7 +3920,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-07-19 13:53:48.673757", + "modified": "2017-09-19 11:22:47.074420", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 7ab790118a0..a46c4b96c33 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -15,6 +15,7 @@ from erpnext.stock import get_warehouse_account_map from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt from erpnext.buying.utils import check_for_closed_status +from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center form_grid_templates = { "items": "templates/form_grid/item_grid.html" @@ -353,6 +354,7 @@ class PurchaseInvoice(BuyingController): self.make_payment_gl_entries(gl_entries) self.make_write_off_gl_entry(gl_entries) + self.make_gle_for_rounding_adjustment(gl_entries) return gl_entries @@ -584,6 +586,21 @@ class PurchaseInvoice(BuyingController): }) ) + def make_gle_for_rounding_adjustment(self, gl_entries): + if self.rounding_adjustment: + round_off_account, round_off_cost_center = \ + get_round_off_account_and_cost_center(self.company) + + gl_entries.append( + self.get_gl_dict({ + "account": round_off_account, + "against": self.supplier, + "debit_in_account_currency": self.rounding_adjustment, + "debit": self.base_rounding_adjustment, + "cost_center": round_off_cost_center, + } + )) + def on_cancel(self): self.check_for_closed_status() @@ -667,7 +684,7 @@ class PurchaseInvoice(BuyingController): if account_type != 'Fixed Asset': frappe.throw(_("Row {0}# Account must be of type 'Fixed Asset'").format(d.idx)) - def on_recurring(self, reference_doc): + def on_recurring(self, reference_doc, subscription_doc): self.due_date = None @frappe.whitelist() diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py index 6141db5d3cd..062a2d2c64f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py @@ -8,7 +8,8 @@ def get_data(): 'Payment Entry': 'reference_name', 'Payment Request': 'reference_name', 'Landed Cost Voucher': 'receipt_document', - 'Purchase Invoice': 'return_against' + 'Purchase Invoice': 'return_against', + 'Subscription': 'reference_document' }, 'internal_links': { 'Purchase Order': ['items', 'purchase_order'], @@ -27,5 +28,9 @@ def get_data(): 'label': _('Returns'), 'items': ['Purchase Invoice'] }, + { + 'label': _('Subscription'), + 'items': ['Subscription'] + }, ] } \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 3454a2e9b49..639620f910f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -256,10 +256,6 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account` where reference_type='Purchase Invoice' and reference_name=%s""", pi.name)) - def test_recurring_invoice(self): - from erpnext.controllers.tests.test_recurring_document import test_recurring_document - test_recurring_document(self, test_records) - def test_total_purchase_cost_for_project(self): existing_purchase_cost = frappe.db.sql("""select sum(base_net_amount) from `tabPurchase Invoice Item` where project = '_Test Project' and docstatus=1""") diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index ef233c67995..11d18253887 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -86,7 +86,11 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte this.make_payment_request, __("Make")); } - + if(!doc.subscription) { + cur_frm.add_custom_button(__('Subscription'), function() { + erpnext.utils.make_subscription(doc.doctype, doc.name) + }, __("Make")) + } } // Show buttons only when pos view is active diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 260c05e3e42..9aa0e6b6a2b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1670,36 +1670,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "net_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": "Net Total", - "length": 0, - "no_copy": 0, - "options": "currency", - "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, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1731,6 +1701,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "net_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": "Net Total", + "length": 0, + "no_copy": 0, + "options": "currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2337,6 +2337,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "base_rounding_adjustment", + "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": "Rounding Adjustment (Company Currency)", + "length": 0, + "no_copy": 1, + "options": "Company:company:default_currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2463,6 +2494,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "rounding_adjustment", + "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": "Rounding Adjustment", + "length": 0, + "no_copy": 1, + "options": "currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -4175,6 +4237,67 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription", + "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": "Subscription", + "length": 0, + "no_copy": 1, + "options": "Subscription", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -4185,7 +4308,7 @@ "depends_on": "eval:doc.docstatus<2 && !doc.__islocal", "fieldname": "recurring_invoice", "fieldtype": "Section Break", - "hidden": 0, + "hidden": 1, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -4688,7 +4811,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-07-07 13:05:37.469682", + "modified": "2017-09-19 11:23:08.675028", "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 780edd8bdf0..7a787c4cba3 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -20,6 +20,7 @@ from erpnext.accounts.doctype.asset.depreciation \ from erpnext.stock.doctype.batch.batch import set_batch_nos from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_delivery_note_serial_no from erpnext.setup.doctype.company.company import update_company_current_month_sales +from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center form_grid_templates = { "items": "templates/form_grid/item_grid.html" @@ -107,7 +108,7 @@ class SalesInvoice(SellingController): def on_submit(self): self.validate_pos_paid_amount() - if not self.recurring_id: + if not self.subscription: frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.base_grand_total, self) @@ -313,7 +314,7 @@ class SalesInvoice(SellingController): for fieldname in ('territory', 'naming_series', 'currency', 'taxes_and_charges', 'letter_head', 'tc_name', 'selling_price_list', 'company', 'select_print_heading', 'cash_bank_account', - 'write_off_account', 'write_off_cost_center'): + 'write_off_account', 'write_off_cost_center', 'apply_discount_on'): if (not for_validate) or (for_validate and not self.get(fieldname)): self.set(fieldname, pos.get(fieldname)) @@ -625,6 +626,7 @@ class SalesInvoice(SellingController): self.make_gle_for_change_amount(gl_entries) self.make_write_off_gl_entry(gl_entries) + self.make_gle_for_rounding_adjustment(gl_entries) return gl_entries @@ -784,6 +786,21 @@ class SalesInvoice(SellingController): }, write_off_account_currency) ) + def make_gle_for_rounding_adjustment(self, gl_entries): + if self.rounding_adjustment: + round_off_account, round_off_cost_center = \ + get_round_off_account_and_cost_center(self.company) + + gl_entries.append( + self.get_gl_dict({ + "account": round_off_account, + "against": self.customer, + "credit_in_account_currency": self.rounding_adjustment, + "credit": self.base_rounding_adjustment, + "cost_center": round_off_cost_center, + } + )) + def update_billing_status_in_dn(self, update_modified=True): updated_delivery_notes = [] for d in self.get("items"): @@ -799,7 +816,7 @@ class SalesInvoice(SellingController): for dn in set(updated_delivery_notes): frappe.get_doc("Delivery Note", dn).update_billing_percentage(update_modified=update_modified) - def on_recurring(self, reference_doc): + def on_recurring(self, reference_doc, subscription_doc): for fieldname in ("c_form_applicable", "c_form_no", "write_off_amount"): self.set(fieldname, reference_doc.get(fieldname)) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py index bc9d76646b9..efd18b52150 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py @@ -8,7 +8,8 @@ def get_data(): 'Journal Entry': 'reference_name', 'Payment Entry': 'reference_name', 'Payment Request': 'reference_name', - 'Sales Invoice': 'return_against' + 'Sales Invoice': 'return_against', + 'Subscription': 'reference_document', }, 'internal_links': { 'Sales Order': ['items', 'sales_order'] @@ -26,5 +27,9 @@ def get_data(): 'label': _('Returns'), 'items': ['Sales Invoice'] }, + { + 'label': _('Subscription'), + 'items': ['Subscription'] + }, ] } \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index e0a453c5c91..4dae78c8c46 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -215,12 +215,12 @@ class TestSalesInvoice(unittest.TestCase): si.save() # with inclusive tax and additional discount - self.assertEquals(si.net_total, 4298.24) + self.assertEquals(si.net_total, 4298.25) self.assertEquals(si.grand_total, 4900.00) def test_sales_invoice_discount_amount(self): si = frappe.copy_doc(test_records[3]) - si.discount_amount = 104.95 + si.discount_amount = 104.94 si.append("taxes", { "charge_type": "On Previous Row Amount", "account_head": "_Test Account Service Tax - _TC", @@ -285,7 +285,7 @@ class TestSalesInvoice(unittest.TestCase): "_Test Account Customs Duty - _TC": [125, 116.35, 1585.40], "_Test Account Shipping Charges - _TC": [100, 100, 1685.40], "_Test Account Discount - _TC": [-180.33, -168.54, 1516.86], - "_Test Account Service Tax - _TC": [-18.03, -16.86, 1500] + "_Test Account Service Tax - _TC": [-18.03, -16.85, 1500.01] } for d in si.get("taxes"): @@ -294,10 +294,12 @@ class TestSalesInvoice(unittest.TestCase): self.assertEquals(si.base_grand_total, 1500) self.assertEquals(si.grand_total, 1500) + self.assertEquals(si.rounding_adjustment, -0.01) def test_discount_amount_gl_entry(self): + frappe.db.set_value("Company", "_Test Company", "round_off_account", "Round Off - _TC") si = frappe.copy_doc(test_records[3]) - si.discount_amount = 104.95 + si.discount_amount = 104.94 si.append("taxes", { "doctype": "Sales Taxes and Charges", "charge_type": "On Previous Row Amount", @@ -327,7 +329,8 @@ class TestSalesInvoice(unittest.TestCase): [test_records[3]["taxes"][5]["account_head"], 0.0, 116.35], [test_records[3]["taxes"][6]["account_head"], 0.0, 100], [test_records[3]["taxes"][7]["account_head"], 168.54, 0.0], - ["_Test Account Service Tax - _TC", 16.86, 0.0] + ["_Test Account Service Tax - _TC", 16.85, 0.0], + ["Round Off - _TC", 0.01, 0.0] ]) for gle in gl_entries: @@ -423,13 +426,12 @@ class TestSalesInvoice(unittest.TestCase): expected_values = { "keys": ["price_list_rate", "discount_percentage", "rate", "amount", "base_price_list_rate", "base_rate", "base_amount", "net_rate", "net_amount"], - "_Test Item Home Desktop 100": [62.5, 0, 62.5, 625.0, 62.5, 62.5, 625.0, 50, 499.98], - "_Test Item Home Desktop 200": [190.66, 0, 190.66, 953.3, 190.66, 190.66, 953.3, 150, 750], + "_Test Item Home Desktop 100": [62.5, 0, 62.5, 625.0, 62.5, 62.5, 625.0, 50, 499.97600115194473], + "_Test Item Home Desktop 200": [190.66, 0, 190.66, 953.3, 190.66, 190.66, 953.3, 150, 749.9968530500239], } # check if children are saved - self.assertEquals(len(si.get("items")), - len(expected_values)-1) + self.assertEquals(len(si.get("items")), len(expected_values)-1) # check if item values are calculated for d in si.get("items"): @@ -437,28 +439,28 @@ class TestSalesInvoice(unittest.TestCase): self.assertEquals(d.get(k), expected_values[d.item_code][i]) # check net total - self.assertEquals(si.base_net_total, 1249.98) + self.assertEquals(si.net_total, 1249.97) self.assertEquals(si.total, 1578.3) # check tax calculation expected_values = { "keys": ["tax_amount", "total"], - "_Test Account Excise Duty - _TC": [140, 1389.98], - "_Test Account Education Cess - _TC": [2.8, 1392.78], - "_Test Account S&H Education Cess - _TC": [1.4, 1394.18], - "_Test Account CST - _TC": [27.88, 1422.06], - "_Test Account VAT - _TC": [156.25, 1578.31], - "_Test Account Customs Duty - _TC": [125, 1703.31], - "_Test Account Shipping Charges - _TC": [100, 1803.31], - "_Test Account Discount - _TC": [-180.33, 1622.98] + "_Test Account Excise Duty - _TC": [140, 1389.97], + "_Test Account Education Cess - _TC": [2.8, 1392.77], + "_Test Account S&H Education Cess - _TC": [1.4, 1394.17], + "_Test Account CST - _TC": [27.88, 1422.05], + "_Test Account VAT - _TC": [156.25, 1578.30], + "_Test Account Customs Duty - _TC": [125, 1703.30], + "_Test Account Shipping Charges - _TC": [100, 1803.30], + "_Test Account Discount - _TC": [-180.33, 1622.97] } for d in si.get("taxes"): for i, k in enumerate(expected_values["keys"]): self.assertEquals(d.get(k), expected_values[d.account_head][i]) - self.assertEquals(si.base_grand_total, 1622.98) - self.assertEquals(si.grand_total, 1622.98) + self.assertEquals(si.base_grand_total, 1622.97) + self.assertEquals(si.grand_total, 1622.97) def test_sales_invoice_calculation_export_currency_with_tax_inclusive_price(self): # prepare @@ -486,7 +488,7 @@ class TestSalesInvoice(unittest.TestCase): "base_rate": 2500, "base_amount": 25000, "net_rate": 40, - "net_amount": 399.98, + "net_amount": 399.9808009215558, "base_net_rate": 2000, "base_net_amount": 19999 }, @@ -500,7 +502,7 @@ class TestSalesInvoice(unittest.TestCase): "base_rate": 7500, "base_amount": 37500, "net_rate": 118.01, - "net_amount": 590.05, + "net_amount": 590.0531205155963, "base_net_rate": 5900.5, "base_net_amount": 29502.5 } @@ -536,8 +538,11 @@ class TestSalesInvoice(unittest.TestCase): for i, k in enumerate(expected_values["keys"]): self.assertEquals(d.get(k), expected_values[d.account_head][i]) - self.assertEquals(si.base_grand_total, 60794.5) - self.assertEquals(si.grand_total, 1215.89) + self.assertEquals(si.base_grand_total, 60795) + self.assertEquals(si.grand_total, 1215.90) + self.assertEquals(si.rounding_adjustment, 0.01) + self.assertEquals(si.base_rounding_adjustment, 0.50) + def test_outstanding(self): w = self.make() @@ -809,10 +814,6 @@ class TestSalesInvoice(unittest.TestCase): self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account` where reference_name=%s""", si.name)) - def test_recurring_invoice(self): - from erpnext.controllers.tests.test_recurring_document import test_recurring_document - test_recurring_document(self, test_records) - def test_serialized(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -1167,8 +1168,15 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(flt(si.outstanding_amount), flt(si.grand_total + si.total_advance, si.precision("outstanding_amount"))) def test_multiple_uom_in_selling(self): - si = frappe.copy_doc(test_records[1]) + frappe.db.sql("""delete from `tabItem Price` + where price_list='_Test Price List' and item_code='_Test Item'""") + item_price = frappe.new_doc("Item Price") + item_price.price_list = "_Test Price List" + item_price.item_code = "_Test Item" + item_price.price_list_rate = 100 + item_price.insert() + si = frappe.copy_doc(test_records[1]) si.items[0].uom = "_Test UOM 1" si.items[0].conversion_factor = None si.items[0].price_list_rate = None @@ -1283,6 +1291,40 @@ class TestSalesInvoice(unittest.TestCase): current_month_sales = frappe.db.get_value("Company", "_Test Company", "total_monthly_sales") self.assertEqual(current_month_sales, existing_current_month_sales) + def test_rounding_adjustment(self): + si = create_sales_invoice(rate=24900, do_not_save=True) + for tax in ["Tax 1", "Tax2"]: + si.append("taxes", { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "description": tax, + "rate": 14, + "cost_center": "_Test Cost Center - _TC", + "included_in_print_rate": 1 + }) + si.save() + + self.assertEqual(si.net_total, 19453.13) + self.assertEqual(si.grand_total, 24900) + self.assertEqual(si.total_taxes_and_charges, 5446.88) + self.assertEqual(si.rounding_adjustment, -0.01) + + expected_values = dict((d[0], d) for d in [ + [si.debit_to, 24900, 0.0], + ["_Test Account Service Tax - _TC", 0.0, 5446.88], + ["Sales - _TC", 0.0, 19453.13], + ["Round Off - _TC", 0.01, 0.0] + ]) + + gl_entries = frappe.db.sql("""select account, debit, credit + from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s + order by account asc""", si.name, as_dict=1) + + for gle in gl_entries: + self.assertEquals(expected_values[gle.account][0], gle.account) + self.assertEquals(expected_values[gle.account][1], gle.debit) + self.assertEquals(expected_values[gle.account][2], gle.credit) + def create_sales_invoice(**args): si = frappe.new_doc("Sales Invoice") args = frappe._dict(args) diff --git a/erpnext/accounts/doctype/subscription/__init__.py b/erpnext/accounts/doctype/subscription/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js new file mode 100644 index 00000000000..9c5b264bc08 --- /dev/null +++ b/erpnext/accounts/doctype/subscription/subscription.js @@ -0,0 +1,68 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Subscription', { + setup: function(frm) { + frm.fields_dict['reference_document'].get_query = function() { + return { + filters: { + "docstatus": 1 + } + }; + }; + + frm.fields_dict['print_format'].get_query = function() { + return { + filters: { + "doc_type": frm.doc.reference_doctype + } + }; + }; + }, + + refresh: function(frm) { + if(frm.doc.docstatus == 1) { + let label = __('View {0}', [frm.doc.reference_doctype]); + frm.add_custom_button(__(label), + function() { + frappe.route_options = { + "subscription": frm.doc.name, + }; + frappe.set_route("List", frm.doc.reference_doctype); + } + ); + + if(frm.doc.status != 'Stopped') { + frm.add_custom_button(__("Stop"), + function() { + frm.events.stop_resume_subscription(frm, "Stopped"); + } + ); + } + + if(frm.doc.status == 'Stopped') { + frm.add_custom_button(__("Resume"), + function() { + frm.events.stop_resume_subscription(frm, "Resumed"); + } + ); + } + } + }, + + stop_resume_subscription: function(frm, status) { + frappe.call({ + method: "erpnext.accounts.doctype.subscription.subscription.stop_resume_subscription", + args: { + subscription: frm.doc.name, + status: status + }, + callback: function(r) { + if(r.message) { + frm.set_value("status", r.message); + frm.reload_doc(); + } + } + }); + } +}); \ No newline at end of file diff --git a/erpnext/accounts/doctype/subscription/subscription.json b/erpnext/accounts/doctype/subscription/subscription.json new file mode 100644 index 00000000000..85779533ea2 --- /dev/null +++ b/erpnext/accounts/doctype/subscription/subscription.json @@ -0,0 +1,771 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "naming_series:", + "beta": 0, + "creation": "2017-07-18 17:50:43.967266", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": "SUB-", + "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 + }, + { + "allow_bulk_edit": 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": 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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "reference_document", + "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": "Reference Document", + "length": 0, + "no_copy": 1, + "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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "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": 1, + "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": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "submit_on_creation", + "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": "Submit on Creation", + "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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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": 1, + "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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "end_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": "End 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "next_schedule_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": "Next Schedule Date", + "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": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "frequency_detail", + "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": "", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "frequency", + "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": "Frequency", + "length": 0, + "no_copy": 0, + "options": "\nDaily\nWeekly\nMonthly\nQuarterly\nHalf-yearly\nYearly", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_12", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval: in_list([\"Monthly\", \"Quarterly\", \"Yearly\"], doc.frequency)", + "fieldname": "repeat_on_day", + "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": "Repeat on Day", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "notification", + "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": "Notification", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "notify_by_email", + "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": "Notify by Email", + "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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "notify_by_email", + "fieldname": "recipients", + "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": "Recipients", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "notify_by_email", + "fieldname": "print_format", + "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": "Print Format", + "length": 0, + "no_copy": 0, + "options": "Print Format", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "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, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Draft", + "fieldname": "status", + "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": "Status", + "length": 0, + "no_copy": 0, + "options": "\nDraft\nStopped\nSubmitted\nCancelled\nCompleted", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": "Subscription", + "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, + "unique": 0 + } + ], + "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": "2017-09-14 12:09:38.471458", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Subscription", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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": 1, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 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": 1, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 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 Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "search_fields": "reference_document", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "reference_document", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py new file mode 100644 index 00000000000..c9df7d461e5 --- /dev/null +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -0,0 +1,228 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import calendar +from frappe import _ +from frappe.desk.form import assign_to +from dateutil.relativedelta import relativedelta +from frappe.utils.user import get_system_managers +from frappe.utils import cstr, getdate, split_emails, add_days, today +from frappe.model.document import Document + +month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12} +class Subscription(Document): + def validate(self): + self.update_status() + self.validate_dates() + self.validate_next_schedule_date() + self.validate_email_id() + + def before_submit(self): + self.set_next_schedule_date() + + def on_submit(self): + self.update_subscription_id() + + def on_update_after_submit(self): + self.validate_dates() + self.set_next_schedule_date() + + def validate_dates(self): + if self.end_date and getdate(self.start_date) > getdate(self.end_date): + frappe.throw(_("End date must be greater than start date")) + + def validate_next_schedule_date(self): + if self.repeat_on_day and self.next_schedule_date: + next_date = getdate(self.next_schedule_date) + if next_date.day != self.repeat_on_day: + # if the repeat day is the last day of the month (31) + # and the current month does not have as many days, + # then the last day of the current month is a valid date + lastday = calendar.monthrange(next_date.year, next_date.month)[1] + if self.repeat_on_day < lastday: + + # the specified day of the month is not same as the day specified + # or the last day of the month + frappe.throw(_("Next Date's day and Repeat on Day of Month must be equal")) + + def validate_email_id(self): + if self.notify_by_email: + if self.recipients: + email_list = split_emails(self.recipients.replace("\n", "")) + + from frappe.utils import validate_email_add + for email in email_list: + if not validate_email_add(email): + frappe.throw(_("{0} is an invalid email address in 'Recipients'").format(email)) + else: + frappe.throw(_("'Recipients' not specified")) + + def set_next_schedule_date(self): + self.next_schedule_date = get_next_schedule_date(self.start_date, + self.frequency, self.repeat_on_day) + + def update_subscription_id(self): + doc = frappe.get_doc(self.reference_doctype, self.reference_document) + if not doc.meta.get_field('subscription'): + frappe.throw(_("Add custom field Subscription Id in the doctype {0}").format(self.reference_doctype)) + + doc.db_set('subscription', self.name) + + def update_status(self, status=None): + self.status = { + '0': 'Draft', + '1': 'Submitted', + '2': 'Cancelled' + }[cstr(self.docstatus or 0)] + + if status and status != 'Resumed': + self.status = status + +def get_next_schedule_date(start_date, frequency, repeat_on_day): + mcount = month_map.get(frequency) + if mcount: + next_date = get_next_date(start_date, mcount, repeat_on_day) + else: + days = 7 if frequency == 'Weekly' else 1 + next_date = add_days(start_date, days) + return next_date + +def make_subscription_entry(date=None): + date = date or today() + for data in get_subscription_entries(date): + schedule_date = getdate(data.next_schedule_date) + while schedule_date <= getdate(today()): + create_documents(data, schedule_date) + schedule_date = get_next_schedule_date(schedule_date, + data.frequency, data.repeat_on_day) + + if schedule_date and not frappe.db.get_value('Subscription', data.name, 'disabled'): + frappe.db.set_value('Subscription', data.name, 'next_schedule_date', schedule_date) + +def get_subscription_entries(date): + return frappe.db.sql(""" select * from `tabSubscription` + where docstatus = 1 and next_schedule_date <=%s + and reference_document is not null and reference_document != '' + and next_schedule_date <= ifnull(end_date, '2199-12-31') + and ifnull(disabled, 0) = 0 and status != 'Stopped' """, (date), as_dict=1) + +def create_documents(data, schedule_date): + try: + doc = make_new_document(data, schedule_date) + if data.notify_by_email and data.recipients: + print_format = data.print_format or "Standard" + send_notification(doc, print_format, data.recipients) + + frappe.db.commit() + except Exception: + frappe.db.rollback() + frappe.db.begin() + frappe.log_error(frappe.get_traceback()) + disabled_subscription(data) + frappe.db.commit() + if data.reference_document and not frappe.flags.in_test: + notify_error_to_user(data) + +def disabled_subscription(data): + subscription = frappe.get_doc('Subscription', data.name) + subscription.db_set('disabled', 1) + +def notify_error_to_user(data): + party = '' + party_type = '' + + if data.reference_doctype in ['Sales Order', 'Sales Invoice', 'Delivery Note']: + party_type = 'customer' + elif data.reference_doctype in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']: + party_type = 'supplier' + + if party_type: + party = frappe.db.get_value(data.reference_doctype, data.reference_document, party_type) + + notify_errors(data.reference_document, data.reference_doctype, party, data.owner, data.name) + +def make_new_document(args, schedule_date): + doc = frappe.get_doc(args.reference_doctype, args.reference_document) + new_doc = frappe.copy_doc(doc, ignore_no_copy=False) + update_doc(new_doc, doc , args, schedule_date) + new_doc.insert(ignore_permissions=True) + + if args.submit_on_creation: + new_doc.submit() + + return new_doc + +def update_doc(new_document, reference_doc, args, schedule_date): + new_document.docstatus = 0 + if new_document.meta.get_field('set_posting_time'): + new_document.set('set_posting_time', 1) + + if new_document.meta.get_field('subscription'): + new_document.set('subscription', args.name) + + new_document.run_method("on_recurring", reference_doc=reference_doc, subscription_doc=args) + for data in new_document.meta.fields: + if data.fieldtype == 'Date' and data.reqd: + new_document.set(data.fieldname, schedule_date) + +def get_next_date(dt, mcount, day=None): + dt = getdate(dt) + dt += relativedelta(months=mcount, day=day) + + return dt + +def send_notification(new_rv, print_format='Standard', recipients=None): + """Notify concerned persons about recurring document generation""" + print_format = print_format + + frappe.sendmail(recipients, + subject= _("New {0}: #{1}").format(new_rv.doctype, new_rv.name), + message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name), + attachments = [frappe.attach_print(new_rv.doctype, new_rv.name, file_name=new_rv.name, print_format=print_format)]) + +def notify_errors(doc, doctype, party, owner, name): + recipients = get_system_managers(only_name=True) + frappe.sendmail(recipients + [frappe.db.get_value("User", owner, "email")], + subject=_("[Urgent] Error while creating recurring %s for %s" % (doctype, doc)), + message = frappe.get_template("templates/emails/recurring_document_failed.html").render({ + "type": _(doctype), + "name": doc, + "party": party or "", + "subscription": name + })) + + assign_task_to_owner(name, "Recurring Documents Failed", recipients) + +def assign_task_to_owner(name, msg, users): + for d in users: + args = { + 'doctype' : 'Subscription', + 'assign_to' : d, + 'name' : name, + 'description' : msg, + 'priority' : 'High' + } + assign_to.add(args) + +@frappe.whitelist() +def make_subscription(doctype, docname): + doc = frappe.new_doc('Subscription') + doc.reference_doctype = doctype + doc.reference_document = docname + return doc + +@frappe.whitelist() +def stop_resume_subscription(subscription, status): + doc = frappe.get_doc('Subscription', subscription) + frappe.msgprint(_("Subscription has been {0}").format(status)) + if status == 'Resumed': + doc.next_schedule_date = get_next_schedule_date(today(), + doc.frequency, doc.repeat_on_day) + + doc.update_status(status) + doc.save() + + return doc.status \ No newline at end of file diff --git a/erpnext/accounts/doctype/subscription/subscription_list.js b/erpnext/accounts/doctype/subscription/subscription_list.js new file mode 100644 index 00000000000..71e3cce79d5 --- /dev/null +++ b/erpnext/accounts/doctype/subscription/subscription_list.js @@ -0,0 +1,16 @@ +frappe.listview_settings['Subscription'] = { + add_fields: ["next_schedule_date"], + get_indicator: function(doc) { + if(doc.disabled) { + return [__("Disabled"), "red"]; + } else if(doc.next_schedule_date >= frappe.datetime.get_today() && doc.status != 'Stopped') { + return [__("Active"), "green"]; + } else if(doc.docstatus === 0) { + return [__("Draft"), "red", "docstatus,=,0"]; + } else if(doc.status === 'Stopped') { + return [__("Stopped"), "red"]; + } else { + return [__("Expired"), "darkgrey"]; + } + } +}; \ No newline at end of file diff --git a/erpnext/accounts/doctype/subscription/test_subscription.js b/erpnext/accounts/doctype/subscription/test_subscription.js new file mode 100644 index 00000000000..2872a2147fb --- /dev/null +++ b/erpnext/accounts/doctype/subscription/test_subscription.js @@ -0,0 +1,32 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Subscription", function (assert) { + assert.expect(4); + let done = assert.async(); + frappe.run_serially([ + // insert a new Subscription + () => { + return frappe.tests.make("Subscription", [ + {reference_doctype: 'Sales Invoice'}, + {reference_document: 'SINV-00004'}, + {start_date: frappe.datetime.month_start()}, + {end_date: frappe.datetime.month_end()}, + {frequency: 'Weekly'} + ]); + }, + () => cur_frm.savesubmit(), + () => frappe.timeout(1), + () => frappe.click_button('Yes'), + () => frappe.timeout(2), + () => { + assert.ok(cur_frm.doc.frequency.includes("Weekly"), "Set frequency Weekly"); + assert.ok(cur_frm.doc.reference_doctype.includes("Sales Invoice"), "Set base doctype Sales Invoice"); + assert.equal(cur_frm.doc.docstatus, 1, "Submitted subscription"); + assert.equal(cur_frm.doc.next_schedule_date, + frappe.datetime.add_days(frappe.datetime.get_today(), 7), "Set schedule date"); + }, + () => done() + ]); +}); diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py new file mode 100644 index 00000000000..b74163c92e9 --- /dev/null +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +from frappe.utils import today, add_days, getdate +from erpnext.accounts.utils import get_fiscal_year +from erpnext.accounts.report.financial_statements import get_months +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order +from erpnext.accounts.doctype.subscription.subscription import make_subscription_entry + +class TestSubscription(unittest.TestCase): + def test_daily_subscription(self): + qo = frappe.copy_doc(quotation_records[0]) + qo.submit() + + doc = make_subscription(reference_document=qo.name) + self.assertEquals(doc.next_schedule_date, today()) + make_subscription_entry() + frappe.db.commit() + + quotation = frappe.get_doc(doc.reference_doctype, doc.reference_document) + self.assertEquals(quotation.subscription, doc.name) + + new_quotation = frappe.db.get_value('Quotation', + {'subscription': doc.name, 'name': ('!=', quotation.name)}, 'name') + + new_quotation = frappe.get_doc('Quotation', new_quotation) + + for fieldname in ['customer', 'company', 'order_type', 'total', 'grand_total']: + self.assertEquals(quotation.get(fieldname), new_quotation.get(fieldname)) + + for fieldname in ['item_code', 'qty', 'rate', 'amount']: + self.assertEquals(quotation.items[0].get(fieldname), + new_quotation.items[0].get(fieldname)) + + def test_monthly_subscription_for_so(self): + current_fiscal_year = get_fiscal_year(today(), as_dict=True) + start_date = current_fiscal_year.year_start_date + end_date = current_fiscal_year.year_end_date + + for doctype in ['Sales Order', 'Sales Invoice']: + if doctype == 'Sales Invoice': + docname = create_sales_invoice(posting_date=start_date) + else: + docname = make_sales_order() + + self.monthly_subscription(doctype, docname.name, start_date, end_date) + + def monthly_subscription(self, doctype, docname, start_date, end_date): + doc = make_subscription(reference_doctype=doctype, frequency = 'Monthly', + reference_document = docname, start_date=start_date, end_date=end_date) + + doc.disabled = 1 + doc.save() + frappe.db.commit() + + make_subscription_entry() + docnames = frappe.get_all(doc.reference_doctype, {'subscription': doc.name}) + self.assertEquals(len(docnames), 1) + + doc = frappe.get_doc('Subscription', doc.name) + doc.disabled = 0 + doc.save() + + months = get_months(getdate(start_date), getdate(today())) + make_subscription_entry() + + docnames = frappe.get_all(doc.reference_doctype, {'subscription': doc.name}) + self.assertEquals(len(docnames), months) + +quotation_records = frappe.get_test_records('Quotation') + +def make_subscription(**args): + args = frappe._dict(args) + doc = frappe.get_doc({ + 'doctype': 'Subscription', + 'reference_doctype': args.reference_doctype or 'Quotation', + 'reference_document': args.reference_document or \ + frappe.db.get_value('Quotation', {'docstatus': 1}, 'name'), + 'frequency': args.frequency or 'Daily', + 'start_date': args.start_date or add_days(today(), -1), + 'end_date': args.end_date or add_days(today(), 1), + 'submit_on_creation': args.submit_on_creation or 0 + }).insert(ignore_permissions=True) + + if not args.do_not_submit: + doc.submit() + + return doc \ No newline at end of file diff --git a/erpnext/accounts/doctype/tax_rule/tax_rule.py b/erpnext/accounts/doctype/tax_rule/tax_rule.py index 7324532a397..2d91a3c85f0 100644 --- a/erpnext/accounts/doctype/tax_rule/tax_rule.py +++ b/erpnext/accounts/doctype/tax_rule/tax_rule.py @@ -135,7 +135,8 @@ def get_tax_template(posting_date, args): for key, value in args.iteritems(): if key=="use_for_shopping_cart": conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0)) - if key == 'customer_group' and value: + if key == 'customer_group': + if not value: value = _("All Customer Groups") customer_group_condition = get_customer_group_condition(value) conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition)) else: diff --git a/erpnext/accounts/doctype/tax_rule/test_tax_rule.py b/erpnext/accounts/doctype/tax_rule/test_tax_rule.py index 383b02b1287..5ad7970a6e3 100644 --- a/erpnext/accounts/doctype/tax_rule/test_tax_rule.py +++ b/erpnext/accounts/doctype/tax_rule/test_tax_rule.py @@ -39,7 +39,7 @@ class TestTaxRule(unittest.TestCase): sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01") tax_rule1.save() - self.assertEquals(get_tax_template("2015-01-01", {"customer_group" : "Commercial"}), + self.assertEquals(get_tax_template("2015-01-01", {"customer_group" : "Commercial", "use_for_shopping_cart":0}), "_Test Sales Taxes and Charges Template") def test_conflict_with_overlapping_dates(self): diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index ceae61cd869..c575d59ae74 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -136,14 +136,7 @@ def round_off_debit_credit(gl_map): make_round_off_gle(gl_map, debit_credit_diff) def make_round_off_gle(gl_map, debit_credit_diff): - round_off_account, round_off_cost_center = frappe.db.get_value("Company", gl_map[0].company, - ["round_off_account", "round_off_cost_center"]) or [None, None] - if not round_off_account: - frappe.throw(_("Please mention Round Off Account in Company")) - - if not round_off_cost_center: - frappe.throw(_("Please mention Round Off Cost Center in Company")) - + round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(gl_map[0].company) round_off_gle = frappe._dict() for k in ["voucher_type", "voucher_no", "company", @@ -165,6 +158,17 @@ def make_round_off_gle(gl_map, debit_credit_diff): gl_map.append(round_off_gle) +def get_round_off_account_and_cost_center(company): + round_off_account, round_off_cost_center = frappe.db.get_value("Company", company, + ["round_off_account", "round_off_cost_center"]) or [None, None] + if not round_off_account: + frappe.throw(_("Please mention Round Off Account in Company")) + + if not round_off_cost_center: + frappe.throw(_("Please mention Round Off Cost Center in Company")) + + return round_off_account, round_off_cost_center + def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None, adv_adj=False, update_outstanding="Yes"): diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 0416b193d7a..be0b6f7b727 100644 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -8,7 +8,16 @@ frappe.pages['pos'].on_page_load = function (wrapper) { single_column: true }); - wrapper.pos = new erpnext.pos.PointOfSale(wrapper) + frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'is_online', (r) => { + if (r && r.use_pos_in_offline_mode && cint(r.use_pos_in_offline_mode)) { + // offline + wrapper.pos = new erpnext.pos.PointOfSale(wrapper); + cur_pos = wrapper.pos; + } else { + // online + frappe.set_route('point-of-sale'); + } + }); } frappe.pages['pos'].refresh = function (wrapper) { diff --git a/erpnext/accounts/page/pos/test_pos.js b/erpnext/accounts/page/pos/test_pos.js index bc5edc9f2a4..8913a9e1cc0 100644 --- a/erpnext/accounts/page/pos/test_pos.js +++ b/erpnext/accounts/page/pos/test_pos.js @@ -1,16 +1,15 @@ -QUnit.test("test:POS Profile", function(assert) { - assert.expect(1); +QUnit.test("test:Sales Invoice", function(assert) { + assert.expect(3); let done = assert.async(); frappe.run_serially([ () => { return frappe.tests.make("POS Profile", [ {naming_series: "SINV"}, - {company: "Test Company"}, {country: "India"}, {currency: "INR"}, - {write_off_account: "Write Off - TC"}, - {write_off_cost_center: "Main - TC"}, + {write_off_account: "Write Off - FT"}, + {write_off_cost_center: "Main - FT"}, {payments: [ [ {"default": 1}, @@ -24,19 +23,10 @@ QUnit.test("test:POS Profile", function(assert) { () => { assert.equal(cur_frm.doc.payments[0].default, 1, "Default mode of payment tested"); }, - () => done() - ]); -}); - -QUnit.test("test:Sales Invoice", function(assert) { - assert.expect(2); - let done = assert.async(); - - frappe.run_serially([ + () => frappe.timeout(1), () => { return frappe.tests.make("Sales Invoice", [ {customer: "Test Customer 2"}, - {company: "Test Company"}, {is_pos: 1}, {posting_date: frappe.datetime.get_today()}, {due_date: frappe.datetime.get_today()}, @@ -44,7 +34,7 @@ QUnit.test("test:Sales Invoice", function(assert) { [ {"item_code": "Test Product 1"}, {"qty": 5}, - {"warehouse":'Stores - TC'} + {"warehouse":'Stores - FT'} ]] } ]); diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index d81c1ebc88e..b0c49dfbd81 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -142,10 +142,16 @@ def get_data(company, root_type, balance_must_be, period_list, filters=None, return out + def calculate_values(accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy): for entries in gl_entries_by_account.values(): for entry in entries: d = accounts_by_name.get(entry.account) + if not d: + frappe.msgprint( + _("Could not retrieve information for {0}.".format(entry.account)), title="Error", + raise_exception=1 + ) for period in period_list: # check if posting date is within the period diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index e2106e2d628..07f6979c40c 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -17,7 +17,6 @@ def execute(filters=None): gross_profit_data = GrossProfitGenerator(filters) data = [] - source = gross_profit_data.grouped_data if filters.get("group_by") != "Invoice" else gross_profit_data.data group_wise_columns = frappe._dict({ "invoice": ["parent", "customer", "customer_group", "posting_date","item_code", "item_name","item_group", "brand", "description", \ @@ -45,7 +44,7 @@ def execute(filters=None): columns = get_columns(group_wise_columns, filters) - for src in source: + for src in gross_profit_data.grouped_data: row = [] for col in group_wise_columns.get(scrub(filters.group_by)): row.append(src.get(col)) @@ -103,6 +102,7 @@ class GrossProfitGenerator(object): self.load_stock_ledger_entries() self.load_product_bundle() self.load_non_stock_items() + self.get_returned_invoice_items() self.process() def process(self): @@ -143,40 +143,68 @@ class GrossProfitGenerator(object): row.gross_profit_percent = 0.0 # add to grouped - if self.filters.group_by != "Invoice": - self.grouped.setdefault(row.get(scrub(self.filters.group_by)), []).append(row) - - self.data.append(row) + self.grouped.setdefault(row.get(scrub(self.filters.group_by)), []).append(row) if self.grouped: self.get_average_rate_based_on_group_by() - else: - self.grouped_data = [] def get_average_rate_based_on_group_by(self): # sum buying / selling totals for group self.grouped_data = [] for key in self.grouped.keys(): - for i, row in enumerate(self.grouped[key]): - if i==0: - new_row = row - else: - new_row.qty += row.qty - new_row.buying_amount += row.buying_amount - new_row.base_amount += row.base_amount + if self.filters.get("group_by") != "Invoice": + for i, row in enumerate(self.grouped[key]): + if i==0: + new_row = row + else: + new_row.qty += row.qty + new_row.buying_amount += row.buying_amount + new_row.base_amount += row.base_amount + new_row = self.set_average_rate(new_row) + self.grouped_data.append(new_row) + else: + for i, row in enumerate(self.grouped[key]): + if row.parent in self.returned_invoices \ + and row.item_code in self.returned_invoices[row.parent]: + returned_item_rows = self.returned_invoices[row.parent][row.item_code] + for returned_item_row in returned_item_rows: + row.qty += returned_item_row.qty + row.base_amount += returned_item_row.base_amount + row.buying_amount = row.qty * row.buying_rate + if row.qty: + row = self.set_average_rate(row) + self.grouped_data.append(row) - new_row.gross_profit = new_row.base_amount - new_row.buying_amount - new_row.gross_profit_percent = ((new_row.gross_profit / new_row.base_amount) * 100.0) \ - if new_row.base_amount else 0 - new_row.buying_rate = (new_row.buying_amount / new_row.qty) \ - if new_row.qty else 0 - new_row.base_rate = (new_row.base_amount / new_row.qty) \ - if new_row.qty else 0 + def set_average_rate(self, new_row): + new_row.gross_profit = new_row.base_amount - new_row.buying_amount + new_row.gross_profit_percent = ((new_row.gross_profit / new_row.base_amount) * 100.0) \ + if new_row.base_amount else 0 + new_row.buying_rate = (new_row.buying_amount / new_row.qty) if new_row.qty else 0 + new_row.base_rate = (new_row.base_amount / new_row.qty) if new_row.qty else 0 + return new_row - self.grouped_data.append(new_row) + def get_returned_invoice_items(self): + returned_invoices = frappe.db.sql(""" + select + si.name, si_item.item_code, si_item.qty, si_item.base_amount, si.return_against + from + `tabSales Invoice` si, `tabSales Invoice Item` si_item + where + si.name = si_item.parent + and si.docstatus = 1 + and si.is_return = 1 + """, as_dict=1) + + self.returned_invoices = frappe._dict() + for inv in returned_invoices: + self.returned_invoices.setdefault(inv.return_against, frappe._dict())\ + .setdefault(inv.item_code, []).append(inv) def skip_row(self, row, product_bundles): - if self.filters.get("group_by") != "Invoice" and not row.get(scrub(self.filters.get("group_by"))): + if self.filters.get("group_by") != "Invoice": + if not row.get(scrub(self.filters.get("group_by"))): + return True + elif row.get("is_return") == 1: return True def get_buying_amount_from_product_bundle(self, row, product_bundle): @@ -268,20 +296,26 @@ class GrossProfitGenerator(object): sales_person_cols = "" sales_team_table = "" - self.si_list = frappe.db.sql("""select `tabSales Invoice Item`.parenttype, `tabSales Invoice Item`.parent, - `tabSales Invoice`.posting_date, `tabSales Invoice`.posting_time, `tabSales Invoice`.project, `tabSales Invoice`.update_stock, - `tabSales Invoice`.customer, `tabSales Invoice`.customer_group, `tabSales Invoice`.territory, - `tabSales Invoice Item`.item_code, `tabSales Invoice Item`.item_name, `tabSales Invoice Item`.description, - `tabSales Invoice Item`.warehouse, `tabSales Invoice Item`.item_group, `tabSales Invoice Item`.brand, - `tabSales Invoice Item`.dn_detail, `tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.stock_qty as qty, - `tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount, `tabSales Invoice Item`.name as "item_row" + self.si_list = frappe.db.sql(""" + select + `tabSales Invoice Item`.parenttype, `tabSales Invoice Item`.parent, + `tabSales Invoice`.posting_date, `tabSales Invoice`.posting_time, + `tabSales Invoice`.project, `tabSales Invoice`.update_stock, + `tabSales Invoice`.customer, `tabSales Invoice`.customer_group, + `tabSales Invoice`.territory, `tabSales Invoice Item`.item_code, + `tabSales Invoice Item`.item_name, `tabSales Invoice Item`.description, + `tabSales Invoice Item`.warehouse, `tabSales Invoice Item`.item_group, + `tabSales Invoice Item`.brand, `tabSales Invoice Item`.dn_detail, + `tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.stock_qty as qty, + `tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount, + `tabSales Invoice Item`.name as "item_row", `tabSales Invoice`.is_return {sales_person_cols} from - `tabSales Invoice` - inner join `tabSales Invoice Item` on `tabSales Invoice Item`.parent = `tabSales Invoice`.name + `tabSales Invoice` inner join `tabSales Invoice Item` + on `tabSales Invoice Item`.parent = `tabSales Invoice`.name {sales_team_table} where - `tabSales Invoice`.docstatus = 1 {conditions} {match_cond} + `tabSales Invoice`.docstatus=1 {conditions} {match_cond} order by `tabSales Invoice`.posting_date desc, `tabSales Invoice`.posting_time desc""" .format(conditions=conditions, sales_person_cols=sales_person_cols, diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index c533e6bbcdd..a51246bcb86 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -13,6 +13,7 @@ frappe.ui.form.on("Purchase Order", { 'Stock Entry': 'Material to Supplier' } }, + onload: function(frm) { erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); @@ -20,8 +21,7 @@ frappe.ui.form.on("Purchase Order", { frm.set_indicator_formatter('item_code', function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" }) - - } + }, }); erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({ @@ -86,8 +86,13 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( if(flt(doc.per_billed)==0 && doc.status != "Delivered") { cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __("Make")); } - cur_frm.page.set_inner_btn_group_as_primary(__("Make")); + if(!doc.subscription) { + cur_frm.add_custom_button(__('Subscription'), function() { + erpnext.utils.make_subscription(doc.doctype, doc.name) + }, __("Make")) + } + cur_frm.page.set_inner_btn_group_as_primary(__("Make")); } }, diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 09c987f0fd8..919707c08d4 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -2102,6 +2102,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "base_rounding_adjustment", + "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": "Rounding Adjustment (Company Currency)", + "length": 0, + "no_copy": 1, + "options": "Company:company:default_currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2227,6 +2258,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "rounding_adjustment", + "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": "Rounding Adjustment", + "length": 0, + "no_copy": 1, + "options": "currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2856,6 +2918,67 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription", + "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": "Subscription", + "length": 0, + "no_copy": 1, + "options": "Subscription", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2866,7 +2989,7 @@ "depends_on": "eval:doc.docstatus<2 && !doc.__islocal", "fieldname": "recurring_order", "fieldtype": "Section Break", - "hidden": 0, + "hidden": 1, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -3335,7 +3458,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-07-19 14:03:51.838328", + "modified": "2017-09-19 11:22:30.190589", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py index df10a541df2..d57b0e2568f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py @@ -5,7 +5,8 @@ def get_data(): 'fieldname': 'purchase_order', 'non_standard_fieldnames': { 'Journal Entry': 'reference_name', - 'Payment Entry': 'reference_name' + 'Payment Entry': 'reference_name', + 'Subscription': 'reference_document' }, 'internal_links': { 'Material Request': ['items', 'material_request'], @@ -23,11 +24,11 @@ def get_data(): }, { 'label': _('Reference'), - 'items': ['Material Request', 'Supplier Quotation', 'Project'] + 'items': ['Material Request', 'Supplier Quotation', 'Project', 'Subscription'] }, { 'label': _('Sub-contracting'), 'items': ['Stock Entry'] - } + }, ] } diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.js b/erpnext/buying/doctype/purchase_order/test_purchase_order.js new file mode 100644 index 00000000000..e9db270b4fd --- /dev/null +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Purchase Order", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially('Purchase Order', [ + // insert a new Purchase Order + () => frappe.tests.make([ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js index 46e8d1f2d69..a4d68aa946d 100644 --- a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js @@ -65,7 +65,7 @@ QUnit.test("test: request_for_quotation", function(assert) { assert.ok(cur_frm.doc.docstatus == 1, "Quotation request submitted"); }, () => frappe.click_button('Send Supplier Emails'), - () => frappe.timeout(3), + () => frappe.timeout(4), () => { assert.ok($('div.modal.fade.in > div.modal-dialog > div > div.modal-body.ui-front > div.msgprint').text().includes("Email sent to supplier Test Supplier"), "Send emails working"); }, diff --git a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js index f831b4f42ff..1a9cd351dc7 100644 --- a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js +++ b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js @@ -12,7 +12,7 @@ QUnit.test("Test: Request for Quotation", function (assert) { () => frappe.new_doc("Request for Quotation"), () => frappe.timeout(1), () => cur_frm.set_value("transaction_date", "04-04-2017"), - () => cur_frm.set_value("company", "_Test Company"), + () => cur_frm.set_value("company", "For Testing"), // Add Suppliers () => { cur_frm.fields_dict.suppliers.grid.grid_rows[0].toggle_view(); @@ -62,7 +62,7 @@ QUnit.test("Test: Request for Quotation", function (assert) { }, () => frappe.timeout(2), () => { - cur_frm.fields_dict.items.grid.grid_rows[0].doc.warehouse = "_Test Warehouse - _TC"; + cur_frm.fields_dict.items.grid.grid_rows[0].doc.warehouse = "_Test Warehouse - FT"; }, () => frappe.click_button('Save'), () => frappe.timeout(1), @@ -104,7 +104,7 @@ QUnit.test("Test: Request for Quotation", function (assert) { () => frappe.timeout(1), () => frappe.click_button('Make Supplier Quotation'), () => frappe.timeout(1), - () => cur_frm.set_value("company", "_Test Company"), + () => cur_frm.set_value("company", "For Testing"), () => cur_frm.fields_dict.items.grid.grid_rows[0].doc.rate = 4.99, () => frappe.timeout(1), () => frappe.click_button('Save'), diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index b2b90637b6a..c715fbd2a62 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -16,7 +16,7 @@ class Supplier(TransactionBase): def onload(self): """Load address and contacts in `__onload`""" - load_address_and_contact(self, "supplier") + load_address_and_contact(self) self.load_dashboard_info() def load_dashboard_info(self): diff --git a/erpnext/buying/doctype/supplier/test_supplier.js b/erpnext/buying/doctype/supplier/test_supplier.js index a953a8dd135..99a5bc616dc 100644 --- a/erpnext/buying/doctype/supplier/test_supplier.js +++ b/erpnext/buying/doctype/supplier/test_supplier.js @@ -13,8 +13,8 @@ QUnit.test("test: supplier", function(assert) { {credit_days_based_on: 'Fixed Days'}, {accounts: [ [ - {'company': "Test Company"}, - {'account': "Creditors - TC"} + {'company': "For Testing"}, + {'account': "Creditors - FT"} ]] } ]); @@ -68,7 +68,7 @@ QUnit.test("test: supplier", function(assert) { assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Name correct"); assert.ok(cur_frm.doc.supplier_type == 'Hardware', "Type correct"); assert.ok(cur_frm.doc.default_currency == 'INR', "Currency correct"); - assert.ok(cur_frm.doc.accounts[0].account == 'Creditors - '+frappe.get_abbr('Test Company'), " Account Head abbr correct"); + assert.ok(cur_frm.doc.accounts[0].account == 'Creditors - '+frappe.get_abbr('For Testing'), " Account Head abbr correct"); assert.ok($('.address-box:nth-child(3) p').text().includes('Shipping City 3'), "Address correct"); assert.ok($('.col-sm-6+ .col-sm-6 .h6').text().includes('Contact 3'), "Contact correct"); }, diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js index 3767248e369..3899bbab114 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js @@ -22,7 +22,9 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext cur_frm.page.set_inner_btn_group_as_primary(__("Make")); cur_frm.add_custom_button(__("Quotation"), this.make_quotation, __("Make")); - + cur_frm.add_custom_button(__('Subscription'), function() { + erpnext.utils.make_subscription(me.frm.doc.doctype, me.frm.doc.name) + }, __("Make")) } else if (this.frm.doc.docstatus===0) { diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index ea3ae74dfe7..d7edc01f8bf 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -1676,6 +1676,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "base_rounding_adjustment", + "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": "Rounding Adjustment (Company Currency", + "length": 0, + "no_copy": 1, + "options": "Company:company:default_currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1801,6 +1832,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "rounding_adjustment", + "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": "Rounding Adjustment", + "length": 0, + "no_copy": 1, + "options": "currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2051,6 +2113,67 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription", + "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": "Subscription", + "length": 0, + "no_copy": 1, + "options": "Subscription", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2247,7 +2370,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-07-19 13:51:18.929697", + "modified": "2017-09-19 11:23:25.268924", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py index df69063aae6..4321f27f2af 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py @@ -3,6 +3,9 @@ from frappe import _ def get_data(): return { 'fieldname': 'supplier_quotation', + 'non_standard_fieldnames': { + 'Subscription': 'reference_document' + }, 'internal_links': { 'Material Request': ['items', 'material_request'], 'Request for Quotation': ['items', 'request_for_quotation'], @@ -17,6 +20,10 @@ def get_data(): 'label': _('Reference'), 'items': ['Material Request', 'Request for Quotation', 'Project'] }, + { + 'label': _('Subscription'), + 'items': ['Subscription'] + }, ] } diff --git a/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.js new file mode 100644 index 00000000000..7097a6dcb2b --- /dev/null +++ b/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Supplier Quotation", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially('Supplier Quotation', [ + // insert a new Supplier Quotation + () => frappe.tests.make([ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js index bc07b753b33..b151824ba68 100644 --- a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js +++ b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js @@ -8,13 +8,13 @@ QUnit.test("test: supplier quotation with item wise discount", function(assert){ () => { return frappe.tests.make('Supplier Quotation', [ {supplier: 'Test Supplier'}, - {company: 'Test Company'}, + {company: 'For Testing'}, {items: [ [ {"item_code": 'Test Product 4'}, {"qty": 5}, {"uom": 'Unit'}, - {"warehouse": 'All Warehouses - TC'}, + {"warehouse": 'All Warehouses - FT'}, {'discount_percentage': 10}, ] ]} diff --git a/erpnext/change_log/v9/v9_0_0.md b/erpnext/change_log/v9/v9_0_0.md new file mode 100644 index 00000000000..5d91ea9f4e1 --- /dev/null +++ b/erpnext/change_log/v9/v9_0_0.md @@ -0,0 +1,30 @@ +- POS - Online & Offline +- Now user has an option to enable or disable Offline POS mode from POS Settings +- Provision to select the Item's serial number from the dropdown while adding item in the cart +- Indicator for stock availability in Online POS Mode. + +#### Subscription +- Setup recurring documents using **Subscription** +- User can schedule the subscription for doctypes other than Sales Invoice, Purchase Invoice etc. + +#### Healthcare Domain +- Clinic / Practice Management + - Patient + - Physician, Physician scheduling + - Appointment + - Vital Signs + - Consultation + - Medical Code Standards + - Patient Medical Record +- Laboratory + - Sample Collection + - Lab Test +- Patient Portal + +#### School Fees Management +- Fee Structure +- Fee Schedule +- Payment against Fees + +#### Setup Wizard +- Broken into 2 parts with a fresh looks \ No newline at end of file diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py index 6d16e9202e6..7c0f540154b 100644 --- a/erpnext/config/accounts.py +++ b/erpnext/config/accounts.py @@ -32,6 +32,12 @@ def get_data(): "label": _("POS"), "description": _("Point of Sale") }, + { + "type": "doctype", + "name": "Subscription", + "label": _("Subscription"), + "description": _("To make recurring documents") + }, { "type": "report", "name": "Accounts Receivable", diff --git a/erpnext/config/desktop.py b/erpnext/config/desktop.py index 029ef747407..ef1ff103fa7 100644 --- a/erpnext/config/desktop.py +++ b/erpnext/config/desktop.py @@ -261,5 +261,12 @@ def get_data(): "icon": "octicon octicon-mortar-board", "type": "module", "label": _("Schools") + }, + { + "module_name": "Healthcare", + "color": "#FF888B", + "icon": "octicon octicon-plus", + "type": "module", + "label": _("Healthcare") } ] diff --git a/erpnext/config/healthcare.py b/erpnext/config/healthcare.py new file mode 100644 index 00000000000..f4bf4f7bd23 --- /dev/null +++ b/erpnext/config/healthcare.py @@ -0,0 +1,157 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + + return [ + { + "label": _("Consultation"), + "icon": "icon-star", + "items": [ + { + "type": "doctype", + "name": "Patient Appointment", + "description": _("Patient Appointment"), + }, + { + "type": "doctype", + "name": "Consultation", + "label": _("Consultation"), + }, + { + "type": "doctype", + "name": "Vital Signs", + "label": _("Vital Signs"), + "description": _("Record Patient Vitals"), + }, + { + "type": "page", + "name": "medical_record", + "label": _("Patient Medical Record"), + }, + { + "type": "page", + "name": "appointment-analytic", + "label": _("Appointment Analytics"), + } + ] + }, + { + "label": _("Laboratory"), + "icon": "icon-list", + "items": [ + { + "type": "doctype", + "name": "Lab Test", + "description": _("Results"), + }, + { + "type": "doctype", + "name": "Sample Collection", + "label": _("Sample Collection"), + }, + { + "type": "report", + "name": "Lab Test Report", + "is_query_report": True + } + ] + }, + { + "label": _("Masters"), + "icon": "icon-list", + "items": [ + { + "type": "doctype", + "name": "Patient", + "label": _("Patient"), + }, + { + "type": "doctype", + "name": "Physician", + "label": "Physician", + }, + { + "type": "doctype", + "name": "Physician Schedule", + "label": _("Physician Schedule"), + }, + { + "type": "doctype", + "name": "Medical Code Standard", + "label": _("Medical Code Standard"), + }, + { + "type": "doctype", + "name": "Medical Code", + "label": _("Medical Code"), + } + ] + }, + { + "label": _("Setup"), + "icon": "icon-cog", + "items": [ + { + "type": "doctype", + "name": "Healthcare Settings", + "label": _("Healthcare Settings"), + }, + { + "type": "doctype", + "name": "Medical Department", + "label": "Medical Department" + }, + { + "type": "doctype", + "name": "Appointment Type", + "description": _("Appointment Type Master"), + }, + { + "type": "doctype", + "name": "Prescription Dosage", + "description": _("Prescription Dosage") + }, + { + "type": "doctype", + "name": "Prescription Duration", + "description": _("Prescription Period") + }, + { + "type": "doctype", + "name": "Complaint", + "description": _("Complaint") + }, + { + "type": "doctype", + "name": "Diagnosis", + "description": _("Diagnosis") + }, + { + "type": "doctype", + "name": "Lab Test Sample", + "description": _("Test Sample Master."), + }, + { + "type": "doctype", + "name": "Lab Test UOM", + "description": _("Lab Test UOM.") + }, + { + "type": "doctype", + "name": "Antibiotic", + "description": _("Antibiotic.") + }, + { + "type": "doctype", + "name": "Sensitivity", + "description": _("Sensitivity Naming.") + }, + { + "type": "doctype", + "name": "Lab Test Template", + "description": _("Lab Test Configurations.") + } + ] + } + ] diff --git a/erpnext/config/schools.py b/erpnext/config/schools.py index b984578ca1e..dbdcd3561d4 100644 --- a/erpnext/config/schools.py +++ b/erpnext/config/schools.py @@ -154,6 +154,10 @@ def get_data(): "type": "doctype", "name": "Fees" }, + { + "type": "doctype", + "name": "Fee Schedule" + }, { "type": "doctype", "name": "Fee Structure" diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index b24e047bcc0..d04143d77d6 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -8,7 +8,6 @@ from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate from erpnext.setup.utils import get_exchange_rate from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency from erpnext.utilities.transaction_base import TransactionBase -from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document from erpnext.controllers.sales_and_purchase_return import validate_return from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled from erpnext.exceptions import InvalidCurrency @@ -53,13 +52,6 @@ class AccountsController(TransactionBase): self.validate_party() self.validate_currency() - if self.meta.get_field("is_recurring"): - if self.amended_from and self.recurring_id == self.amended_from: - self.recurring_id = None - if not self.get("__islocal"): - validate_recurring_document(self) - convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date")) - if self.doctype == 'Purchase Invoice': self.validate_paid_amount() @@ -84,11 +76,6 @@ class AccountsController(TransactionBase): else: frappe.db.set(self,'paid_amount',0) - def on_update_after_submit(self): - if self.meta.get_field("is_recurring"): - validate_recurring_document(self) - convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date")) - def set_missing_values(self, for_validate=False): if frappe.flags.in_test: for fieldname in ["posting_date","transaction_date"]: diff --git a/erpnext/controllers/recurring_document.py b/erpnext/controllers/recurring_document.py deleted file mode 100644 index 713e9bac2f1..00000000000 --- a/erpnext/controllers/recurring_document.py +++ /dev/null @@ -1,230 +0,0 @@ -from __future__ import unicode_literals -import frappe -import calendar -import frappe.utils -import frappe.defaults - -from frappe.utils import cint, cstr, getdate, nowdate, \ - get_first_day, get_last_day, split_emails - -from frappe import _, msgprint, throw - -month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12} -date_field_map = { - "Sales Order": "transaction_date", - "Sales Invoice": "posting_date", - "Purchase Order": "transaction_date", - "Purchase Invoice": "posting_date" -} - -def create_recurring_documents(): - manage_recurring_documents("Sales Order") - manage_recurring_documents("Sales Invoice") - manage_recurring_documents("Purchase Order") - manage_recurring_documents("Purchase Invoice") - -def manage_recurring_documents(doctype, next_date=None, commit=True): - """ - Create recurring documents on specific date by copying the original one - and notify the concerned people - """ - next_date = next_date or nowdate() - - date_field = date_field_map[doctype] - - condition = " and ifnull(status, '') != 'Closed'" if doctype in ("Sales Order", "Purchase Order") else "" - - recurring_documents = frappe.db.sql("""select name, recurring_id - from `tab{0}` where is_recurring=1 - and (docstatus=1 or docstatus=0) and next_date=%s - and next_date <= ifnull(end_date, '2199-12-31') {1}""".format(doctype, condition), next_date) - - exception_list = [] - for ref_document, recurring_id in recurring_documents: - if not frappe.db.sql("""select name from `tab%s` - where %s=%s and recurring_id=%s and (docstatus=1 or docstatus=0)""" - % (doctype, date_field, '%s', '%s'), (next_date, recurring_id)): - try: - reference_doc = frappe.get_doc(doctype, ref_document) - new_doc = make_new_document(reference_doc, date_field, next_date) - if reference_doc.notify_by_email: - send_notification(new_doc) - if commit: - frappe.db.commit() - except: - if commit: - frappe.db.rollback() - - frappe.db.begin() - frappe.db.sql("update `tab%s` \ - set is_recurring = 0 where name = %s" % (doctype, '%s'), - (ref_document)) - notify_errors(ref_document, doctype, reference_doc.get("customer") or reference_doc.get("supplier"), - reference_doc.owner) - frappe.db.commit() - - exception_list.append(frappe.get_traceback()) - finally: - if commit: - frappe.db.begin() - - if exception_list: - exception_message = "\n\n".join([cstr(d) for d in exception_list]) - frappe.throw(exception_message) - -def make_new_document(reference_doc, date_field, posting_date): - new_document = frappe.copy_doc(reference_doc, ignore_no_copy=False) - mcount = month_map[reference_doc.recurring_type] - - from_date = get_next_date(reference_doc.from_date, mcount) - - # get last day of the month to maintain period if the from date is first day of its own month - # and to date is the last day of its own month - if (cstr(get_first_day(reference_doc.from_date)) == cstr(reference_doc.from_date)) and \ - (cstr(get_last_day(reference_doc.to_date)) == cstr(reference_doc.to_date)): - to_date = get_last_day(get_next_date(reference_doc.to_date, mcount)) - else: - to_date = get_next_date(reference_doc.to_date, mcount) - - new_document.update({ - date_field: posting_date, - "from_date": from_date, - "to_date": to_date, - "next_date": get_next_date(reference_doc.next_date, mcount,cint(reference_doc.repeat_on_day_of_month)) - }) - - if new_document.meta.get_field('set_posting_time'): - new_document.set('set_posting_time', 1) - - # copy document fields - for fieldname in ("owner", "recurring_type", "repeat_on_day_of_month", - "recurring_id", "notification_email_address", "is_recurring", "end_date", - "title", "naming_series", "select_print_heading", "ignore_pricing_rule", - "posting_time", "remarks", 'submit_on_creation'): - if new_document.meta.get_field(fieldname): - new_document.set(fieldname, reference_doc.get(fieldname)) - - # copy item fields - for i, item in enumerate(new_document.items): - for fieldname in ("page_break",): - item.set(fieldname, reference_doc.items[i].get(fieldname)) - - new_document.run_method("on_recurring", reference_doc=reference_doc) - - if reference_doc.submit_on_creation: - new_document.insert() - new_document.submit() - else: - new_document.docstatus=0 - new_document.insert() - - return new_document - -def get_next_date(dt, mcount, day=None): - dt = getdate(dt) - - from dateutil.relativedelta import relativedelta - dt += relativedelta(months=mcount, day=day) - - return dt - -def send_notification(new_rv): - """Notify concerned persons about recurring document generation""" - - frappe.sendmail(new_rv.notification_email_address, - subject= _("New {0}: #{1}").format(new_rv.doctype, new_rv.name), - message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name), - attachments = [frappe.attach_print(new_rv.doctype, new_rv.name, file_name=new_rv.name, print_format=new_rv.recurring_print_format)]) - -def notify_errors(doc, doctype, party, owner): - from frappe.utils.user import get_system_managers - recipients = get_system_managers(only_name=True) - - frappe.sendmail(recipients + [frappe.db.get_value("User", owner, "email")], - subject="[Urgent] Error while creating recurring %s for %s" % (doctype, doc), - message = frappe.get_template("templates/emails/recurring_document_failed.html").render({ - "type": doctype, - "name": doc, - "party": party - })) - - assign_task_to_owner(doc, doctype, "Recurring Invoice Failed", recipients) - -def assign_task_to_owner(doc, doctype, msg, users): - for d in users: - from frappe.desk.form import assign_to - args = { - 'assign_to' : d, - 'doctype' : doctype, - 'name' : doc, - 'description' : msg, - 'priority' : 'High' - } - assign_to.add(args) - -def validate_recurring_document(doc): - if doc.is_recurring: - validate_notification_email_id(doc) - if not doc.recurring_type: - frappe.throw(_("Please select {0}").format(doc.meta.get_label("recurring_type"))) - - elif not (doc.from_date and doc.to_date): - frappe.throw(_("Period From and Period To dates mandatory for recurring {0}").format(doc.doctype)) - -def validate_recurring_next_date(doc): - posting_date = doc.get("posting_date") or doc.get("transaction_date") - if getdate(posting_date) > getdate(doc.next_date): - frappe.throw(_("Next Date must be greater than Posting Date")) - - next_date = getdate(doc.next_date) - if next_date.day != doc.repeat_on_day_of_month: - - # if the repeat day is the last day of the month (31) - # and the current month does not have as many days, - # then the last day of the current month is a valid date - lastday = calendar.monthrange(next_date.year, next_date.month)[1] - if doc.repeat_on_day_of_month < lastday: - - # the specified day of the month is not same as the day specified - # or the last day of the month - frappe.throw(_("Next Date's day and Repeat on Day of Month must be equal")) - -def convert_to_recurring(doc, posting_date): - if doc.is_recurring: - if not doc.recurring_id: - doc.db_set("recurring_id", doc.name) - - set_next_date(doc, posting_date) - - if doc.next_date: - validate_recurring_next_date(doc) - - elif doc.recurring_id: - doc.db_set("recurring_id", None) - -def validate_notification_email_id(doc): - if doc.notify_by_email: - if doc.notification_email_address: - email_list = split_emails(doc.notification_email_address.replace("\n", "")) - - from frappe.utils import validate_email_add - for email in email_list: - if not validate_email_add(email): - throw(_("{0} is an invalid email address in 'Notification \ - Email Address'").format(email)) - - else: - frappe.throw(_("'Notification Email Addresses' not specified for recurring %s") \ - % doc.doctype) - -def set_next_date(doc, posting_date): - """ Set next date on which recurring document will be created""" - if not doc.repeat_on_day_of_month: - msgprint(_("Please enter 'Repeat on Day of Month' field value"), raise_exception=1) - - next_date = get_next_date(posting_date, month_map[doc.recurring_type], - cint(doc.repeat_on_day_of_month)) - - doc.db_set('next_date', next_date) - - msgprint(_("Next Recurring {0} will be created on {1}").format(doc.doctype, next_date)) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index c627664ea5b..e85e56b4d6c 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -121,9 +121,10 @@ class calculate_taxes_and_totals(object): cumulated_tax_fraction += tax.tax_fraction_for_current_item if cumulated_tax_fraction and not self.discount_amount_applied and item.qty: - item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction), item.precision("net_amount")) + item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction)) item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate")) - item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage")) + item.discount_percentage = flt(item.discount_percentage, + item.precision("discount_percentage")) self._set_in_company_currency(item, ["net_rate", "net_amount"]) @@ -173,6 +174,7 @@ class calculate_taxes_and_totals(object): self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"]) def calculate_taxes(self): + self.doc.rounding_adjustment = 0 # maintain actual tax rate based on idx actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))] for tax in self.doc.get("taxes") if tax.charge_type == "Actual"]) @@ -222,7 +224,9 @@ class calculate_taxes_and_totals(object): # adjust Discount Amount loss in last tax iteration if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \ and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total": - self.adjust_discount_amount_loss(tax) + self.doc.rounding_adjustment = flt(self.doc.grand_total + - flt(self.doc.discount_amount) - tax.total, + self.doc.precision("rounding_adjustment")) def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax): # if just for valuation, do not add the tax amount in total @@ -277,36 +281,26 @@ class calculate_taxes_and_totals(object): tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount")) - def adjust_discount_amount_loss(self, tax): - discount_amount_loss = self.doc.grand_total - flt(self.doc.discount_amount) - tax.total - tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount + - discount_amount_loss, tax.precision("tax_amount")) - tax.total = flt(tax.total + discount_amount_loss, tax.precision("total")) - - self._set_in_company_currency(tax, ["total", "tax_amount_after_discount_amount"]) - def manipulate_grand_total_for_inclusive_tax(self): # if fully inclusive taxes and diff - if self.doc.get("taxes") and all(cint(t.included_in_print_rate) for t in self.doc.get("taxes")): + if self.doc.get("taxes") and any([cint(t.included_in_print_rate) for t in self.doc.get("taxes")]): last_tax = self.doc.get("taxes")[-1] - diff = self.doc.total - flt(last_tax.total, self.doc.precision("grand_total")) - - if diff and abs(diff) <= (2.0 / 10**last_tax.precision("tax_amount")): - last_tax.tax_amount += diff - last_tax.tax_amount_after_discount_amount += diff - last_tax.total += diff - - self._set_in_company_currency(last_tax, - ["total", "tax_amount", "tax_amount_after_discount_amount"]) + non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount) + for d in self.doc.get("taxes") if not d.included_in_print_rate]) + diff = self.doc.total + non_inclusive_tax_amount \ + - flt(last_tax.total, last_tax.precision("total")) + if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")): + self.doc.rounding_adjustment = flt(flt(self.doc.rounding_adjustment) + + flt(diff), self.doc.precision("rounding_adjustment")) def calculate_totals(self): - self.doc.grand_total = flt(self.doc.get("taxes")[-1].total - if self.doc.get("taxes") else self.doc.net_total) + self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \ + if self.doc.get("taxes") else flt(self.doc.net_total) - self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total, - self.doc.precision("total_taxes_and_charges")) + self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total + - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges")) - self._set_in_company_currency(self.doc, ["total_taxes_and_charges"]) + self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"]) if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]: self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \ @@ -326,13 +320,22 @@ class calculate_taxes_and_totals(object): if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \ else self.doc.base_net_total - self._set_in_company_currency(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]) + self._set_in_company_currency(self.doc, + ["taxes_and_charges_added", "taxes_and_charges_deducted"]) self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"]) + self.set_rounded_total() + + def set_rounded_total(self): + if frappe.db.get_single_value("Global Defaults", "disable_rounded_total"): + self.doc.rounded_total = self.doc.base_rounded_total = 0 + return + if self.doc.meta.get_field("rounded_total"): self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")) + if self.doc.meta.get_field("base_rounded_total"): company_currency = erpnext.get_company_currency(self.doc.company) @@ -525,7 +528,7 @@ def get_itemised_tax_breakup_html(doc): for tax in doc.taxes: if getattr(tax, "category", None) and tax.category=="Valuation": continue - if tax.description not in tax_accounts: + if tax.description not in tax_accounts and tax.tax_amount_after_discount_amount: tax_accounts.append(tax.description) headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts) @@ -565,26 +568,21 @@ def get_itemised_tax(taxes): if getattr(tax, "category", None) and tax.category=="Valuation": continue - tax_amount_precision = tax.precision("tax_amount") - tax_rate_precision = tax.precision("rate") - item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {} - - for item_code, tax_data in item_tax_map.items(): - itemised_tax.setdefault(item_code, frappe._dict()) + if item_tax_map: + for item_code, tax_data in item_tax_map.items(): + itemised_tax.setdefault(item_code, frappe._dict()) - if isinstance(tax_data, list): - precision = tax_amount_precision if tax.charge_type == "Actual" else tax_rate_precision - - itemised_tax[item_code][tax.description] = frappe._dict(dict( - tax_rate=flt(tax_data[0]), - tax_amount=flt(tax_data[1]) - )) - else: - itemised_tax[item_code][tax.description] = frappe._dict(dict( - tax_rate=flt(tax_data), - tax_amount=0.0 - )) + if isinstance(tax_data, list): + itemised_tax[item_code][tax.description] = frappe._dict(dict( + tax_rate=flt(tax_data[0]), + tax_amount=flt(tax_data[1]) + )) + else: + itemised_tax[item_code][tax.description] = frappe._dict(dict( + tax_rate=flt(tax_data), + tax_amount=0.0 + )) return itemised_tax diff --git a/erpnext/controllers/tests/test_recurring_document.py b/erpnext/controllers/tests/test_recurring_document.py deleted file mode 100644 index d47c5c77013..00000000000 --- a/erpnext/controllers/tests/test_recurring_document.py +++ /dev/null @@ -1,149 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - -import frappe -import frappe.permissions -from erpnext.controllers.recurring_document import date_field_map -from frappe.utils import get_first_day, get_last_day, add_to_date, nowdate, getdate, add_days - -def test_recurring_document(obj, test_records): - frappe.db.set_value("Print Settings", "Print Settings", "send_print_as_pdf", 1) - today = nowdate() - base_doc = frappe.copy_doc(test_records[0]) - - base_doc.update({ - "is_recurring": 1, - "submit_on_create": 1, - "recurring_type": "Monthly", - "notification_email_address": "test@example.com, test1@example.com, test2@example.com", - "repeat_on_day_of_month": getdate(today).day, - "due_date": None, - "from_date": get_first_day(today), - "to_date": get_last_day(today) - }) - - date_field = date_field_map[base_doc.doctype] - base_doc.set(date_field, today) - - if base_doc.doctype == "Sales Order": - base_doc.set("delivery_date", add_days(today, 15)) - - # monthly - doc1 = frappe.copy_doc(base_doc) - doc1.insert() - doc1.submit() - _test_recurring_document(obj, doc1, date_field, True) - - # monthly without a first and last day period - if getdate(today).day != 1: - doc2 = frappe.copy_doc(base_doc) - doc2.update({ - "from_date": today, - "to_date": add_to_date(today, days=30) - }) - doc2.insert() - doc2.submit() - _test_recurring_document(obj, doc2, date_field, False) - - # quarterly - doc3 = frappe.copy_doc(base_doc) - doc3.update({ - "recurring_type": "Quarterly", - "from_date": get_first_day(today), - "to_date": get_last_day(add_to_date(today, months=3)) - }) - doc3.insert() - doc3.submit() - _test_recurring_document(obj, doc3, date_field, True) - - # quarterly without a first and last day period - doc4 = frappe.copy_doc(base_doc) - doc4.update({ - "recurring_type": "Quarterly", - "from_date": today, - "to_date": add_to_date(today, months=3) - }) - doc4.insert() - doc4.submit() - _test_recurring_document(obj, doc4, date_field, False) - - # yearly - doc5 = frappe.copy_doc(base_doc) - doc5.update({ - "recurring_type": "Yearly", - "from_date": get_first_day(today), - "to_date": get_last_day(add_to_date(today, years=1)) - }) - doc5.insert() - doc5.submit() - _test_recurring_document(obj, doc5, date_field, True) - - # yearly without a first and last day period - doc6 = frappe.copy_doc(base_doc) - doc6.update({ - "recurring_type": "Yearly", - "from_date": today, - "to_date": add_to_date(today, years=1) - }) - doc6.insert() - doc6.submit() - _test_recurring_document(obj, doc6, date_field, False) - - # change date field but keep recurring day to be today - doc7 = frappe.copy_doc(base_doc) - doc7.update({ - date_field: today, - }) - doc7.insert() - doc7.submit() - - # setting so that _test function works - # doc7.set(date_field, today) - _test_recurring_document(obj, doc7, date_field, True) - -def _test_recurring_document(obj, base_doc, date_field, first_and_last_day): - from frappe.utils import add_months, get_last_day - from erpnext.controllers.recurring_document import manage_recurring_documents, \ - get_next_date - - no_of_months = ({"Monthly": 1, "Quarterly": 3, "Yearly": 12})[base_doc.recurring_type] - - def _test(i): - obj.assertEquals(i+1, frappe.db.sql("""select count(*) from `tab%s` - where recurring_id=%s and (docstatus=1 or docstatus=0)""" % (base_doc.doctype, '%s'), - (base_doc.recurring_id))[0][0]) - - next_date = get_next_date(base_doc.get(date_field), no_of_months, - base_doc.repeat_on_day_of_month) - - manage_recurring_documents(base_doc.doctype, next_date=next_date, commit=False) - - recurred_documents = frappe.db.sql("""select name from `tab%s` - where recurring_id=%s and (docstatus=1 or docstatus=0) order by name desc""" - % (base_doc.doctype, '%s'), (base_doc.recurring_id)) - - obj.assertEquals(i+2, len(recurred_documents)) - - new_doc = frappe.get_doc(base_doc.doctype, recurred_documents[0][0]) - - for fieldname in ["is_recurring", "recurring_type", - "repeat_on_day_of_month", "notification_email_address"]: - obj.assertEquals(base_doc.get(fieldname), - new_doc.get(fieldname)) - - obj.assertEquals(new_doc.get(date_field), getdate(next_date)) - - obj.assertEquals(new_doc.from_date, getdate(add_months(base_doc.from_date, no_of_months))) - - if first_and_last_day: - obj.assertEquals(new_doc.to_date, getdate(get_last_day(add_months(base_doc.to_date, no_of_months)))) - else: - obj.assertEquals(new_doc.to_date, getdate(add_months(base_doc.to_date, no_of_months))) - - return new_doc - - # if yearly, test 1 repetition, else test 5 repetitions - count = 1 if (no_of_months == 12) else 5 - for i in xrange(count): - base_doc = _test(i) diff --git a/erpnext/crm/doctype/item/test_item.js b/erpnext/crm/doctype/item/test_item.js index 58cf549fef8..c9b14ca1a96 100644 --- a/erpnext/crm/doctype/item/test_item.js +++ b/erpnext/crm/doctype/item/test_item.js @@ -19,7 +19,7 @@ QUnit.test("test: item", function (assert) { {is_stock_item: is_stock_item}, {standard_rate: keyboard_cost}, {opening_stock: no_of_items_to_stock}, - {default_warehouse: "Stores - RB"} + {default_warehouse: "Stores - FT"} ] ), () => { @@ -45,7 +45,7 @@ QUnit.test("test: item", function (assert) { {is_stock_item: is_stock_item}, {standard_rate: screen_cost}, {opening_stock: no_of_items_to_stock}, - {default_warehouse: "Stores - RB"} + {default_warehouse: "Stores - FT"} ] ), @@ -57,7 +57,7 @@ QUnit.test("test: item", function (assert) { {is_stock_item: is_stock_item}, {standard_rate: CPU_cost}, {opening_stock: no_of_items_to_stock}, - {default_warehouse: "Stores - RB"} + {default_warehouse: "Stores - FT"} ] ), @@ -66,7 +66,7 @@ QUnit.test("test: item", function (assert) { "Item", [ {item_code: "Laptop"}, {item_group: "Products"}, - {default_warehouse: "Stores - RB"} + {default_warehouse: "Stores - FT"} ] ), () => frappe.tests.make( @@ -85,7 +85,7 @@ QUnit.test("test: item", function (assert) { {is_stock_item: is_stock_item}, {standard_rate: scrap_cost}, {opening_stock: no_of_items_to_stock}, - {default_warehouse: "Stores - RB"} + {default_warehouse: "Stores - FT"} ] ), () => frappe.tests.make( diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index a05bacd3f65..eb6e8763e14 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -20,7 +20,7 @@ class Lead(SellingController): def onload(self): customer = frappe.db.get_value("Customer", {"lead_name": self.name}) self.get("__onload").is_customer = customer - load_address_and_contact(self, "lead") + load_address_and_contact(self) def validate(self): self._prev = frappe._dict({ diff --git a/erpnext/demo/data/drug_list.json b/erpnext/demo/data/drug_list.json new file mode 100644 index 00000000000..51b029c1dd7 --- /dev/null +++ b/erpnext/demo/data/drug_list.json @@ -0,0 +1,5420 @@ +[ + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Atocopherol", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Atocopherol", + "item_group": "Drug", + "item_name": "Atocopherol", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:16.577151", + "name": "Atocopherol", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Abacavir", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Abacavir", + "item_group": "Drug", + "item_name": "Abacavir", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:16.678257", + "name": "Abacavir", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Abciximab", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Abciximab", + "item_group": "Drug", + "item_name": "Abciximab", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:16.695413", + "name": "Abciximab", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Acacia", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Acacia", + "item_group": "Drug", + "item_name": "Acacia", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:16.797774", + "name": "Acacia", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Acamprosate", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Acamprosate", + "item_group": "Drug", + "item_name": "Acamprosate", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:16.826952", + "name": "Acamprosate", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Acarbose", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Acarbose", + "item_group": "Drug", + "item_name": "Acarbose", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:16.843890", + "name": "Acarbose", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Acebrofylline", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Acebrofylline", + "item_group": "Drug", + "item_name": "Acebrofylline", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:16.969984", + "name": "Acebrofylline", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Acebrofylline (SR)", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Acebrofylline (SR)", + "item_group": "Drug", + "item_name": "Acebrofylline (SR)", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:16.987354", + "name": "Acebrofylline (SR)", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Aceclofenac", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Aceclofenac", + "item_group": "Drug", + "item_name": "Aceclofenac", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.004369", + "name": "Aceclofenac", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Ash", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Ash", + "item_group": "Drug", + "item_name": "Ash", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.021192", + "name": "Ash", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Asparaginase", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Asparaginase", + "item_group": "Drug", + "item_name": "Asparaginase", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.038058", + "name": "Asparaginase", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Aspartame", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Aspartame", + "item_group": "Drug", + "item_name": "Aspartame", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.054463", + "name": "Aspartame", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Aspartic Acid", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Aspartic Acid", + "item_group": "Drug", + "item_name": "Aspartic Acid", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.071001", + "name": "Aspartic Acid", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Bleomycin", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Bleomycin", + "item_group": "Drug", + "item_name": "Bleomycin", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.087170", + "name": "Bleomycin", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Bleomycin Sulphate", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Bleomycin Sulphate", + "item_group": "Drug", + "item_name": "Bleomycin Sulphate", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.103691", + "name": "Bleomycin Sulphate", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Blue cap contains", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Blue cap contains", + "item_group": "Drug", + "item_name": "Blue cap contains", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.120040", + "name": "Blue cap contains", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Boran", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Boran", + "item_group": "Drug", + "item_name": "Boran", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.135964", + "name": "Boran", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Borax", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Borax", + "item_group": "Drug", + "item_name": "Borax", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.152575", + "name": "Borax", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Chlorbutanol", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Chlorbutanol", + "item_group": "Drug", + "item_name": "Chlorbutanol", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.168998", + "name": "Chlorbutanol", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Chlorbutol", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Chlorbutol", + "item_group": "Drug", + "item_name": "Chlorbutol", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.185316", + "name": "Chlorbutol", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Chlordiazepoxide", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Chlordiazepoxide", + "item_group": "Drug", + "item_name": "Chlordiazepoxide", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.208361", + "name": "Chlordiazepoxide", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Chlordiazepoxide and Clidinium Bromide", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Chlordiazepoxide and Clidinium Bromide", + "item_group": "Drug", + "item_name": "Chlordiazepoxide and Clidinium Bromide", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.224341", + "name": "Chlordiazepoxide and Clidinium Bromide", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Chlorhexidine", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Chlorhexidine", + "item_group": "Drug", + "item_name": "Chlorhexidine", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.240634", + "name": "Chlorhexidine", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Chlorhexidine 40%", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Chlorhexidine 40%", + "item_group": "Drug", + "item_name": "Chlorhexidine 40%", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.256922", + "name": "Chlorhexidine 40%", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Chlorhexidine Acetate", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Chlorhexidine Acetate", + "item_group": "Drug", + "item_name": "Chlorhexidine Acetate", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.274789", + "name": "Chlorhexidine Acetate", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Chlorhexidine Gluconate", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Chlorhexidine Gluconate", + "item_group": "Drug", + "item_name": "Chlorhexidine Gluconate", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.295371", + "name": "Chlorhexidine Gluconate", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Chlorhexidine HCL", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Chlorhexidine HCL", + "item_group": "Drug", + "item_name": "Chlorhexidine HCL", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.312916", + "name": "Chlorhexidine HCL", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Chlorhexidine Hydrochloride", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Chlorhexidine Hydrochloride", + "item_group": "Drug", + "item_name": "Chlorhexidine Hydrochloride", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.329570", + "name": "Chlorhexidine Hydrochloride", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Chloride", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Chloride", + "item_group": "Drug", + "item_name": "Chloride", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.346088", + "name": "Chloride", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Fosfomycin Tromethamine", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Fosfomycin Tromethamine", + "item_group": "Drug", + "item_name": "Fosfomycin Tromethamine", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.362777", + "name": "Fosfomycin Tromethamine", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Fosinopril", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Fosinopril", + "item_group": "Drug", + "item_name": "Fosinopril", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.379465", + "name": "Fosinopril", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Iodochlorhydroxyquinoline", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Iodochlorhydroxyquinoline", + "item_group": "Drug", + "item_name": "Iodochlorhydroxyquinoline", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.396068", + "name": "Iodochlorhydroxyquinoline", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Iodochlorohydroxyquinoline", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Iodochlorohydroxyquinoline", + "item_group": "Drug", + "item_name": "Iodochlorohydroxyquinoline", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.412734", + "name": "Iodochlorohydroxyquinoline", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Ipratropium", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Ipratropium", + "item_group": "Drug", + "item_name": "Ipratropium", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.429333", + "name": "Ipratropium", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Mebeverine hydrochloride", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Mebeverine hydrochloride", + "item_group": "Drug", + "item_name": "Mebeverine hydrochloride", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.445814", + "name": "Mebeverine hydrochloride", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Mecetronium ethylsulphate", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Mecetronium ethylsulphate", + "item_group": "Drug", + "item_name": "Mecetronium ethylsulphate", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.461696", + "name": "Mecetronium ethylsulphate", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Meclizine", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Meclizine", + "item_group": "Drug", + "item_name": "Meclizine", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.478020", + "name": "Meclizine", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Oxaprozin", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Oxaprozin", + "item_group": "Drug", + "item_name": "Oxaprozin", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.496221", + "name": "Oxaprozin", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Oxazepam", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Oxazepam", + "item_group": "Drug", + "item_name": "Oxazepam", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.511933", + "name": "Oxazepam", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Oxcarbazepine", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Oxcarbazepine", + "item_group": "Drug", + "item_name": "Oxcarbazepine", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.528472", + "name": "Oxcarbazepine", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Oxetacaine", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Oxetacaine", + "item_group": "Drug", + "item_name": "Oxetacaine", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.544177", + "name": "Oxetacaine", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Oxethazaine", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Oxethazaine", + "item_group": "Drug", + "item_name": "Oxethazaine", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.560193", + "name": "Oxethazaine", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Suxamethonium Chloride", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Suxamethonium Chloride", + "item_group": "Drug", + "item_name": "Suxamethonium Chloride", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.576447", + "name": "Suxamethonium Chloride", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Tacrolimus", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Tacrolimus", + "item_group": "Drug", + "item_name": "Tacrolimus", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.593481", + "name": "Tacrolimus", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Ubiquinol", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Ubiquinol", + "item_group": "Drug", + "item_name": "Ubiquinol", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.609930", + "name": "Ubiquinol", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Vitamin B12", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Vitamin B12", + "item_group": "Drug", + "item_name": "Vitamin B12", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.626225", + "name": "Vitamin B12", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Vitamin B1Hydrochloride", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Vitamin B1Hydrochloride", + "item_group": "Drug", + "item_name": "Vitamin B1Hydrochloride", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.642423", + "name": "Vitamin B1Hydrochloride", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Vitamin B1Monohydrate", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Vitamin B1Monohydrate", + "item_group": "Drug", + "item_name": "Vitamin B1Monohydrate", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.658946", + "name": "Vitamin B1Monohydrate", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Vitamin B2", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Vitamin B2", + "item_group": "Drug", + "item_name": "Vitamin B2", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.675234", + "name": "Vitamin B2", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Vitamin B3", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Vitamin B3", + "item_group": "Drug", + "item_name": "Vitamin B3", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.691598", + "name": "Vitamin B3", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Vitamin D4", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Vitamin D4", + "item_group": "Drug", + "item_name": "Vitamin D4", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.707840", + "name": "Vitamin D4", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Vitamin E", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Vitamin E", + "item_group": "Drug", + "item_name": "Vitamin E", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.723859", + "name": "Vitamin E", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Wheat Germ Oil", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Wheat Germ Oil", + "item_group": "Drug", + "item_name": "Wheat Germ Oil", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.739829", + "name": "Wheat Germ Oil", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Wheatgrass extr", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Wheatgrass extr", + "item_group": "Drug", + "item_name": "Wheatgrass extr", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.757695", + "name": "Wheatgrass extr", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Whey Protein", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Whey Protein", + "item_group": "Drug", + "item_name": "Whey Protein", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.774098", + "name": "Whey Protein", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Xylometazoline", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Xylometazoline", + "item_group": "Drug", + "item_name": "Xylometazoline", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.790224", + "name": "Xylometazoline", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Xylometazoline Hydrochloride", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Xylometazoline Hydrochloride", + "item_group": "Drug", + "item_name": "Xylometazoline Hydrochloride", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.806359", + "name": "Xylometazoline Hydrochloride", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Yeast", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Yeast", + "item_group": "Drug", + "item_name": "Yeast", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.823305", + "name": "Yeast", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Yellow Fever Vaccine", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Yellow Fever Vaccine", + "item_group": "Drug", + "item_name": "Yellow Fever Vaccine", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.840250", + "name": "Yellow Fever Vaccine", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Zafirlukast", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Zafirlukast", + "item_group": "Drug", + "item_name": "Zafirlukast", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.856856", + "name": "Zafirlukast", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Zaleplon", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Zaleplon", + "item_group": "Drug", + "item_name": "Zaleplon", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.873287", + "name": "Zaleplon", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Zaltoprofen", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Zaltoprofen", + "item_group": "Drug", + "item_name": "Zaltoprofen", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.889263", + "name": "Zaltoprofen", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + }, + { + "asset_category": null, + "attributes": [], + "barcode": null, + "brand": null, + "buying_cost_center": null, + "country_of_origin": null, + "create_new_batch": 0, + "customer_code": "", + "customer_items": [], + "customs_tariff_number": null, + "default_bom": null, + "default_material_request_type": null, + "default_supplier": null, + "default_warehouse": null, + "delivered_by_supplier": 0, + "description": "Zanamivir", + "disabled": 0, + "docstatus": 0, + "doctype": "Item", + "end_of_life": null, + "expense_account": null, + "gst_hsn_code": null, + "has_batch_no": 0, + "has_serial_no": 0, + "has_variants": 0, + "image": null, + "income_account": null, + "inspection_required_before_delivery": 0, + "inspection_required_before_purchase": 0, + "is_fixed_asset": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_stock_item": 1, + "is_sub_contracted_item": 0, + "item_code": "Zanamivir", + "item_group": "Drug", + "item_name": "Zanamivir", + "last_purchase_rate": 0.0, + "lead_time_days": 0, + "manufacturer": null, + "manufacturer_part_no": null, + "max_discount": 0.0, + "min_order_qty": 0.0, + "modified": "2017-07-06 12:53:17.905022", + "name": "Zanamivir", + "naming_series": null, + "net_weight": 0.0, + "opening_stock": 0.0, + "publish_in_hub": 1, + "quality_parameters": [], + "reorder_levels": [], + "route": null, + "safety_stock": 0.0, + "selling_cost_center": null, + "serial_no_series": null, + "show_in_website": 0, + "show_variant_in_website": 0, + "slideshow": null, + "standard_rate": 0.0, + "stock_uom": "Nos", + "supplier_items": [], + "synced_with_hub": 0, + "taxes": [], + "thumbnail": null, + "tolerance": 0.0, + "total_projected_qty": 0.0, + "uoms": [ + { + "conversion_factor": 1.0, + "uom": "Nos" + } + ], + "valuation_method": null, + "valuation_rate": 0.0, + "variant_based_on": null, + "variant_of": null, + "warranty_period": null, + "web_long_description": null, + "website_image": null, + "website_item_groups": [], + "website_specifications": [], + "website_warehouse": null, + "weight_uom": null, + "weightage": 0 + } +] diff --git a/erpnext/demo/data/item.json b/erpnext/demo/data/item.json index e2085220d4b..fe12ce892a0 100644 --- a/erpnext/demo/data/item.json +++ b/erpnext/demo/data/item.json @@ -167,6 +167,7 @@ "item_group": "Products", "item_name": "Wind Turbine-S", "variant_of": "Wind Turbine", + "valuation_rate": 300, "attributes":[ { "attribute": "Size", @@ -183,6 +184,7 @@ "item_group": "Products", "item_name": "Wind Turbine-M", "variant_of": "Wind Turbine", + "valuation_rate": 300, "attributes":[ { "attribute": "Size", @@ -199,6 +201,7 @@ "item_group": "Products", "item_name": "Wind Turbine-L", "variant_of": "Wind Turbine", + "valuation_rate": 300, "attributes":[ { "attribute": "Size", diff --git a/erpnext/demo/data/patient.json b/erpnext/demo/data/patient.json new file mode 100644 index 00000000000..6d95a202021 --- /dev/null +++ b/erpnext/demo/data/patient.json @@ -0,0 +1,27 @@ +[ + { + "patient_name": "lila", + "gender": "Female" + }, + { + "patient_name": "charline", + "gender": "Female" + }, + { + "patient_name": "soren", + "last_name": "le gall", + "gender": "Male" + }, + { + "patient_name": "fanny", + "gender": "Female" + }, + { + "patient_name": "julie", + "gender": "Female" + }, + { + "patient_name": "louka", + "gender": "Male" + } +] diff --git a/erpnext/demo/data/physician.json b/erpnext/demo/data/physician.json new file mode 100644 index 00000000000..3afea993e60 --- /dev/null +++ b/erpnext/demo/data/physician.json @@ -0,0 +1,17 @@ +[ + { + "doctype": "Physician", + "first_name": "Eddie Jessup", + "department": "Pathology" + }, + { + "doctype": "Physician", + "first_name": "Deepshi Garg", + "department": "ENT" + }, + { + "doctype": "Physician", + "first_name": "Amit Jain", + "department": "Microbiology" + } +] diff --git a/erpnext/demo/demo.py b/erpnext/demo/demo.py index 9fde2646c8a..35256b580fa 100644 --- a/erpnext/demo/demo.py +++ b/erpnext/demo/demo.py @@ -4,7 +4,7 @@ import frappe, sys import erpnext import frappe.utils from erpnext.demo.user import hr, sales, purchase, manufacturing, stock, accounts, projects, fixed_asset, schools -from erpnext.demo.setup import education, manufacture, setup_data +from erpnext.demo.setup import education, manufacture, setup_data, healthcare """ Make a demo @@ -30,6 +30,8 @@ def make(domain='Manufacturing', days=100): manufacture.setup_data() elif domain== 'Education': education.setup_data() + elif domain== 'Healthcare': + healthcare.setup_data() site = frappe.local.site frappe.destroy() diff --git a/erpnext/demo/domains.py b/erpnext/demo/domains.py index 5743e27bf29..456eb5df32d 100644 --- a/erpnext/demo/domains.py +++ b/erpnext/demo/domains.py @@ -15,5 +15,8 @@ data = { }, 'Education': { 'company_name': 'Whitmore College' + }, + 'Healthcare': { + 'company_name': 'ABC Hospital Ltd.' } } \ No newline at end of file diff --git a/erpnext/demo/setup/healthcare.py b/erpnext/demo/setup/healthcare.py new file mode 100644 index 00000000000..d645e309f87 --- /dev/null +++ b/erpnext/demo/setup/healthcare.py @@ -0,0 +1,166 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe, json +from frappe.utils.make_random import get_random +import datetime +from erpnext.demo.setup.setup_data import import_json +from frappe.utils import getdate +from erpnext.healthcare.doctype.lab_test.lab_test import create_test_from_template + +def setup_data(): + frappe.flags.mute_emails = True + make_masters() + make_patient() + make_lab_test() + make_consulation() + make_appointment() + consulation_on_appointment() + lab_test_on_consultation() + frappe.db.commit() + frappe.clear_cache() + +def make_masters(): + import_json("Physician") + import_drug() + frappe.db.commit() + +def make_patient(): + file_path = get_json_path("Patient") + with open(file_path, "r") as open_file: + patient_data = json.loads(open_file.read()) + count = 1 + + for d in enumerate(patient_data): + patient = frappe.new_doc("Patient") + patient.patient_name = d[1]['patient_name'].title() + patient.sex = d[1]['gender'] + patient.blood_group = "A Positive" + patient.date_of_birth = datetime.datetime(1990, 3, 25) + patient.email_id = d[1]['patient_name'] + "_" + patient.date_of_birth.strftime('%m/%d/%Y') + "@example.com" + if count <5: + patient.insert() + frappe.db.commit() + count+=1 + +def make_appointment(): + i = 1 + while i <= 4: + physician = get_random("Physician") + department = frappe.get_value("Physician", physician, "department") + patient = get_random("Patient") + patient_sex = frappe.get_value("Patient", patient, "sex") + appointment = frappe.new_doc("Patient Appointment") + startDate = datetime.datetime.now() + for x in random_date(startDate,0): + appointment_datetime = x + appointment.appointment_datetime = appointment_datetime + appointment.appointment_time = appointment_datetime + appointment.appointment_date = appointment_datetime + appointment.patient = patient + appointment.patient_sex = patient_sex + appointment.physician = physician + appointment.department = department + appointment.save(ignore_permissions = True) + i += 1 + +def make_consulation(): + for i in xrange(3): + physician = get_random("Physician") + department = frappe.get_value("Physician", physician, "department") + patient = get_random("Patient") + patient_sex = frappe.get_value("Patient", patient, "sex") + consultation = set_consultation(patient, patient_sex, physician, department, getdate(), i) + consultation.save(ignore_permissions=True) + +def consulation_on_appointment(): + for i in xrange(3): + appointment = get_random("Patient Appointment") + appointment = frappe.get_doc("Patient Appointment",appointment) + consultation = set_consultation(appointment.patient, appointment.patient_sex, appointment.physician, appointment.department, appointment.appointment_date, i) + consultation.appointment = appointment.name + consultation.save(ignore_permissions=True) + +def set_consultation(patient, patient_sex, physician, department, consultation_date, i): + consultation = frappe.new_doc("Consultation") + consultation.patient = patient + consultation.patient_sex = patient_sex + consultation.physician = physician + consultation.visit_department = department + consultation.consultation_date = consultation_date + if i > 2 and patient_sex=='Female': + consultation.symptoms = "Having chest pains for the last week." + consultation.diagnosis = """This patient's description of dull, aching, + exertion related substernal chest pain is suggestive of ischemic + cardiac origin. Her findings of a FH of early ASCVD, hypertension, + and early surgical menopause are pertinent risk factors for development + of coronary artery disease. """ + else: + consultation = append_drug_rx(consultation) + consultation = append_test_rx(consultation) + return consultation + +def make_lab_test(): + physician = get_random("Physician") + patient = get_random("Patient") + patient_sex = frappe.get_value("Patient", patient, "sex") + template = get_random("Lab Test Template") + set_lab_test(patient, patient_sex, physician, template) + +def lab_test_on_consultation(): + i = 1 + while i <= 2: + test_rx = get_random("Lab Prescription", filters={'test_created': 0}) + test_rx = frappe.get_doc("Lab Prescription", test_rx) + consultation = frappe.get_doc("Consultation", test_rx.parent) + set_lab_test(consultation.patient, consultation.patient_sex, consultation.physician, test_rx.test_code, test_rx.name) + i += 1 + +def set_lab_test(patient, patient_sex, physician, template, rx=None): + lab_test = frappe.new_doc("Lab Test") + lab_test.physician = physician + lab_test.patient = patient + lab_test.patient_sex = patient_sex + lab_test.template = template + lab_test.prescription = rx + create_test_from_template(lab_test) + +def append_test_rx(consultation): + i = 1 + while i <= 2: + test_rx = consultation.append("test_prescription") + test_rx.test_code = get_random("Lab Test Template") + i += 1 + return consultation + +def append_drug_rx(consultation): + i = 1 + while i <= 3: + drug = get_random("Item", filters={"item_group":"Drug"}) + drug = frappe.get_doc("Item", drug) + drug_rx = consultation.append("drug_prescription") + drug_rx.drug_code = drug.item_code + drug_rx.drug_name = drug.item_name + drug_rx.dosage = get_random("Prescription Dosage") + drug_rx.period = get_random("Prescription Duration") + i += 1 + return consultation + +def random_date(start,l): + current = start + while l >= 0: + curr = current + datetime.timedelta(minutes=60) + yield curr + l-=1 + +def import_drug(): + frappe.flags.in_import = True + data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'drug_list.json')).read()) + for d in data: + doc = frappe.new_doc("Item") + doc.update(d) + doc.insert() + frappe.flags.in_import = False + +def get_json_path(doctype): + return frappe.get_app_path('erpnext', 'demo', 'data', frappe.scrub(doctype) + '.json') diff --git a/erpnext/demo/setup/setup_data.py b/erpnext/demo/setup/setup_data.py index cec425ce6b9..c4df777c88b 100644 --- a/erpnext/demo/setup/setup_data.py +++ b/erpnext/demo/setup/setup_data.py @@ -184,7 +184,8 @@ def setup_user_roles(): user.add_roles('HR User', 'HR Manager', 'Accounts User', 'Accounts Manager', 'Stock User', 'Stock Manager', 'Sales User', 'Sales Manager', 'Purchase User', 'Purchase Manager', 'Projects User', 'Manufacturing User', 'Manufacturing Manager', - 'Support Team', 'Academics User') + 'Support Team', 'Academics User', 'Physician', 'Healthcare Administrator', 'Laboratory User', + 'Nursing User', 'Patient') if not frappe.db.get_global('demo_hr_user'): user = frappe.get_doc('User', 'CharmaineGaudreau@example.com') @@ -387,5 +388,3 @@ def import_json(doctype, submit=False, values=None): frappe.db.commit() frappe.flags.in_import = False - - diff --git a/erpnext/docs/assets/img/accounts/subscription.gif b/erpnext/docs/assets/img/accounts/subscription.gif new file mode 100644 index 00000000000..68488053d67 Binary files /dev/null and b/erpnext/docs/assets/img/accounts/subscription.gif differ diff --git a/erpnext/docs/assets/img/accounts/subscription.png b/erpnext/docs/assets/img/accounts/subscription.png new file mode 100644 index 00000000000..8b2cdc30294 Binary files /dev/null and b/erpnext/docs/assets/img/accounts/subscription.png differ diff --git a/erpnext/docs/assets/img/articles/brand-logo.gif b/erpnext/docs/assets/img/articles/brand-logo.gif new file mode 100644 index 00000000000..99a10d3b34b Binary files /dev/null and b/erpnext/docs/assets/img/articles/brand-logo.gif differ diff --git a/erpnext/docs/assets/img/articles/download-backup-1.png b/erpnext/docs/assets/img/articles/download-backup-1.png new file mode 100644 index 00000000000..93597f453f9 Binary files /dev/null and b/erpnext/docs/assets/img/articles/download-backup-1.png differ diff --git a/erpnext/docs/assets/img/articles/download-backup-2.gif b/erpnext/docs/assets/img/articles/download-backup-2.gif new file mode 100644 index 00000000000..4109dff0187 Binary files /dev/null and b/erpnext/docs/assets/img/articles/download-backup-2.gif differ diff --git a/erpnext/docs/assets/img/articles/email-error.png b/erpnext/docs/assets/img/articles/email-error.png new file mode 100644 index 00000000000..e8c5d73dc35 Binary files /dev/null and b/erpnext/docs/assets/img/articles/email-error.png differ diff --git a/erpnext/docs/assets/img/articles/fetching-1.png b/erpnext/docs/assets/img/articles/fetching-1.png new file mode 100644 index 00000000000..fcf3fcbac24 Binary files /dev/null and b/erpnext/docs/assets/img/articles/fetching-1.png differ diff --git a/erpnext/docs/assets/img/articles/fetching-2.png b/erpnext/docs/assets/img/articles/fetching-2.png new file mode 100644 index 00000000000..c5594d56f4f Binary files /dev/null and b/erpnext/docs/assets/img/articles/fetching-2.png differ diff --git a/erpnext/docs/assets/img/articles/fetching-3.gif b/erpnext/docs/assets/img/articles/fetching-3.gif new file mode 100644 index 00000000000..5deea3e5f8c Binary files /dev/null and b/erpnext/docs/assets/img/articles/fetching-3.gif differ diff --git a/erpnext/docs/assets/img/articles/item-valuation-1.png b/erpnext/docs/assets/img/articles/item-valuation-1.png new file mode 100644 index 00000000000..16bde1d1202 Binary files /dev/null and b/erpnext/docs/assets/img/articles/item-valuation-1.png differ diff --git a/erpnext/docs/assets/img/articles/item-valuation-2.png b/erpnext/docs/assets/img/articles/item-valuation-2.png new file mode 100644 index 00000000000..cc8b8442f3f Binary files /dev/null and b/erpnext/docs/assets/img/articles/item-valuation-2.png differ diff --git a/erpnext/docs/assets/img/articles/purchase-return.gif b/erpnext/docs/assets/img/articles/purchase-return.gif new file mode 100644 index 00000000000..de5f18cf337 Binary files /dev/null and b/erpnext/docs/assets/img/articles/purchase-return.gif differ diff --git a/erpnext/docs/assets/img/articles/report-header-1.png b/erpnext/docs/assets/img/articles/report-header-1.png new file mode 100644 index 00000000000..848c3018bbf Binary files /dev/null and b/erpnext/docs/assets/img/articles/report-header-1.png differ diff --git a/erpnext/docs/assets/img/articles/report-header-2.png b/erpnext/docs/assets/img/articles/report-header-2.png new file mode 100644 index 00000000000..47d86958be6 Binary files /dev/null and b/erpnext/docs/assets/img/articles/report-header-2.png differ diff --git a/erpnext/docs/assets/img/healthcare/._.DS_Store b/erpnext/docs/assets/img/healthcare/._.DS_Store new file mode 100755 index 00000000000..77c0a115d2c Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._.DS_Store differ diff --git a/erpnext/docs/assets/img/healthcare/._appointment_1.png b/erpnext/docs/assets/img/healthcare/._appointment_1.png new file mode 100755 index 00000000000..9185da5b7ce Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._appointment_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/._appointment_2.png b/erpnext/docs/assets/img/healthcare/._appointment_2.png new file mode 100755 index 00000000000..901903ac3bf Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._appointment_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/._appointment_3.png b/erpnext/docs/assets/img/healthcare/._appointment_3.png new file mode 100755 index 00000000000..acac3260799 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._appointment_3.png differ diff --git a/erpnext/docs/assets/img/healthcare/._consultation_1.png b/erpnext/docs/assets/img/healthcare/._consultation_1.png new file mode 100755 index 00000000000..26693f48525 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._consultation_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/._consultation_2.png b/erpnext/docs/assets/img/healthcare/._consultation_2.png new file mode 100755 index 00000000000..ddb28b02829 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._consultation_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/._consultation_3.png b/erpnext/docs/assets/img/healthcare/._consultation_3.png new file mode 100755 index 00000000000..75615ee82cf Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._consultation_3.png differ diff --git a/erpnext/docs/assets/img/healthcare/._consultation_4.png b/erpnext/docs/assets/img/healthcare/._consultation_4.png new file mode 100755 index 00000000000..a18b518d04c Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._consultation_4.png differ diff --git a/erpnext/docs/assets/img/healthcare/._home.png b/erpnext/docs/assets/img/healthcare/._home.png new file mode 100755 index 00000000000..38c9eafb70b Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._home.png differ diff --git a/erpnext/docs/assets/img/healthcare/._lab_test_1.png b/erpnext/docs/assets/img/healthcare/._lab_test_1.png new file mode 100755 index 00000000000..36be754257c Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._lab_test_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/._lab_test_2.png b/erpnext/docs/assets/img/healthcare/._lab_test_2.png new file mode 100755 index 00000000000..684798b4bd6 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._lab_test_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/._medical_code_1.png b/erpnext/docs/assets/img/healthcare/._medical_code_1.png new file mode 100755 index 00000000000..a27e93348ef Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._medical_code_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/._medical_record_1.png b/erpnext/docs/assets/img/healthcare/._medical_record_1.png new file mode 100755 index 00000000000..df79c91aef6 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._medical_record_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/._medical_record_2.png b/erpnext/docs/assets/img/healthcare/._medical_record_2.png new file mode 100755 index 00000000000..89916673154 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._medical_record_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/._module.png b/erpnext/docs/assets/img/healthcare/._module.png new file mode 100755 index 00000000000..f8a4a0ff686 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._module.png differ diff --git a/erpnext/docs/assets/img/healthcare/._patient_1.png b/erpnext/docs/assets/img/healthcare/._patient_1.png new file mode 100755 index 00000000000..589703e587c Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._patient_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/._patient_2.png b/erpnext/docs/assets/img/healthcare/._patient_2.png new file mode 100755 index 00000000000..0ac360ae142 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._patient_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/._patient_3.png b/erpnext/docs/assets/img/healthcare/._patient_3.png new file mode 100755 index 00000000000..d8e0ed2cc85 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._patient_3.png differ diff --git a/erpnext/docs/assets/img/healthcare/._physician_1.png b/erpnext/docs/assets/img/healthcare/._physician_1.png new file mode 100755 index 00000000000..bf7889bc997 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._physician_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/._physician_2.png b/erpnext/docs/assets/img/healthcare/._physician_2.png new file mode 100755 index 00000000000..a726f9a85ba Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._physician_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/._physician_schedule_1.png b/erpnext/docs/assets/img/healthcare/._physician_schedule_1.png new file mode 100755 index 00000000000..6dba9d58aad Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._physician_schedule_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/._physician_schedule_2.png b/erpnext/docs/assets/img/healthcare/._physician_schedule_2.png new file mode 100755 index 00000000000..02eec95ea6c Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._physician_schedule_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/._sample_collection_1.png b/erpnext/docs/assets/img/healthcare/._sample_collection_1.png new file mode 100755 index 00000000000..c72cbf7759e Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._sample_collection_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/._vitals_1.png b/erpnext/docs/assets/img/healthcare/._vitals_1.png new file mode 100755 index 00000000000..cae923b68cd Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._vitals_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/._vitals_2.png b/erpnext/docs/assets/img/healthcare/._vitals_2.png new file mode 100755 index 00000000000..77846b9a1c7 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/._vitals_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/__init__.py b/erpnext/docs/assets/img/healthcare/__init__.py new file mode 100755 index 00000000000..e69de29bb2d diff --git a/erpnext/docs/assets/img/healthcare/appointment_1.png b/erpnext/docs/assets/img/healthcare/appointment_1.png new file mode 100755 index 00000000000..afd308df75a Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/appointment_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/appointment_2.png b/erpnext/docs/assets/img/healthcare/appointment_2.png new file mode 100755 index 00000000000..104a919748b Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/appointment_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/appointment_3.png b/erpnext/docs/assets/img/healthcare/appointment_3.png new file mode 100755 index 00000000000..004a978862a Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/appointment_3.png differ diff --git a/erpnext/docs/assets/img/healthcare/consultation_1.png b/erpnext/docs/assets/img/healthcare/consultation_1.png new file mode 100755 index 00000000000..a7bc60da3bf Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/consultation_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/consultation_2.png b/erpnext/docs/assets/img/healthcare/consultation_2.png new file mode 100755 index 00000000000..fd5ceee2397 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/consultation_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/consultation_3.png b/erpnext/docs/assets/img/healthcare/consultation_3.png new file mode 100755 index 00000000000..e2804a91709 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/consultation_3.png differ diff --git a/erpnext/docs/assets/img/healthcare/consultation_4.png b/erpnext/docs/assets/img/healthcare/consultation_4.png new file mode 100755 index 00000000000..3f9817ca4e0 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/consultation_4.png differ diff --git a/erpnext/docs/assets/img/healthcare/home.png b/erpnext/docs/assets/img/healthcare/home.png new file mode 100755 index 00000000000..afe57a3640b Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/home.png differ diff --git a/erpnext/docs/assets/img/healthcare/lab_test_1.png b/erpnext/docs/assets/img/healthcare/lab_test_1.png new file mode 100755 index 00000000000..b2ddbeb3245 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/lab_test_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/lab_test_2.png b/erpnext/docs/assets/img/healthcare/lab_test_2.png new file mode 100755 index 00000000000..c06732a5ec7 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/lab_test_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/medical_code_1.png b/erpnext/docs/assets/img/healthcare/medical_code_1.png new file mode 100755 index 00000000000..4497e5a2d43 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/medical_code_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/medical_record_1.png b/erpnext/docs/assets/img/healthcare/medical_record_1.png new file mode 100755 index 00000000000..60586525907 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/medical_record_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/medical_record_2.png b/erpnext/docs/assets/img/healthcare/medical_record_2.png new file mode 100755 index 00000000000..a482704180a Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/medical_record_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/module.png b/erpnext/docs/assets/img/healthcare/module.png new file mode 100755 index 00000000000..c795df15bcd Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/module.png differ diff --git a/erpnext/docs/assets/img/healthcare/patient_1.png b/erpnext/docs/assets/img/healthcare/patient_1.png new file mode 100755 index 00000000000..03728aff62d Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/patient_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/patient_2.png b/erpnext/docs/assets/img/healthcare/patient_2.png new file mode 100755 index 00000000000..2a632c7a8b1 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/patient_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/patient_3.png b/erpnext/docs/assets/img/healthcare/patient_3.png new file mode 100755 index 00000000000..738ce2ccbd5 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/patient_3.png differ diff --git a/erpnext/docs/assets/img/healthcare/physician_1.png b/erpnext/docs/assets/img/healthcare/physician_1.png new file mode 100755 index 00000000000..f5705151cc8 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/physician_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/physician_2.png b/erpnext/docs/assets/img/healthcare/physician_2.png new file mode 100755 index 00000000000..7b3d1edf3a4 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/physician_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/physician_schedule_1.png b/erpnext/docs/assets/img/healthcare/physician_schedule_1.png new file mode 100755 index 00000000000..a9102e24937 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/physician_schedule_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/physician_schedule_2.png b/erpnext/docs/assets/img/healthcare/physician_schedule_2.png new file mode 100755 index 00000000000..d9105688ff4 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/physician_schedule_2.png differ diff --git a/erpnext/docs/assets/img/healthcare/sample_collection_1.png b/erpnext/docs/assets/img/healthcare/sample_collection_1.png new file mode 100755 index 00000000000..76238474ccf Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/sample_collection_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/vitals_1.png b/erpnext/docs/assets/img/healthcare/vitals_1.png new file mode 100755 index 00000000000..43bf5bfcd94 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/vitals_1.png differ diff --git a/erpnext/docs/assets/img/healthcare/vitals_2.png b/erpnext/docs/assets/img/healthcare/vitals_2.png new file mode 100755 index 00000000000..3e2129ae358 Binary files /dev/null and b/erpnext/docs/assets/img/healthcare/vitals_2.png differ diff --git a/erpnext/docs/assets/img/project/timesheet/make_invoice_from_timesheet.gif b/erpnext/docs/assets/img/project/timesheet/make_invoice_from_timesheet.gif new file mode 100644 index 00000000000..90cc6d6e869 Binary files /dev/null and b/erpnext/docs/assets/img/project/timesheet/make_invoice_from_timesheet.gif differ diff --git a/erpnext/docs/assets/img/schools/admission/program-enrollment-tool.gif b/erpnext/docs/assets/img/schools/admission/program-enrollment-tool.gif new file mode 100644 index 00000000000..c25b1799674 Binary files /dev/null and b/erpnext/docs/assets/img/schools/admission/program-enrollment-tool.gif differ diff --git a/erpnext/docs/assets/img/schools/admission/program-enrollment-tool01.gif b/erpnext/docs/assets/img/schools/admission/program-enrollment-tool01.gif new file mode 100644 index 00000000000..8b1f6314be0 Binary files /dev/null and b/erpnext/docs/assets/img/schools/admission/program-enrollment-tool01.gif differ diff --git a/erpnext/docs/assets/img/schools/admission/program-enrollment.gif b/erpnext/docs/assets/img/schools/admission/program-enrollment.gif new file mode 100644 index 00000000000..616ab17df90 Binary files /dev/null and b/erpnext/docs/assets/img/schools/admission/program-enrollment.gif differ diff --git a/erpnext/docs/assets/img/schools/admission/program-enrollment.png b/erpnext/docs/assets/img/schools/admission/program-enrollment.png deleted file mode 100644 index df96f3c320d..00000000000 Binary files a/erpnext/docs/assets/img/schools/admission/program-enrollment.png and /dev/null differ diff --git a/erpnext/docs/assets/img/schools/admission/student-applicant-enroll.png b/erpnext/docs/assets/img/schools/admission/student-applicant-enroll.png index fb92a679dff..7cf5e75be4d 100644 Binary files a/erpnext/docs/assets/img/schools/admission/student-applicant-enroll.png and b/erpnext/docs/assets/img/schools/admission/student-applicant-enroll.png differ diff --git a/erpnext/docs/assets/img/schools/admission/student-application-actions.png b/erpnext/docs/assets/img/schools/admission/student-application-actions.png new file mode 100644 index 00000000000..e3a4c15ec19 Binary files /dev/null and b/erpnext/docs/assets/img/schools/admission/student-application-actions.png differ diff --git a/erpnext/docs/assets/img/schools/schedule/student-attendance.gif b/erpnext/docs/assets/img/schools/schedule/student-attendance.gif new file mode 100644 index 00000000000..82549d46922 Binary files /dev/null and b/erpnext/docs/assets/img/schools/schedule/student-attendance.gif differ diff --git a/erpnext/docs/assets/img/schools/schedule/student-leave-application.gif b/erpnext/docs/assets/img/schools/schedule/student-leave-application.gif new file mode 100644 index 00000000000..3b09fac27b2 Binary files /dev/null and b/erpnext/docs/assets/img/schools/schedule/student-leave-application.gif differ diff --git a/erpnext/docs/assets/img/schools/setup/Course-schedule-error.png b/erpnext/docs/assets/img/schools/setup/Course-schedule-error.png new file mode 100644 index 00000000000..d9913d857a4 Binary files /dev/null and b/erpnext/docs/assets/img/schools/setup/Course-schedule-error.png differ diff --git a/erpnext/docs/assets/img/schools/setup/Room-Assesment-plan.png b/erpnext/docs/assets/img/schools/setup/Room-Assesment-plan.png new file mode 100644 index 00000000000..4b23cd7ea38 Binary files /dev/null and b/erpnext/docs/assets/img/schools/setup/Room-Assesment-plan.png differ diff --git a/erpnext/docs/assets/img/schools/setup/course-fee-program.png b/erpnext/docs/assets/img/schools/setup/course-fee-program.png new file mode 100644 index 00000000000..c8527d04e0d Binary files /dev/null and b/erpnext/docs/assets/img/schools/setup/course-fee-program.png differ diff --git a/erpnext/docs/assets/img/schools/setup/setup-section.png b/erpnext/docs/assets/img/schools/setup/setup-section.png deleted file mode 100644 index c02bbc4733d..00000000000 Binary files a/erpnext/docs/assets/img/schools/setup/setup-section.png and /dev/null differ diff --git a/erpnext/docs/assets/img/schools/setup/student-attendance-tool.gif b/erpnext/docs/assets/img/schools/setup/student-attendance-tool.gif new file mode 100644 index 00000000000..c8c1c05e809 Binary files /dev/null and b/erpnext/docs/assets/img/schools/setup/student-attendance-tool.gif differ diff --git a/erpnext/docs/assets/img/schools/setup/student-group-instructor.png b/erpnext/docs/assets/img/schools/setup/student-group-instructor.png new file mode 100644 index 00000000000..d9457a6d9c4 Binary files /dev/null and b/erpnext/docs/assets/img/schools/setup/student-group-instructor.png differ diff --git a/erpnext/docs/assets/img/schools/student/guardian.png b/erpnext/docs/assets/img/schools/student/guardian.png new file mode 100644 index 00000000000..01045be4901 Binary files /dev/null and b/erpnext/docs/assets/img/schools/student/guardian.png differ diff --git a/erpnext/docs/assets/img/schools/student/schools-settings.png b/erpnext/docs/assets/img/schools/student/schools-settings.png new file mode 100644 index 00000000000..8a71ac92edb Binary files /dev/null and b/erpnext/docs/assets/img/schools/student/schools-settings.png differ diff --git a/erpnext/docs/assets/img/schools/student/student group.gif b/erpnext/docs/assets/img/schools/student/student group.gif new file mode 100644 index 00000000000..13b1cf43420 Binary files /dev/null and b/erpnext/docs/assets/img/schools/student/student group.gif differ diff --git a/erpnext/docs/assets/img/schools/student/student-admission.gif b/erpnext/docs/assets/img/schools/student/student-admission.gif new file mode 100644 index 00000000000..a9ce8dc9b50 Binary files /dev/null and b/erpnext/docs/assets/img/schools/student/student-admission.gif differ diff --git a/erpnext/docs/assets/img/schools/student/student-batch-validation.gif b/erpnext/docs/assets/img/schools/student/student-batch-validation.gif new file mode 100644 index 00000000000..dd9f0e821d4 Binary files /dev/null and b/erpnext/docs/assets/img/schools/student/student-batch-validation.gif differ diff --git a/erpnext/docs/assets/img/schools/student/student-batch.gif b/erpnext/docs/assets/img/schools/student/student-batch.gif new file mode 100644 index 00000000000..43bae7734d7 Binary files /dev/null and b/erpnext/docs/assets/img/schools/student/student-batch.gif differ diff --git a/erpnext/docs/assets/img/schools/student/student-batch.png b/erpnext/docs/assets/img/schools/student/student-batch.png deleted file mode 100644 index 75a33140cc7..00000000000 Binary files a/erpnext/docs/assets/img/schools/student/student-batch.png and /dev/null differ diff --git a/erpnext/docs/assets/img/schools/student/student-course-validation.gif b/erpnext/docs/assets/img/schools/student/student-course-validation.gif new file mode 100644 index 00000000000..6ab9d00472b Binary files /dev/null and b/erpnext/docs/assets/img/schools/student/student-course-validation.gif differ diff --git a/erpnext/docs/assets/img/schools/student/student-group-attendance.gif b/erpnext/docs/assets/img/schools/student/student-group-attendance.gif new file mode 100644 index 00000000000..da9aa1ac269 Binary files /dev/null and b/erpnext/docs/assets/img/schools/student/student-group-attendance.gif differ diff --git a/erpnext/docs/assets/img/schools/student/student-group-creation-tool.gif b/erpnext/docs/assets/img/schools/student/student-group-creation-tool.gif new file mode 100644 index 00000000000..1fe1a6f0201 Binary files /dev/null and b/erpnext/docs/assets/img/schools/student/student-group-creation-tool.gif differ diff --git a/erpnext/docs/assets/img/schools/student/student-group-creation-tool.png b/erpnext/docs/assets/img/schools/student/student-group-creation-tool.png deleted file mode 100644 index 32e6498f8b2..00000000000 Binary files a/erpnext/docs/assets/img/schools/student/student-group-creation-tool.png and /dev/null differ diff --git a/erpnext/docs/assets/img/schools/student/student-group.png b/erpnext/docs/assets/img/schools/student/student-group.png deleted file mode 100644 index c562b001328..00000000000 Binary files a/erpnext/docs/assets/img/schools/student/student-group.png and /dev/null differ diff --git a/erpnext/docs/user/manual/en/accounts/articles/freeze-account.md b/erpnext/docs/user/manual/en/accounts/articles/freeze-account.md new file mode 100644 index 00000000000..3582c281248 --- /dev/null +++ b/erpnext/docs/user/manual/en/accounts/articles/freeze-account.md @@ -0,0 +1,13 @@ +# Freeze an Account + +Once an Account is Frozen, you won't be able to use it any accounting transaction. Since this is a critical action, you need to explicitly define a Role who can set an Account as Frozen. You can define this Role in the Account Settings. + +`Accounts > Account Settings` + +To freeze an Account, go to Chart of Accounts, and edit an Account. + +Download Backup + +If User has Role define in the  Account Setting assigned, then he/she will be able to set an Account as Frozen. + +Download Backup \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/accounts/articles/index.txt b/erpnext/docs/user/manual/en/accounts/articles/index.txt index 8b7768a2d18..af8572c2332 100644 --- a/erpnext/docs/user/manual/en/accounts/articles/index.txt +++ b/erpnext/docs/user/manual/en/accounts/articles/index.txt @@ -13,4 +13,5 @@ what-is-the-differences-of-total-and-valuation-in-tax-and-charges withdrawing-salary-from-owners-equity-account adjust-withhold-amount-payment-entry common-receivable-account.md -types-in-tax-template \ No newline at end of file +types-in-tax-template +freeze-account \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/accounts/index.txt b/erpnext/docs/user/manual/en/accounts/index.txt index 6a0da3a8945..41cb243dc66 100644 --- a/erpnext/docs/user/manual/en/accounts/index.txt +++ b/erpnext/docs/user/manual/en/accounts/index.txt @@ -6,6 +6,7 @@ purchase-invoice payments journal-entry payment-entry +subscription multi-currency-accounting advance-payment-entry payment-request diff --git a/erpnext/docs/user/manual/en/accounts/subscription.md b/erpnext/docs/user/manual/en/accounts/subscription.md new file mode 100644 index 00000000000..9afab58e257 --- /dev/null +++ b/erpnext/docs/user/manual/en/accounts/subscription.md @@ -0,0 +1,24 @@ +# Subscription + +If you have a contract with the Customer where your organization gives bill to the Customer on a monthly, quarterly, half-yearly or annual basis, you can use subscription feature to make auto invoicing. + +Subscription + +#### Scenario + +Subscription for your hosted ERPNext account requires yearly renewal. We use Sales Invoice for generating proforma invoices. To automate proforma invoicing for renewal, we set original Sales Invoice on the subscription form. Recurring proforma invoice is created automatically just before customer's account is about to expire, and requires renewal. This recurring Proforma Invoice is also emailed automatically to the customer. + +To set the subscription for the sales invoice +Goto Subscription > select base doctype "Sales Invoice" > select base docname "Invoice No" > Save + +Subscription + +**From Date and To Date**: This defines contract period with the customer. + +**Repeat on Day**: If frequency is set as Monthly, then it will be day of the month on which recurring invoice will be generated. + +**Notify By Email**: If you want to notify the user about auto recurring invoice. + +**Print Format**: Select a print format to define document view which should be emailed to customer. + +**Disabled**: It will stop to make auto recurring documents against the subscription \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/customize-erpnext/articles/creating-custom-link-field.md b/erpnext/docs/user/manual/en/customize-erpnext/articles/creating-custom-link-field.md index 829015db6d6..9a480bdca5a 100644 --- a/erpnext/docs/user/manual/en/customize-erpnext/articles/creating-custom-link-field.md +++ b/erpnext/docs/user/manual/en/customize-erpnext/articles/creating-custom-link-field.md @@ -11,7 +11,7 @@ You can insert Custom Link Field by following steps below. ####Step 2: Select Form -In Customize Form, select Document Type (Quotation, Sales Order, Purchase Invoice Item etc.). Once field are updated in table, open field before which you wish to insert Custom Field. Then click on "Insert Above" to insert new Custom Field. +In Customize Form, select Document Type (Quotation, Sales Order, Purchase Invoice Item etc.). Once fields are updated in the accompanying table below, open a field above the one you wish to insert your Custom Field. Then click on "Insert Above" to insert the new Custom Field. Select Docytpe @@ -26,4 +26,4 @@ To set field as Link, enter values as below. Enter Values - \ No newline at end of file + diff --git a/erpnext/docs/user/manual/en/customize-erpnext/articles/fetching-data-from-a-document.md b/erpnext/docs/user/manual/en/customize-erpnext/articles/fetching-data-from-a-document.md new file mode 100644 index 00000000000..04b20ac4b9f --- /dev/null +++ b/erpnext/docs/user/manual/en/customize-erpnext/articles/fetching-data-from-a-document.md @@ -0,0 +1,19 @@ +# Fetching Data from one Document to Another + +**Question:** We track Customer's PO No and PO Date field in the Sales Order. To have these values fetched into Sales Invoice as well, we have inserted Custom Field in the Sales Invoice. However, when we create Sales Invoice from the Sales Order, Customer's PO details are not being fetched. + +**Answer:** When data is fetched from one transaction to the another transaction, then the mapping of data is done based on the field names. If two transactions have fields with the exact same name, then it's values are mapped. + +For example, if you want Customer's PO No. and PO Date to be fetched from Sales Order to Sales Invoice, then you should ensure that Custom Fields added in the Sales Invoice has an exact same field name as in the Sales Order. + +Sales Order (standard fields) + +Standard fields in Sales Order + +Sales Invoice (custom fields) + +Custom Field in Sales Invoice + +Since names for the Customer's PO related fields are same in the Sales Order and Sales Invoice, when creating Sales Invoice from the Sales Order, values in these fields are auto-fetched. + +Values fetching from Sales Order to Sales Invoice \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/customize-erpnext/articles/index.txt b/erpnext/docs/user/manual/en/customize-erpnext/articles/index.txt index 1740ef0e943..56e7040b47a 100644 --- a/erpnext/docs/user/manual/en/customize-erpnext/articles/index.txt +++ b/erpnext/docs/user/manual/en/customize-erpnext/articles/index.txt @@ -15,4 +15,5 @@ set-language set-precision user-restriction maximum-numbers-of-fields-in-a-form -child-table \ No newline at end of file +child-table +fetching-data-from-a-document \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/healthcare/._.DS_Store b/erpnext/docs/user/manual/en/healthcare/._.DS_Store new file mode 100755 index 00000000000..ed70b669b54 Binary files /dev/null and b/erpnext/docs/user/manual/en/healthcare/._.DS_Store differ diff --git a/erpnext/docs/user/manual/en/healthcare/__init__.py b/erpnext/docs/user/manual/en/healthcare/__init__.py new file mode 100755 index 00000000000..e69de29bb2d diff --git a/erpnext/docs/user/manual/en/healthcare/appointment.md b/erpnext/docs/user/manual/en/healthcare/appointment.md new file mode 100755 index 00000000000..a5cea36b20e --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/appointment.md @@ -0,0 +1,38 @@ +# Patient Appointment +ERPNext Healthcare allows you to book Patient appointments for any date and if configured, send them alerts via Email or SMS. + +You can create a Patient Appointment from +> Healthcare > Patient Appointment > New Patient Appointment + +You can book appointments for a registered Patient by searching and selecting the Patient field. You can search the Patient by Patient ID, Name, Email or Mobile number. You can also register a new Patient from the Appointment screen by selecting "Create a new patient" in the Patient field. + +ERPNext Healthcare + +If you have a front desk executive to manage your appointments, you can configure a user role to have access to Patient Appointment so that she can do the bookings by selecting the Physician whom the Patient wish to consult and the date for booking. "Check Availability" button will pop up all the available time slots with status indicators for the date. She can select a time slot and "Book" the Appointment for the Patient. + +ERPNext Healthcare + +After Booking, the scheduled time of the Appointment and duration will be updated and seved in the document. + +ERPNext Healthcare + +You can configure ERPNext to send an SMS alert to the Patient about the booking confirmation or a reminder on the day of Appointment by doing necessary configurations in - + +> Healthcare > Healthcare Settings > Out Patient SMS Alerts + +The screen also allows the executive to select a Referring Physician so that you can track the source the appointment. + +### Actions + * Billing: If you collect the consultation fee while booking the Appointment itself you can do so by using the "Create > Invoice" button. This will take you to the ERPNext Accounts Sales Invoice screen. + + * Vital Signs: "Create > Vital Signs" button will take you to the new Vital Signs screen to record the vitals of the Patient. + + * Consultation: From the Appointment screen you can directly create a Consultation to record the details of patient encounter. + + * View Patient Medical Record. + +> Note: User should have privileges (User Role) to view the buttons + +A Patient can also book an appointment with a Physician by checking the Physician's availability directly through the **ERPNext Portal**. + +{next} diff --git a/erpnext/docs/user/manual/en/healthcare/consultation.md b/erpnext/docs/user/manual/en/healthcare/consultation.md new file mode 100755 index 00000000000..a98713bcb56 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/consultation.md @@ -0,0 +1,28 @@ +# Consultation +ERPNext Healthcare allows you to record Patient encounters through the Consultation document. You can create a Consultation based on a previously booked Appointment or directly by creating a new Consultation +>Healthcare > Consultation > Consultation + +If you are creating the Consultation document from an Appointment, Patient and other related data will automatically be populated else you can search the Patient by name, email phone number etc. The Patient Details section will list the latest Vital Signs record of the patient and other information captured in the Patient screen. + +ERPNext Healthcare + +### Assessment + +Encounter Impression section allows you to select (or create new) Complaints and your assessment based on the presented complaints. You can opt to include the captured data in Consultation print by selecting the "In Print" flag + +ERPNext Healthcare + +### Prescriptions + +You can prescribe medicines in the Drug Prescription section by selecting the drug codes (Stock Item) and appropriate dosages. If you are not managing Stock and Items are not configured, you can simply enter the Medicine name and strength in the Strength field which will printed. + +Prescribing a laboratory investigation is similar and if you have Lab Tests configured, you can select from the list. Or key in the Lab Test name to be printed as part of the Prescription. + +ERPNext Healthcare + +### Medical Coding +You can also attach one or more Medical Codes to designate the Diagnosis in the Medical Coding Section. You will have to select the Medical Code Standard you wish to encode the diagnosis and then select the Code by searching the Code itself or the Code Description. + +ERPNext Healthcare + +{next} diff --git a/erpnext/docs/user/manual/en/healthcare/index.md b/erpnext/docs/user/manual/en/healthcare/index.md new file mode 100755 index 00000000000..85e3b6fec89 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/index.md @@ -0,0 +1,13 @@ +# Healthcare + +ERPNext Healthcare helps you manage your Clinic or Practice efficiently by scheduling **Appointments** and recording **Patient Encounters** (Consultations). You can easily pull out a **Patient's Health Record** anytime to review all the history of treatments assisting you in providing effective, high quality care. + +ERPNext Healthcare + +Patients can view various documents relevant to them and book Appointments via the **ERPNext Portal**. The healthcare module is integrated with **Accounts** and **Human Resources** modules, helping you in **Billing**, **Payroll Management** etc. and benefit from other rich features of ERPNext. You can configure the **Selling** and **Stock** modules manage your Pharmacy. + +ERPNext Healthcare also includes features for effectively managing the functions of an associated **Laboratory** by helping you record **Sample Collection**, emailing and printing **Lab Test** results etc. ERPNext Healthcare allows you to upload **Medical Code Standards** like **ICD10** and attach to Consultations. + +### Topics + +{index} diff --git a/erpnext/docs/user/manual/en/healthcare/index.txt b/erpnext/docs/user/manual/en/healthcare/index.txt new file mode 100755 index 00000000000..471d91d5c91 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/index.txt @@ -0,0 +1,12 @@ +patient +appointment +vital_signs +consultation +medical_record +sample_collection +lab_test +invoicing +physician +physician_schedule +medical_codes +setup diff --git a/erpnext/docs/user/manual/en/healthcare/invoicing.md b/erpnext/docs/user/manual/en/healthcare/invoicing.md new file mode 100755 index 00000000000..bc9dead49f4 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/invoicing.md @@ -0,0 +1,8 @@ +# Invoicing +Billing is an integral part of any undertaking and ERPNext Healthcare achieves this by making use of the ERPNext Accounts module. + +> Note: All transactions of a Patient is booked against the Customer which it is linked to. + +All ERPNext Healthcare documents which require Invoicing will have buttons which would take you to the Sales Invoice with the Items configured for the service. You can then proceed by following the ERPNExt Accounts module workflows. Please note that your User account should have appropriate privileges to access the Accounts documents. + +{next} diff --git a/erpnext/docs/user/manual/en/healthcare/lab_test.md b/erpnext/docs/user/manual/en/healthcare/lab_test.md new file mode 100755 index 00000000000..bdf0cfcee34 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/lab_test.md @@ -0,0 +1,22 @@ +# Lab Test + +ERPNext Healthcare allows you to manage a clinical laboratory efficiently by allowing you to enter Lab Tests and print or email test results, manage samples collected, create Invoice etc. ERPNext Healthcare comes pre-packed with some sample tests, you can reconfigure Lab Test Templates for each Test and its result format or crate new ones. You can do this in +>Healthcare > Setup > Lab Text Templates + +Once you have all necessary Lab Test Templates configured, you can start creating Lab Tests by selecting a Test Template every time you create a Test. To create a new Lab Test +>Healthcare > Laboratory > Lab Test > New Lab Test + +ERPNext Healthcare + +You can record the test results in the Lab Test document as the results gets ready. + +ERPNext Healthcare + +> Note: To create Sample Collection documents for every Lab Test, check "Manage Sample Collection" flag in Healthcare Settings and select Sample in the Lab Test Template + +In many Laboratories, approval of Lab Tests is a must before printing and submitting the document. ERPNext Healthcare allows you to create Users with Role "Lab Test Approver" for this. You will also have to enable this in +>Healthcare Settings > Laboratory Settings > Require Lab Test Approval + +This will ensure that emailing or printing of Lab Tests can only be done after Approval of the Lab Test by the Lab Test Approver. + +{next} diff --git a/erpnext/docs/user/manual/en/healthcare/medical_codes.md b/erpnext/docs/user/manual/en/healthcare/medical_codes.md new file mode 100755 index 00000000000..c0997b1ebe1 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/medical_codes.md @@ -0,0 +1,9 @@ +# Medical Code Standards +Medical Coding are in many countries required for regulatory compliance and many of the Medical Insurance companies do that pricing based on Medical Code standards. ERPNext Healthcare offers support, however limited, to encode diagnosis and assessments recorded as part of Consultation. This can be done if you configure the Medical Code Standard and related Medical Codes - this is easily done by data import as the code data tends to be quite large. You can create as many Medical Code Standards you wish +> Healthcare > Masters > Medical Code Standard + +Medical Code Standard document is used to name the Code Standard and act as a container for all the medical codes which are standardized under it. Medical Codes and descriptions can then be imported to the Medical Code document, after ensuring that you set the Medical Code Standard field to the appropriate Standard name. + +ERPNext Healthcare + +{next} diff --git a/erpnext/docs/user/manual/en/healthcare/medical_record.md b/erpnext/docs/user/manual/en/healthcare/medical_record.md new file mode 100755 index 00000000000..9d2697d6004 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/medical_record.md @@ -0,0 +1,13 @@ +# Patient Medical Record +The maintenance of complete and accurate medical records is a requirement of healthcare providers and is critical in rendering effective, high quality care. ERPNext Healthcare allows you to draw up the treatment history of a Patient anytime by merely selecting the Patient. "Medical Record" button is available in various screens so that you can easily switch to the Medical Record page to view the patient history. + +Medical Record automatically keeps track of all Consultations, recorded Vital Signs, Lab Investigations etc. Complaints, Diagnosis etc. captured as part of consultation are easily viewable but to look at the details of other documents, links are provided. + +ERPNext Healthcare + +##### Adding notes manually to Medical Record +In the Patient screen Create > Medical Record will allow you to record notes to the Medical Record manually. You can also attach files when doing this, and the Medical Record will display links to the attached file along side the notes. Create > Medical Record button is also made available in the Consultation screen + +ERPNext Healthcare + +{next} diff --git a/erpnext/docs/user/manual/en/healthcare/patient.md b/erpnext/docs/user/manual/en/healthcare/patient.md new file mode 100755 index 00000000000..ed6810e88f2 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/patient.md @@ -0,0 +1,43 @@ +# Patient + +In ERPNext Healthcare, the Patient document corresponds any individual who is the recipient of healthcare services you provide. For every ERPNext Healthcare document, it is important to have a Patient associated with it. You can create a new Patient from +> Healthcare > Masters > Patient > New Patient + +ERPNext Healthcare + +The Patient document holds most details that are required to identify and qualify a patient. You can enter as much information available while creating the Patient. All information in the patient document is presented on the Consultation screen for easy lookup and you can always update this information. Other data like observations, vital signs etc. are not part of the Patient document. These could be recorded during patient encounters and will be available as part of the Patient Medical Record. + +ERPNext Healthcare + +### Patient as a Customer + +ERPNext Accounts makes use of "Customer" document for booking all transactions. So, you may want to associate every Patient to be associated with a Customer in ERPNext. By default, ERPNext Healthcare creates a Customer alongside a Patient and links to it - every transaction against a Patient is booked against the associated Customer. If, for some reason you do not intend to use the ERPNext Accounts module you can turn this behavior off by unchecking this flag +>Healthcare > Setup > Healthcare Settings > Manage Customer + +In many cases, you may want to associate multiple Patients to a single Customer against whom you want to book the transactions. For instance, a Veterinarian would require the care services provided to different pets of an individual invoiced against a single Customer. + +ERPNext Healthcare + +The Patient Relation section of the Patient allows you to select how a Patient is related to another Patient in the system. This is optional, but will be quite handy if you want to use ERPNext in a fertility clinic, for example. + +### Registration Fee +Many clinical facilities collect a registration fee during Registration. You can turn this feature on and set the registration fee amount by checking this flag +> Healthcare > Setup > Healthcare Settings > Collect Fee for Patient Registration + +If you have this enabled, all new Patients you create will by default be in Disabled mode and will be enabled only after Invoicing the Registration Fee. To create Invoice and record the payment receipt, you can use the "Invoice Patient Registration" button in the Patient document. + +> Note: For all ERPNext Healthcare documents, "Disabled" Patients are filtered out. + +### Grant access to Patient Portal +ERPNext Healthcare allows you to create a portal user associated with a Patient by simply entering the user email id. A welcome email will be sent to the Patient email address to "Complete" registration. + +### Actions +From the Patient document, the following links are enabled + +* Vital Signs: "Create > Vital Signs" button will take you to the new Vital Signs screen to record the vitals of the Patient. + +* View Patient Medical Record. + +* Consultation: You can directly create a new Consultation to record the details of patient encounter. + +> Note: User should have privileges (User Role) to view the buttons diff --git a/erpnext/docs/user/manual/en/healthcare/physician.md b/erpnext/docs/user/manual/en/healthcare/physician.md new file mode 100755 index 00000000000..7b7995bcd97 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/physician.md @@ -0,0 +1,18 @@ +# Physician +ERPNext Healthcare allows you to create multiple physicians and optionally link to a User with appropriate Roles. You can create a Physician here - +>Healthcare > Masters > Physician + +Linking a User to the Physician makes the system populate the Physician field in all documents to the Physician associated with the logged in User. +>Note: You should also relate the User to an Employee to utilize the various features of Human Resources module. + +ERPNext Healthcare + +### Scheduling and Availability +Each Physician can have a "Physician Schedule" and a "Time per Appointment" on the basis of which, the scheduler will book Appointments. Also, you can select appropriate Income Accounts for a Physician to book all Consultation charges into separate accounts. + +ERPNext Healthcare + +### Referring Physicians +You may also want to manage a list of Doctors who refers Patients to your facility. You can manage such data in the Physician document itself by leaving out the User link. + +{next} diff --git a/erpnext/docs/user/manual/en/healthcare/physician_schedule.md b/erpnext/docs/user/manual/en/healthcare/physician_schedule.md new file mode 100755 index 00000000000..0da7896014f --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/physician_schedule.md @@ -0,0 +1,13 @@ +# Physician Schedule +Physician Schedule will help you to configure the availability and work hours of Physicians. You can then select an applicable schedule for each Physician. + +You can create Physician Schedule from - +> Healthcare > Masters > Physician Schedule + +ERPNext Healthcare + +After naming the schedule you can use the "Add Time Slots" button to create time slots for each day of the week. These time slots will then be displayed while checking the availability of a Physician when booking an Appointment. + +ERPNext Healthcare + +{next} diff --git a/erpnext/docs/user/manual/en/healthcare/sample_collection.md b/erpnext/docs/user/manual/en/healthcare/sample_collection.md new file mode 100755 index 00000000000..3473dafd105 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/sample_collection.md @@ -0,0 +1,13 @@ +# Sample Collection +It's critical for a Laboratory to manage collected samples and you may want to ID the sample, print stickers etc. ERPNext Healthcare "Sample Collection" document helps you to easily manage the sample collection process by creating a sample collection document for every Lab Test automatically. You will have to turn on the flag in Healthcare Settings to enable this feature. +> Healthcare Settings > Laboratory Settings > Manage Sample Collection + +ERPNext Healthcare + +> Note: You will have to select a Sample in the Lab Test Template for the system to automatically create a Sample Collection document + +You will have to enter the sample collected date and time to Submit the document signaling that the sample is collected. + +Printing on sample identification tags is also possible. By default a sample sticker print template is made available, but you can always create a custom Print Format by using "Customize" button in the print preview. + +{next} diff --git a/erpnext/docs/user/manual/en/healthcare/setup/__init__.py b/erpnext/docs/user/manual/en/healthcare/setup/__init__.py new file mode 100755 index 00000000000..e69de29bb2d diff --git a/erpnext/docs/user/manual/en/healthcare/setup/index.md b/erpnext/docs/user/manual/en/healthcare/setup/index.md new file mode 100755 index 00000000000..721f521eda8 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/setup/index.md @@ -0,0 +1,7 @@ +# Setup + +Once you setup ERPNext (Company, Chart Of Accounts etc.), you can start with setting up your domain. To setup Healthcare module, User should have Healthcare Admin Role enabled. You can configure each of the departments as detailed in the Topics below. + +### Topics + +{index} diff --git a/erpnext/docs/user/manual/en/healthcare/setup/index.txt b/erpnext/docs/user/manual/en/healthcare/setup/index.txt new file mode 100755 index 00000000000..f2423811eca --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/setup/index.txt @@ -0,0 +1,3 @@ +Setting up Practice Management +Setting up Laboratory +Setting up Pharmacy (Stock) diff --git a/erpnext/docs/user/manual/en/healthcare/setup/setup_laboratory.md b/erpnext/docs/user/manual/en/healthcare/setup/setup_laboratory.md new file mode 100755 index 00000000000..93e53b8a8e5 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/setup/setup_laboratory.md @@ -0,0 +1,67 @@ +# Laboratory + +If you wish to use features of Laboratory, you can create Users with "Laboratory User". Lab Tests, Sample Collection etc. are only visible to users with this Role enabled. + +### Laboratory Settings +> Healthcare > Setup > Healthcare Settings > Laboratory Settings + +* Manage Sample Collection - If this flag is enabled, every time you create a Lab Test, a Sample Collection document will be created. + +* Require Lab Test Approval - Turning this on will restrict printing and emailing of Lab Tests only if the documents are in Approved status. You can use this flag to ensure that every Test result leaves your facility after verification. + +* Enable the third option 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. + +##### SMS Alerts +You can configure ERPNext Healthcare to alert Patients via SMS when the Lab Test result gets ready (Submit) and when you Email the result. You an configure the templates for the SMS as registered with your provider here. +> Healthcare > Setup > Healthcare Settings > Laboratory SMS Alerts + + +### Lab Test Templates +Whenever you create a new Lab Test, the Lab Test document is loaded based on the template configured for that particular test. This means, you will have to have separate templates configured for each Lab Test. + +Here's how you can configure various types of templates. +> Healthcare > Setup > Lab Test Template > New Lab Test Template + +After providing the Name for the Test you will have to select a Code and Item group for creating the mapped Item. ERPNext Healthcare maps every Lab Test (every other billable healthcare service) to an Item with "Maintain Stock" set to false. This way, the Accounts Module will invoice the Item and you can see the Sales related reports of Selling Module. You can also set selling rate of the Lab Test here - this will update the Selling Price List. + +> The Standard Selling Rate field behaves similar to the Item Standard Selling Rate, updating this will not update the Selling Price List + +The Is Billable flag in Lab Test Template creates the Item, but as Disabled. Likewise, unchecking this flag will Enable the Item. + +###### Result Format +Following are the result formats available in ERPNext Healthcare + +* Single - select this format for results which require only a single input, result UOM and normal value +* Compound - allows you to configure results which require multiple input fields with corresponding event names, result UOMs and normal values +* Descriptive - this format is helpful for results which have multiple result components and corresponding result entry fields. +* Grouped - You can group test templates which are already configured and combine as a single test. For such templates select "Grouped". +* No Result - Select this if you don not need to enter or manage test result. Also, no Lab Test document will be created. e.g., Sub Tests for Grouped results. + +###### Normal values +For Single and Compound result formats, you can set the normal values. + +###### Sample +You will have to select the Sample required for the test. You can also mention the quantity of sample that needs to be collected. These details will be used when creating the Sample Collection document for the Lab Test. + +### Medical Department +To organize your clinic into departments, you can create multiple Medical Departments. You can select appropriate departments in Lab Test Template and will be included in the Lab Test result print. +> Healthcare > Setup > Medical Department > New Medical Department + +### Lab Test Sample +You can create various masters for Samples that are to be collected for a Lab Test. +> Healthcare > Setup > Lab Test Sample > New Lab Test Sample + + +### Lab Test UOM +You can create various masters for Unit of Measures to be used in Lab Test document. +> Healthcare > Setup > Lab Test UOM > New Lab Test UOM + +### Antibiotic +You can create masters for a list of Antibiotics. +> Healthcare > Setup > Antibiotic > New Antibiotic + +### Sensitivity +You can create masters for a list of Sensitivity to various Antibiotics. +> Healthcare > Setup > Sensitivity > New Sensitivity + +{next} diff --git a/erpnext/docs/user/manual/en/healthcare/setup/setup_pharmacy.md b/erpnext/docs/user/manual/en/healthcare/setup/setup_pharmacy.md new file mode 100755 index 00000000000..7f9c7199370 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/setup/setup_pharmacy.md @@ -0,0 +1,4 @@ +# Pharmacy +ERPNext Healthcare do not have a Pharmacy module - but you can configure the Stock module to manage your stock and Accounts and Buying modules for Billing and Purchases. The stock module allows you to configure Items with serial numbers and Batches. Expiry dates can be set if you turn on the "Has Batch No" check. You can also configure the auto reorder levels if required. + +{next} diff --git a/erpnext/docs/user/manual/en/healthcare/setup/setup_practice.md b/erpnext/docs/user/manual/en/healthcare/setup/setup_practice.md new file mode 100755 index 00000000000..81604826a4f --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/setup/setup_practice.md @@ -0,0 +1,38 @@ +# Clinic / Practice +Configuring ERPNext Healthcare for your practice is simple. +> Healthcare > Setup > Healthcare Settings > Out Patient Settings + +By default Patient document uses the patient name as the name, but you can opt to use a naming series if required. + +The "Manage Customer" option will enable the system to create and link a Customer whenever a new Patient is created. This Customer is used while booking all transactions. + +Here, you can also select the default Medical Code Standard to use. + +###### Collect Fee for Patient Registration +If you enable this, all new Patients you create will by default be in Disabled mode and will be enabled only after Invoicing the Registration Fee. To create Invoice and record the payment receipt, you can use the "Invoice Patient Registration" button in the Patient document. Also note that all ERPNext Healthcare documents, "Disabled" Patients are filtered out. You can set the registration fee to be collected here. + +###### Consultation Fee validity +Many healthcare facilities do not charge for follow up consultations within a time period after the first visit. You can configure the number of free visits allowed as well as the time period for free consultations here. + +### Medical Department +To organize your clinic into departments, you can create multiple Medical Departments. +> Healthcare > Setup > Medical Department > New Medical Department + +### Appointment Type +You can create masters for various type of Appointments. This is optional and not considered while appointment scheduling. +> Healthcare > Setup > Appointment Type > New Appointment Type + +### Prescription Dosage & Duration +You can configure different dosages to be used while prescribing medication to patients. You can name the Prescription dosage in anyway you want (for example, BID or I-0-I), and then set the strength of the drug and the times at which it should be administered. +> Healthcare > Setup > Prescription Dosage > New Prescription Dosage + +> Healthcare > Setup > Prescription Duration > New Prescription Duration + +### Complaint and Diagnosis +To ease the data entry while recording the encounter impression, ERPNext Healthcare allows you to save each of the Complaint / Diagnosis data you enter, from the Consultation screen itself. This way, the database keeps building a list of all complaints and diagnosis you entered. Later on, every time you start keying in, you will be able to select the previously entered word / sentence from the search field. You can also configure the masters manually. + +> Healthcare > Setup > Complaints > New Complaint + +> Healthcare > Setup > Diagnosis > New Diagnosis + +{next} diff --git a/erpnext/docs/user/manual/en/healthcare/vital_signs.md b/erpnext/docs/user/manual/en/healthcare/vital_signs.md new file mode 100755 index 00000000000..21b25d96d18 --- /dev/null +++ b/erpnext/docs/user/manual/en/healthcare/vital_signs.md @@ -0,0 +1,13 @@ +# Vital Signs +ERPNext Healthcare allows you to record Vital Signs of Patients and manage this information as part of the Patient's health record. You can create a new document and record Vital Signs of a Patient from most of the Healthcare documents or directly by +> Healthcare > Consultation > Vital Signs > New Vital Signs + +ERPNext Healthcare + +You can select the Patient for whom you are recording the vitals and start by entering each of the fields. Normal values or ranges are provided for ease of assessment. Also present is an auto BMI calculator. + +ERPNext Healthcare + +All recorded Vital Signs are made available in the Patient Medical Record and the last recorded Vital Sign is displayed on the left hand side pane for easy review. + +{next} diff --git a/erpnext/docs/user/manual/en/human-resources/training.md b/erpnext/docs/user/manual/en/human-resources/training.md index 39b9790246d..2aa06791f05 100644 --- a/erpnext/docs/user/manual/en/human-resources/training.md +++ b/erpnext/docs/user/manual/en/human-resources/training.md @@ -9,22 +9,23 @@ Schedule seminars, workshops, conferences etc using Training Event. You can also ### Inviting Employees for Event You can invite your employees to attend the event. You can do so by selecting the employees to be invited in the employee table. + By default the status of the employee will be 'Open'. -The system shall notify the employee with status 'Open' by sending a email to the office email address of the employee as mentioned in the employee master if you have selected 'Send Email' checkbox. -The status is changed to 'Invited' when an invitation email is sent to the employee by the system. -When an Employee confirms his/her presence for the event you can change the status to 'Confirmed'. Employee +When you submit the Training Event, a notifcation will be sent to the employee notifying that the Training has been scheduled. This is sent via Email Alert "Training Scheduled". You can modifiy this Email Alert to customize the message. + ### Training Result -After compleation of the training Employee Wise training results can be stored based on the Feedback received from the Trainer. +After compleation of the training Employee-wise training results can be stored based on the Feedback received from the Trainer. Employee +When the Training Result is submitted, all the employees will receive an email notifying them that they must share their feedback via "Training Feedback". This is also managed via an Email Alert, so you can customize this alert too. -### Trainig Feedback +### Training Feedback -Collect feedback regarding the event from your Employees using Training Feedback. +Employees can then share their feedback via Training Feedback. Employee diff --git a/erpnext/docs/user/manual/en/index.txt b/erpnext/docs/user/manual/en/index.txt index fff4da7d8ef..712ab8eabd3 100644 --- a/erpnext/docs/user/manual/en/index.txt +++ b/erpnext/docs/user/manual/en/index.txt @@ -9,6 +9,7 @@ manufacturing projects support human-resources +subscription customer-portal website using-erpnext diff --git a/erpnext/docs/user/manual/en/projects/timesheet/sales-invoice-from-timesheet.md b/erpnext/docs/user/manual/en/projects/timesheet/sales-invoice-from-timesheet.md index 9be2bad0e21..4dbdcbb6dc3 100644 --- a/erpnext/docs/user/manual/en/projects/timesheet/sales-invoice-from-timesheet.md +++ b/erpnext/docs/user/manual/en/projects/timesheet/sales-invoice-from-timesheet.md @@ -48,6 +48,8 @@ In the Timesheet, if "Is Billable" is checked, you will find option to create Sa Sales Invoice +Sales Invoice timesheet + ####Sales Invoice Sales Invoice has dedicated table for the Timesheet table where Timesheet details will be updated. You can select more Timesheets in this table. diff --git a/erpnext/docs/user/manual/en/schools/Assessment/assessment_criteria_group.md b/erpnext/docs/user/manual/en/schools/Assessment/assessment_criteria_group.md new file mode 100644 index 00000000000..4287ca86179 --- /dev/null +++ b/erpnext/docs/user/manual/en/schools/Assessment/assessment_criteria_group.md @@ -0,0 +1 @@ +# \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/Attendance/__init__.py b/erpnext/docs/user/manual/en/schools/Attendance/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/docs/user/manual/en/schools/Attendance/index.md b/erpnext/docs/user/manual/en/schools/Attendance/index.md new file mode 100644 index 00000000000..3153a590227 --- /dev/null +++ b/erpnext/docs/user/manual/en/schools/Attendance/index.md @@ -0,0 +1,6 @@ +#Attendance + + +### Topics + +{index} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/Attendance/index.txt b/erpnext/docs/user/manual/en/schools/Attendance/index.txt new file mode 100644 index 00000000000..8cd02627a20 --- /dev/null +++ b/erpnext/docs/user/manual/en/schools/Attendance/index.txt @@ -0,0 +1,3 @@ +student-attendance +student-leave-application +student-attendance-tool \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/Attendance/student-attendance-tool.md b/erpnext/docs/user/manual/en/schools/Attendance/student-attendance-tool.md new file mode 100644 index 00000000000..5c4ce3072a3 --- /dev/null +++ b/erpnext/docs/user/manual/en/schools/Attendance/student-attendance-tool.md @@ -0,0 +1,15 @@ +# Student Attendance Tool + +The Student Attendance tool allow you to bulk update the attendance for students based on **Student Group and Course Schedule**. + +To mark the **Attedance* based on Student Group select the group based on + +**1. Batch + 2. Course + 3. Activity ** + +Student detials will be autofetched and you can mark the attendance of the given date. + +Student Attendance + +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/Attendance/student-attendance.md b/erpnext/docs/user/manual/en/schools/Attendance/student-attendance.md new file mode 100644 index 00000000000..1b917d0ae26 --- /dev/null +++ b/erpnext/docs/user/manual/en/schools/Attendance/student-attendance.md @@ -0,0 +1,15 @@ +# Student Attendance + +Attendance doctype allows you to track and manage attendance of a student in all the days at any time. The Attendance module is designed to help teachers easily mark student attendance during class. + +Attendance Records can be created against Student on daily basis. + +To create Attendance record : + +Select the **Student, Course Schedule and Student Group** for which attendance is to be marked for the given date. Set the Status to Present/Absent and save. + +Student Attendance + +**Student Attendance tool** can be used for bulk updation of the attendance based on **Batch, Course or Activity**. + +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/Attendance/student-leave-application.md b/erpnext/docs/user/manual/en/schools/Attendance/student-leave-application.md new file mode 100644 index 00000000000..5620bdadd73 --- /dev/null +++ b/erpnext/docs/user/manual/en/schools/Attendance/student-leave-application.md @@ -0,0 +1,13 @@ +#Student Leave Application + +ERPNext allows you to record the leave application for a student. + +To create a Student Leave application record, enter the Student and the date for the leave is applied and save. + +Student Attendance + +Incase the student is not attending the school in order to participate or represent school in any event, he/she can be mark as present from the Leave Application itself. + +Once a Leave Application is recorded for a student it will not be recorded in the absent student report as he has applied for a leave. + +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/admission/index.md b/erpnext/docs/user/manual/en/schools/admission/index.md index 87cdfe5c899..b602271bbbd 100644 --- a/erpnext/docs/user/manual/en/schools/admission/index.md +++ b/erpnext/docs/user/manual/en/schools/admission/index.md @@ -1,6 +1,6 @@ # Admission -This section contains student admission related documents. +The Admission section allow you to create all records starting from Student application till the program enrollment. Below is the list of documents for Student addmission. ### Topics diff --git a/erpnext/docs/user/manual/en/schools/admission/index.txt b/erpnext/docs/user/manual/en/schools/admission/index.txt index ec9e768116a..680d7790897 100644 --- a/erpnext/docs/user/manual/en/schools/admission/index.txt +++ b/erpnext/docs/user/manual/en/schools/admission/index.txt @@ -1,2 +1,4 @@ +student-admission student-applicant -program-enrollment \ No newline at end of file +program-enrollment +program-enrollment-Tool diff --git a/erpnext/docs/user/manual/en/schools/admission/program-enrollment-tool.md b/erpnext/docs/user/manual/en/schools/admission/program-enrollment-tool.md new file mode 100644 index 00000000000..2a2fa1e58e7 --- /dev/null +++ b/erpnext/docs/user/manual/en/schools/admission/program-enrollment-tool.md @@ -0,0 +1,16 @@ +# Program Enrollment Tool + +The Program Enrollment tool allows the bulk enrollment of the **Student Applicants** in a Program. + + +You can create the the Program Enrollment for : + +1. **Student Applicants** >> List of Student Applicants will be fetched for the selected **Program** and **Academic year**. + +Student Applicant Enrollment + +2. **Program Enrollment** >> You can bulk update the **Program** for the students from one academic year to another in the same **Program** or a new **Program**. + +Student Applicant Enrollment + +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/admission/program-enrollment.md b/erpnext/docs/user/manual/en/schools/admission/program-enrollment.md index 2dcc6cb2cec..cc1308cf6c1 100644 --- a/erpnext/docs/user/manual/en/schools/admission/program-enrollment.md +++ b/erpnext/docs/user/manual/en/schools/admission/program-enrollment.md @@ -1,7 +1,12 @@ # Program Enrollment -This form allows you to enroll a student to a program. A student can be enrolled to multiple programs. +Program Enrollment describes an educational model where students must complete a defined set of courses towards their academic objective in a specified sequence. Enrollment is a program driven process in which the student select the Program to enrol for in a Academic Year. -Student Applicant Enrollment +Once a student have applied for the **Program** and the application is approved, the program enrollment is done for that student. + +Student Applicant Enrollment + +- A student can be enrolled in multiple Course for a program in a given academeic year. +- Based on the Fee structure selected at the time of enrollment Fee detials are created of the student. {next} diff --git a/erpnext/docs/user/manual/en/schools/admission/student-applicant.md b/erpnext/docs/user/manual/en/schools/admission/student-applicant.md index e67c1dc79e6..22d370939d1 100644 --- a/erpnext/docs/user/manual/en/schools/admission/student-applicant.md +++ b/erpnext/docs/user/manual/en/schools/admission/student-applicant.md @@ -18,7 +18,9 @@ You can Approve or Reject a student applicant. By accepting a student applicant and will not allow you to change the application status unless the student record is deleted. ### Student Enrollment +Once the form is submitted you can either approve or reject the application form. +Student Applicant Enrollment Once you approve a Student Applicant you can enroll them to a program. When you click the 'Enroll' buttom, the system shall create a student against that applicant and redirect you to the [Program Enrollment form](/docs/user/manual/en/schools/student/program-enrollment.html). diff --git a/erpnext/docs/user/manual/en/schools/admission/student_admission.md b/erpnext/docs/user/manual/en/schools/admission/student_admission.md new file mode 100644 index 00000000000..eeaa977912b --- /dev/null +++ b/erpnext/docs/user/manual/en/schools/admission/student_admission.md @@ -0,0 +1,13 @@ +# Student Admission + +The admission process begins with filling the admission form. The Student Admission record enables to intitate your admission process for a given **Academic year**. ERPNext admission module allow you to create an admission record which can be then published on the ERPNext generate website. + +To create a Student Admission record go to : + +**Schools** >> **Admissions** >> **Student Admission** >> + + +Student Applicant + + +Once a admission record is created it can be published on the website and the student can apply from the web portal itself. \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/index.md b/erpnext/docs/user/manual/en/schools/index.md index 8c766d6db49..d317d39bfaa 100644 --- a/erpnext/docs/user/manual/en/schools/index.md +++ b/erpnext/docs/user/manual/en/schools/index.md @@ -1,7 +1,7 @@ # Schools -The School Modules is designed to meet requirements of Schools, Colleges & Educational Institutes. +The School Modules in ERPNext is designed to meet requirements of Schools, Colleges & Educational Institutes. This is a centralized system, which maintains and updates all the activities related to an Institution. This will ease the process of each and every aspect of a School, be it Students, Admission, Examination and Fee. Fees Section diff --git a/erpnext/docs/user/manual/en/schools/index.txt b/erpnext/docs/user/manual/en/schools/index.txt index b485fdcfea5..159089c6f38 100644 --- a/erpnext/docs/user/manual/en/schools/index.txt +++ b/erpnext/docs/user/manual/en/schools/index.txt @@ -1,6 +1,7 @@ student admission +Attendance schedule fees setup -assessment \ No newline at end of file +Assessment \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/schedule/student-attendance.md b/erpnext/docs/user/manual/en/schools/schedule/student-attendance.md deleted file mode 100644 index 84e1ea8669d..00000000000 --- a/erpnext/docs/user/manual/en/schools/schedule/student-attendance.md +++ /dev/null @@ -1,7 +0,0 @@ -# Student Attendance - -Maintains attendance record of the student. Attendance Records can be created against Course Schedules. - -Student Attendance - -{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/setup/academic-term.md b/erpnext/docs/user/manual/en/schools/setup/academic-term.md index 6306444b616..b52bea71086 100644 --- a/erpnext/docs/user/manual/en/schools/setup/academic-term.md +++ b/erpnext/docs/user/manual/en/schools/setup/academic-term.md @@ -1,5 +1,9 @@ # Academic Term +An academic term (or simply "term") is a portion of an academic year, the time during which an educational institution holds classes. The schedules adopted vary widely. The academic term can be a quater, trimester or a semester. + +The **Academic term** form in ERPNext enables you to create academic terms within in a year. Based on the term schedule enter the start and end date for the schedule and generate the term for a Academic year. + Academic Term diff --git a/erpnext/docs/user/manual/en/schools/setup/academic-year.md b/erpnext/docs/user/manual/en/schools/setup/academic-year.md index 3913eb634b2..4fc5f929a56 100644 --- a/erpnext/docs/user/manual/en/schools/setup/academic-year.md +++ b/erpnext/docs/user/manual/en/schools/setup/academic-year.md @@ -1,5 +1,11 @@ # Academic Year +An academic year is a period of time which schools, colleges and universities use to measure a quantity of study. + +The **Academic year** form have the Start and End date for the Academic year. + Academic Year -{next} \ No newline at end of file +**Student group** link is given to view or add the respective groups to the Academic year. + +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/setup/course.md b/erpnext/docs/user/manual/en/schools/setup/course.md index 140131dccd2..dc10ed54b17 100644 --- a/erpnext/docs/user/manual/en/schools/setup/course.md +++ b/erpnext/docs/user/manual/en/schools/setup/course.md @@ -1,5 +1,15 @@ # Course + A course is a unit of teaching that typically lasts one academic term, is led by one or more instructors (teachers or professors), and has a fixed number of students. Students may receive a grade and academic credit after completion of the course. + +To create a **Course** enter the Course name and Code. Code for the course should be unique for every course. You can also link the department under which the course is conducted. + Course +Once a **Course** is created, a course schedule can defined for the same. + +Course + +The Course form is further linked to **Program, Student Group and Assessment Plan** doctypes. The links allow to view/create the related documents for a **Course**. + {next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/setup/index.md b/erpnext/docs/user/manual/en/schools/setup/index.md index 9de753614c8..4e0915277b7 100644 --- a/erpnext/docs/user/manual/en/schools/setup/index.md +++ b/erpnext/docs/user/manual/en/schools/setup/index.md @@ -1,5 +1,7 @@ # Setup +The Setup section of Schools module provides facility to make some basic configuration. Below are doctypes for basic configuration. + Setup Section ### Topics diff --git a/erpnext/docs/user/manual/en/schools/setup/index.txt b/erpnext/docs/user/manual/en/schools/setup/index.txt index fb9ba05d090..8fb9bb2def9 100644 --- a/erpnext/docs/user/manual/en/schools/setup/index.txt +++ b/erpnext/docs/user/manual/en/schools/setup/index.txt @@ -2,5 +2,8 @@ course program instructor room +student-category +student-batch-name academic-term -academic-year \ No newline at end of file +academic-year +school-settings \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/setup/instructor.md b/erpnext/docs/user/manual/en/schools/setup/instructor.md index 1a4d35161c2..6150f025fe7 100644 --- a/erpnext/docs/user/manual/en/schools/setup/instructor.md +++ b/erpnext/docs/user/manual/en/schools/setup/instructor.md @@ -1,5 +1,18 @@ # Instructor +An instructoe is a teacher, or professor, of a specialised subject that involves skill. + +You can create an Instructor and link it to the Employee master and a Departmemt. + Instructor +An **Instructor** is further linked to a **Course Schedule**, where you can define the schedule for a **Course** for a give date and **Room no**. + +Instructor +It is also linked to **Student group** where an **Instructor** is assigned to the Student group. + +Instructor + +An **Instructor** is also linked to an **Assesment Plan** for a Student group. The Instructor can be an Examiner or the supervisor for the assesment. + {next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/setup/program.md b/erpnext/docs/user/manual/en/schools/setup/program.md index 64513e20b0f..be10166e7be 100644 --- a/erpnext/docs/user/manual/en/schools/setup/program.md +++ b/erpnext/docs/user/manual/en/schools/setup/program.md @@ -1,5 +1,19 @@ # Program +An educational program is a program written by the institutions which determines the learning progress of each subject in all the stages of formal education. + +To create a Program go to : + +###Schools >> Setup >> Program >> New Program + +Enter a unique code for every **Program**. You can also link the **Program** to the department under which it is conducted. + Program +Add the relevant Course and the Fee details for a program. + +Program + +The Program Doctype is further linked to the **Student applicant**, **Program enrollment, Student group, Fee structre and Fee**. The links allow to view or create the related document for a Program. + {next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/setup/room.md b/erpnext/docs/user/manual/en/schools/setup/room.md index bb265e3ea21..43ddde92245 100644 --- a/erpnext/docs/user/manual/en/schools/setup/room.md +++ b/erpnext/docs/user/manual/en/schools/setup/room.md @@ -1,6 +1,17 @@ # Room +A classroom is a space (room or lab) where you want to schedule courses or examinations. A room in an educational institute can be a Class room, a laboratory or a Examination hall. + +The Room doctype allows you to record the room number and the seating capacity for a classroom. Once a room is created Course schedule link is provided in the Room doctype to view or add the course schedule for the classroom. Room +The course schedule validate the availability of the Room number and an alert message is shown if there is an overlap for the Room number for a given time slot. + +Room + +The Room number is further linked to the Assesment plan. It validates the availability of examination room for the assessment to be held for a given date and time. + +Room + {next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/setup/school-settings.md b/erpnext/docs/user/manual/en/schools/setup/school-settings.md new file mode 100644 index 00000000000..ce9e9144b7a --- /dev/null +++ b/erpnext/docs/user/manual/en/schools/setup/school-settings.md @@ -0,0 +1,15 @@ +#School Settings + +The Schools settings page allow you to setup basic settings like **Academic Year and Term** for the Schools setup. + +Student + +The checkbox to Validate Batch for Students in Student Group enables the Student Batch validation for every Student from the Program Enrollment for the **Batch** based on **Student Group** + +Student + +You can enable the validation of Course for every Student from the enrolled Courses in Program Enrollment,for Course based Student Group by checking the settings for **Validate Enrolled Course for Students in Student Group** + +Student + +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/setup/student-batch-name.md b/erpnext/docs/user/manual/en/schools/setup/student-batch-name.md new file mode 100644 index 00000000000..056f9108636 --- /dev/null +++ b/erpnext/docs/user/manual/en/schools/setup/student-batch-name.md @@ -0,0 +1,10 @@ +# Student Batch + +Student batch is a collection of students from Student Groups. **Student batch** allows you to create **Student Group** based on a batch. When a student is enrolled for a **Program**, the Student batch is selected to enroll the student for the given Program and batch + +Student + +You can also get a **Student Batch-Wise Attendance** report to view the number of student present from the Batch. + + +{next} diff --git a/erpnext/docs/user/manual/en/schools/setup/student-category.md b/erpnext/docs/user/manual/en/schools/setup/student-category.md new file mode 100644 index 00000000000..0d769279727 --- /dev/null +++ b/erpnext/docs/user/manual/en/schools/setup/student-category.md @@ -0,0 +1,12 @@ +# Student Category + +Student Category doctype allow you to classify student based various categories. In Institutions, there may be fee concession for some categories such as Handicapped students, foreign, nationals, reserved category by the government etc. + +To create Student category go to Setup >> Student Category >> New. + +We can create new student category by adding a name and save it + +Student + + +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/student/guardian.md b/erpnext/docs/user/manual/en/schools/student/guardian.md new file mode 100644 index 00000000000..7bbdbc22c5c --- /dev/null +++ b/erpnext/docs/user/manual/en/schools/student/guardian.md @@ -0,0 +1,9 @@ +#Guardian + +The Guardian doctype allows you to record the guardian details for a **Student**. + +Student + +The email id added in the **Guardian** detail can be linked to a email group for sending newsletter or announcements. + +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/student/index.txt b/erpnext/docs/user/manual/en/schools/student/index.txt index 9b31be4dc98..89704b1fc79 100644 --- a/erpnext/docs/user/manual/en/schools/student/index.txt +++ b/erpnext/docs/user/manual/en/schools/student/index.txt @@ -1,5 +1,6 @@ student +guardian student-log student-batch student-group -student-group-creation-tool +student-group-creation-tool \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/student/student-batch.md b/erpnext/docs/user/manual/en/schools/student/student-batch.md index 4987c0313f2..056f9108636 100644 --- a/erpnext/docs/user/manual/en/schools/student/student-batch.md +++ b/erpnext/docs/user/manual/en/schools/student/student-batch.md @@ -1,7 +1,10 @@ # Student Batch -Student batch is a collection of students from Student Groups. +Student batch is a collection of students from Student Groups. **Student batch** allows you to create **Student Group** based on a batch. When a student is enrolled for a **Program**, the Student batch is selected to enroll the student for the given Program and batch + +Student + +You can also get a **Student Batch-Wise Attendance** report to view the number of student present from the Batch. -Student {next} diff --git a/erpnext/docs/user/manual/en/schools/student/student-group-creation-tool.md b/erpnext/docs/user/manual/en/schools/student/student-group-creation-tool.md index 130c94491eb..1cd9b1e796f 100644 --- a/erpnext/docs/user/manual/en/schools/student/student-group-creation-tool.md +++ b/erpnext/docs/user/manual/en/schools/student/student-group-creation-tool.md @@ -1,8 +1,17 @@ # Student Group Creation Tool -This tool allows you to create student groups in bulk. You can specify multiple parameters to create them. +The Student group creation tool allows you to create student groups in bulk. +To create Student group using this tool go to -Student Group Creation Tool +##Schools >>Student >> Student Group creation tool + +Select the **Academic Term** and the **Program** for which a student group is to be created. + +Student Group Creation Tool + +By default the student group is created based on the **Course** only. The check box for "Separate course based Group for every Batch" allows you to create batchwise Student groups for a course. + +You can leave it unchecked if you don't want to consider batch while making course based groups. {next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/student/student-group.md b/erpnext/docs/user/manual/en/schools/student/student-group.md index 191e917a8bf..cf3f82c5a2d 100644 --- a/erpnext/docs/user/manual/en/schools/student/student-group.md +++ b/erpnext/docs/user/manual/en/schools/student/student-group.md @@ -1,8 +1,22 @@ # Student Group -A student group is a collection of students taking a same course. You can create Course Schedules and Examinations against a Student Group. -A student group needs to be created for every course in a particular academic term and academic year. +A student group is a collection of students taking same course. You can create Course Schedules and Examinations against a Student Group. + +A Student Group needs to be created for every course for **Academic Term** and **Academic Year**. The student group can be create based on **Batch, Course and Activity**. + +To create a Student Group go to: + +Schools >> Student >> New Student Group + +Student Group + +To create a Student group based on **Batch**, select the **Progam** and **Batch**, where as to create a Student group based on **Course**, you will only have to select the Course Code. Creating a student group based on activity allows you to group of student for events and activities happening in the institute. + +Once a student group is created you can mark attendance for the group. + +Student Group + +You can also update the **Email Group** for the Student Group. Click on Update Email Group to add all the email ids of the gaurdians in the respective email group and **Newsletter** can be created and sent to the Email group. -Student Group {next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/schools/student/student-log.md b/erpnext/docs/user/manual/en/schools/student/student-log.md index a037a231ac6..160e39fcdc4 100644 --- a/erpnext/docs/user/manual/en/schools/student/student-log.md +++ b/erpnext/docs/user/manual/en/schools/student/student-log.md @@ -1,6 +1,7 @@ # Student Log -You can make a note of student activities using student log. +The Student log Doctype enables you to add and edit addtional information for a student. +You can make a note of student activities using Student log. Logs can be categorised as 'General', 'Academic', 'Medical' or 'Achievement' Student diff --git a/erpnext/docs/user/manual/en/schools/student/student.md b/erpnext/docs/user/manual/en/schools/student/student.md index dd99a80d2f4..09e44714e68 100644 --- a/erpnext/docs/user/manual/en/schools/student/student.md +++ b/erpnext/docs/user/manual/en/schools/student/student.md @@ -1,10 +1,12 @@ # Student A Student is a person who has enrolled at your institute and you have accepted their application. -The student doctype maintains personal details of the student. - -You can view everything related to a particular student on this page. Eg : Fees, Student Group, etc +The Student doctype maintains detials like personal information, date of birth, address etc. It also records the **Guardian** and sibling details. Student +The student is enrolled in a **Program** when the application is approved. Once the enrollement is done the **Student Applicant** status is update to Admitted. -{next} +You can view every doctype created for a particular student. Eg : Fees, Student Group, etc + + +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/setting-up/articles/email-error.md b/erpnext/docs/user/manual/en/setting-up/articles/email-error.md new file mode 100644 index 00000000000..9d5abd021e7 --- /dev/null +++ b/erpnext/docs/user/manual/en/setting-up/articles/email-error.md @@ -0,0 +1,9 @@ +# Email Error in Sending or Receiving + +In ERPNext, you can customize the Incoming and Outgoing Email Gateway. On saving an Email Account, ERPNext tries establishing a connection with your email gateway. If your ERPNext account is able to connect fine, then Email Account is saved successfully. If not, then you might receive an error as below. + +Email Error + +This indicates that using login credentials and other email gateway details provided in the Email Account, ERPNext is not able to connect to your email server. Please ensure that you have entered valid email credentials for your Email Gateway. Once you have configured Email Account successfully, you should be able to send and receive emails from your ERPNext account fine. + +Note: Your ERPNext account is connected with an ERPNext email server by default. If you don't want to use your own email server, you can continue sending emails using ERPNext email server, without any configuration required in the Email Account. \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/setting-up/articles/index.txt b/erpnext/docs/user/manual/en/setting-up/articles/index.txt index d175d929dae..259229ec8d6 100644 --- a/erpnext/docs/user/manual/en/setting-up/articles/index.txt +++ b/erpnext/docs/user/manual/en/setting-up/articles/index.txt @@ -14,4 +14,5 @@ using-custom-domain-on-erpnext setup-two-factor-authentication difference-between-system-user-and-website-user outgoing-email-gateway -print-format-sections \ No newline at end of file +print-format-sections +email-error \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/setting-up/data/download-backup.md b/erpnext/docs/user/manual/en/setting-up/data/download-backup.md new file mode 100644 index 00000000000..6c98cf34f84 --- /dev/null +++ b/erpnext/docs/user/manual/en/setting-up/data/download-backup.md @@ -0,0 +1,13 @@ +# Download Backup + +In the ERPNext, you can manually download database backup. To get the latest database backup, go to: + +`Setup > Data > Download Backup` + +Backup available for the download is updated in every eight hours. Click on the link to download the backups at a given time. + +Download Backup + +By default three latest backups will be available for the download. If you want to customize no. of backups, then click on "Set Number of Backups". In the System Settings, you can set Number of Backups available for the download at a time. + +Download Backup \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/setting-up/data/index.txt b/erpnext/docs/user/manual/en/setting-up/data/index.txt index 998b5a21647..d15f9175bb3 100644 --- a/erpnext/docs/user/manual/en/setting-up/data/index.txt +++ b/erpnext/docs/user/manual/en/setting-up/data/index.txt @@ -1,2 +1,3 @@ data-import-tool bulk-rename +download-backup diff --git a/erpnext/docs/user/manual/en/setting-up/setup-wizard/index.md b/erpnext/docs/user/manual/en/setting-up/setup-wizard/index.md index 244d893c7bb..c05c4af5b6a 100644 --- a/erpnext/docs/user/manual/en/setting-up/setup-wizard/index.md +++ b/erpnext/docs/user/manual/en/setting-up/setup-wizard/index.md @@ -1,6 +1,6 @@ # Setup Wizard -The Setup Wizard helps you quickly setup your ERPnext by helping you create your company, Items, Customer, Suppliers and will also setup a basic website with this data. +The Setup Wizard helps you quickly setup ERPnext as per your locale and sets up your organisation. Here is a quick overview of the steps: diff --git a/erpnext/docs/user/manual/en/setting-up/setup-wizard/index.txt b/erpnext/docs/user/manual/en/setting-up/setup-wizard/index.txt index eb7655826e7..b2f680a388b 100644 --- a/erpnext/docs/user/manual/en/setting-up/setup-wizard/index.txt +++ b/erpnext/docs/user/manual/en/setting-up/setup-wizard/index.txt @@ -1,10 +1,5 @@ step-1-language step-2-currency-and-timezone step-3-user-details +step-4-two-factor-authentication step-5-company-details -step-6-letterhead-and-logo -step-7-add-users -step-8-tax-details -step-9-customer-names -step-10-suppliers -step-11-item diff --git a/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-10-suppliers.md b/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-10-suppliers.md deleted file mode 100644 index 364b4d6982c..00000000000 --- a/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-10-suppliers.md +++ /dev/null @@ -1,12 +0,0 @@ -# Step 10: Suppliers - -Enter a few of your Suppliers' names. - -Suppliers - ---- - -To understand Suppliers in detail visit [Supplier Master](/docs/user/manual/en/buying/supplier.html) - -{next} diff --git a/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-11-item.md b/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-11-item.md deleted file mode 100644 index 42d7e3d26d5..00000000000 --- a/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-11-item.md +++ /dev/null @@ -1,16 +0,0 @@ -# Step 11: Item Names - -In this final step, please enter the names of the Items you buy or sell. - -Add Items - -Please set the group of the item (Product / Service) and unit of measure. Don't worry you will be able to edit all of this later. - ---- - -## Thats it! - -Once you are done with the setup wizard you will see the familiar desktop page. - -{next} diff --git a/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-6-letterhead-and-logo.md b/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-6-letterhead-and-logo.md deleted file mode 100644 index 0286a3ee23e..00000000000 --- a/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-6-letterhead-and-logo.md +++ /dev/null @@ -1,23 +0,0 @@ -# Step 6: Letterhead and Logo - -Attach Company Letterhead and Company Logo. - -Company Logo and Letterhead - ---- - -### Letterhead - -A letterhead is the heading at the top of a sheet of letter paper (stationery). That heading usually consists of a name and an address, and a logo or corporate design. - -Click on the box ‘Attach Letterhead’ . Select the image file from the place it is stored and click enter. - -You may choose to skip this step if your letterhead is not ready. - -To select letterhead later through the setup module, read [Letter-head](/docs/user/manual/en/setting-up/print/letter-head.html) - -#### To "attach as web-link" - -For any attachments in ERPNext, you can also attach as a web-link. If you are using other tools like Dropbox or Google Docs to manage your files, you can set its public link. - -{next} diff --git a/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-7-add-users.md b/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-7-add-users.md deleted file mode 100644 index c92721c91b0..00000000000 --- a/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-7-add-users.md +++ /dev/null @@ -1,7 +0,0 @@ -# Step 7: Add Users - -Add other users and assign them roles based on their job responsibilities. - -Users - -{next} diff --git a/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-8-tax-details.md b/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-8-tax-details.md deleted file mode 100644 index dae88e4432a..00000000000 --- a/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-8-tax-details.md +++ /dev/null @@ -1,21 +0,0 @@ -# Step 8: Tax Details - -Enter any three types of taxes which you regularly pay. This wizard will create a tax master which will calculate the taxes as per the tax-type. - -Tax Details - -Just set the tax name and the standard percentage levied. - ---- - -Some examples of tax types are given below. - -#### VAT - -A value added tax (VAT) is a form of consumption tax. From the perspective of the buyer, it is a tax on the purchase price. From that of the seller, it is a tax only on the value added to a product, material, or a service. From an accounting point of view, by the stage of its manufacture or distribution. The manufacturer remits to the government the difference between these two amounts, and retains the rest for themselves to offset the taxes they had previously paid on the inputs. - -The purpose of VAT is to generate tax revenues to the government similar to the corporate income tax or the personal income tax. For Example: When you shop at a departmental store and avail discount on the products, the store charges you 5% extra on the total bill as the VAT. - -To setup VAT in the setup wizard , simply enter the percentage amount levied by your government. To setup VAT at a later stage read [setting-up-taxes](/docs/user/manual/en/setting-up/setting-up-taxes.html) - -{next} diff --git a/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-9-customer-names.md b/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-9-customer-names.md deleted file mode 100644 index e3489433fcb..00000000000 --- a/erpnext/docs/user/manual/en/setting-up/setup-wizard/step-9-customer-names.md +++ /dev/null @@ -1,22 +0,0 @@ -# Step 9: Customers - -Enter your Customer names and the contact person from that organisation. - - -Customers - ---- - -#### Difference between a customer name and a contact name - -A customer name is the name of the organisation and a contact name is the name of the person from that organisation. - -For Example: If American Power Mills is an organisation name and their founder Shiv Agarwal has installed ERPNext on his system. Then, - -Customer Name: American Power Mills - -Contact Name: Shiv Agarwal - -To understand Customer in detail visit [Customer Details](/docs/user/manual/en/CRM/customer.html) - -{next} diff --git a/erpnext/docs/user/manual/en/stock/articles/delivery-note-stock-error.md b/erpnext/docs/user/manual/en/stock/articles/delivery-note-stock-error.md new file mode 100644 index 00000000000..afb6de92d8c --- /dev/null +++ b/erpnext/docs/user/manual/en/stock/articles/delivery-note-stock-error.md @@ -0,0 +1,7 @@ +# Delivery Note Negative Stock Error + +**Question**: When submitting a Delivery Note, receiving a message says that item's stock is insufficient, but we have item's stock available in the Warehouse. + +**Answer**: On submission of Delivery Note, stock level is checked as on Posting Date and Posting Time of a Delivery Note. It's possible that you have stock of an Item available in the Warehouse. But if you are creating back-dated Delivery Note, and if item was not available in the warehouse on the Posting Date and Posting Time of Delivery Note, you are likely to receive an error message on the negative stock. You can refer to the Stock Ledger report to confirm the same. + +If this is the case, you should edit the Posting Date and Time of a Delivery Note, and ensure that it is after the Posting Date and Time of item's receipt entry. \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/stock/articles/index.txt b/erpnext/docs/user/manual/en/stock/articles/index.txt index df779de628a..acf1375214e 100644 --- a/erpnext/docs/user/manual/en/stock/articles/index.txt +++ b/erpnext/docs/user/manual/en/stock/articles/index.txt @@ -10,4 +10,8 @@ repack-entry serial-no-naming stock-entry-purpose stock-level-report -track-items-using-barcode \ No newline at end of file +track-items-using-barcode +stock-received-but-not-billed +return-rejected-item +item-valuation-transactions +delivery-note-stock-error \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/stock/articles/item-valuation-transactions.md b/erpnext/docs/user/manual/en/stock/articles/item-valuation-transactions.md new file mode 100644 index 00000000000..5cc6529d89a --- /dev/null +++ b/erpnext/docs/user/manual/en/stock/articles/item-valuation-transactions.md @@ -0,0 +1,17 @@ +# Item Valuation Methods and Transactions + +In ERPNext, Item's stock valuation is updated on the creation of one of the following transaction. + +1. Purchase Receipt +2. Stock Entry of type Material Receipt +3. Stock Reconciliation made for updating stock opening balance + +You can select valuation method based on which item's value will be calculated. Valuation Method can be set globally for all the items from the Stock Settings. + +Download Backup + +You can also set Valuation Method in the item master, especially when a valuation method for an item is different from the default Method. + +Download Backup + +[Click here to learn about the valuation methods available in the ERPNext, and how it works.](https://frappe.io/blog/erpnext-features/inventory-valuation-method-fifo-vs-moving-average) diff --git a/erpnext/docs/user/manual/en/stock/articles/return-rejected-item.md b/erpnext/docs/user/manual/en/stock/articles/return-rejected-item.md new file mode 100644 index 00000000000..aaaec2dea0b --- /dev/null +++ b/erpnext/docs/user/manual/en/stock/articles/return-rejected-item.md @@ -0,0 +1,13 @@ +# Return Rejected Items + +In the Purchase Receipt, you can receive the Items in the Accepted or the Rejected Warehouse. + +If you are creating Purchase Return for the items received in the Rejected Warehouse, then create return entry following these steps. + +1. In the Purchase Receipt Item table, for the item to be returned, in the Received Qty field, enter return entry in negative. +2. In the Accepted Warehouse field, set value as zero. +3. In the Rejected Warehouse field, set the quantity to be returned in negative. + +For detailed steps on how to create Purchase Return Entry for the Rejected Item, refer to the below example. + +Returning Rejected Items \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/stock/articles/stock-received-but-not-billed.md b/erpnext/docs/user/manual/en/stock/articles/stock-received-but-not-billed.md new file mode 100644 index 00000000000..25ef3b8aa68 --- /dev/null +++ b/erpnext/docs/user/manual/en/stock/articles/stock-received-but-not-billed.md @@ -0,0 +1,9 @@ +# Purpose of Stock Received but not Billed + +When purchased items are received, an accounts posting is done based on the value of the purchased items in the Stock-in-hand / fixed-assets account. When you sell and deliver those items, an expense (cost-of-goods-sold) is booked, equal to the buying cost of the items. + +As stock balance increases through Purchase Receipt, Warehouse account is debited and an adjustment account called **Stock Received But Not Billed** account is credited. At the same time, the negative expense is booked in account **Expense included in Valuation** for the amount added for valuation purpose, to avoid double expense booking. + +On receiving Bill from the supplier, you will make Purchase Invoice against a Purchase Receipt. Here **Stock Received But Not Billed** account is debited, hence nullifies the balance in the Stock Received but not Billed Account. + +The balance in the Stock Received but not Billed account indicates the value of items for which Purchase Receipt has been made, but billing is pending. \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/subscription/__init__.py b/erpnext/docs/user/manual/en/subscription/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/docs/user/manual/en/subscription/index.md b/erpnext/docs/user/manual/en/subscription/index.md new file mode 100644 index 00000000000..24d75eda95d --- /dev/null +++ b/erpnext/docs/user/manual/en/subscription/index.md @@ -0,0 +1,22 @@ +If you have a contract with the Customer where your organization gives bill to the Customer on a monthly, quarterly, half-yearly or annual basis, you can use subscription feature to make auto invoicing. + +Subscription + +#### Scenario + +Subscription for your hosted ERPNext account requires yearly renewal. We use Sales Invoice for generating proforma invoices. To automate proforma invoicing for renewal, we set original Sales Invoice on the subscription form. Recurring proforma invoice is created automatically just before customer's account is about to expire, and requires renewal. This recurring Proforma Invoice is also emailed automatically to the customer. + +To set the subscription for the sales invoice +Goto Subscription > select base doctype "Sales Invoice" > select base docname "Invoice No" > Save + +Subscription + +**From Date and To Date**: This defines contract period with the customer. + +**Repeat on Day**: If frequency is set as Monthly, then it will be day of the month on which recurring invoice will be generated. + +**Notify By Email**: If you want to notify the user about auto recurring invoice. + +**Print Format**: Select a print format to define document view which should be emailed to customer. + +**Disabled**: It will stop to make auto recurring documents against the subscription \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/subscription/index.txt b/erpnext/docs/user/manual/en/subscription/index.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/docs/user/manual/en/using-erpnext/articles/index.txt b/erpnext/docs/user/manual/en/using-erpnext/articles/index.txt index 4ca21aa969b..9dc7df3f31f 100644 --- a/erpnext/docs/user/manual/en/using-erpnext/articles/index.txt +++ b/erpnext/docs/user/manual/en/using-erpnext/articles/index.txt @@ -8,4 +8,5 @@ bulk-rename renaming-documents search-filter tree-master-renaming -pos-view \ No newline at end of file +pos-view +letter-head-in-the-report \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/using-erpnext/articles/letter-head-in-the-report.md b/erpnext/docs/user/manual/en/using-erpnext/articles/letter-head-in-the-report.md new file mode 100644 index 00000000000..4f8b2e9ea3e --- /dev/null +++ b/erpnext/docs/user/manual/en/using-erpnext/articles/letter-head-in-the-report.md @@ -0,0 +1,15 @@ +# Letter Head in the Report's Print Format + +In the reports, Letter Head is fetched from the Company master. To have company's Letter Head fetched correctly in the report, please ensure that you have updated default Letter Head in the Company master. + +`Explore > Accounts > Company` + +Company Letter + +In a Company master, if no Letter Head is set as default, then in the reports, Letter Head having Default field checked will be fetched. + +Default Letter Head + +If you are managing multiple companies in a single ERPNext account, then ensure that for each Company, default Letter Head is set in the Company master. + +After updating Letter Head in the Company master, refresh your ERPNext account, and then check the print format of a report. \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/website/articles/website-banner.md b/erpnext/docs/user/manual/en/website/articles/website-banner.md new file mode 100644 index 00000000000..514e23e58de --- /dev/null +++ b/erpnext/docs/user/manual/en/website/articles/website-banner.md @@ -0,0 +1,9 @@ +# Website Banner Resizing + +Each ERPNext account website automatically generated from it. On a website, logo is set based on logo image selected in the Setup Wizard. You can change or edit property for your company's logo from the Website Settings. + +`Explore > Website > Website Settings` + +For the exact steps on how to upload a Website Banner and resize it, please refer to the help given below. + +Website Banner image \ No newline at end of file diff --git a/erpnext/healthcare/__init__.py b/erpnext/healthcare/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/__init__.py b/erpnext/healthcare/doctype/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/antibiotic/__init__.py b/erpnext/healthcare/doctype/antibiotic/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/antibiotic/antibiotic.js b/erpnext/healthcare/doctype/antibiotic/antibiotic.js new file mode 100644 index 00000000000..42e6adb6030 --- /dev/null +++ b/erpnext/healthcare/doctype/antibiotic/antibiotic.js @@ -0,0 +1,5 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Antibiotic', { +}); diff --git a/erpnext/healthcare/doctype/antibiotic/antibiotic.json b/erpnext/healthcare/doctype/antibiotic/antibiotic.json new file mode 100644 index 00000000000..d481036ee60 --- /dev/null +++ b/erpnext/healthcare/doctype/antibiotic/antibiotic.json @@ -0,0 +1,115 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:antibiotic_name", + "beta": 1, + "creation": "2016-02-23 11:11:30.749731", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "antibiotic_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": "Antibiotic 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, + "unique": 0 + } + ], + "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:44:43.199657", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Antibiotic", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "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 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "antibiotic_name", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "antibiotic_name", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/antibiotic/antibiotic.py b/erpnext/healthcare/doctype/antibiotic/antibiotic.py new file mode 100644 index 00000000000..8236c8ab73a --- /dev/null +++ b/erpnext/healthcare/doctype/antibiotic/antibiotic.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class Antibiotic(Document): + pass diff --git a/erpnext/healthcare/doctype/antibiotic/test_antibiotic.js b/erpnext/healthcare/doctype/antibiotic/test_antibiotic.js new file mode 100644 index 00000000000..b92103d7509 --- /dev/null +++ b/erpnext/healthcare/doctype/antibiotic/test_antibiotic.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Antibiotic", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Antibiotic + () => frappe.tests.make('Antibiotic', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/antibiotic/test_antibiotic.py b/erpnext/healthcare/doctype/antibiotic/test_antibiotic.py new file mode 100644 index 00000000000..6ac4f4f9fc0 --- /dev/null +++ b/erpnext/healthcare/doctype/antibiotic/test_antibiotic.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +class TestAntibiotic(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/appointment_type/__init__.py b/erpnext/healthcare/doctype/appointment_type/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.js b/erpnext/healthcare/doctype/appointment_type/appointment_type.js new file mode 100644 index 00000000000..15916a5134a --- /dev/null +++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.js @@ -0,0 +1,5 @@ +// Copyright (c) 2016, ESS LLP and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Appointment Type', { +}); diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.json b/erpnext/healthcare/doctype/appointment_type/appointment_type.json new file mode 100644 index 00000000000..4b34892ffd3 --- /dev/null +++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.json @@ -0,0 +1,145 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "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, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "appointment_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": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "fieldname": "ip", + "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": "Is Inpatient", + "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 + } + ], + "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:57.142289", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Appointment Type", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "apply_user_permissions": 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 + } + ], + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.py b/erpnext/healthcare/doctype/appointment_type/appointment_type.py new file mode 100644 index 00000000000..1dacffab357 --- /dev/null +++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class AppointmentType(Document): + pass diff --git a/erpnext/healthcare/doctype/appointment_type/test_appointment_type.js b/erpnext/healthcare/doctype/appointment_type/test_appointment_type.js new file mode 100644 index 00000000000..93274e55c7b --- /dev/null +++ b/erpnext/healthcare/doctype/appointment_type/test_appointment_type.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Appointment Type", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Appointment Type + () => frappe.tests.make('Appointment Type', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/appointment_type/test_appointment_type.py b/erpnext/healthcare/doctype/appointment_type/test_appointment_type.py new file mode 100644 index 00000000000..04452e470e2 --- /dev/null +++ b/erpnext/healthcare/doctype/appointment_type/test_appointment_type.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and Contributors +# See license.txt +from __future__ import unicode_literals + +import unittest + +# test_records = frappe.get_test_records('Appointment Type') + +class TestAppointmentType(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/codification_table/__init__.py b/erpnext/healthcare/doctype/codification_table/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/codification_table/codification_table.json b/erpnext/healthcare/doctype/codification_table/codification_table.json new file mode 100644 index 00000000000..c4117782ffd --- /dev/null +++ b/erpnext/healthcare/doctype/codification_table/codification_table.json @@ -0,0 +1,135 @@ +{ + "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", + "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": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "code", + "fieldtype": "Read Only", + "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": "Code", + "length": 0, + "no_copy": 0, + "options": "medical_code.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": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "fieldtype": "Read Only", + "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", + "length": 0, + "no_copy": 0, + "options": "medical_code.description", + "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 + } + ], + "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-08-31 14:06:50.281545", + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/codification_table/codification_table.py b/erpnext/healthcare/doctype/codification_table/codification_table.py new file mode 100644 index 00000000000..ae29c03bbbd --- /dev/null +++ b/erpnext/healthcare/doctype/codification_table/codification_table.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class CodificationTable(Document): + pass diff --git a/erpnext/healthcare/doctype/complaint/__init__.py b/erpnext/healthcare/doctype/complaint/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/complaint/complaint.js b/erpnext/healthcare/doctype/complaint/complaint.js new file mode 100644 index 00000000000..5a2d219fe3b --- /dev/null +++ b/erpnext/healthcare/doctype/complaint/complaint.js @@ -0,0 +1,5 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Complaint', { +}); diff --git a/erpnext/healthcare/doctype/complaint/complaint.json b/erpnext/healthcare/doctype/complaint/complaint.json new file mode 100644 index 00000000000..0899a399715 --- /dev/null +++ b/erpnext/healthcare/doctype/complaint/complaint.json @@ -0,0 +1,116 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:complaints", + "beta": 1, + "creation": "2017-02-15 12:25:28.045267", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "complaints", + "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": "Complaints", + "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 + } + ], + "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:44:31.848346", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Complaint", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "apply_user_permissions": 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 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "complaints", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "complaints", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/complaint/complaint.py b/erpnext/healthcare/doctype/complaint/complaint.py new file mode 100644 index 00000000000..717f9dbb4ae --- /dev/null +++ b/erpnext/healthcare/doctype/complaint/complaint.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class Complaint(Document): + pass diff --git a/erpnext/healthcare/doctype/complaint/test_complaint.js b/erpnext/healthcare/doctype/complaint/test_complaint.js new file mode 100644 index 00000000000..9ff44d8da46 --- /dev/null +++ b/erpnext/healthcare/doctype/complaint/test_complaint.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Complaint", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Complaint + () => frappe.tests.make('Complaint', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/complaint/test_complaint.py b/erpnext/healthcare/doctype/complaint/test_complaint.py new file mode 100644 index 00000000000..2b9273a9675 --- /dev/null +++ b/erpnext/healthcare/doctype/complaint/test_complaint.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +class TestComplaint(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/consultation/__init__.py b/erpnext/healthcare/doctype/consultation/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/consultation/consultation.js b/erpnext/healthcare/doctype/consultation/consultation.js new file mode 100644 index 00000000000..15a1c7f2340 --- /dev/null +++ b/erpnext/healthcare/doctype/consultation/consultation.js @@ -0,0 +1,317 @@ +// Copyright (c) 2016, ESS LLP and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Consultation', { + setup: function(frm) { + frm.get_field('drug_prescription').grid.editable_fields = [ + {fieldname: 'drug_code', columns: 2}, + {fieldname: 'drug_name', columns: 2}, + {fieldname: 'dosage', columns: 2}, + {fieldname: 'period', columns: 2} + ]; + frm.get_field('test_prescription').grid.editable_fields = [ + {fieldname: 'test_code', columns: 2}, + {fieldname: 'test_name', columns: 4}, + {fieldname: 'test_comment', columns: 4} + ]; + }, + onload: 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 = null; + if(data.message.dob){ + age = calculate_age(data.message.dob); + } + frappe.model.set_value(frm.doctype,frm.docname, "patient_age", age); + show_details(data.message); + } + }); + } + }, + refresh: function(frm) { + refresh_field('drug_prescription'); + refresh_field('test_prescription'); + + frm.add_custom_button(__('Medical Record'), function() { + if (frm.doc.patient) { + frappe.route_options = {"patient": frm.doc.patient}; + frappe.set_route("medical_record"); + } else { + frappe.msgprint("Please select Patient"); + } + },"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.set_query("patient", function () { + return { + filters: {"disabled": 0} + }; + }); + frm.set_query("drug_code", "drug_prescription", function() { + return { + filters: { + is_stock_item:'1' + } + }; + }); + frm.set_query("test_code", "test_prescription", function() { + return { + filters: { + 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() { + return { + filters: { + // Scheduled filter for demo ... + status:['in',["Open","Scheduled"]] + } + }; + }); + if(!frm.doc.__islocal && !frm.doc.invoice && (frappe.user.has_role("Accounts User"))){ + frm.add_custom_button(__('Invoice'), function() { + btn_invoice_consultation(frm); + },__("Create")); + } + 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("physician", "read_only", frm.doc.__islocal ? 0:1); + frm.set_df_property("visit_department", "read_only", frm.doc.__islocal ? 0:1); + frm.set_df_property("consultation_date", "read_only", frm.doc.__islocal ? 0:1); + frm.set_df_property("consultation_time", "read_only", frm.doc.__islocal ? 0:1); + } +}); + +var btn_invoice_consultation = function(frm){ + var doc = frm.doc; + frappe.call({ + method: + "erpnext.healthcare.doctype.consultation.consultation.create_invoice", + args: {company: doc.company, patient: doc.patient, physician: doc.physician, consultation_id: doc.name }, + callback: function(data){ + if(!data.exc){ + if(data.message){ + frappe.set_route("Form", "Sales Invoice", data.message); + } + cur_frm.reload_doc(); + } + } + }); +}; + +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"); +}; + +var btn_create_vital_signs = function (frm) { + if(!frm.doc.patient){ + frappe.throw("Please select patient"); + } + frappe.route_options = { + "patient": frm.doc.patient, + }; + frappe.new_doc("Vital Signs"); +}; + +var show_details = function(data){ + var personal_details = ""; + var age = null; + if(data.dob){ + age = calculate_age(data.dob); + personal_details += "
Age : " + age; + } + if(data.sex) personal_details += "
Gender : " + data.sex; + if(data.blood_group) personal_details += "
Blood group : " + data.blood_group; + if(data.occupation) personal_details += "
Occupation : " + data.occupation; + if(data.email) personal_details += "
Email : " + data.email; + if(data.mobile) personal_details += "
Mobile : " + data.mobile; + + if(personal_details){ + personal_details = "

Personal Details" + personal_details + "
"; + } + + var details = ""; + if(data.allergies) details += "

Allergies : "+ data.allergies; + if(data.medication) details += "
Medication : "+ data.medication; + if(data.alcohol_current_use) details += "

Alcohol use : "+ data.alcohol_current_use; + if(data.alcohol_past_use) details += "
Alcohol past use : "+ data.alcohol_past_use; + if(data.tobacco_current_use) details += "
Tobacco use : "+ data.tobacco_current_use; + if(data.tobacco_past_use) details += "
Tobacco past use : "+ data.tobacco_past_use; + if(data.medical_history) details += "

Medical history : "+ data.medical_history; + if(data.surgical_history) details += "
Surgical history : "+ data.surgical_history; + if(data.surrounding_factors) details += "

Occupational hazards : "+ data.surrounding_factors; + if(data.other_risk_factors) details += "
Other risk factors : " + data.other_risk_factors; + if(data.patient_details) details += "

More info : " + data.patient_details; + + if(details){ + details = "

Patient Details" + details + "
"; + } + + var vitals = ""; + if(data.temperature) vitals += "
Temperature : " + data.temperature; + if(data.pulse) vitals += ", Pulse : " + data.pulse; + if(data.respiratory_rate) vitals += ", Respiratory Rate : " + data.respiratory_rate; + if(data.bp) vitals += ", BP : " + data.bp; + if(data.bmi) vitals += "
BMI : " + data.bmi; + if(data.nutrition_note) vitals += " (" + data.nutrition_note + ")"; + if(data.height) vitals += ", Height : " + data.height; + if(data.weight) vitals += ", Weight : " + data.weight; + if(data.signs_date) vitals += "
Date : " + data.signs_date; + + if(vitals){ + vitals = "

Vital Signs" + vitals + "
"; + } + details = personal_details + vitals + details; + cur_frm.fields_dict.patient_details_html.$wrapper.html(details); +}; + +frappe.ui.form.on("Consultation", "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, "physician", data.message.physician); + frappe.model.set_value(frm.doctype,frm.docname, "invoice", data.message.sales_invoice); + } + }); + } +}); + +frappe.ui.form.on("Consultation", "physician", function(frm) { + if(frm.doc.physician){ + frappe.call({ + "method": "frappe.client.get", + args: { + doctype: "Physician", + name: frm.doc.physician + }, + callback: function (data) { + frappe.model.set_value(frm.doctype,frm.docname, "visit_department",data.message.department); + } + }); + } +}); + +frappe.ui.form.on("Consultation", "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("Consultation", "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("Consultation", "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); + if(frm.doc.__islocal) show_details(data.message); + } + }); + } +}); + +frappe.ui.form.on("Drug Prescription", { + drug_code: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if(child.drug_code){ + frappe.call({ + "method": "frappe.client.get", + args: { + doctype: "Item", + name: child.drug_code, + }, + callback: function (data) { + frappe.model.set_value(cdt, cdn, 'drug_name',data.message.item_name); + } + }); + } + }, + 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'); + frappe.model.set_value(cdt, cdn, 'interval', 1); + } + }, + period: function(frm, cdt, cdn){ + frappe.model.set_value(cdt, cdn, 'update_schedule', 1); + }, + in_every: function(frm, cdt, cdn){ + frappe.model.set_value(cdt, cdn, 'update_schedule', 1); + var child = locals[cdt][cdn]; + if(child.in_every == "Hour"){ + frappe.model.set_value(cdt, cdn, 'dosage', null); + } + } +}); + + +var calculate_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)"; +}; diff --git a/erpnext/healthcare/doctype/consultation/consultation.json b/erpnext/healthcare/doctype/consultation/consultation.json new file mode 100644 index 00000000000..70482cba555 --- /dev/null +++ b/erpnext/healthcare/doctype/consultation/consultation.json @@ -0,0 +1,1046 @@ +{ + "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, + "fields": [ + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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": "C-", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fieldname": "appointment", + "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": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "type", + "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": "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": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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": 1, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "patient_age", + "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": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "patient_sex", + "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": "Gender", + "length": 0, + "no_copy": 0, + "options": "\nMale\nFemale", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "physician", + "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": "Doctor", + "length": 0, + "no_copy": 0, + "options": "Physician", + "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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "visit_department", + "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": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Today", + "fieldname": "consultation_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": "Consultation 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", + "fieldname": "consultation_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": "Consultation 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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": "Invoice", + "length": 0, + "no_copy": 0, + "options": "Sales Invoice", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "patient_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": "Patient Details", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "patient_details_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, + "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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "symptoms_select", + "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": "Complaints", + "length": 0, + "no_copy": 0, + "options": "Complaint", + "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 + }, + { + "allow_bulk_edit": 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": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fieldname": "diagnosis_select", + "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": "Diagnosis", + "length": 0, + "no_copy": 0, + "options": "Diagnosis", + "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 + }, + { + "allow_bulk_edit": 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": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": "Drug Prescription", + "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 + }, + { + "allow_bulk_edit": 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": "Drug Prescription", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "consultation_comment", + "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": "Review 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": "Consultation", + "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, + "unique": 0 + } + ], + "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": "2017-08-31 14:24:59.935498", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Consultation", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "apply_user_permissions": 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 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "patient, physician, visit_department", + "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/consultation/consultation.py b/erpnext/healthcare/doctype/consultation/consultation.py new file mode 100755 index 00000000000..b8155b9b490 --- /dev/null +++ b/erpnext/healthcare/doctype/consultation/consultation.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import getdate +import json +from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account + +class Consultation(Document): + def on_update(self): + if(self.appointment): + frappe.db.set_value("Patient Appointment",self.appointment,"status","Closed") + update_consultation_to_medical_record(self) + + def after_insert(self): + insert_consultation_to_medical_record(self) + + def on_submit(self): + if not self.diagnosis or not self.symptoms: + frappe.throw("Diagnosis and Complaints cannot be left blank") + + physician = frappe.get_doc("Physician",self.physician) + if(frappe.session.user != physician.user_id): + frappe.throw(_("You don't have permission to submit")) + +def set_sales_invoice_fields(company, patient): + sales_invoice = frappe.new_doc("Sales Invoice") + sales_invoice.customer = frappe.get_value("Patient", patient, "customer") + # patient is custom field in sales inv. + sales_invoice.due_date = getdate() + sales_invoice.is_pos = '0' + sales_invoice.debit_to = get_receivable_account(company) + + return sales_invoice + +def create_sales_invoice_item_lines(item, sales_invoice): + sales_invoice_line = sales_invoice.append("items") + sales_invoice_line.item_code = item.item_code + sales_invoice_line.item_name = item.item_name + sales_invoice_line.qty = 1.0 + sales_invoice_line.description = item.description + return sales_invoice_line + +@frappe.whitelist() +def create_drug_invoice(company, patient, prescriptions): + list_ids = json.loads(prescriptions) + if not (company or patient or prescriptions): + return False + + sales_invoice = set_sales_invoice_fields(company, patient) + sales_invoice.update_stock = 1 + + for line_id in list_ids: + line_obj = frappe.get_doc("Drug Prescription", line_id) + if line_obj: + if(line_obj.drug_code): + item = frappe.get_doc("Item", line_obj.drug_code) + sales_invoice_line = create_sales_invoice_item_lines(item, sales_invoice) + sales_invoice_line.qty = line_obj.get_quantity() + #income_account and cost_center in itemlines - by set_missing_values() + sales_invoice.set_missing_values() + return sales_invoice.as_dict() + +@frappe.whitelist() +def create_invoice(company, patient, physician, consultation_id): + if not consultation_id: + return False + sales_invoice = frappe.new_doc("Sales Invoice") + sales_invoice.customer = frappe.get_value("Patient", patient, "customer") + sales_invoice.due_date = getdate() + sales_invoice.is_pos = '0' + sales_invoice.debit_to = get_receivable_account(company) + + create_invoice_items(physician, sales_invoice, company) + + sales_invoice.save(ignore_permissions=True) + frappe.db.sql(_("""update tabConsultation set invoice='{0}' where name='{1}'""").format(sales_invoice.name, consultation_id)) + appointment = frappe.db.get_value("Consultation", consultation_id, "appointment") + if appointment: + frappe.db.set_value("Patient Appointment", appointment, "sales_invoice", sales_invoice.name) + return sales_invoice.name + +def create_invoice_items(physician, invoice, company): + item_line = invoice.append("items") + item_line.item_name = "Consulting Charges" + item_line.description = "Consulting Charges: " + physician + item_line.qty = 1 + item_line.uom = "Nos" + item_line.conversion_factor = 1 + item_line.income_account = get_income_account(physician,company) + op_consulting_charge = frappe.get_value("Physician",physician,"op_consulting_charge") + if op_consulting_charge: + item_line.rate = op_consulting_charge + item_line.amount = op_consulting_charge + return invoice + +def insert_consultation_to_medical_record(doc): + subject = set_subject_field(doc) + medical_record = frappe.new_doc("Patient Medical Record") + medical_record.patient = doc.patient + medical_record.subject = subject + medical_record.status = "Open" + medical_record.communication_date = doc.consultation_date + medical_record.reference_doctype = "Consultation" + medical_record.reference_name = doc.name + medical_record.reference_owner = doc.owner + medical_record.save(ignore_permissions=True) + +def update_consultation_to_medical_record(consultation): + medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s",(consultation.name)) + if(medical_record_id[0][0]): + subject = set_subject_field(consultation) + frappe.db.set_value("Patient Medical Record",medical_record_id[0][0],"subject",subject) + +def set_subject_field(consultation): + subject = "No Diagnosis " + if(consultation.diagnosis): + subject = "Diagnosis: \n"+ str(consultation.diagnosis)+". " + if(consultation.drug_prescription): + subject +="\nDrug(s) Prescribed. " + if(consultation.test_prescription): + subject += " Test(s) Prescribed." + + return subject diff --git a/erpnext/healthcare/doctype/consultation/consultation_dashboard.py b/erpnext/healthcare/doctype/consultation/consultation_dashboard.py new file mode 100644 index 00000000000..61708919f63 --- /dev/null +++ b/erpnext/healthcare/doctype/consultation/consultation_dashboard.py @@ -0,0 +1,15 @@ +from frappe import _ + +def get_data(): + return { + 'fieldname': 'consultation', + 'non_standard_fieldnames': { + 'Patient Medical Record': 'reference_name' + }, + 'transactions': [ + { + 'label': _('Records'), + 'items': ['Vital Signs', 'Patient Medical Record'] + }, + ] + } diff --git a/erpnext/healthcare/doctype/consultation/consultation_list.js b/erpnext/healthcare/doctype/consultation/consultation_list.js new file mode 100755 index 00000000000..ea3906db7e7 --- /dev/null +++ b/erpnext/healthcare/doctype/consultation/consultation_list.js @@ -0,0 +1,7 @@ +/* +(c) ESS 2015-16 +*/ +frappe.listview_settings['Consultation'] = { + filters:[["docstatus","!=","1"]] +}; + diff --git a/erpnext/healthcare/doctype/consultation/test_consultation.js b/erpnext/healthcare/doctype/consultation/test_consultation.js new file mode 100644 index 00000000000..803e73c3814 --- /dev/null +++ b/erpnext/healthcare/doctype/consultation/test_consultation.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Consultation", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Consultation + () => frappe.tests.make('Consultation', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/consultation/test_consultation.py b/erpnext/healthcare/doctype/consultation/test_consultation.py new file mode 100644 index 00000000000..24dc011c361 --- /dev/null +++ b/erpnext/healthcare/doctype/consultation/test_consultation.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +# test_records = frappe.get_test_records('Consultation') + +class TestConsultation(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/diagnosis/__init__.py b/erpnext/healthcare/doctype/diagnosis/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/diagnosis/diagnosis.js b/erpnext/healthcare/doctype/diagnosis/diagnosis.js new file mode 100644 index 00000000000..fb2557facea --- /dev/null +++ b/erpnext/healthcare/doctype/diagnosis/diagnosis.js @@ -0,0 +1,5 @@ +// Copyright (c) 2016, ESS LLP and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Diagnosis', { +}); diff --git a/erpnext/healthcare/doctype/diagnosis/diagnosis.json b/erpnext/healthcare/doctype/diagnosis/diagnosis.json new file mode 100644 index 00000000000..b14ce9491c6 --- /dev/null +++ b/erpnext/healthcare/doctype/diagnosis/diagnosis.json @@ -0,0 +1,116 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:diagnosis", + "beta": 1, + "creation": "2017-02-15 12:23:59.341108", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "diagnosis", + "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": "Diagnosis", + "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 + } + ], + "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:40:09.731904", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Diagnosis", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "apply_user_permissions": 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 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "diagnosis", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "diagnosis", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/diagnosis/diagnosis.py b/erpnext/healthcare/doctype/diagnosis/diagnosis.py new file mode 100644 index 00000000000..f56e79062a5 --- /dev/null +++ b/erpnext/healthcare/doctype/diagnosis/diagnosis.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class Diagnosis(Document): + pass diff --git a/erpnext/healthcare/doctype/diagnosis/test_diagnosis.js b/erpnext/healthcare/doctype/diagnosis/test_diagnosis.js new file mode 100644 index 00000000000..cacfef5b17d --- /dev/null +++ b/erpnext/healthcare/doctype/diagnosis/test_diagnosis.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Diagnosis", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Diagnosis + () => frappe.tests.make('Diagnosis', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/diagnosis/test_diagnosis.py b/erpnext/healthcare/doctype/diagnosis/test_diagnosis.py new file mode 100644 index 00000000000..c79164db37a --- /dev/null +++ b/erpnext/healthcare/doctype/diagnosis/test_diagnosis.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and Contributors +# See license.txt +from __future__ import unicode_literals + +import unittest + +# test_records = frappe.get_test_records('Diagnosis') + +class TestDiagnosis(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/dosage_form/__init__.py b/erpnext/healthcare/doctype/dosage_form/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/dosage_form/dosage_form.js b/erpnext/healthcare/doctype/dosage_form/dosage_form.js new file mode 100644 index 00000000000..60e96969f61 --- /dev/null +++ b/erpnext/healthcare/doctype/dosage_form/dosage_form.js @@ -0,0 +1,5 @@ +// Copyright (c) 2017, ESS LLP and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Dosage Form', { +}); diff --git a/erpnext/healthcare/doctype/dosage_form/dosage_form.json b/erpnext/healthcare/doctype/dosage_form/dosage_form.json new file mode 100644 index 00000000000..1de0e937d9f --- /dev/null +++ b/erpnext/healthcare/doctype/dosage_form/dosage_form.json @@ -0,0 +1,114 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:dosage_form", + "beta": 1, + "creation": "2017-04-08 12:04:33.987972", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "dosage_form", + "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": "Dosage Form", + "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 + } + ], + "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:40:19.973532", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Dosage Form", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "apply_user_permissions": 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 + } + ], + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/dosage_form/dosage_form.py b/erpnext/healthcare/doctype/dosage_form/dosage_form.py new file mode 100644 index 00000000000..046af08094c --- /dev/null +++ b/erpnext/healthcare/doctype/dosage_form/dosage_form.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class DosageForm(Document): + pass diff --git a/erpnext/healthcare/doctype/dosage_form/test_dosage_form.js b/erpnext/healthcare/doctype/dosage_form/test_dosage_form.js new file mode 100644 index 00000000000..ba54ab16faa --- /dev/null +++ b/erpnext/healthcare/doctype/dosage_form/test_dosage_form.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Dosage Form", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Dosage Form + () => frappe.tests.make('Dosage Form', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/dosage_form/test_dosage_form.py b/erpnext/healthcare/doctype/dosage_form/test_dosage_form.py new file mode 100644 index 00000000000..81cfcf6f7de --- /dev/null +++ b/erpnext/healthcare/doctype/dosage_form/test_dosage_form.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, ESS LLP and Contributors +# See license.txt +from __future__ import unicode_literals + +import unittest + +class TestDosageForm(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/dosage_strength/__init__.py b/erpnext/healthcare/doctype/dosage_strength/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/dosage_strength/dosage_strength.json b/erpnext/healthcare/doctype/dosage_strength/dosage_strength.json new file mode 100644 index 00000000000..da4f1a79646 --- /dev/null +++ b/erpnext/healthcare/doctype/dosage_strength/dosage_strength.json @@ -0,0 +1,102 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 1, + "creation": "2017-02-14 15:40:14.385707", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "strength", + "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": "Strength", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "strength_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": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "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-08-31 14:11:59.874645", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Dosage Strength", + "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": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/dosage_strength/dosage_strength.py b/erpnext/healthcare/doctype/dosage_strength/dosage_strength.py new file mode 100644 index 00000000000..e36a0160cf3 --- /dev/null +++ b/erpnext/healthcare/doctype/dosage_strength/dosage_strength.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class DosageStrength(Document): + pass diff --git a/erpnext/healthcare/doctype/drug_prescription/__init__.py b/erpnext/healthcare/doctype/drug_prescription/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json b/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json new file mode 100644 index 00000000000..c96df228c2c --- /dev/null +++ b/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json @@ -0,0 +1,381 @@ +{ + "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", + "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": 0, + "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 + }, + { + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "dosage", + "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": "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "period", + "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": "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "dosage_form", + "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": "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 + }, + { + "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 + }, + { + "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": 0, + "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 + }, + { + "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 + }, + { + "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 + }, + { + "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 + }, + { + "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": 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 + } + ], + "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-08-31 13:59:48.222282", + "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 +} \ 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 new file mode 100755 index 00000000000..0d99198fdef --- /dev/null +++ b/erpnext/healthcare/doctype/drug_prescription/drug_prescription.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class DrugPrescription(Document): + def get_quantity(self): + quantity = 0 + dosage = None + period = None + + 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) + + 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())): + quantity = period.get_days()/self.interval + elif(interval_in == 'Hour' and (self.interval < period.get_hours())): + quantity = period.get_hours()/self.interval + if quantity > 0: + return quantity + else: + return 1 diff --git a/erpnext/healthcare/doctype/fee_validity/__init__.py b/erpnext/healthcare/doctype/fee_validity/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.js b/erpnext/healthcare/doctype/fee_validity/fee_validity.js new file mode 100644 index 00000000000..7ea22136192 --- /dev/null +++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.js @@ -0,0 +1,5 @@ +// Copyright (c) 2016, ESS LLP and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Fee Validity', { +}); diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.json b/erpnext/healthcare/doctype/fee_validity/fee_validity.json new file mode 100644 index 00000000000..4bbd20b9bae --- /dev/null +++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.json @@ -0,0 +1,248 @@ +{ + "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", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "physician", + "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": "Physician", + "length": 0, + "no_copy": 0, + "options": "Physician", + "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 + }, + { + "allow_bulk_edit": 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": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + } + ], + "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:36:52.108407", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Fee Validity", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "physician,patient", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "physician", + "track_changes": 0, + "track_seen": 0 +} \ 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 new file mode 100644 index 00000000000..53a17417cee --- /dev/null +++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class FeeValidity(Document): + pass diff --git a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.js b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.js new file mode 100644 index 00000000000..0ebb97438ca --- /dev/null +++ b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Fee Validity", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Fee Validity + () => frappe.tests.make('Fee Validity', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py new file mode 100644 index 00000000000..23458729832 --- /dev/null +++ b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +from erpnext.healthcare.doctype.patient_appointment.patient_appointment import create_invoice +from frappe.utils.make_random import get_random +from frappe.utils import nowdate, add_days +# test_records = frappe.get_test_records('Fee Validity') + +class TestFeeValidity(unittest.TestCase): + def test_fee_validity(self): + patient = get_random("Patient") + physician = get_random("Physician") + + if not patient: + patient = frappe.new_doc("Patient") + patient.patient_name = "Test Patient" + patient.sex = "Male" + patient.save(ignore_permissions = True) + patient = patient.name + + if not physician: + physician = frappe.new_doc("Physician") + physician.first_name= "Amit Jain" + physician.save(ignore_permissions = True) + physician = physician.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, physician, nowdate()) + invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice") + self.assertEqual(invoice, None) + create_invoice(frappe.defaults.get_global_default("company"), physician, patient, appointment.name, appointment.appointment_date) + appointment = create_appointment(patient, physician, add_days(nowdate(), 4)) + invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice") + self.assertTrue(invoice) + appointment = create_appointment(patient, physician, add_days(nowdate(), 5)) + invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice") + self.assertEqual(invoice, None) + appointment = create_appointment(patient, physician, add_days(nowdate(), 10)) + invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice") + self.assertEqual(invoice, None) + +def create_appointment(patient, physician, appointment_date): + appointment = frappe.new_doc("Patient Appointment") + appointment.patient = patient + appointment.physician = physician + appointment.appointment_date = appointment_date + appointment.save(ignore_permissions = True) + return appointment diff --git a/erpnext/healthcare/doctype/healthcare.py b/erpnext/healthcare/doctype/healthcare.py new file mode 100644 index 00000000000..6fd2015fa2e --- /dev/null +++ b/erpnext/healthcare/doctype/healthcare.py @@ -0,0 +1,5 @@ +from __future__ import unicode_literals + +def get_data(): + + return [] diff --git a/erpnext/healthcare/doctype/healthcare_settings/__init__.py b/erpnext/healthcare/doctype/healthcare_settings/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js new file mode 100644 index 00000000000..75b0584f1f0 --- /dev/null +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js @@ -0,0 +1,25 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Healthcare Settings', { + setup: function(frm) { + frm.set_query('account', 'receivable_account', function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + 'account_type': 'Receivable', + 'company': d.company, + } + }; + }); + frm.set_query('account', 'income_account', function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + 'root_type': 'Income', + 'company': d.company, + } + }; + }); + } +}); diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json new file mode 100644 index 00000000000..8681641fde5 --- /dev/null +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json @@ -0,0 +1,1073 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 1, + "creation": "2017-05-09 11:26:22.337760", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sb_op_settings", + "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": "Out Patient Settings", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "patient_master_name", + "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": "Patient Name By", + "length": 0, + "no_copy": 0, + "options": "Patient Name\nNaming Series", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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", + "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": "Manage Customer", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "default_medical_code_standard", + "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": "Default Medical Code Standard", + "length": 0, + "no_copy": 0, + "options": "Medical Code Standard", + "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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "collect_registration_fee", + "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": "Collect Fee for Patient Registration", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "collect_registration_fee", + "fieldname": "registration_fee", + "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": "Registration Fee", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": "Consultations in valid days", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "valid_days", + "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": "Valid number of days", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "out_patient_sms_alerts", + "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": "Out Patient SMS Alerts", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "reg_sms", + "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": "Patient Registration", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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", + "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": "Registration 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "app_con", + "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": "Appointment Confirmation", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Hello {{doc.patient}}, You have scheduled an appointment with {{doc.physician}} by {{doc.start_dt}} at {{doc.company}}.\nThank you, Good day!", + "depends_on": "app_con", + "fieldname": "app_con_msg", + "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": "Confirmation 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "app_con", + "description": "Do not confirm if appointment is created for the same day", + "fieldname": "no_con", + "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": "Avoid Confirmation", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_16", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "app_rem", + "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": "Appointment Reminder", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Hello {{doc.patient}}, You have an appointment with {{doc.physician}} by {{doc.appointment_time}} at {{doc.company}}.\nThank you, Good day!\n", + "depends_on": "app_rem", + "fieldname": "app_rem_msg", + "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": "Reminder 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "app_rem", + "fieldname": "rem_before", + "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": "Remind Before", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "description": "Default income accounts to be used if not set in Physician to book Consultation charges.", + "fieldname": "sb_in_ac", + "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": "Income Account", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "income_account", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "description": "Default receivable accounts to be used if not set in Patient to book Consultation charges.", + "fieldname": "sb_r_ac", + "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": "Receivable Account", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "receivable_account", + "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": "Receivable 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "sb_lab_settings", + "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": "Laboratory Settings", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "Create documents for sample collection", + "fieldname": "require_sample_collection", + "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": "Manage 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "require_test_result_approval", + "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": "Require Lab Test Approval", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_34", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "fieldname": "employee_name_and_designation_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": "Employee name and designation in print", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.employee_name_and_designation_in_print == '0'\n", + "fieldname": "custom_signature_in_print", + "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": "Custom Signature in Print", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "laboratory_sms_alerts", + "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": "Laboratory SMS Alerts", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Hello {{doc.patient}}, Your {{doc.test_name}} result is ready with {{doc.company }}. \nThank You, Good day!", + "fieldname": "sms_printed", + "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": "Result Printed", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_28", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Hello {{doc.patient}}, Your {{doc.test_name}} result has been emailed to {{doc.email}}. \n{{doc.company }}. \nThank You, Good day!", + "fieldname": "sms_emailed", + "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": "Result Emailed", + "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 + } + ], + "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": "2017-08-31 13:25:48.193218", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Healthcare Settings", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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": "Healthcare Administrator", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 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", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py new file mode 100644 index 00000000000..7206e4b58c3 --- /dev/null +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, 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 +from frappe.core.doctype.sms_settings.sms_settings import send_sms +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"]: + 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") + +@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 + +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) + + +def get_receivable_account(company): + receivable_account = get_account(None, "receivable_account", "Healthcare Settings", company) + if receivable_account: + return receivable_account + return frappe.db.get_value("Company", company, "default_receivable_account") + +def get_income_account(physician, company): + if(physician): + income_account = get_account("Physician", None, physician, 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.db.get_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") diff --git a/erpnext/healthcare/doctype/healthcare_settings/test_healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/test_healthcare_settings.js new file mode 100644 index 00000000000..ca10925e598 --- /dev/null +++ b/erpnext/healthcare/doctype/healthcare_settings/test_healthcare_settings.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Healthcare Settings", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Healthcare Settings + () => frappe.tests.make('Healthcare Settings', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/lab_prescription/__init__.py b/erpnext/healthcare/doctype/lab_prescription/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json b/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json new file mode 100644 index 00000000000..2d1a8d6b255 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json @@ -0,0 +1,223 @@ +{ + "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, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_code", + "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": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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": "test_code.test_name", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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": "Invoice", + "length": 0, + "no_copy": 0, + "options": "Sales Invoice", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_comment", + "fieldtype": "Small Text", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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": 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 + } + ], + "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-08-31 13:47:22.789095", + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/lab_prescription/lab_prescription.py b/erpnext/healthcare/doctype/lab_prescription/lab_prescription.py new file mode 100644 index 00000000000..b788a0db7e7 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_prescription/lab_prescription.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class LabPrescription(Document): + pass diff --git a/erpnext/healthcare/doctype/lab_test/__init__.py b/erpnext/healthcare/doctype/lab_test/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js new file mode 100644 index 00000000000..2a453cd898a --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test/lab_test.js @@ -0,0 +1,323 @@ +// Copyright (c) 2016, ESS and contributors +// For license information, please see license.txt + +cur_frm.cscript.custom_refresh = function(doc) { + cur_frm.toggle_display("sb_sensitivity", doc.sensitivity_toggle=="1"); + cur_frm.toggle_display("sb_special", doc.special_toggle=="1"); + cur_frm.toggle_display("sb_normal", doc.normal_toggle=="1"); +}; + +frappe.ui.form.on('Lab Test', { + setup: function(frm) { + frm.get_field('normal_test_items').grid.editable_fields = [ + {fieldname: 'test_name', columns: 3}, + {fieldname: 'test_event', columns: 2}, + {fieldname: 'result_value', columns: 2}, + {fieldname: 'test_uom', columns: 1}, + {fieldname: 'normal_range', columns: 2} + ]; + frm.get_field('special_test_items').grid.editable_fields = [ + {fieldname: 'test_particulars', columns: 3}, + {fieldname: 'result_value', columns: 7} + ]; + }, + refresh : function(frm){ + refresh_field('normal_test_items'); + refresh_field('special_test_items'); + if(!frm.doc.__islocal && !frm.doc.invoice && frappe.user.has_role("Accounts User")){ + frm.add_custom_button(__('Make Invoice'), function() { + make_invoice(frm); + }); + } + if(frm.doc.__islocal){ + frm.add_custom_button(__('Get from Consultation'), function () { + 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")){ + frm.add_custom_button(__('Approve'), function() { + status_update(1,frm); + }); + frm.add_custom_button(__('Reject'), function() { + status_update(0,frm); + }); + } + if(frm.doc.docstatus==1 && frm.doc.sms_sent==0){ + frm.add_custom_button(__('Send SMS'), function() { + frappe.call({ + method: "erpnext.healthcare.doctype.healthcare_settings.healthcare_settings.get_sms_text", + args:{doc: frm.doc.name}, + callback: function(r) { + if(!r.exc) { + var emailed = r.message.emailed; + var printed = r.message.printed; + make_dialog(frm, emailed, printed); + } + } + }); + }); + } + + }, + onload: function (frm) { + frm.add_fetch("physician", "department", "department"); + if(frm.doc.employee){ + frappe.call({ + method: "frappe.client.get", + args:{ + doctype: "Employee", + name: frm.doc.employee + }, + callback: function(arg){ + frappe.model.set_value(frm.doctype,frm.docname,"employee_name", arg.message.employee_name); + frappe.model.set_value(frm.doctype,frm.docname,"employee_designation", arg.message.designation); + } + }); + } + } +}); + +frappe.ui.form.on("Lab Test", "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 = 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_sex", data.message.sex); + frappe.model.set_value(frm.doctype,frm.docname, "email", data.message.email); + frappe.model.set_value(frm.doctype,frm.docname, "mobile", data.message.mobile); + frappe.model.set_value(frm.doctype,frm.docname, "report_preference", data.message.report_preference); + } + }); + } +}); + +frappe.ui.form.on('Normal Test Items', { + normal_test_items_remove: function() { + frappe.msgprint("Not permitted, configure Lab Test Template as required"); + cur_frm.reload_doc(); + } +}); + +frappe.ui.form.on('Special Test Items', { + special_test_items_remove: function() { + frappe.msgprint("Not permitted, configure Lab Test Template as required"); + cur_frm.reload_doc(); + } +}); + +var status_update = function(approve,frm){ + var doc = frm.doc; + var status = null; + if(approve == 1){ + status = "Approved"; + } + else { + status = "Rejected"; + } + frappe.call({ + method: "erpnext.healthcare.doctype.lab_test.lab_test.update_status", + args: {status: status, name: doc.name}, + callback: function(){ + cur_frm.reload_doc(); + } + }); +}; + +var get_lab_test_prescribed = function(frm){ + if(frm.doc.patient){ + frappe.call({ + method: "erpnext.healthcare.doctype.lab_test.lab_test.get_lab_test_prescribed", + args: {patient: frm.doc.patient}, + callback: function(r){ + show_lab_tests(frm, r.message); + } + }); + } + else{ + frappe.msgprint("Please select Patient to get Lab Tests"); + } +}; + +var show_lab_tests = function(frm, result){ + var d = new frappe.ui.Dialog({ + title: __("Lab Test Prescriptions"), + fields: [ + { + fieldtype: "HTML", fieldname: "lab_test" + } + ] + }); + var html_field = d.fields_dict.lab_test.$wrapper; + html_field.empty(); + $.each(result, function(x, y){ + var row = $(repl('
\ +
%(lab_test)s
\ +
%(consultation)s
\ +
%(physician)s
\ +
%(date)s
\ +
\ +
', {name:y[0], lab_test: y[1], consultation:y[2], invoice:y[3], physician:y[4], date:y[5]})).appendTo(html_field); + row.find("a").click(function() { + frm.doc.template = $(this).attr("data-lab-test"); + frm.doc.prescription = $(this).attr("data-name"); + frm.doc.physician = $(this).attr("data-physician"); + frm.set_df_property("template", "read_only", 1); + frm.set_df_property("patient", "read_only", 1); + frm.set_df_property("physician", "read_only", 1); + if($(this).attr("data-invoice") != 'null'){ + frm.doc.invoice = $(this).attr("data-invoice"); + refresh_field("invoice"); + }else { + frm.doc.invoice = ""; + refresh_field("invoice"); + } + + refresh_field("template"); + d.hide(); + return false; + }); + }); + if(!result){ + var msg = "There are no Lab Test prescribed for "+frm.doc.patient; + $(repl('
%(msg)s
', {msg: msg})).appendTo(html_field); + } + d.show(); +}; + +var make_invoice = function(frm){ + var doc = frm.doc; + frappe.call({ + method: "erpnext.healthcare.doctype.lab_test.lab_test.create_invoice", + args: {company:doc.company, patient:doc.patient, lab_tests: [doc.name], prescriptions:[]}, + callback: function(r){ + if(!r.exc){ + if(r.message){ + /* frappe.show_alert(__('Sales Invoice {0} created', + ['' + r.message+ ''])); */ + frappe.set_route("Form", "Sales Invoice", r.message); + } + cur_frm.reload_doc(); + } + } + }); +}; + +cur_frm.cscript.custom_before_submit = function(doc) { + if(doc.normal_test_items){ + for(let result in doc.normal_test_items){ + if(!doc.normal_test_items[result].result_value && doc.normal_test_items[result].require_result_value == 1){ + frappe.msgprint("Please input all required Result Value(s)"); + throw("Error"); + } + } + } + if(doc.special_test_items){ + for(let result in doc.special_test_items){ + if(!doc.special_test_items[result].result_value && doc.special_test_items[result].require_result_value == 1){ + frappe.msgprint("Please input all required Result Value(s)"); + throw("Error"); + } + } + } +}; + +var make_dialog = function(frm, emailed, printed) { + var number = frm.doc.mobile; + + var dialog = new frappe.ui.Dialog({ + title: 'Send SMS', + width: 400, + fields: [ + {fieldname:'sms_type', fieldtype:'Select', label:'Type', options: + ['Emailed','Printed']}, + {fieldname:'number', fieldtype:'Data', label:'Mobile Number', reqd:1}, + {fieldname:'messages_label', fieldtype:'HTML'}, + {fieldname:'messages', fieldtype:'HTML', reqd:1} + ], + primary_action_label: __("Send"), + primary_action : function(){ + var values = dialog.fields_dict; + if(!values){ + return; + } + send_sms(values,frm); + dialog.hide(); + } + }); + if(frm.doc.report_preference == "Email"){ + dialog.set_values({ + 'sms_type': "Emailed", + 'number': number + }); + dialog.fields_dict.messages_label.html("Message".bold()); + dialog.fields_dict.messages.html(emailed); + }else{ + dialog.set_values({ + 'sms_type': "Printed", + 'number': number + }); + dialog.fields_dict.messages_label.html("Message".bold()); + dialog.fields_dict.messages.html(printed); + } + var fd = dialog.fields_dict; + $(fd.sms_type.input).change(function(){ + if(dialog.get_value('sms_type') == 'Emailed'){ + dialog.set_values({ + 'number': number + }); + fd.messages_label.html("Message".bold()); + fd.messages.html(emailed); + }else{ + dialog.set_values({ + 'number': number + }); + fd.messages_label.html("Message".bold()); + fd.messages.html(printed); + } + }); + dialog.show(); +}; + +var send_sms = function(v,frm){ + var doc = frm.doc; + var number = v.number.last_value; + var messages = v.messages.wrapper.innerText; + frappe.call({ + method: "frappe.core.doctype.sms_settings.sms_settings.send_sms", + args: { + receiver_list: [number], + msg: messages + }, + callback: function(r) { + if(r.exc) {frappe.msgprint(r.exc); return; } + else{ + frappe.call({ + method: "erpnext.healthcare.doctype.lab_test.lab_test.update_lab_test_print_sms_email_status", + args: {print_sms_email: "sms_sent", name: doc.name}, + callback: function(){ + cur_frm.reload_doc(); + } + }); + } + } + }); +}; + +var calculate_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)"; +}; diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json new file mode 100644 index 00000000000..93e40ccb716 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test/lab_test.json @@ -0,0 +1,1470 @@ +{ + "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, + "fields": [ + { + "allow_bulk_edit": 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": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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": "Invoice", + "length": 0, + "no_copy": 0, + "options": "Sales Invoice", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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": 1, + "set_only_once": 1, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "patient_name", + "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": "Patient Name", + "length": 0, + "no_copy": 0, + "options": "patient.patient_name", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "physician", + "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": "Doctor", + "length": 0, + "no_copy": 0, + "options": "Physician", + "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 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "options": "Employee", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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": 0, + "options": "employee.employee_name", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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": 0, + "options": "employee.designation", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "options": "User", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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": "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": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_comment", + "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": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "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": "2017-08-31 13:52:14.180774", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Lab Test", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "apply_user_permissions": 0, + "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, + "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": "LabTest Approver", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "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": "Physician", + "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,invoice,physician,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 new file mode 100644 index 00000000000..0daf9cba73d --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -0,0 +1,295 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document +import json +from frappe.utils import getdate +from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account +from frappe import _ + +class LabTest(Document): + def on_submit(self): + frappe.db.set_value(self.doctype,self.name,"submitted_date", getdate()) + insert_lab_test_to_medical_record(self) + frappe.db.set_value("Lab Test", self.name, "status", "Completed") + + def on_cancel(self): + delete_lab_test_from_medical_record(self) + frappe.db.set_value("Lab Test", self.name, "status", "Cancelled") + self.reload() + + def on_update(self): + if(self.sensitivity_test_items): + sensitivity = sorted(self.sensitivity_test_items, key=lambda x: x.antibiotic_sensitivity) + for i, item in enumerate(sensitivity): + item.idx = i+1 + self.sensitivity_test_items = sensitivity + + def after_insert(self): + if(self.prescription): + frappe.db.set_value("Lab Prescription", self.prescription, "test_created", 1) + if not self.test_name and self.template: + self.load_test_from_template() + self.reload() + + def load_test_from_template(self): + lab_test = self + create_test_from_template(lab_test) + self.reload() + +def create_test_from_template(lab_test): + template = frappe.get_doc("Lab Test Template", lab_test.template) + patient = frappe.get_doc("Patient", lab_test.patient) + + lab_test.test_name = template.test_name + lab_test.result_date = getdate() + lab_test.department = template.department + lab_test.test_group = template.test_group + + lab_test = create_sample_collection(lab_test, template, patient, None) + lab_test = load_result_format(lab_test, template, None, None) + +@frappe.whitelist() +def update_status(status, name): + frappe.db.sql("""update `tabLab Test` set status=%s, approved_date=%s where name = %s""", (status, getdate(), name)) + +@frappe.whitelist() +def update_lab_test_print_sms_email_status(print_sms_email, name): + frappe.db.set_value("Lab Test",name,print_sms_email,1) + +def create_lab_test_doc(invoice, consultation, patient, template): + #create Test Result for template, copy vals from Invoice + lab_test = frappe.new_doc("Lab Test") + if(invoice): + lab_test.invoice = invoice + if(consultation): + lab_test.physician = consultation.physician + lab_test.patient = patient.name + lab_test.patient_age = patient.get_age() + lab_test.patient_sex = patient.sex + lab_test.email = patient.email + lab_test.mobile = patient.mobile + lab_test.department = template.department + lab_test.test_name = template.test_name + lab_test.template = template.name + lab_test.test_group = template.test_group + lab_test.result_date = getdate() + lab_test.report_preference = patient.report_preference + return lab_test + +def create_normals(template, lab_test): + lab_test.normal_toggle = "1" + normal = lab_test.append("normal_test_items") + normal.test_name = template.test_name + normal.test_uom = template.test_uom + normal.normal_range = template.test_normal_range + normal.require_result_value = 1 + normal.template = template.name + +def create_compounds(template, lab_test, is_group): + lab_test.normal_toggle = "1" + for normal_test_template in template.normal_test_templates: + normal = lab_test.append("normal_test_items") + if is_group: + normal.test_event = normal_test_template.test_event + else: + normal.test_name = normal_test_template.test_event + + normal.test_uom = normal_test_template.test_uom + normal.normal_range = normal_test_template.normal_range + normal.require_result_value = 1 + normal.template = template.name + +def create_specials(template, lab_test): + lab_test.special_toggle = "1" + if(template.sensitivity): + lab_test.sensitivity_toggle = "1" + for special_test_template in template.special_test_template: + special = lab_test.append("special_test_items") + special.test_particulars = special_test_template.particulars + special.require_result_value = 1 + special.template = template.name + +def create_sample_doc(template, patient, invoice): + if(template.sample): + sample_exist = 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.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) + + else: + #create Sample Collection for template, copy vals from Invoice + sample_collection = frappe.new_doc("Sample Collection") + if(invoice): + sample_collection.invoice = invoice + sample_collection.patient = patient.name + sample_collection.patient_age = patient.get_age() + 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.test_name+"\n"+"Collection Detials:\n\t"+template.sample_collection_details + sample_collection.save(ignore_permissions=True) + + return sample_collection + +@frappe.whitelist() +def create_lab_test_from_desk(patient, template, prescription, invoice=None): + lab_test_exist = frappe.db.exists({ + "doctype": "Lab Test", + "prescription": prescription + }) + if lab_test_exist: + return + template = frappe.get_doc("Lab Test Template", template) + #skip the loop if there is no test_template for Item + if not (template): + return + patient = frappe.get_doc("Patient", patient) + consultation_id = frappe.get_value("Lab Prescription", prescription, "parent") + consultation = frappe.get_doc("Consultation", consultation_id) + lab_test = create_lab_test(patient, template, prescription, consultation, invoice) + return lab_test.name + +def create_sample_collection(lab_test, template, patient, invoice): + if(frappe.db.get_value("Healthcare Settings", None, "require_sample_collection") == "1"): + sample_collection = create_sample_doc(template, patient, invoice) + if(sample_collection): + lab_test.sample = sample_collection.name + return lab_test + +def load_result_format(lab_test, template, prescription, invoice): + if(template.test_template_type == 'Single'): + create_normals(template, lab_test) + elif(template.test_template_type == 'Compound'): + create_compounds(template, lab_test, False) + elif(template.test_template_type == 'Descriptive'): + create_specials(template, lab_test) + elif(template.test_template_type == 'Grouped'): + #iterate for each template in the group and create one result for all. + for test_group in template.test_groups: + #template_in_group = None + if(test_group.test_template): + template_in_group = frappe.get_doc("Lab Test Template", + test_group.test_template) + if(template_in_group): + if(template_in_group.test_template_type == 'Single'): + create_normals(template_in_group, lab_test) + elif(template_in_group.test_template_type == 'Compound'): + normal_heading = lab_test.append("normal_test_items") + normal_heading.test_name = template_in_group.test_name + normal_heading.require_result_value = 0 + normal_heading.template = template_in_group.name + create_compounds(template_in_group, lab_test, True) + elif(template_in_group.test_template_type == 'Descriptive'): + special_heading = lab_test.append("special_test_items") + special_heading.test_name = template_in_group.test_name + special_heading.require_result_value = 0 + special_heading.template = template_in_group.name + create_specials(template_in_group, lab_test) + else: + normal = lab_test.append("normal_test_items") + normal.test_name = test_group.group_event + normal.test_uom = test_group.group_test_uom + normal.normal_range = test_group.group_test_normal_range + normal.require_result_value = 1 + normal.template = template.name + if(template.test_template_type != 'No Result'): + if(prescription): + lab_test.prescription = prescription + if(invoice): + frappe.db.set_value("Lab Prescription", prescription, "invoice", invoice) + lab_test.save(ignore_permissions=True) # insert the result + return lab_test + +def create_lab_test(patient, template, prescription, consultation, invoice): + lab_test = create_lab_test_doc(invoice, consultation, patient, template) + lab_test = create_sample_collection(lab_test, template, patient, invoice) + lab_test = load_result_format(lab_test, template, prescription, invoice) + return lab_test + +@frappe.whitelist() +def get_employee_by_user_id(user_id): + emp_id = frappe.db.get_value("Employee",{"user_id":user_id}) + employee = frappe.get_doc("Employee",emp_id) + return employee + +def insert_lab_test_to_medical_record(doc): + subject = str(doc.test_name) + if(doc.test_comment): + subject += ", \n"+str(doc.test_comment) + medical_record = frappe.new_doc("Patient Medical Record") + medical_record.patient = doc.patient + medical_record.subject = subject + medical_record.status = "Open" + medical_record.communication_date = doc.result_date + medical_record.reference_doctype = "Lab Test" + medical_record.reference_name = doc.name + medical_record.reference_owner = doc.owner + medical_record.save(ignore_permissions=True) + +def delete_lab_test_from_medical_record(self): + medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s",(self.name)) + + if(medical_record_id[0][0]): + frappe.delete_doc("Patient Medical Record", medical_record_id[0][0]) + +def create_item_line(test_code, sales_invoice): + if test_code: + item = frappe.get_doc("Item", test_code) + if item: + if not item.disabled: + sales_invoice_line = sales_invoice.append("items") + sales_invoice_line.item_code = item.item_code + sales_invoice_line.item_name = item.item_name + sales_invoice_line.qty = 1.0 + sales_invoice_line.description = item.description + +@frappe.whitelist() +def create_invoice(company, patient, lab_tests, prescriptions): + test_ids = json.loads(lab_tests) + line_ids = json.loads(prescriptions) + if not test_ids and not line_ids: + return + sales_invoice = frappe.new_doc("Sales Invoice") + sales_invoice.customer = frappe.get_value("Patient", patient, "customer") + sales_invoice.due_date = getdate() + sales_invoice.is_pos = '0' + sales_invoice.debit_to = get_receivable_account(company) + for line in line_ids: + test_code = frappe.get_value("Lab Prescription", line, "test_code") + create_item_line(test_code, sales_invoice) + for test in test_ids: + template = frappe.get_value("Lab Test", test, "template") + test_code = frappe.get_value("Lab Test Template", template, "item") + create_item_line(test_code, sales_invoice) + sales_invoice.set_missing_values() + sales_invoice.save() + #set invoice in lab test + for test in test_ids: + frappe.db.set_value("Lab Test", test, "invoice", sales_invoice.name) + prescription = frappe.db.get_value("Lab Test", test, "prescription") + if prescription: + frappe.db.set_value("Lab Prescription", prescription, "invoice", sales_invoice.name) + #set invoice in prescription + for line in line_ids: + frappe.db.set_value("Lab Prescription", line, "invoice", sales_invoice.name) + return sales_invoice.name + +@frappe.whitelist() +def get_lab_test_prescribed(patient): + return frappe.db.sql(_("""select cp.name, cp.test_code, cp.parent, cp.invoice, ct.physician, ct.consultation_date from tabConsultation ct, + `tabLab Prescription` cp where ct.patient='{0}' and cp.parent=ct.name and cp.test_created=0""").format(patient)) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test_list.js b/erpnext/healthcare/doctype/lab_test/lab_test_list.js new file mode 100644 index 00000000000..c36c115f99b --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test/lab_test_list.js @@ -0,0 +1,15 @@ +/* +(c) ESS 2015-16 +*/ +frappe.listview_settings['Lab Test'] = { + add_fields: ["name", "status", "invoice"], + filters:[["docstatus","=","0"]], + get_indicator: function(doc) { + if(doc.status=="Approved"){ + return [__("Approved"), "green", "status,=,Approved"]; + } + if(doc.status=="Rejected"){ + return [__("Rejected"), "yellow", "status,=,Rejected"]; + } + } +}; diff --git a/erpnext/healthcare/doctype/lab_test/test_lab_test.js b/erpnext/healthcare/doctype/lab_test/test_lab_test.js new file mode 100644 index 00000000000..57cb22b2699 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test/test_lab_test.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Lab Test", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Lab Test + () => frappe.tests.make('Lab Test', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/lab_test/test_lab_test.py b/erpnext/healthcare/doctype/lab_test/test_lab_test.py new file mode 100644 index 00000000000..4131ad99b74 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test/test_lab_test.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +# test_records = frappe.get_test_records('Lab Test') + +class TestLabTest(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/lab_test_groups/__init__.py b/erpnext/healthcare/doctype/lab_test_groups/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/lab_test_groups/lab_test_groups.json b/erpnext/healthcare/doctype/lab_test_groups/lab_test_groups.json new file mode 100644 index 00000000000..d5607f05616 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_groups/lab_test_groups.json @@ -0,0 +1,291 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 1, + "creation": "2016-03-29 17:37:29.913583", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Add Test", + "depends_on": "", + "fieldname": "template_or_new_line", + "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": "", + "length": 0, + "no_copy": 0, + "options": "Add Test\nAdd new line", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.template_or_new_line == 'Add Test'", + "fieldname": "test_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": "Test Name", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_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", + "length": 0, + "no_copy": 0, + "options": "test_template.test_rate", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_description", + "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", + "length": 0, + "no_copy": 0, + "options": "test_template.test_description", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.template_or_new_line == 'Add new line'", + "fieldname": "group_event", + "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": "Event", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.template_or_new_line =='Add new line'", + "fieldname": "group_test_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": "Lab Test 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.template_or_new_line == 'Add new line'", + "fieldname": "group_test_normal_range", + "fieldtype": "Long 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": "Normal Range", + "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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + } + ], + "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-08-31 14:07:35.188661", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Lab Test Groups", + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/lab_test_groups/lab_test_groups.py b/erpnext/healthcare/doctype/lab_test_groups/lab_test_groups.py new file mode 100644 index 00000000000..c67531c179d --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_groups/lab_test_groups.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class LabTestGroups(Document): + pass diff --git a/erpnext/healthcare/doctype/lab_test_sample/__init__.py b/erpnext/healthcare/doctype/lab_test_sample/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.js b/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.js new file mode 100644 index 00000000000..a5f4b4de996 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.js @@ -0,0 +1,5 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Lab Test Sample', { +}); diff --git a/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.json b/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.json new file mode 100644 index 00000000000..46c04c696e2 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.json @@ -0,0 +1,145 @@ +{ + "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, + "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 + }, + { + "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 + } + ], + "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", + "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, + "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 + } + ], + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.py b/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.py new file mode 100644 index 00000000000..4c66b7276b6 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class LabTestSample(Document): + pass diff --git a/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.js b/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.js new file mode 100644 index 00000000000..ace60de752a --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Lab Test Sample", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Lab Test Sample + () => frappe.tests.make('Lab Test Sample', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.py b/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.py new file mode 100644 index 00000000000..2bc56bd3241 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +class TestLabTestSample(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/lab_test_template/__init__.py b/erpnext/healthcare/doctype/lab_test_template/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js new file mode 100644 index 00000000000..a8202982d95 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js @@ -0,0 +1,132 @@ +// Copyright (c) 2016, ESS +// License: ESS license.txt + +frappe.ui.form.on("Lab Test Template",{ + test_name: function(frm) { + if(!frm.doc.test_code) + frm.set_value("test_code", frm.doc.test_name); + if(!frm.doc.test_description) + frm.set_value("test_description", frm.doc.test_name); + }, + refresh : function(frm){ + // Restrict Special, Grouped type templates in Child TestGroups + frm.set_query("test_template", "test_groups", function() { + return { + filters: { + test_template_type:['in',['Single','Compound']] + } + }; + }); + } +}); + +cur_frm.cscript.custom_refresh = function(doc) { + cur_frm.set_df_property("test_code", "read_only", doc.__islocal ? 0 : 1); + + if(!doc.__islocal) { + cur_frm.add_custom_button(__('Change Template Code'), function() { + change_template_code(cur_frm,doc); + } ); + if(doc.disabled == 1){ + cur_frm.add_custom_button(__('Enable Template'), function() { + enable_template(cur_frm); + } ); + } + else{ + cur_frm.add_custom_button(__('Disable Template'), function() { + disable_template(cur_frm); + } ); + } + } +}; + +var disable_template = function(frm){ + var doc = frm.doc; + frappe.call({ + method: "erpnext.healthcare.doctype.lab_test_template.lab_test_template.disable_enable_test_template", + args: {status: 1, name: doc.name, is_billable: doc.is_billable}, + callback: function(){ + cur_frm.reload_doc(); + } + }); +}; + +var enable_template = function(frm){ + var doc = frm.doc; + frappe.call({ + method: "erpnext.healthcare.doctype.lab_test_template.lab_test_template.disable_enable_test_template", + args: {status: 0, name: doc.name, is_billable: doc.is_billable}, + callback: function(){ + cur_frm.reload_doc(); + } + }); +}; + + +var change_template_code = function(frm,doc){ + var d = new frappe.ui.Dialog({ + title:__("Change Template Code"), + fields:[ + { + "fieldtype": "Data", + "label": "Test Template Code", + "fieldname": "Test Code", + reqd:1 + }, + { + "fieldtype": "Button", + "label": __("Change Code"), + click: function() { + var values = d.get_values(); + if(!values) + return; + change_test_code_from_template(values["Test Code"],doc); + d.hide(); + } + } + ] + }); + d.show(); + d.set_values({ + 'Test Code': doc.test_code + }); + + var change_test_code_from_template = function(test_code,doc){ + frappe.call({ + "method": "erpnext.healthcare.doctype.lab_test_template.lab_test_template.change_test_code_from_template", + "args": {test_code: test_code, doc: doc}, + callback: function (data) { + frappe.set_route("Form", "Lab Test Template", data.message); + } + }); + }; +}; + +frappe.ui.form.on("Lab Test Template", "test_name", function(frm){ + + frm.doc.change_in_item = 1; + +}); +frappe.ui.form.on("Lab Test Template", "test_rate", function(frm){ + + frm.doc.change_in_item = 1; + +}); +frappe.ui.form.on("Lab Test Template", "test_group", function(frm){ + + frm.doc.change_in_item = 1; + +}); +frappe.ui.form.on("Lab Test Template", "test_description", function(frm){ + + frm.doc.change_in_item = 1; + +}); + +frappe.ui.form.on("Lab Test Groups", "template_or_new_line", function (frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if(child.template_or_new_line =="Add new line"){ + frappe.model.set_value(cdt, cdn, 'test_template', ""); + frappe.model.set_value(cdt, cdn, 'test_description', ""); + } +}); diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json new file mode 100644 index 00000000000..9e692d5418f --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json @@ -0,0 +1,978 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:test_code", + "beta": 1, + "creation": "2016-03-29 17:35:36.761223", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 0, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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 Name", + "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": 1, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_code", + "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 Code", + "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": 1, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "", + "fieldname": "test_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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", + "description": "Single for results which require only a single input, result UOM and normal value \n
\nCompound for results which require multiple input fields with corresponding event names, result UOMs and normal values\n
\nDescriptive for tests which have multiple result components and corresponding result entry fields. \n
\nGrouped for test templates which are a group of other test templates.\n
\nNo Result for tests with no results. Also, no Lab Test is created. e.g.. Sub Tests for Grouped results.", + "fieldname": "test_template_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": "Result Format", + "length": 0, + "no_copy": 0, + "options": "\nSingle\nCompound\nDescriptive\nGrouped\nNo Result", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "depends_on": "eval:doc.test_template_type != 'Grouped'", + "description": "If unchecked, the item wont be appear in Sales Invoice, but can be used in group test creation. ", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.is_billable == 1", + "description": "This value is updated in the Default Sales Price List.", + "fieldname": "test_rate", + "fieldtype": "Currency", + "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": "Standard Selling 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": "", + "columns": 0, + "depends_on": "eval:doc.test_template_type == 'Single'", + "fieldname": "section_break_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, + "label": "Lab Routine", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_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": "Lab Test 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_normal_range", + "fieldtype": "Long 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": "Normal Range", + "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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": "", + "columns": 0, + "depends_on": "eval:doc.test_template_type == 'Compound'", + "fieldname": "section_break_compound", + "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": "Compound", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "normal_test_templates", + "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 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.test_template_type == 'Descriptive'", + "fieldname": "section_break_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, + "label": "Special", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fieldname": "sensitivity", + "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": "Sensitivity", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "special_test_template", + "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 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.test_template_type == 'Grouped'", + "fieldname": "section_break_group", + "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": "Group", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_groups", + "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": "", + "length": 0, + "no_copy": 0, + "options": "Lab Test Groups", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_description", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_description", + "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": "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": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sb_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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "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": "sample.sample_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": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sample_collection_details", + "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": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "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": 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 + } + ], + "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:45:27.655822", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Lab Test Template", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "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 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "test_code,test_name,test_template_type", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "test_name", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py new file mode 100644 index 00000000000..bb0ead62cfe --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, json +from frappe.model.document import Document +from frappe import _ + +class LabTestTemplate(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 not item_price_exist(self): + if(self.test_rate != 0.0): + price_list_name = frappe.db.get_value("Price List", {"selling": 1}) + if(self.test_rate): + make_item_price(self.test_code, price_list_name, self.test_rate) + else: + make_item_price(self.test_code, price_list_name, 0.0) + + 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) + self.reload() + + def after_insert(self): + if not self.item: + create_item_from_template(self) + + #Call before delete the template + def on_trash(self): + # remove template refernce from item and disable item + if(self.item): + try: + frappe.delete_doc("Item",self.item) + except Exception: + frappe.throw("""Not permitted. Please disable the Test Template""") + +def item_price_exist(doc): + item_price = frappe.db.exists({ + "doctype": "Item Price", + "item_code": doc.test_code}) + if(item_price): + return True + else: + return False + +def updating_item(self): + 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""", + (self.test_name, self.test_group , self.test_rate, self.test_description, self.item)) + +def create_item_from_template(doc): + if(doc.is_billable == 1): + disabled = 0 + else: + disabled = 1 + #insert item + item = frappe.get_doc({ + "doctype": "Item", + "item_code": doc.test_code, + "item_name":doc.test_name, + "item_group": doc.test_group, + "description":doc.test_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) + + #insert item price + #get item price list to insert item price + if(doc.test_rate != 0.0): + price_list_name = frappe.db.get_value("Price List", {"selling": 1}) + if(doc.test_rate): + make_item_price(item.name, price_list_name, doc.test_rate) + item.standard_rate = doc.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 to the template + frappe.db.set_value("Lab Test Template", doc.name, "item", item.name) + + doc.reload() #refresh the doc after insert. + +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) + +@frappe.whitelist() +def change_test_code_from_template(test_code, doc): + args = json.loads(doc) + doc = frappe._dict(args) + + item_exist = frappe.db.exists({ + "doctype": "Item", + "item_code": test_code}) + if(item_exist): + frappe.throw(_("Code {0} already exist").format(test_code)) + else: + frappe.rename_doc("Item", doc.name, test_code, ignore_permissions = True) + frappe.db.set_value("Lab Test Template",doc.name,"test_code",test_code) + frappe.rename_doc("Lab Test Template", doc.name, test_code, ignore_permissions = True) + return test_code + +@frappe.whitelist() +def disable_enable_test_template(status, name, is_billable): + frappe.db.set_value("Lab Test Template",name,"disabled",status) + if(is_billable == 1): + frappe.db.set_value("Item",name,"disabled",status) diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js new file mode 100644 index 00000000000..26f55c33b8a --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js @@ -0,0 +1,15 @@ +/* +(c) ESS 2015-16 +*/ +frappe.listview_settings['Lab Test Template'] = { + add_fields: ["test_name", "test_code", "test_rate"], + filters:[["disabled","=",0]], + /* get_indicator: function(doc) { + if(doc.disabled==1){ + return [__("Disabled"), "red", "disabled,=,Disabled"]; + } + if(doc.disabled==0){ + return [__("Enabled"), "green", "disabled,=,0"]; + } + } */ +}; diff --git a/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.js b/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.js new file mode 100644 index 00000000000..7c2ec8c3483 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Lab Test Template", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Lab Test Template + () => frappe.tests.make('Lab Test Template', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.py new file mode 100644 index 00000000000..4c9f55a839e --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +# test_records = frappe.get_test_records('Lab Test Template') + +class TestLabTestTemplate(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/lab_test_uom/__init__.py b/erpnext/healthcare/doctype/lab_test_uom/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.js b/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.js new file mode 100644 index 00000000000..2107e79c0f6 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.js @@ -0,0 +1,5 @@ +// Copyright (c) 2016, ESS and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Lab Test UOM', { +}); diff --git a/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.json b/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.json new file mode 100644 index 00000000000..daa533dfa6f --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.json @@ -0,0 +1,145 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:test_uom", + "beta": 1, + "creation": "2016-03-29 17:28:08.630148", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_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": "Test 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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "uom_description", + "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", + "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 + } + ], + "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:41:40.186676", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Lab Test UOM", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "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 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "test_uom", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "test_uom", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.py b/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.py new file mode 100644 index 00000000000..7ce8d2d1c81 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class LabTestUOM(Document): + pass diff --git a/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.js b/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.js new file mode 100644 index 00000000000..1328dda2826 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Lab Test UOM", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Lab Test UOM + () => frappe.tests.make('Lab Test UOM', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.py b/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.py new file mode 100644 index 00000000000..0b3f516497d --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +# test_records = frappe.get_test_records('Lab Test UOM') + +class TestLabTestUOM(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/medical_code/__init__.py b/erpnext/healthcare/doctype/medical_code/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/medical_code/medical_code.js b/erpnext/healthcare/doctype/medical_code/medical_code.js new file mode 100644 index 00000000000..0422d774353 --- /dev/null +++ b/erpnext/healthcare/doctype/medical_code/medical_code.js @@ -0,0 +1,5 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Medical Code', { +}); diff --git a/erpnext/healthcare/doctype/medical_code/medical_code.json b/erpnext/healthcare/doctype/medical_code/medical_code.json new file mode 100644 index 00000000000..71a2278b071 --- /dev/null +++ b/erpnext/healthcare/doctype/medical_code/medical_code.json @@ -0,0 +1,156 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "beta": 1, + "creation": "2017-06-21 13:02:56.122897", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "medical_code_standard", + "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 Code Standard", + "length": 0, + "no_copy": 0, + "options": "Medical Code Standard", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "code", + "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": "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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 1, + "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": 1, + "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 + } + ], + "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 14:01:18.817484", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Medical Code", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "code, description", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/medical_code/medical_code.py b/erpnext/healthcare/doctype/medical_code/medical_code.py new file mode 100644 index 00000000000..0deaac38ca3 --- /dev/null +++ b/erpnext/healthcare/doctype/medical_code/medical_code.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class MedicalCode(Document): + def autoname(self): + self.name = self.medical_code_standard+" "+self.code diff --git a/erpnext/healthcare/doctype/medical_code/test_medical_code.js b/erpnext/healthcare/doctype/medical_code/test_medical_code.js new file mode 100644 index 00000000000..8cc7c40025c --- /dev/null +++ b/erpnext/healthcare/doctype/medical_code/test_medical_code.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Medical Code", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Medical Code + () => frappe.tests.make('Medical Code', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/medical_code/test_medical_code.py b/erpnext/healthcare/doctype/medical_code/test_medical_code.py new file mode 100644 index 00000000000..b1e04027907 --- /dev/null +++ b/erpnext/healthcare/doctype/medical_code/test_medical_code.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +class TestMedicalCode(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/medical_code_standard/__init__.py b/erpnext/healthcare/doctype/medical_code_standard/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.js b/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.js new file mode 100644 index 00000000000..4bf6d3ea160 --- /dev/null +++ b/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.js @@ -0,0 +1,5 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Medical Code Standard', { +}); diff --git a/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.json b/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.json new file mode 100644 index 00000000000..886938d12ff --- /dev/null +++ b/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.json @@ -0,0 +1,94 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:medical_code", + "beta": 1, + "creation": "2017-06-21 13:07:00.463176", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "medical_code", + "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": "Medical 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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "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 14:15:40.820693", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Medical Code Standard", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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 + } + ], + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.py b/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.py new file mode 100644 index 00000000000..7b2731c1def --- /dev/null +++ b/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class MedicalCodeStandard(Document): + pass diff --git a/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.js b/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.js new file mode 100644 index 00000000000..6ab6d531dfb --- /dev/null +++ b/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Medical Code Standard", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Medical Code Standard + () => frappe.tests.make('Medical Code Standard', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.py b/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.py new file mode 100644 index 00000000000..fde095d8098 --- /dev/null +++ b/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +class TestMedicalCodeStandard(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/medical_department/__init__.py b/erpnext/healthcare/doctype/medical_department/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/medical_department/medical_department.js b/erpnext/healthcare/doctype/medical_department/medical_department.js new file mode 100644 index 00000000000..25aeeb85e23 --- /dev/null +++ b/erpnext/healthcare/doctype/medical_department/medical_department.js @@ -0,0 +1,5 @@ +// Copyright (c) 2016, ESS LLP and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Medical Department', { +}); diff --git a/erpnext/healthcare/doctype/medical_department/medical_department.json b/erpnext/healthcare/doctype/medical_department/medical_department.json new file mode 100644 index 00000000000..40f14caf729 --- /dev/null +++ b/erpnext/healthcare/doctype/medical_department/medical_department.json @@ -0,0 +1,156 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:department", + "beta": 1, + "creation": "2017-02-27 13:38:30.806362", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "department", + "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": "Department", + "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 + } + ], + "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:41:59.611698", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Medical Department", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "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": "Physician", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "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": "Nursing User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "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": 1 + }, + { + "amend": 0, + "apply_user_permissions": 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 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "department", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "department", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/medical_department/medical_department.py b/erpnext/healthcare/doctype/medical_department/medical_department.py new file mode 100644 index 00000000000..0f2d4fc5f91 --- /dev/null +++ b/erpnext/healthcare/doctype/medical_department/medical_department.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class MedicalDepartment(Document): + pass diff --git a/erpnext/healthcare/doctype/medical_department/test_medical_department.js b/erpnext/healthcare/doctype/medical_department/test_medical_department.js new file mode 100644 index 00000000000..fdf49718dc8 --- /dev/null +++ b/erpnext/healthcare/doctype/medical_department/test_medical_department.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Medical Department", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Medical Department + () => frappe.tests.make('Medical Department', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/medical_department/test_medical_department.py b/erpnext/healthcare/doctype/medical_department/test_medical_department.py new file mode 100644 index 00000000000..543750a7e7d --- /dev/null +++ b/erpnext/healthcare/doctype/medical_department/test_medical_department.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +# test_records = frappe.get_test_records('Medical Department') + +class TestMedicalDepartment(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/normal_test_items/__init__.py b/erpnext/healthcare/doctype/normal_test_items/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/normal_test_items/normal_test_items.js b/erpnext/healthcare/doctype/normal_test_items/normal_test_items.js new file mode 100644 index 00000000000..0371ddd5c9e --- /dev/null +++ b/erpnext/healthcare/doctype/normal_test_items/normal_test_items.js @@ -0,0 +1,4 @@ +// Copyright (c) 2016, ESS +// License: ESS license.txt + + diff --git a/erpnext/healthcare/doctype/normal_test_items/normal_test_items.json b/erpnext/healthcare/doctype/normal_test_items/normal_test_items.json new file mode 100644 index 00000000000..9e754a74dc0 --- /dev/null +++ b/erpnext/healthcare/doctype/normal_test_items/normal_test_items.json @@ -0,0 +1,284 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 1, + "creation": "2016-02-22 15:06:08.295224", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_event", + "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": "Event", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.require_result_value == 1 ", + "fieldname": "result_value", + "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": "Result Value", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_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, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "normal_range", + "fieldtype": "Long Text", + "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": "Normal Range", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_comment", + "fieldtype": "Data", + "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": "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fieldname": "require_result_value", + "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": "Require Result Value", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "template", + "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": "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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "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-08-31 13:59:33.202695", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Normal Test Items", + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/normal_test_items/normal_test_items.py b/erpnext/healthcare/doctype/normal_test_items/normal_test_items.py new file mode 100644 index 00000000000..a0069d7252f --- /dev/null +++ b/erpnext/healthcare/doctype/normal_test_items/normal_test_items.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class NormalTestItems(Document): + pass diff --git a/erpnext/healthcare/doctype/normal_test_template/__init__.py b/erpnext/healthcare/doctype/normal_test_template/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/normal_test_template/normal_test_template.json b/erpnext/healthcare/doctype/normal_test_template/normal_test_template.json new file mode 100644 index 00000000000..ef5abd5652b --- /dev/null +++ b/erpnext/healthcare/doctype/normal_test_template/normal_test_template.json @@ -0,0 +1,191 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 1, + "creation": "2016-02-22 16:09:54.310628", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "heading_text", + "fieldtype": "Heading", + "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": "Test", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_event", + "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": "Event", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_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": "Lab Test 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "normal_range", + "fieldtype": "Long Text", + "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": "Normal Range", + "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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + } + ], + "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-08-31 14:07:11.277187", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Normal Test Template", + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/normal_test_template/normal_test_template.py b/erpnext/healthcare/doctype/normal_test_template/normal_test_template.py new file mode 100644 index 00000000000..bc2c9914846 --- /dev/null +++ b/erpnext/healthcare/doctype/normal_test_template/normal_test_template.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class NormalTestTemplate(Document): + pass diff --git a/erpnext/healthcare/doctype/patient/__init__.py b/erpnext/healthcare/doctype/patient/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/patient/patient.js b/erpnext/healthcare/doctype/patient/patient.js new file mode 100644 index 00000000000..57e5eef8d98 --- /dev/null +++ b/erpnext/healthcare/doctype/patient/patient.js @@ -0,0 +1,119 @@ +// Copyright (c) 2016, ESS LLP and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Patient', { + refresh: function (frm) { + frm.set_query("patient", "patient_relation", function () { + return { + filters: [ + ["Patient", "name", "!=", frm.doc.name] + ] + }; + }); + if (frappe.defaults.get_default("patient_master_name") != "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) { + frm.add_custom_button(__('Invoice Patient Registration'), function () { + btn_invoice_registration(frm); + }); + } + if (frm.doc.patient_name && frappe.user.has_role("Physician")) { + frm.add_custom_button(__('Medical Record'), function () { + frappe.route_options = { "patient": frm.doc.name }; + frappe.set_route("medical_record"); + },"View"); + } + 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"); + frm.add_custom_button(__('Medical Record'), function () { + create_medical_record(frm); + }, "Create"); + frm.add_custom_button(__('Consultation'), function () { + btn_create_consultation(frm); + }, "Create"); + } + }, + onload: function (frm) { + if(!frm.doc.dob){ + $(frm.fields_dict['age_html'].wrapper).html("Age not specified"); + } + if(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", ""); + } + else{ + var age_str = get_age(frm.doc.dob); + $(frm.fields_dict['age_html'].wrapper).html("AGE : " + age_str); + } + } +}); + +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_consultation = function (frm) { + if (!frm.doc.name) { + frappe.throw("Please save the patient first"); + } + frappe.route_options = { + "patient": frm.doc.name, + }; + frappe.new_doc("Consultation"); +}; + +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(); + } + } + }); +}; diff --git a/erpnext/healthcare/doctype/patient/patient.json b/erpnext/healthcare/doctype/patient/patient.json new file mode 100644 index 00000000000..f5ce458f7d8 --- /dev/null +++ b/erpnext/healthcare/doctype/patient/patient.json @@ -0,0 +1,1356 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "naming_series:", + "beta": 1, + "creation": "2017-01-23 14:03:49.084370", + "custom": 0, + "description": "Patient", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 0, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "basic_info", + "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": "Patient Demographics", + "length": 0, + "no_copy": 0, + "oldfieldtype": "Section Break", + "options": "fa fa-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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": "Patient ID", + "length": 0, + "no_copy": 0, + "options": "PID-", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "fieldname": "patient_name", + "fieldtype": "Data", + "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": "Full Name", + "length": 0, + "no_copy": 1, + "oldfieldtype": "Data", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "blood_group", + "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": "Blood Group", + "length": 0, + "no_copy": 0, + "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "dob", + "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": "DOB", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "age_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": "Age", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Active", + "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": "Active\nDormant\nOpen", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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, + "unique": 0, + "width": "50%" + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "customer", + "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", + "length": 0, + "no_copy": 0, + "options": "Customer", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "report_preference", + "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": "Report Preference", + "length": 0, + "no_copy": 0, + "options": "\nEmail\nPrint", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "mobile", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "email", + "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": "Email", + "length": 0, + "no_copy": 0, + "options": "Email", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "phone", + "fieldtype": "Data", + "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": "Phone", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "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": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "sb_relation", + "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": "Patient Relation", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "patient_relation", + "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": "Patient Relation", + "length": 0, + "no_copy": 0, + "options": "Patient Relation", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "allergy_medical_and_surgical_history", + "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": "Allergies, Medical and Surgical History", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "allergies", + "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": "Allergies", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "medication", + "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": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_20", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "medical_history", + "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": "Medical History", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "surgical_history", + "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": "Surgical History", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "personal_and_social_history", + "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": "Personal and Social History", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "occupation", + "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": "Occupation", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_25", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "marital_status", + "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": "Marital Status", + "length": 0, + "no_copy": 0, + "options": "Single\nMarried\nDivorced\nWidow", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "risk_factors", + "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": "Risk Factors", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "tobacco_past_use", + "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": "Tobacco Past Use", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "tobacco_current_use", + "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": "Tobacco Current Use", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "alcohol_past_use", + "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": "Alcohol Past Use", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "alcohol_current_use", + "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": "Alcohol Current Use", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_32", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "surrounding_factors", + "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": "Occupational Hazards and Environmental Factors", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "other_risk_factors", + "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": "Other Risk Factors", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "collapsible_depends_on": "patient_details", + "columns": 0, + "fieldname": "more_info", + "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 Information", + "length": 0, + "no_copy": 0, + "oldfieldtype": "Section Break", + "options": "fa fa-file-text", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "Additional information regarding the patient", + "fieldname": "patient_details", + "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": "Patient 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "ac_sb", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "fa fa-user", + "idx": 0, + "image_field": "image", + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 50, + "modified": "2017-08-31 13:50:25.474398", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient", + "name_case": "Title Case", + "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": "Physician", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "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": "Laboratory User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "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": "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_name,mobile,email,phone", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "ASC", + "title_field": "patient_name", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py new file mode 100644 index 00000000000..98526cc027e --- /dev/null +++ b/erpnext/healthcare/doctype/patient/patient.py @@ -0,0 +1,122 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import cint, cstr, getdate +import dateutil +from frappe.model.naming import make_autoname +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): + self.add_as_website_user() + + def add_as_website_user(self): + 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" + }) + user.flags.no_welcome_email = True + user.flags.ignore_permissions = True + user.add_roles("Patient") + + def autoname(self): + patient_master_name = frappe.defaults.get_global_default('patient_master_name') + if patient_master_name == 'Patient Name': + self.name = self.get_patient_name() + else: + if not self.naming_series: + frappe.throw(_("Series is mandatory"), frappe.MandatoryError) + + self.name = make_autoname(self.naming_series+'.#####') + + def get_patient_name(self): + name = self.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 + return "{0} - {1}".format(name, cstr(count)) + + return name + + def get_age(self): + 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)" + return age_str + + def invoice_patient_registration(self): + frappe.db.set_value("Patient", self.name, "disabled", 0) + send_registration_sms(self) + if(frappe.get_value("Healthcare Settings", None, "registration_fee")>0): + sales_invoice = make_invoice(self.name, self.company) + sales_invoice.save(ignore_permissions=True) + 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") + 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.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) + +def make_invoice(patient, company): + sales_invoice = frappe.new_doc("Sales Invoice") + sales_invoice.customer = frappe.get_value("Patient", patient, "customer") + sales_invoice.due_date = getdate() + sales_invoice.company = company + 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.qty = 1 + item_line.uom = "Nos" + 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.amount = item_line.rate + sales_invoice.set_missing_values() + return sales_invoice + +@frappe.whitelist() +def get_patient_detail(patient, company=None): + patient_dict = frappe.db.sql(_("""select * from tabPatient where name='{0}'""").format(patient), as_dict=1) + if not patient_dict: + frappe.throw("Patient not found") + vital_sign = frappe.db.sql(_("""select * from `tabVital Signs` where patient='{0}' order by signs_date desc limit 1""").format(patient), as_dict=1) + + details = patient_dict[0] + if vital_sign: + details.update(vital_sign[0]) + return details diff --git a/erpnext/healthcare/doctype/patient/patient_dashboard.py b/erpnext/healthcare/doctype/patient/patient_dashboard.py new file mode 100644 index 00000000000..cb98f0dcf40 --- /dev/null +++ b/erpnext/healthcare/doctype/patient/patient_dashboard.py @@ -0,0 +1,18 @@ +from frappe import _ + +def get_data(): + return { + 'heatmap': True, + 'heatmap_message': _('This is based on transactions against this Patient. See timeline below for details'), + 'fieldname': 'patient', + 'transactions': [ + { + 'label': _('Appointments and Consultations'), + 'items': ['Patient Appointment', 'Consultation'] + }, + { + 'label': _('Lab Tests'), + 'items': ['Lab Test'] + } + ] + } diff --git a/erpnext/healthcare/doctype/patient/test_patient.js b/erpnext/healthcare/doctype/patient/test_patient.js new file mode 100644 index 00000000000..e1d9ecbd242 --- /dev/null +++ b/erpnext/healthcare/doctype/patient/test_patient.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Patient", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially('Patient', [ + // insert a new Patient + () => frappe.tests.make([ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/patient/test_patient.py b/erpnext/healthcare/doctype/patient/test_patient.py new file mode 100644 index 00000000000..aebaa6b989b --- /dev/null +++ b/erpnext/healthcare/doctype/patient/test_patient.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and Contributors +# See license.txt +from __future__ import unicode_literals + +import unittest + +# test_records = frappe.get_test_records('Patient') + +class TestPatient(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/patient_appointment/__init__.py b/erpnext/healthcare/doctype/patient_appointment/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js new file mode 100644 index 00000000000..86b18842cb6 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -0,0 +1,256 @@ +// Copyright (c) 2016, ESS LLP and contributors +// For license information, please see license.txt +frappe.provide("erpnext.queries"); +frappe.ui.form.on('Patient Appointment', { + setup: function(frm) { + frm.custom_make_buttons = { + 'Sales Invoice': 'Invoice', + 'Vital Signs': 'Vital Signs', + 'Consultation': 'Consultation' + }; + }, + refresh: function(frm) { + frm.set_query("patient", function () { + return { + filters: {"disabled": 0} + }; + }); + if(frm.doc.patient){ + frm.add_custom_button(__('Medical Record'), function() { + frappe.route_options = {"patient": frm.doc.patient}; + frappe.set_route("medical_record"); + },__("View")); + } + if(frm.doc.status == "Open"){ + frm.add_custom_button(__('Cancel'), function() { + btn_update_status(frm, "Cancelled"); + }); + } + if(frm.doc.status == "Scheduled" && !frm.doc.__islocal){ + frm.add_custom_button(__('Cancel'), function() { + btn_update_status(frm, "Cancelled"); + }); + } + 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.add_custom_button(__("Consultation"),function(){ + btn_create_consultation(frm); + },"Create"); + + frm.add_custom_button(__('Vital Signs'), function() { + btn_create_vital_signs(frm); + },"Create"); + + if(!frm.doc.__islocal){ + if(frm.doc.sales_invoice && frappe.user.has_role("Accounts User")){ + frm.add_custom_button(__('Invoice'), function() { + frappe.set_route("Form", "Sales Invoice", frm.doc.sales_invoice); + },__("View") ); + } + else if(frm.doc.status != "Cancelled" && frappe.user.has_role("Accounts User")){ + frm.add_custom_button(__('Invoice'), function() { + btn_invoice_consultation(frm); + },__("Create")); + } + } + }, + check_availability: function(frm) { + var { physician, appointment_date } = frm.doc; + if(!(physician && appointment_date)) { + frappe.throw(__("Please select Physician and Date")); + } + + // show booking modal + frm.call({ + method: 'get_availability_data', + args: { + physician: physician, + date: appointment_date + }, + callback: (r) => { + // console.log(r); + var data = r.message; + if(data.available_slots.length > 0) { + show_availability(data); + } else { + show_empty_state(); + } + } + }); + + function show_empty_state() { + frappe.msgprint({ + title: __('Not Available'), + message: __("Physician {0} not available on {1}", [physician.bold(), appointment_date.bold()]), + indicator: 'red' + }); + } + + function show_availability(data) { + var d = new frappe.ui.Dialog({ + title: __("Available slots"), + fields: [{ fieldtype: 'HTML', fieldname: 'available_slots'}], + primary_action_label: __("Book"), + primary_action: function() { + // book slot + frm.set_value('appointment_time', selected_slot); + frm.set_value('duration', data.time_per_appointment); + d.hide(); + frm.save(); + } + }); + var $wrapper = d.fields_dict.available_slots.$wrapper; + var selected_slot = null; + + // disable dialog action initially + d.get_primary_btn().attr('disabled', true); + + // make buttons for each slot + var slot_html = data.available_slots.map(slot => { + return ``; + }).join(""); + + $wrapper + .css('margin-bottom', 0) + .addClass('text-center') + .html(slot_html); + + // disable buttons for which appointments are booked + data.appointments.map(slot => { + if(slot.status == "Scheduled" || slot.status == "Open" || slot.status == "Closed"){ + $wrapper + .find(`button[data-name="${slot.appointment_time}"]`) + .attr('disabled', true); + } + }); + + // blue button when clicked + $wrapper.on('click', 'button', function() { + var $btn = $(this); + $wrapper.find('button').removeClass('btn-primary'); + $btn.addClass('btn-primary'); + selected_slot = $btn.attr('data-name'); + + // enable dialog action + d.get_primary_btn().attr('disabled', null); + }); + + d.show(); + } + }, + onload:function(frm){ + if(frm.is_new()) { + frm.set_value("appointment_time", null); + frm.disable_save(); + } + }, +}); + +var btn_create_consultation = function(frm){ + var doc = frm.doc; + frappe.call({ + method:"erpnext.healthcare.doctype.patient_appointment.patient_appointment.create_consultation", + 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"); + } + frappe.route_options = { + "patient": frm.doc.patient, + }; + frappe.new_doc("Vital Signs"); +}; + +var btn_update_status = function(frm, status){ + var doc = frm.doc; + frappe.call({ + method: + "erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_status", + args: {appointmentId: doc.name, status:status}, + callback: function(data){ + if(!data.exc){ + cur_frm.reload_doc(); + } + } + }); +}; + +var btn_invoice_consultation = function(frm){ + var doc = frm.doc; + frappe.call({ + method: + "erpnext.healthcare.doctype.patient_appointment.patient_appointment.create_invoice", + args: {company: doc.company, physician:doc.physician, patient: doc.patient, + appointment_id: doc.name, appointment_date:doc.appointment_date }, + callback: function(data){ + if(!data.exc){ + if(data.message){ + frappe.set_route("Form", "Sales Invoice", data.message); + } + cur_frm.reload_doc(); + } + } + }); +}; + +frappe.ui.form.on("Patient Appointment", "physician", function(frm) { + if(frm.doc.physician){ + frappe.call({ + "method": "frappe.client.get", + args: { + doctype: "Physician", + name: frm.doc.physician + }, + callback: function (data) { + frappe.model.set_value(frm.doctype,frm.docname, "department",data.message.department); + } + }); + } +}); + +frappe.ui.form.on("Patient Appointment", "patient", function(frm) { + if(frm.doc.patient){ + frappe.call({ + "method": "frappe.client.get", + args: { + doctype: "Patient", + name: frm.doc.patient + }, + callback: function (data) { + var age = null; + if(data.message.dob){ + age = calculate_age(data.message.dob); + } + frappe.model.set_value(frm.doctype,frm.docname, "patient_age", age); + } + }); + } +}); + +var calculate_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)"; +}; diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json new file mode 100644 index 00000000000..9520e7766bd --- /dev/null +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -0,0 +1,817 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "OP-.######", + "beta": 1, + "creation": "2017-05-04 11:52:40.941507", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 0, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 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": 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": 1, + "set_only_once": 1, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "physician", + "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": "Physician", + "length": 0, + "no_copy": 0, + "options": "Physician", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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": 1, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Scheduled", + "fieldname": "status", + "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": "Status", + "length": 0, + "no_copy": 0, + "options": "\nScheduled\nOpen\nClosed\nPending\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": 1, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "appointment_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": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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": 0, + "search_index": 0, + "set_only_once": 1, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": "", + "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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 1, + "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": 1, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sales_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": "Sales Invoice", + "length": 0, + "no_copy": 0, + "options": "Sales Invoice", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_2", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "patient_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": "Patient 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "patient", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "patient_sex", + "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": "Gender", + "length": 0, + "no_copy": 0, + "options": "patient.sex", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "referring_physician", + "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": "Referring Physician", + "length": 0, + "no_copy": 0, + "options": "Physician", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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 + } + ], + "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:27:39.208298", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient Appointment", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "apply_user_permissions": 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 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "patient, physician, appointment_datetime,department", + "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_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py new file mode 100755 index 00000000000..eab2f2d004f --- /dev/null +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -0,0 +1,244 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document +import json +from frappe.utils import getdate +from frappe import _ +import datetime +from frappe.core.doctype.sms_settings.sms_settings import send_sms +from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account + +class PatientAppointment(Document): + def on_update(self): + today = datetime.date.today() + 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() + + def after_insert(self): + #Check fee validity exists + appointment = self + validity_exist = validity_exists(appointment.physician, appointment.patient) + if validity_exist : + fee_validity = frappe.get_doc("Fee Validity",validity_exist[0][0]) + #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,"sales_invoice",fee_validity.ref_invoice) + frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till)) + confirm_sms(self) + +def appointment_cancel(appointmentId): + appointment = frappe.get_doc("Patient Appointment",appointmentId) + #If invoice --> fee_validity update with -1 visit + if (appointment.sales_invoice): + validity = frappe.db.exists({"doctype": "Fee Validity","ref_invoice": appointment.sales_invoice}) + if(validity): + fee_validity = frappe.get_doc("Fee Validity",validity[0][0]) + visited = fee_validity.visited - 1 + frappe.db.set_value("Fee Validity",fee_validity.name,"visited",visited) + if visited <= 0: + frappe.msgprint(_("Appointment cancelled, Please review and cancel the invoice {0}".format(appointment.sales_invoice))) + else: + frappe.msgprint(_("Appointment cancelled")) + +@frappe.whitelist() +def get_availability_data(date, physician): + # get availability data of 'physician' on 'date' + date = getdate(date) + weekday = date.strftime("%A") + + available_slots = [] + # get physicians schedule + physician_schedule_name = frappe.db.get_value("Physician", physician, "physician_schedule") + physician_schedule = frappe.get_doc("Physician Schedule", physician_schedule_name) + time_per_appointment = frappe.db.get_value("Physician", physician, "time_per_appointment") + + for t in physician_schedule.time_slots: + if weekday == t.day: + available_slots.append(t) + + # if physician not available return + if not available_slots: + # TODO: return available slots in nearby dates + frappe.throw(_("Physician not available on {0}").format(weekday)) + + # if physician on leave return + + # if holiday return + # if is_holiday(weekday): + + # get appointments on that day for physician + appointments = frappe.get_all( + "Patient Appointment", + filters={"physician": physician, "appointment_date": date}, + fields=["name", "appointment_time", "duration", "status"]) + + return { + "available_slots": available_slots, + "appointments": appointments, + "time_per_appointment": time_per_appointment + } + +@frappe.whitelist() +def update_status(appointmentId, status): + frappe.db.set_value("Patient Appointment",appointmentId,"status",status) + if(status=="Cancelled"): + appointment_cancel(appointmentId) + +@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") + send_message(doc, message) + +@frappe.whitelist() +def create_invoice(company, physician, patient, appointment_id, appointment_date): + if not appointment_id: + return False + sales_invoice = frappe.new_doc("Sales Invoice") + sales_invoice.customer = frappe.get_value("Patient", patient, "customer") + sales_invoice.appointment = appointment_id + sales_invoice.due_date = getdate() + sales_invoice.is_pos = '0' + sales_invoice.debit_to = get_receivable_account(company) + + fee_validity = get_fee_validity(physician, patient, appointment_date) + create_invoice_items(appointment_id, physician, company, sales_invoice) + + sales_invoice.save(ignore_permissions=True) + frappe.db.sql(_("""update `tabPatient Appointment` set sales_invoice='{0}' where name='{1}'""").format(sales_invoice.name, appointment_id)) + frappe.db.set_value("Fee Validity", fee_validity.name, "ref_invoice", sales_invoice.name) + consultation = frappe.db.exists({ + "doctype": "Consultation", + "appointment": appointment_id}) + if consultation: + frappe.db.set_value("Consultation", consultation[0][0], "invoice", sales_invoice.name) + return sales_invoice.name + +def get_fee_validity(physician, patient, date): + validity_exist = validity_exists(physician, patient) + if validity_exist : + fee_validity = frappe.get_doc("Fee Validity",validity_exist[0][0]) + fee_validity = update_fee_validity(fee_validity, date) + else: + fee_validity = create_fee_validity(physician, patient, date) + return fee_validity + +def validity_exists(physician, patient): + return frappe.db.exists({ + "doctype": "Fee Validity", + "physician": physician, + "patient": patient}) + +def update_fee_validity(fee_validity, date): + 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 + fee_validity.visited = 1 + fee_validity.valid_till = valid_till + fee_validity.save(ignore_permissions=True) + return fee_validity + +def create_fee_validity(physician, patient, date): + fee_validity = frappe.new_doc("Fee Validity") + fee_validity.physician = physician + fee_validity.patient = patient + fee_validity = update_fee_validity(fee_validity, date) + return fee_validity + +def create_invoice_items(appointment_id, physician, company, invoice): + item_line = invoice.append("items") + item_line.item_name = "Consulting Charges" + item_line.description = "Consulting Charges: " + physician + item_line.qty = 1 + item_line.uom = "Nos" + item_line.conversion_factor = 1 + item_line.income_account = get_income_account(physician,company) + op_consulting_charge = frappe.db.get_value("Physician", physician, "op_consulting_charge") + if op_consulting_charge: + item_line.rate = op_consulting_charge + item_line.amount = op_consulting_charge + return invoice + +@frappe.whitelist() +def create_consultation(appointment): + appointment = frappe.get_doc("Patient Appointment",appointment) + consultation = frappe.new_doc("Consultation") + consultation.appointment = appointment.name + consultation.patient = appointment.patient + consultation.physician = appointment.physician + consultation.visit_department = appointment.department + consultation.patient_sex = appointment.patient_sex + consultation.consultation_date = appointment.appointment_date + if appointment.sales_invoice: + consultation.invoice = appointment.sales_invoice + return consultation.as_dict() + +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) + + 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)) + + 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") + send_message(doc, message) + frappe.db.set_value("Patient Appointment",doc.name,"reminded",1) + +def send_message(doc, message): + 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")) + #jinja to string convertion happens here + message = frappe.render_template(message, context) + number = [patient.mobile] + send_sms(number,message) + +@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("Patient Appointment", filters) + data = frappe.db.sql("""select name, patient, physician, status, + duration, timestamp(appointment_date, appointment_time) as + 'start' from `tabPatient Appointment` where + (appointment_date between %(start)s and %(end)s) + and docstatus < 2 {conditions}""".format(conditions=conditions), + {"start": start, "end": end}, as_dict=True, update={"allDay": 0}) + for item in data: + item.end = item.start + datetime.timedelta(minutes = item.duration) + return data diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_calendar.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_calendar.js new file mode 100644 index 00000000000..bfb53b83b8e --- /dev/null +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_calendar.js @@ -0,0 +1,44 @@ + +frappe.views.calendar["Patient Appointment"] = { + field_map: { + "start": "start", + "end": "end", + "id": "name", + "title": "patient", + "allDay": "allDay" + }, + gantt: true, + get_events_method: "erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_events", + filters: [ + { + 'fieldtype': 'Link', + 'fieldname': 'physician', + 'options': 'Physician', + 'label': __('Physician') + }, + { + 'fieldtype': 'Link', + 'fieldname': 'patient', + 'options': 'Patient', + 'label': __('Patient') + }, + { + 'fieldtype': 'Link', + 'fieldname': 'appointment_type', + 'options': 'Appointment Type', + 'label': __('Appointment Type') + }, + { + 'fieldtype': 'Link', + 'fieldname': 'department', + 'options': 'Medical Department', + 'label': __('Department') + }, + { + 'fieldtype': 'Select', + 'fieldname': 'status', + 'options': 'Scheduled\nOpen\nClosed\nPending', + 'label': __('Status') + } + ] +}; diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py new file mode 100644 index 00000000000..1862915cfca --- /dev/null +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py @@ -0,0 +1,19 @@ +from frappe import _ + +def get_data(): + return { + 'fieldname': 'appointment', + 'non_standard_fieldnames': { + 'Patient Medical Record': 'reference_name' + }, + 'transactions': [ + { + 'label': _('Consultations'), + 'items': ['Consultation', 'Vital Signs', 'Patient Medical Record'] + }, + { + 'label': _('Billing'), + 'items': ['Sales Invoice'] + } + ] + } diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_list.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_list.js new file mode 100644 index 00000000000..701cb69806e --- /dev/null +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_list.js @@ -0,0 +1,6 @@ +/* +(c) ESS 2015-16 +*/ +frappe.listview_settings['Patient Appointment'] = { + filters: [["status", "=", "Open"]], +}; diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.js new file mode 100644 index 00000000000..71fc1778455 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Patient Appointment", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Patient Appointment + () => frappe.tests.make('Patient Appointment', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py new file mode 100644 index 00000000000..3536a5f9513 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +# test_records = frappe.get_test_records('Patient Appointment') + +class TestPatientAppointment(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/patient_medical_record/__init__.py b/erpnext/healthcare/doctype/patient_medical_record/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.js b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.js new file mode 100644 index 00000000000..93ff70e6437 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.js @@ -0,0 +1,5 @@ +// Copyright (c) 2016, ESS LLP and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Patient Medical Record', { +}); diff --git a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json new file mode 100644 index 00000000000..06149230e67 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json @@ -0,0 +1,430 @@ +{ + "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, + "fields": [ + { + "allow_bulk_edit": 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": "PMR-", + "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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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 + }, + { + "allow_bulk_edit": 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": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "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": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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": 0, + "options": "reference_name.owner", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "__user", + "fieldname": "user", + "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": "User", + "length": 0, + "no_copy": 0, + "options": "User", + "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, + "unique": 0 + } + ], + "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": "2017-09-04 14:29:48.679751", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient Medical Record", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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 + } + ], + "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", + "track_changes": 1, + "track_seen": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.py b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.py new file mode 100644 index 00000000000..35e42bd6bd5 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class PatientMedicalRecord(Document): + def after_insert(self): + if self.reference_doctype == "Patient Medical Record" : + frappe.db.set_value("Patient Medical Record", self.name, "reference_name", self.name) diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.js b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.js new file mode 100644 index 00000000000..66dda09e256 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Patient Medical Record", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Patient Medical Record + () => frappe.tests.make('Patient Medical Record', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); 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 new file mode 100644 index 00000000000..460774a7ec8 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +# test_records = frappe.get_test_records('Patient Medical Record') + +class TestPatientMedicalRecord(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/patient_relation/__init__.py b/erpnext/healthcare/doctype/patient_relation/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/patient_relation/patient_relation.json b/erpnext/healthcare/doctype/patient_relation/patient_relation.json new file mode 100644 index 00000000000..e97e1b24c03 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_relation/patient_relation.json @@ -0,0 +1,134 @@ +{ + "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", + "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": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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 + }, + { + "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": 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, + "unique": 0 + } + ], + "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-08-31 14:13:37.497114", + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_relation/patient_relation.py b/erpnext/healthcare/doctype/patient_relation/patient_relation.py new file mode 100644 index 00000000000..150b96200d0 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_relation/patient_relation.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class PatientRelation(Document): + pass diff --git a/erpnext/healthcare/doctype/physician/__init__.py b/erpnext/healthcare/doctype/physician/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/physician/physician.js b/erpnext/healthcare/doctype/physician/physician.js new file mode 100755 index 00000000000..37389fe36ab --- /dev/null +++ b/erpnext/healthcare/doctype/physician/physician.js @@ -0,0 +1,73 @@ +// Copyright (c) 2016, ESS LLP and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Physician', { + setup: function(frm) { + frm.set_query('account', 'accounts', function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + 'root_type': 'Income', + 'company': d.company, + } + }; + }); + }, + refresh: function(frm) { + frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Physician'}; + if(!frm.is_new()) { + frappe.contacts.render_address_and_contact(frm); + } + } +}); + +frappe.ui.form.on("Physician", "user_id",function(frm) { + if(frm.doc.user_id){ + frappe.call({ + "method": "frappe.client.get", + args: { + doctype: "User", + name: frm.doc.user_id + }, + callback: function (data) { + if(!frm.doc.employee){ + frappe.model.get_value('Employee', {'user_id': frm.doc.user_id}, 'name', + function(data) { + if(data) + frappe.model.set_value(frm.doctype,frm.docname, "employee", data.name); + }); + } + if(!frm.doc.first_name) + frappe.model.set_value(frm.doctype,frm.docname, "first_name", data.message.first_name); + if(!frm.doc.middle_name) + frappe.model.set_value(frm.doctype,frm.docname, "middle_name", data.message.middle_name); + if(!frm.doc.last_name) + frappe.model.set_value(frm.doctype,frm.docname, "last_name", data.message.last_name); + if(!frm.doc.mobile_phone) + frappe.model.set_value(frm.doctype,frm.docname, "mobile_phone", data.message.phone); + } + }); + } +}); + +frappe.ui.form.on("Physician", "employee", function(frm) { + if(frm.doc.employee){ + frappe.call({ + "method": "frappe.client.get", + args: { + doctype: "Employee", + name: frm.doc.employee + }, + callback: function (data) { + if(!frm.doc.designation) + frappe.model.set_value(frm.doctype,frm.docname, "designation", data.message.designation); + if(!frm.doc.first_name) + frappe.model.set_value(frm.doctype,frm.docname, "first_name", data.message.employee_name); + if(!frm.doc.mobile_phone) + frappe.model.set_value(frm.doctype,frm.docname, "mobile_phone", data.message.cell_number); + if(!frm.doc.address) + frappe.model.set_value(frm.doctype,frm.docname, "address", data.message.current_address); + } + }); + } +}); diff --git a/erpnext/healthcare/doctype/physician/physician.json b/erpnext/healthcare/doctype/physician/physician.json new file mode 100644 index 00000000000..565653b89c0 --- /dev/null +++ b/erpnext/healthcare/doctype/physician/physician.json @@ -0,0 +1,891 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "", + "beta": 1, + "creation": "2016-02-23 11:20:53.565119", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "fields": [ + { + "allow_bulk_edit": 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": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "designation", + "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": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "fieldname": "physician_schedule", + "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": "Physician Schedule", + "length": 0, + "no_copy": 0, + "options": "Physician 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "time_per_appointment", + "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": "Time per Appointment", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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": 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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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": 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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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, + "unique": 0 + } + ], + "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": "2017-08-31 13:51:28.975120", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Physician", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "apply_user_permissions": 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, + "apply_user_permissions": 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", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "first_name", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/physician/physician.py b/erpnext/healthcare/doctype/physician/physician.py new file mode 100644 index 00000000000..8680d5d1273 --- /dev/null +++ b/erpnext/healthcare/doctype/physician/physician.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document +from frappe import throw, _ +from frappe.utils import cstr +from erpnext.accounts.party import validate_party_accounts +from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address + +class Physician(Document): + def onload(self): + load_address_and_contact(self) + + def autoname(self): + # physician first_name and last_name + self.name = " ".join(filter(None, + [cstr(self.get(f)).strip() for f in ["first_name","middle_name","last_name"]])) + + def validate(self): + validate_party_accounts(self) + if self.user_id: + self.validate_for_enabled_user_id() + self.validate_duplicate_user_id() + existing_user_id = frappe.db.get_value("Physician", self.name, "user_id") + if(self.user_id != existing_user_id): + frappe.permissions.remove_user_permission( + "Physician", self.name, existing_user_id) + + + else: + existing_user_id = frappe.db.get_value("Physician", self.name, "user_id") + if existing_user_id: + frappe.permissions.remove_user_permission( + "Physician", self.name, existing_user_id) + + def on_update(self): + if self.user_id: + frappe.permissions.add_user_permission("Physician", self.name, self.user_id) + + + 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_duplicate_user_id(self): + physician = frappe.db.sql_list("""select name from `tabPhysician` where + user_id=%s and name!=%s""", (self.user_id, self.name)) + if physician: + throw(_("User {0} is already assigned to Physician {1}").format( + self.user_id, physician[0]), frappe.DuplicateEntryError) + + def on_trash(self): + delete_contact_and_address('Physician', self.name) diff --git a/erpnext/healthcare/doctype/physician/test_physician.js b/erpnext/healthcare/doctype/physician/test_physician.js new file mode 100644 index 00000000000..43750acaacf --- /dev/null +++ b/erpnext/healthcare/doctype/physician/test_physician.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Physician", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Physician + () => frappe.tests.make('Physician', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/physician/test_physician.py b/erpnext/healthcare/doctype/physician/test_physician.py new file mode 100644 index 00000000000..b6ea92cc720 --- /dev/null +++ b/erpnext/healthcare/doctype/physician/test_physician.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +# test_records = frappe.get_test_records('Physician') + +class TestPhysician(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/physician_schedule/__init__.py b/erpnext/healthcare/doctype/physician_schedule/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/physician_schedule/physician_schedule.js b/erpnext/healthcare/doctype/physician_schedule/physician_schedule.js new file mode 100644 index 00000000000..e198d35fd32 --- /dev/null +++ b/erpnext/healthcare/doctype/physician_schedule/physician_schedule.js @@ -0,0 +1,59 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Physician Schedule', { + refresh: function(frm) { + frm.add_custom_button(__('Add Time Slots'), () => { + var d = new frappe.ui.Dialog({ + fields: [ + {fieldname: 'day', label: __('Day'), fieldtype:'Select', + options:[ + {value:'Sunday', label:__('Sunday')}, + {value:'Monday', label:__('Monday')}, + {value:'Tuesday', label:__('Tuesday')}, + {value:'Wednesday', label:__('Wednesday')}, + {value:'Thursday', label:__('Thursday')}, + {value:'Friday', label:__('Friday')}, + {value:'Saturday', label:__('Saturday')}, + ], reqd: 1, 'default': 'Monday'}, + {fieldname: 'from_time', label:__('From'), fieldtype:'Time', + 'default': '09:00:00', reqd: 1}, + {fieldname: 'to_time', label:__('To'), fieldtype:'Time', + 'default': '12:00:00', reqd: 1}, + {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 cur_time = moment(values.from_time, 'HH:mm:ss'); + let end_time = moment(values.to_time, 'HH:mm:ss'); + + + while(cur_time < end_time) { + let to_time = cur_time.clone().add(values.duration, 'minutes'); + if(to_time < end_time) { + + // add a new timeslot + frm.add_child('time_slots', { + from_time: cur_time.format('HH:mm:ss'), + to_time: to_time.format('HH:mm:ss'), + day: values.day + }); + } + cur_time = to_time; + } + + frm.refresh_field('time_slots'); + frappe.show_alert({ + message:__('Time slots added'), + indicator:'green' + }); + } + }, + }); + d.show(); + }); + } +}); diff --git a/erpnext/healthcare/doctype/physician_schedule/physician_schedule.json b/erpnext/healthcare/doctype/physician_schedule/physician_schedule.json new file mode 100644 index 00000000000..bd8e539c539 --- /dev/null +++ b/erpnext/healthcare/doctype/physician_schedule/physician_schedule.json @@ -0,0 +1,155 @@ +{ + "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", + "fields": [ + { + "allow_bulk_edit": 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": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": "Physician 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + } + ], + "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:42:26.713507", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Physician Schedule", + "name_case": "", + "owner": "rmehta@gmail.com", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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 + } + ], + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/physician_schedule/physician_schedule.py b/erpnext/healthcare/doctype/physician_schedule/physician_schedule.py new file mode 100644 index 00000000000..5cbdd126a91 --- /dev/null +++ b/erpnext/healthcare/doctype/physician_schedule/physician_schedule.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class PhysicianSchedule(Document): + pass diff --git a/erpnext/healthcare/doctype/physician_schedule/test_physician_schedule.js b/erpnext/healthcare/doctype/physician_schedule/test_physician_schedule.js new file mode 100644 index 00000000000..c397f18398c --- /dev/null +++ b/erpnext/healthcare/doctype/physician_schedule/test_physician_schedule.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Physician Schedule", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Physician Schedule + () => frappe.tests.make('Physician Schedule', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/physician_schedule/test_physician_schedule.py b/erpnext/healthcare/doctype/physician_schedule/test_physician_schedule.py new file mode 100644 index 00000000000..ece357882a6 --- /dev/null +++ b/erpnext/healthcare/doctype/physician_schedule/test_physician_schedule.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +class TestPhysicianSchedule(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/physician_schedule_time_slot/__init__.py b/erpnext/healthcare/doctype/physician_schedule_time_slot/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/physician_schedule_time_slot/physician_schedule_time_slot.json b/erpnext/healthcare/doctype/physician_schedule_time_slot/physician_schedule_time_slot.json new file mode 100644 index 00000000000..8aece2bf952 --- /dev/null +++ b/erpnext/healthcare/doctype/physician_schedule_time_slot/physician_schedule_time_slot.json @@ -0,0 +1,133 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 1, + "creation": "2017-05-03 17:27:07.466088", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "day", + "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": "Day", + "length": 0, + "no_copy": 0, + "options": "Sunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "from_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": "From 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "to_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": "To 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, + "unique": 0 + } + ], + "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-07-12 14:28:01.985998", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Physician Schedule Time Slot", + "name_case": "", + "owner": "rmehta@gmail.com", + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/physician_schedule_time_slot/physician_schedule_time_slot.py b/erpnext/healthcare/doctype/physician_schedule_time_slot/physician_schedule_time_slot.py new file mode 100644 index 00000000000..abdce51b9a5 --- /dev/null +++ b/erpnext/healthcare/doctype/physician_schedule_time_slot/physician_schedule_time_slot.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class PhysicianScheduleTimeSlot(Document): + pass diff --git a/erpnext/healthcare/doctype/prescription_dosage/__init__.py b/erpnext/healthcare/doctype/prescription_dosage/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.js b/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.js new file mode 100644 index 00000000000..94b444cbaa2 --- /dev/null +++ b/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.js @@ -0,0 +1,5 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Prescription Dosage', { +}); diff --git a/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.json b/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.json new file mode 100644 index 00000000000..9c095061931 --- /dev/null +++ b/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.json @@ -0,0 +1,145 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:dosage", + "beta": 1, + "creation": "2016-09-16 15:49:25.327610", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 0, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "dosage", + "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": "Dosage", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "dosage_strength", + "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": "Dosage Strength", + "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 + } + ], + "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:42:40.305284", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Prescription Dosage", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "apply_user_permissions": 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 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "dosage", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.py b/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.py new file mode 100644 index 00000000000..dea263dca09 --- /dev/null +++ b/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class PrescriptionDosage(Document): + pass diff --git a/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.js b/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.js new file mode 100644 index 00000000000..009614ff5dd --- /dev/null +++ b/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Prescription Dosage", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Prescription Dosage + () => frappe.tests.make('Prescription Dosage', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.py b/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.py new file mode 100644 index 00000000000..e61a4188966 --- /dev/null +++ b/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +class TestPrescriptionDosage(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/prescription_duration/__init__.py b/erpnext/healthcare/doctype/prescription_duration/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/prescription_duration/prescription_duration.js b/erpnext/healthcare/doctype/prescription_duration/prescription_duration.js new file mode 100644 index 00000000000..dd5887c9296 --- /dev/null +++ b/erpnext/healthcare/doctype/prescription_duration/prescription_duration.js @@ -0,0 +1,5 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Prescription Duration', { +}); diff --git a/erpnext/healthcare/doctype/prescription_duration/prescription_duration.json b/erpnext/healthcare/doctype/prescription_duration/prescription_duration.json new file mode 100644 index 00000000000..c4f6c5f10da --- /dev/null +++ b/erpnext/healthcare/doctype/prescription_duration/prescription_duration.json @@ -0,0 +1,145 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "", + "beta": 1, + "creation": "2016-09-16 15:50:28.895789", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 0, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "number", + "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": "Number", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "period", + "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": "Period", + "length": 0, + "no_copy": 0, + "options": "Hour\nDay\nWeek\nMonth", + "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 + } + ], + "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:42:51.325725", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Prescription Duration", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "apply_user_permissions": 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 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "", + "show_name_in_global_search": 0, + "sort_field": "", + "sort_order": "ASC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/prescription_duration/prescription_duration.py b/erpnext/healthcare/doctype/prescription_duration/prescription_duration.py new file mode 100644 index 00000000000..96ddf8dafe8 --- /dev/null +++ b/erpnext/healthcare/doctype/prescription_duration/prescription_duration.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document +from frappe.utils import cstr + +class PrescriptionDuration(Document): + def autoname(self): + self.name = " ".join(filter(None, + [cstr(self.get(f)).strip() for f in ["number", "period"]])) + def get_days(self): + days = 0 + duration = self + if(duration.period == 'Day'): + days = duration.number + if(duration.period == 'Hour'): + days = (duration.number)/24 + if(duration.period == 'Week'): + days = (duration.number*7) + if(duration.period == 'Month'): + days = (duration.number*30) + return days + def get_weeks(self): + weeks = 0 + duration = self + if(duration.period == 'Day'): + weeks = (duration.number)/7 + #if(duration.period == 'Hour'): + # weeks = (duration.number)/x + if(duration.period == 'Week'): + weeks = duration.number + if(duration.period == 'Month'): + weeks = duration.number*4 + return weeks + def get_months(self): + months = 0 + duration = self + if(duration.period == 'Day'): + months = (duration.number)/30 + #if(duration.period == 'Hour'): + # months = (duration.number)/x + if(duration.period == 'Week'): + months = (duration.number)/4 + if(duration.period == 'Month'): + months = duration.number + return months + def get_hours(self): + hours = 0 + duration = self + if(duration.period == 'Day'): + hours = (duration.number*24) + if(duration.period == 'Hour'): + hours = duration.number + if(duration.period == 'Week'): + hours = (duration.number*24)*7 + if(duration.period == 'Month'): + hours = (duration.number*24)*30 + return hours + def get_minutes(self): + minutes = 0 + duration = self + if(duration.period == 'Day'): + minutes = (duration.number*1440) + if(duration.period == 'Hour'): + minutes = (duration.number*60) + if(duration.period == 'Week'): + minutes = (duration.number*10080) + if(duration.period == 'Month'): + minutes = (duration.number*43800) + return minutes diff --git a/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.js b/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.js new file mode 100644 index 00000000000..4971e79198c --- /dev/null +++ b/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Prescription Duration", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Prescription Duration + () => frappe.tests.make('Prescription Duration', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.py b/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.py new file mode 100644 index 00000000000..fe5524c5aa7 --- /dev/null +++ b/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +class TestPrescriptionDuration(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/sample_collection/__init__.py b/erpnext/healthcare/doctype/sample_collection/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.js b/erpnext/healthcare/doctype/sample_collection/sample_collection.js new file mode 100644 index 00000000000..9934ce48451 --- /dev/null +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.js @@ -0,0 +1,40 @@ +// Copyright (c) 2016, ESS and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Sample Collection', { + refresh: function(frm) { + if(frappe.defaults.get_default("require_sample_collection")){ + frm.add_custom_button(__("View Lab Tests"), function() { + frappe.route_options = {"sample": frm.doc.name}; + frappe.set_route("List", "Lab Test"); + }); + } + } +}); + +frappe.ui.form.on("Sample Collection", "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 = 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_sex", data.message.sex); + } + }); + } +}); + +var calculate_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)"; +}; diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json new file mode 100644 index 00000000000..ee2f83f01c7 --- /dev/null +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.json @@ -0,0 +1,614 @@ +{ + "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, + "fields": [ + { + "allow_bulk_edit": 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": "SMPL-", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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": "Invoice", + "length": 0, + "no_copy": 0, + "options": "Sales Invoice", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "options": "patient.sex", + "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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 1, + "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": 1, + "search_index": 1, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "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": 1, + "in_standard_filter": 0, + "label": "UOM", + "length": 0, + "no_copy": 0, + "options": "sample.sample_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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "collected_by", + "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": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "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, + "unique": 0 + } + ], + "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": "2017-08-31 13:30:29.303026", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Sample Collection", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "apply_user_permissions": 0, + "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, + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.py b/erpnext/healthcare/doctype/sample_collection/sample_collection.py new file mode 100644 index 00000000000..2c64320fac9 --- /dev/null +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class SampleCollection(Document): + pass diff --git a/erpnext/healthcare/doctype/sample_collection/test_sample_collection.js b/erpnext/healthcare/doctype/sample_collection/test_sample_collection.js new file mode 100644 index 00000000000..2b4aed756bf --- /dev/null +++ b/erpnext/healthcare/doctype/sample_collection/test_sample_collection.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Sample Collection", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Sample Collection + () => frappe.tests.make('Sample Collection', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/sample_collection/test_sample_collection.py b/erpnext/healthcare/doctype/sample_collection/test_sample_collection.py new file mode 100644 index 00000000000..0b16173dd53 --- /dev/null +++ b/erpnext/healthcare/doctype/sample_collection/test_sample_collection.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and Contributors +# See license.txt +from __future__ import unicode_literals + +import unittest + +# test_records = frappe.get_test_records('Sample Collection') + +class TestSampleCollection(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/sensitivity/__init__.py b/erpnext/healthcare/doctype/sensitivity/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/sensitivity/sensitivity.js b/erpnext/healthcare/doctype/sensitivity/sensitivity.js new file mode 100644 index 00000000000..f9c9002fe6d --- /dev/null +++ b/erpnext/healthcare/doctype/sensitivity/sensitivity.js @@ -0,0 +1,5 @@ +// Copyright (c) 2016, ESS LLP and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Sensitivity', { +}); diff --git a/erpnext/healthcare/doctype/sensitivity/sensitivity.json b/erpnext/healthcare/doctype/sensitivity/sensitivity.json new file mode 100644 index 00000000000..620f37a59aa --- /dev/null +++ b/erpnext/healthcare/doctype/sensitivity/sensitivity.json @@ -0,0 +1,115 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:sensitivity", + "beta": 1, + "creation": "2016-02-23 11:12:54.623249", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sensitivity", + "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": "Sensitivity", + "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 + } + ], + "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:44:07.147326", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Sensitivity", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "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 + }, + { + "amend": 0, + "apply_user_permissions": 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 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "sensitivity", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "sensitivity", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/sensitivity/sensitivity.py b/erpnext/healthcare/doctype/sensitivity/sensitivity.py new file mode 100644 index 00000000000..bf7c36b9d28 --- /dev/null +++ b/erpnext/healthcare/doctype/sensitivity/sensitivity.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class Sensitivity(Document): + pass diff --git a/erpnext/healthcare/doctype/sensitivity/test_sensitivity.js b/erpnext/healthcare/doctype/sensitivity/test_sensitivity.js new file mode 100644 index 00000000000..c2cf406f96d --- /dev/null +++ b/erpnext/healthcare/doctype/sensitivity/test_sensitivity.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Sensitivity", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Sensitivity + () => frappe.tests.make('Sensitivity', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/sensitivity/test_sensitivity.py b/erpnext/healthcare/doctype/sensitivity/test_sensitivity.py new file mode 100644 index 00000000000..1616d2decbb --- /dev/null +++ b/erpnext/healthcare/doctype/sensitivity/test_sensitivity.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +# test_records = frappe.get_test_records('Sensitivity') + +class TestSensitivity(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/sensitivity_test_items/__init__.py b/erpnext/healthcare/doctype/sensitivity_test_items/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/sensitivity_test_items/sensitivity_test_items.json b/erpnext/healthcare/doctype/sensitivity_test_items/sensitivity_test_items.json new file mode 100644 index 00000000000..e656b512792 --- /dev/null +++ b/erpnext/healthcare/doctype/sensitivity_test_items/sensitivity_test_items.json @@ -0,0 +1,103 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 1, + "creation": "2016-02-22 15:18:01.769903", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "antibiotic", + "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": "Antibiotic", + "length": 0, + "no_copy": 0, + "options": "Antibiotic", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "antibiotic_sensitivity", + "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": "Sensitivity", + "length": 0, + "no_copy": 0, + "options": "Sensitivity", + "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 + } + ], + "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-08-31 13:46:40.609983", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Sensitivity Test Items", + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/sensitivity_test_items/sensitivity_test_items.py b/erpnext/healthcare/doctype/sensitivity_test_items/sensitivity_test_items.py new file mode 100644 index 00000000000..35c8efde79c --- /dev/null +++ b/erpnext/healthcare/doctype/sensitivity_test_items/sensitivity_test_items.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class SensitivityTestItems(Document): + pass diff --git a/erpnext/healthcare/doctype/special_test_items/__init__.py b/erpnext/healthcare/doctype/special_test_items/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/special_test_items/special_test_items.json b/erpnext/healthcare/doctype/special_test_items/special_test_items.json new file mode 100644 index 00000000000..89545e9acd1 --- /dev/null +++ b/erpnext/healthcare/doctype/special_test_items/special_test_items.json @@ -0,0 +1,166 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 1, + "creation": "2016-02-22 15:12:36.202380", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "test_particulars", + "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": "Particulars", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.require_result_value == 1", + "fieldname": "result_value", + "fieldtype": "Small Text", + "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": "Value", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "", + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "" + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "require_result_value", + "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": "Require Result Value", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "template", + "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": "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": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "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-08-31 14:12:12.623714", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Special Test Items", + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/special_test_items/special_test_items.py b/erpnext/healthcare/doctype/special_test_items/special_test_items.py new file mode 100644 index 00000000000..17080b7e3bb --- /dev/null +++ b/erpnext/healthcare/doctype/special_test_items/special_test_items.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class SpecialTestItems(Document): + pass diff --git a/erpnext/healthcare/doctype/special_test_template/__init__.py b/erpnext/healthcare/doctype/special_test_template/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/special_test_template/special_test_template.json b/erpnext/healthcare/doctype/special_test_template/special_test_template.json new file mode 100644 index 00000000000..83f3cb6fd93 --- /dev/null +++ b/erpnext/healthcare/doctype/special_test_template/special_test_template.json @@ -0,0 +1,72 @@ +{ + "allow_copy": 1, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 1, + "creation": "2016-02-22 16:12:12.394200", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "particulars", + "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": "Result Component", + "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 + } + ], + "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-08-31 14:08:18.833796", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Special Test Template", + "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 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/special_test_template/special_test_template.py b/erpnext/healthcare/doctype/special_test_template/special_test_template.py new file mode 100644 index 00000000000..e4e0d5b7bdb --- /dev/null +++ b/erpnext/healthcare/doctype/special_test_template/special_test_template.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class SpecialTestTemplate(Document): + pass diff --git a/erpnext/healthcare/doctype/vital_signs/__init__.py b/erpnext/healthcare/doctype/vital_signs/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/doctype/vital_signs/test_vital_signs.js b/erpnext/healthcare/doctype/vital_signs/test_vital_signs.js new file mode 100644 index 00000000000..f4ab4466be4 --- /dev/null +++ b/erpnext/healthcare/doctype/vital_signs/test_vital_signs.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Vital Signs", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Vital Signs + () => frappe.tests.make('Vital Signs', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/vital_signs/test_vital_signs.py b/erpnext/healthcare/doctype/vital_signs/test_vital_signs.py new file mode 100644 index 00000000000..5d3e00740f9 --- /dev/null +++ b/erpnext/healthcare/doctype/vital_signs/test_vital_signs.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +# test_records = frappe.get_test_records('Vital Signs') + +class TestVitalSigns(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.js b/erpnext/healthcare/doctype/vital_signs/vital_signs.js new file mode 100644 index 00000000000..a5f9d612952 --- /dev/null +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.js @@ -0,0 +1,49 @@ +// 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", "weight", function(frm) { + if(frm.doc.height && frm.doc.weight){ + calculate_bmi(frm); + } +}); + +var 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"; + } + 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); +}; diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.json b/erpnext/healthcare/doctype/vital_signs/vital_signs.json new file mode 100644 index 00000000000..e44af280082 --- /dev/null +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.json @@ -0,0 +1,813 @@ +{ + "allow_copy": 1, + "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", + "fields": [ + { + "allow_bulk_edit": 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": 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 + }, + { + "allow_bulk_edit": 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": 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": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "consultation", + "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": "Consultation", + "length": 0, + "no_copy": 0, + "options": "Consultation", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": 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": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + } + ], + "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": "2017-09-04 14:31:57.851546", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Vital Signs", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "apply_user_permissions": 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, + "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.py b/erpnext/healthcare/doctype/vital_signs/vital_signs.py new file mode 100644 index 00000000000..436f79fcb91 --- /dev/null +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class VitalSigns(Document): + def on_submit(self): + insert_vital_signs_to_medical_record(self) + + def on_cancel(self): + delete_vital_signs_from_medical_record(self) + +def insert_vital_signs_to_medical_record(doc): + subject = set_subject_field(doc) + medical_record = frappe.new_doc("Patient Medical Record") + medical_record.patient = doc.patient + medical_record.subject = subject + medical_record.status = "Open" + medical_record.communication_date = doc.signs_date + medical_record.reference_doctype = "Vital Signs" + medical_record.reference_name = doc.name + medical_record.reference_owner = doc.owner + 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[0][0]): + frappe.delete_doc("Patient Medical Record", medical_record_id[0][0]) + +def set_subject_field(doc): + subject = " " + if(doc.temperature): + subject += "Temperature: \n"+ str(doc.temperature)+". " + if(doc.pulse): + subject += "Pulse: \n"+ str(doc.pulse)+". " + if(doc.respiratory_rate): + subject += "Respiratory Rate: \n"+ str(doc.respiratory_rate)+". " + if(doc.bp): + subject += "BP: \n"+ str(doc.bp)+". " + if(doc.bmi): + subject += "BMI: \n"+ str(doc.bmi)+". " + if(doc.nutrition_note): + subject += "Note: \n"+ str(doc.nutrition_note)+". " + + return subject diff --git a/erpnext/healthcare/page/__init__.py b/erpnext/healthcare/page/__init__.py new file mode 100644 index 00000000000..baffc488252 --- /dev/null +++ b/erpnext/healthcare/page/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/erpnext/healthcare/page/appointment_analytic/__init__.py b/erpnext/healthcare/page/appointment_analytic/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/page/appointment_analytic/appointment_analytic.js b/erpnext/healthcare/page/appointment_analytic/appointment_analytic.js new file mode 100644 index 00000000000..971d810c2f5 --- /dev/null +++ b/erpnext/healthcare/page/appointment_analytic/appointment_analytic.js @@ -0,0 +1,209 @@ +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", "Physician", "Medical Department", "Appointment Type", "Patient"], + tree_grid: { show: true } + }); + + this.tree_grids = { + "Medical Department": { + label: __("Department"), + show: true, + item_key: "physician", + parent_field: "department", + formatter: function(item) { + return item.name; + } + }, + "Physician": { + label: __("Physician"), + show: true, + item_key: "physician", + formatter: function(item) { + return item.name; + } + }, + }; + }, + setup_columns: function() { + this.tree_grid = this.tree_grids[this.tree_type]; + + var std_columns = [ + {id: "_check", name: __("Plot"), field: "_check", width: 40, + formatter: this.check_formatter}, + {id: "name", name: this.tree_grid.label, field: "name", width: 300, + formatter: this.tree_formatter}, + {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:["Physician", "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: __("Physician"), link:"Physician", fieldname: "physician", + default_value: __("Select Physician..."), filter: function(val, item, opts) { + return val == opts.default_value || item.name == val || item._show; + }, link_formatter: {filter_input: "physician"}}, + {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", "physician", "department", "status", "type"]); + + // this.show_zero_check() + this.setup_chart_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=='Physician') { + items = frappe.report_dump.data["Physician"]; + } if(me.tree_type=='Medical Department') { + items = this.prepare_tree("Physician", "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 new file mode 100644 index 00000000000..4deff806bae --- /dev/null +++ b/erpnext/healthcare/page/appointment_analytic/appointment_analytic.json @@ -0,0 +1,22 @@ +{ + "content": null, + "creation": "2016-08-18 12:29:52.497819", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2016-08-18 12:29:52.497819", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "appointment-analytic", + "owner": "Administrator", + "page_name": "Appointment Analytics", + "roles": [ + { + "role": "Physician" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "title": "Appointment Analytics" +} diff --git a/erpnext/healthcare/page/medical_record/__init__.py b/erpnext/healthcare/page/medical_record/__init__.py new file mode 100644 index 00000000000..baffc488252 --- /dev/null +++ b/erpnext/healthcare/page/medical_record/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/erpnext/healthcare/page/medical_record/medical_record.css b/erpnext/healthcare/page/medical_record/medical_record.css new file mode 100644 index 00000000000..e76df0f82e5 --- /dev/null +++ b/erpnext/healthcare/page/medical_record/medical_record.css @@ -0,0 +1,73 @@ +#page-medical_record .label { + display: inline-block; + margin-right: 7px; +} + +#page-medical_record .list-row { + border: none; + padding: 0px; + cursor: pointer; +} + +.medical_record-label { + max-width: 100px; + margin-bottom: -4px; +} + +.date-indicator { + background:none; + font-size:12px; + vertical-align:middle; + font-weight:bold; + color:#6c7680; +} +.date-indicator::after { + margin:0 -4px 0 12px; + content:''; + display:inline-block; + height:8px; + width:8px; + border-radius:8px; + background: #d1d8dd; +} + +.date-indicator.blue { + color: #5e64ff; +} + +.date-indicator.blue::after { + background: #5e64ff; +} + +.medical_record-message { + border-left: 1px solid #d1d8dd; + padding: 15px; + padding-right: 30px; +} + +.medical_record-date { + padding: 15px; + padding-right: 0px; + z-index: 1; +} + +#page-medical_record .plot-wrapper { + padding: 20px 15px; + border-bottom: 1px solid #d1d8dd; + text-align: center; +} + +#page-medical_record .plot { + height: 140px ; + width: 97% ; + margin: auto; +} + +#page-medical_record .list-filters { + display: none ; +} + +#page-medical_record .octicon-heart { + color: #ff5858; + margin: 0px 5px; +} diff --git a/erpnext/healthcare/page/medical_record/medical_record.js b/erpnext/healthcare/page/medical_record/medical_record.js new file mode 100644 index 00000000000..df19d8f4dca --- /dev/null +++ b/erpnext/healthcare/page/medical_record/medical_record.js @@ -0,0 +1,182 @@ +frappe.provide("frappe.medical_record"); +frappe.pages['medical_record'].on_page_load = function(wrapper) { + var me = this; + var page = frappe.ui.make_app_page({ + parent: wrapper, + title: 'Medical Record', + }); + + frappe.breadcrumbs.add("Medical"); + + page.main.html(frappe.render_template("patient_select", {})); + var patient = frappe.ui.form.make_control({ + parent: page.main.find(".patient"), + df: { + fieldtype: "Link", + options: "Patient", + fieldname: "patient", + change: function(){ + page.main.find(".frappe-list").html(""); + draw_page(patient.get_value(), me); + } + }, + only_input: true, + }); + patient.refresh(); + + + this.page.main.on("click", ".medical_record-message", function() { + var doctype = $(this).attr("data-doctype"), + docname = $(this).attr("data-docname"); + + if (doctype && docname) { + frappe.route_options = { + scroll_to: { "doctype": doctype, "name": docname } + }; + frappe.set_route(["Form", doctype, docname]); + } + }); + + this.page.sidebar.on("click", ".edit-details", function() { + patient = patient.get_value(); + if (patient) { + frappe.set_route(["Form", "Patient", patient]); + } + }); + +}; + +frappe.pages['medical_record'].refresh = function() { + var me = this; + + if(frappe.route_options) { + if(frappe.route_options.patient){ + me.page.main.find(".frappe-list").html(""); + var patient = frappe.route_options.patient; + draw_page(patient,me); + me.page.main.find("[data-fieldname='patient']").val(patient); + frappe.route_options = null; + } + } +}; +var show_patient_info = function(patient, me){ + frappe.call({ + "method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail", + args: { + patient: patient + }, + callback: function (r) { + var data = r.message; + var details = ""; + if(data.email) details += "
Email : " + data.email; + if(data.mobile) details += "
Mobile : " + data.mobile; + if(data.occupation) details += "
Occupation : " + data.occupation; + if(data.blood_group) details += "
Blood group : " + data.blood_group; + if(data.allergies) details += "

Allergies : "+ data.allergies; + if(data.medication) details += "
Medication : "+ data.medication; + if(data.alcohol_current_use) details += "

Alcohol use : "+ data.alcohol_current_use; + if(data.alcohol_past_use) details += "
Alcohol past use : "+ data.alcohol_past_use; + if(data.tobacco_current_use) details += "
Tobacco use : "+ data.tobacco_current_use; + if(data.tobacco_past_use) details += "
Tobacco past use : "+ data.tobacco_past_use; + if(data.medical_history) details += "

Medical history : "+ data.medical_history; + if(data.surgical_history) details += "
Surgical history : "+ data.surgical_history; + if(data.surrounding_factors) details += "

Occupational hazards : "+ data.surrounding_factors; + if(data.other_risk_factors) details += "
Other risk factors : " + data.other_risk_factors; + if(data.patient_details) details += "

More info : " + data.patient_details; + + if(details){ + details = "

Patient Details" + details + "
"; + } + + var vitals = ""; + if(data.temperature) vitals += "
Temperature : " + data.temperature; + if(data.pulse) vitals += "
Pulse : " + data.pulse; + if(data.respiratory_rate) vitals += "
Respiratory Rate : " + data.respiratory_rate; + if(data.bp) vitals += "
BP : " + data.bp; + if(data.bmi) vitals += "
BMI : " + data.bmi; + if(data.height) vitals += "
Height : " + data.height; + if(data.weight) vitals += "
Weight : " + data.weight; + if(data.signs_date) vitals += "
Date : " + data.signs_date; + + if(vitals){ + vitals = "

Vital Signs" + vitals + "
"; + details = vitals + details; + } + if(details) details += "

Edit Details
"; + + me.page.sidebar.addClass("col-sm-3"); + me.page.sidebar.html(details); + me.page.wrapper.find(".layout-main-section-wrapper").addClass("col-sm-9"); + } + }); +}; +var draw_page = function(patient, me){ + frappe.model.with_doctype("Patient Medical Record", function() { + me.page.list = new frappe.ui.BaseList({ + hide_refresh: true, + page: me.page, + method: 'erpnext.healthcare.page.medical_record.medical_record.get_feed', + args: {name: patient}, + parent: $("
").appendTo(me.page.main), + render_view: function(values) { + var me = this; + var wrapper = me.page.main.find(".result-list").get(0); + values.map(function (value) { + var row = $('
').data("data", value).appendTo($(wrapper)).get(0); + new frappe.medical_record.Feed(row, value); + }); + }, + show_filters: true, + doctype: "Patient Medical Record", + }); + show_patient_info(patient, me); + me.page.list.run(); + }); +}; + +frappe.medical_record.last_feed_date = false; +frappe.medical_record.Feed = Class.extend({ + init: function(row, data) { + this.scrub_data(data); + this.add_date_separator(row, data); + if(!data.add_class) + data.add_class = "label-default"; + + data.link = ""; + if (data.reference_doctype && data.reference_name) { + data.link = frappe.format(data.reference_name, {fieldtype: "Link", options: data.reference_doctype}, + {label: __(data.reference_doctype)}); + } + + $(row) + .append(frappe.render_template("medical_record_row", data)) + .find("a").addClass("grey"); + }, + scrub_data: function(data) { + data.by = frappe.user.full_name(data.owner); + data.imgsrc = frappe.utils.get_file_link(frappe.user_info(data.owner).image); + + data.icon = "icon-flag"; + }, + add_date_separator: function(row, data) { + var date = frappe.datetime.str_to_obj(data.creation); + var last = frappe.medical_record.last_feed_date; + + if((last && frappe.datetime.obj_to_str(last) != frappe.datetime.obj_to_str(date)) || (!last)) { + var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); + if(diff < 1) { + var pdate = 'Today'; + } else if(diff < 2) { + pdate = 'Yesterday'; + } else { + pdate = frappe.datetime.global_date_format(date); + } + data.date_sep = pdate; + data.date_class = pdate=='Today' ? "date-indicator blue" : "date-indicator"; + } else { + data.date_sep = null; + data.date_class = ""; + } + frappe.medical_record.last_feed_date = date; + } +}); diff --git a/erpnext/healthcare/page/medical_record/medical_record.json b/erpnext/healthcare/page/medical_record/medical_record.json new file mode 100644 index 00000000000..7c786cae6b7 --- /dev/null +++ b/erpnext/healthcare/page/medical_record/medical_record.json @@ -0,0 +1,23 @@ +{ + "content": null, + "creation": "2016-06-09 11:33:14.025787", + "docstatus": 0, + "doctype": "Page", + "icon": "icon-play", + "idx": 0, + "modified": "2017-03-06 11:20:40.174661", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "medical_record", + "owner": "Administrator", + "page_name": "medical_record", + "roles": [ + { + "role": "Physician" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "title": "Medical Record" +} diff --git a/erpnext/healthcare/page/medical_record/medical_record.py b/erpnext/healthcare/page/medical_record/medical_record.py new file mode 100644 index 00000000000..22c5852b05e --- /dev/null +++ b/erpnext/healthcare/page/medical_record/medical_record.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import cint + +@frappe.whitelist() +def get_feed(start, page_length, name): + """get feed""" + result = frappe.db.sql("""select name, owner, modified, creation, + reference_doctype, reference_name, subject + from `tabPatient Medical Record` + where patient=%(patient)s + order by creation desc + limit %(start)s, %(page_length)s""", + { + "start": cint(start), + "page_length": cint(page_length), + "patient": name + }, as_dict=True) + + return result diff --git a/erpnext/healthcare/page/medical_record/medical_record_row.html b/erpnext/healthcare/page/medical_record/medical_record_row.html new file mode 100644 index 00000000000..6b791465dc8 --- /dev/null +++ b/erpnext/healthcare/page/medical_record/medical_record_row.html @@ -0,0 +1,21 @@ +
+
+ {%= date_sep || "" %} +
+
+ +
+ +
+ + {% if (reference_doctype && reference_name) { %} + {%= __("{0}: {1}", [link, "" + subject + ""]) %} + {% } else { %} + {%= subject %} + {% } %} + +
+
diff --git a/erpnext/healthcare/page/medical_record/patient_select.html b/erpnext/healthcare/page/medical_record/patient_select.html new file mode 100644 index 00000000000..321baf738d7 --- /dev/null +++ b/erpnext/healthcare/page/medical_record/patient_select.html @@ -0,0 +1,5 @@ +
+ +

{%= __("Select Patient") %}

+

+
diff --git a/erpnext/healthcare/print_format/__init__.py b/erpnext/healthcare/print_format/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/print_format/consultation_print/__init__.py b/erpnext/healthcare/print_format/consultation_print/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/print_format/consultation_print/consultation_print.json b/erpnext/healthcare/print_format/consultation_print/consultation_print.json new file mode 100644 index 00000000000..05b8b43bf3f --- /dev/null +++ b/erpnext/healthcare/print_format/consultation_print/consultation_print.json @@ -0,0 +1,22 @@ +{ + "align_labels_left": 0, + "creation": "2017-04-10 14:05:53.355863", + "custom_format": 1, + "disabled": 0, + "doc_type": "Consultation", + "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.physician %}\n\t
\n\t\t\t: {{doc.physician}}\n\t
\n {%- endif -%}\n\t
\n\n {% if doc.consultation_date %}\n\t
\n\t\t
\n\t\t\n\t\t
\n\t\t
\n\t\t: {{doc.consultation_date}}\n\t\t
\n
\n\t {%- endif -%}\n {% if doc.consultation_time %}\n\t
\n\t\t
\n\t\t\n\t\t
\n\t\t
\n\t\t: {{doc.consultation_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.test_prescription %}\n Investigations,\n \n \n \n\n {%- for row in doc.test_prescription -%}\n \n \n \n \n\n\t {%- endfor -%}\n \n
\n {%- if row.test_name -%}{{ row.test_name }}{%- endif -%}\n \n\t\t\t
\n {%- if row.test_comment -%}{{ row.test_comment }}{%- endif -%}\n
\n\t\t
\n\n\n {%- endif -%}\n
\n
\n {% if doc.consultation_comment %}\n
\n {{doc.consultation_comment}}\n {%- endif -%}\n
\n", + "idx": 0, + "line_breaks": 0, + "modified": "2017-05-15 16:36:06.034909", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Consultation 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/__init__.py b/erpnext/healthcare/print_format/lab_test_print/__init__.py new file mode 100644 index 00000000000..e69de29bb2d 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 new file mode 100644 index 00000000000..e3e21a71ceb --- /dev/null +++ b/erpnext/healthcare/print_format/lab_test_print/lab_test_print.json @@ -0,0 +1,22 @@ +{ + "align_labels_left": 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 {% if doc.invoice %}\n
\n
\n \n
\n
\n : {{doc.invoice}}\n
\n
\n {%- endif -%}\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.physician %}\n
\n : {{doc.physician}}\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.test_name }}
\n {%- if doc.normal_test_items|length > 1 %}  {%- endif -%}\n {%- if row.test_name -%}{{ row.test_name }}\n {%- else -%}   {%- endif -%}\n {%- if row.test_event -%}   {{ row.test_event }}{%- endif -%}\n \n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%} \n {%- if row.test_uom -%}{{ row.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.test_name }}
  {{ row.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": "2017-04-27 12:11:58.342145", + "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" +} diff --git a/erpnext/healthcare/print_format/sample_id_print/__init__.py b/erpnext/healthcare/print_format/sample_id_print/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/print_format/sample_id_print/sample_id_print.json b/erpnext/healthcare/print_format/sample_id_print/sample_id_print.json new file mode 100644 index 00000000000..e99ce708f46 --- /dev/null +++ b/erpnext/healthcare/print_format/sample_id_print/sample_id_print.json @@ -0,0 +1,22 @@ +{ + "align_labels_left": 0, + "creation": "2017-02-17 17:40:52.967840", + "custom_format": 1, + "disabled": 0, + "doc_type": "Sample Collection", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "html": "\n{% set column = 0 %}\n\n{% for _ in range(0, doc.num_print) %}\n{% if column == 0 -%}{% endif %}\n\t\n{% if column == 0 %}{% set column = column+1 %}\n{% elif column == 2%} {%- set column = 0 %}\n{% else %}{%- set column = column+1 -%}{%- endif %}\n\t\n{% endfor %}\n
{{doc.name}}
{{doc.patient}}
\n{% if doc.patient_age %}{{doc.patient_age}}, {% endif %} {% if doc.patient_sex %}{{doc.patient_sex}}{% endif %}
{% if doc.collected_time %}{{doc.collected_time}} {% endif %}
{% if doc.collected_by %} {{doc.collected_by}} {% endif %}
", + "idx": 0, + "line_breaks": 0, + "modified": "2017-03-30 18:09:39.537609", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Sample ID Print", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Server", + "show_section_headings": 0, + "standard": "Yes" +} diff --git a/erpnext/healthcare/report/__init__.py b/erpnext/healthcare/report/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/report/lab_test_report/__init__.py b/erpnext/healthcare/report/lab_test_report/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.js b/erpnext/healthcare/report/lab_test_report/lab_test_report.js new file mode 100644 index 00000000000..3128f819bb1 --- /dev/null +++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.js @@ -0,0 +1,32 @@ +// Copyright (c) 2016, ESS +// License: See license.txt + +frappe.query_reports["Lab Test Report"] = { + "filters": [ + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.now_date(), + "width": "80" + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.now_date() + }, + { + "fieldname":"patient", + "label": __("Patient"), + "fieldtype": "Link", + "options": "Patient" + }, + { + "fieldname":"department", + "label": __("Medical Department"), + "fieldtype": "Link", + "options": "Medical Department" + } + ] +}; diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.json b/erpnext/healthcare/report/lab_test_report/lab_test_report.json new file mode 100644 index 00000000000..f133a8e1b73 --- /dev/null +++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.json @@ -0,0 +1,26 @@ +{ + "add_total_row": 1, + "apply_user_permissions": 1, + "creation": "2013-04-23 18:15:29", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 1, + "is_standard": "Yes", + "modified": "2017-08-23 14:54:12.593140", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Lab Test Report", + "owner": "Administrator", + "ref_doctype": "Lab Test", + "report_name": "Lab Test Report", + "report_type": "Script Report", + "roles": [ + { + "role": "Laboratory User" + }, + { + "role": "System Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.py b/erpnext/healthcare/report/lab_test_report/lab_test_report.py new file mode 100644 index 00000000000..61769f82b9a --- /dev/null +++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.py @@ -0,0 +1,58 @@ +# Copyright (c) 2016, ESS +# License: See license.txt + +from __future__ import unicode_literals +import frappe +from frappe import msgprint, _ + +def execute(filters=None): + if not filters: filters = {} + + lab_test_list = get_lab_test(filters) + columns = get_columns() + + if not lab_test_list: + msgprint(_("No record found")) + return columns, lab_test_list + + data = [] + for lab_test in lab_test_list: + row = [ lab_test.test_name, lab_test.patient, lab_test.physician, lab_test.invoice, lab_test.status, lab_test.result_date, lab_test.department] + data.append(row) + + return columns, data + + +def get_columns(): + columns = [ + _("Test") + ":Data:120", + _("Patient") + ":Link/Patient:180", + _("Doctor") + ":Link/Physician:120", + _("Invoice") + ":Link/Sales Invoice:120", + _("Status") + ":Data:120", + _("Result Date") + ":Date:120", + _("Department") + ":Data:120", + ] + + return columns + +def get_conditions(filters): + conditions = "" + + if filters.get("patient"): + conditions += "and patient = %(patient)s" + if filters.get("from_date"): + conditions += "and result_date >= %(from_date)s" + if filters.get("to_date"): + conditions += " and result_date <= %(to_date)s" + if filters.get("department"): + conditions += " and department = %(department)s" + + return conditions + +def get_lab_test(filters): + conditions = get_conditions(filters) + return frappe.db.sql("""select name, patient, test_name, patient_name, status, result_date, physician, invoice, department + from `tabLab Test` + where docstatus<2 %s order by submitted_date desc, name desc""" % + conditions, filters, as_dict=1) diff --git a/erpnext/healthcare/web_form/__init__.py b/erpnext/healthcare/web_form/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/web_form/lab_test/__init__.py b/erpnext/healthcare/web_form/lab_test/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/web_form/lab_test/lab_test.js b/erpnext/healthcare/web_form/lab_test/lab_test.js new file mode 100644 index 00000000000..efcd8abc896 --- /dev/null +++ b/erpnext/healthcare/web_form/lab_test/lab_test.js @@ -0,0 +1,34 @@ +frappe.ready(function() { + // bind events here + var normal_test_items = $('div[data-fieldname = "normal_test_items"]'); + var normal_test_items_add_btn = $('button[data-fieldname = "normal_test_items"]'); + var special_test_items = $('div[data-fieldname = "special_test_items"]'); + var special_test_items_add_btn = $('button[data-fieldname = "special_test_items"]'); + var sensitivity_test_items = $('div[data-fieldname = "sensitivity_test_items"]'); + var sensitivity_test_items_add_btn = $('button[data-fieldname = "sensitivity_test_items"]'); + var sensitivity_toggle = $('input[name = "sensitivity_toggle"]'); + var special_toggle = $('input[name = "special_toggle"]'); + var normal_toggle = $('input[name = "normal_toggle"]'); + if(normal_toggle.val() == 1){ + // normal_test_items[0].style.display = "none"; + // normal_test_items[0].setAttribute("hidden", true); + // normal_test_items_add_btn[0].style.visibility = "hidden"; + special_test_items[0].style.display = "none"; + special_test_items_add_btn[0].style.display = "none"; + sensitivity_test_items[0].style.display = "none"; + sensitivity_test_items_add_btn[0].style.display = "none"; + normal_test_items_add_btn[0].style.display = "none"; + }else if(sensitivity_toggle.val() == 1){ + special_test_items[0].style.display = "none"; + special_test_items_add_btn[0].style.display = "none"; + normal_test_items[0].style.display = "none"; + normal_test_items_add_btn[0].style.display = "none"; + sensitivity_test_items_add_btn[0].style.display = "none"; + }else if(special_toggle.val() == 1){ + normal_test_items[0].style.display = "none"; + normal_test_items_add_btn[0].style.display = "none"; + sensitivity_test_items[0].style.display = "none"; + sensitivity_test_items_add_btn[0].style.display = "none"; + special_test_items_add_btn[0].style.display = "none"; + } +}); diff --git a/erpnext/healthcare/web_form/lab_test/lab_test.json b/erpnext/healthcare/web_form/lab_test/lab_test.json new file mode 100644 index 00000000000..fec34b6dbcc --- /dev/null +++ b/erpnext/healthcare/web_form/lab_test/lab_test.json @@ -0,0 +1,235 @@ +{ + "accept_payment": 0, + "allow_comments": 0, + "allow_delete": 0, + "allow_edit": 1, + "allow_incomplete": 0, + "allow_multiple": 1, + "allow_print": 1, + "amount": 0.0, + "amount_based_on_field": 0, + "creation": "2017-06-06 16:12:33.052258", + "currency": "INR", + "doc_type": "Lab Test", + "docstatus": 0, + "doctype": "Web Form", + "idx": 0, + "introduction_text": "Lab Test", + "is_standard": 1, + "login_required": 1, + "max_attachment_size": 0, + "modified": "2017-06-07 16:03:06.781364", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "lab-test", + "owner": "Administrator", + "payment_button_label": "Buy Now", + "print_format": "Lab Test Print", + "published": 1, + "route": "lab-test", + "show_sidebar": 1, + "sidebar_items": [], + "success_url": "/lab-test", + "title": "Lab Test", + "web_form_fields": [ + { + "fieldname": "naming_series", + "fieldtype": "Select", + "hidden": 0, + "label": "Series", + "max_length": 0, + "max_value": 0, + "options": "LP-", + "read_only": 0, + "reqd": 1 + }, + { + "fieldname": "invoice", + "fieldtype": "Link", + "hidden": 0, + "label": "Invoice", + "max_length": 0, + "max_value": 0, + "options": "Sales Invoice", + "read_only": 0, + "reqd": 0 + }, + { + "fieldname": "patient", + "fieldtype": "Link", + "hidden": 0, + "label": "Patient", + "max_length": 0, + "max_value": 0, + "options": "Patient", + "read_only": 0, + "reqd": 1 + }, + { + "fieldname": "patient_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Patient Name", + "max_length": 0, + "max_value": 0, + "options": "patient.patient_name", + "read_only": 0, + "reqd": 0 + }, + { + "fieldname": "physician", + "fieldtype": "Link", + "hidden": 0, + "label": "Doctor", + "max_length": 0, + "max_value": 0, + "options": "Physician", + "read_only": 0, + "reqd": 0 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "hidden": 0, + "label": "Status", + "max_length": 0, + "max_value": 0, + "options": "Draft\nCompleted\nApproved\nRejected\nCancelled", + "read_only": 0, + "reqd": 0 + }, + { + "fieldname": "department", + "fieldtype": "Link", + "hidden": 0, + "label": "Department", + "max_length": 0, + "max_value": 0, + "options": "Medical Department", + "read_only": 0, + "reqd": 0 + }, + { + "fieldname": "sample", + "fieldtype": "Link", + "hidden": 0, + "label": "Sample ID", + "max_length": 0, + "max_value": 0, + "options": "Sample Collection", + "read_only": 0, + "reqd": 0 + }, + { + "default": "", + "fieldname": "result_date", + "fieldtype": "Date", + "hidden": 0, + "label": "Result Date", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0 + }, + { + "fieldname": "report_preference", + "fieldtype": "Data", + "hidden": 0, + "label": "Report Preference", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0 + }, + { + "fieldname": "test_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Test Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0 + }, + { + "fieldname": "normal_test_items", + "fieldtype": "Table", + "hidden": 0, + "max_length": 0, + "max_value": 0, + "options": "Normal Test Items", + "read_only": 1, + "reqd": 0 + }, + { + "fieldname": "special_test_items", + "fieldtype": "Table", + "hidden": 0, + "max_length": 0, + "max_value": 0, + "options": "Special Test Items", + "read_only": 1, + "reqd": 0 + }, + { + "fieldname": "sensitivity_test_items", + "fieldtype": "Table", + "hidden": 0, + "max_length": 0, + "max_value": 0, + "options": "Sensitivity Test Items", + "read_only": 1, + "reqd": 0 + }, + { + "fieldname": "test_comment", + "fieldtype": "Text", + "hidden": 0, + "label": "Comments", + "max_length": 0, + "max_value": 0, + "read_only": 1, + "reqd": 0 + }, + { + "fieldname": "custom_result", + "fieldtype": "Text Editor", + "hidden": 0, + "label": "Custom Result", + "max_length": 0, + "max_value": 0, + "read_only": 1, + "reqd": 0 + }, + { + "default": "0", + "fieldname": "sensitivity_toggle", + "fieldtype": "Check", + "hidden": 1, + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0 + }, + { + "default": "0", + "fieldname": "special_toggle", + "fieldtype": "Check", + "hidden": 1, + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0 + }, + { + "default": "0", + "fieldname": "normal_toggle", + "fieldtype": "Check", + "hidden": 1, + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0 + } + ] +} \ No newline at end of file diff --git a/erpnext/healthcare/web_form/lab_test/lab_test.py b/erpnext/healthcare/web_form/lab_test/lab_test.py new file mode 100644 index 00000000000..5a8c8a421cd --- /dev/null +++ b/erpnext/healthcare/web_form/lab_test/lab_test.py @@ -0,0 +1,25 @@ +from __future__ import unicode_literals + +import frappe + +def get_context(context): + context.read_only = 1 + +def get_list_context(context): + context.row_template = "erpnext/templates/includes/healthcare/lab_test_row_template.html" + context.get_list = get_lab_test_list + +def get_lab_test_list(doctype, txt, filters, limit_start, limit_page_length = 20, order_by='modified desc'): + patient = get_patient() + lab_tests = frappe.db.sql("""select * from `tabLab Test` + where patient = %s order by result_date""", patient, as_dict = True) + return lab_tests + +def get_patient(): + return frappe.get_value("Patient",{"email": frappe.session.user}, "name") + +def has_website_permission(doc, ptype, user, verbose=False): + if doc.patient == get_patient(): + return True + else: + return False diff --git a/erpnext/healthcare/web_form/patient_appointments/__init__.py b/erpnext/healthcare/web_form/patient_appointments/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/web_form/patient_appointments/patient_appointments.js b/erpnext/healthcare/web_form/patient_appointments/patient_appointments.js new file mode 100644 index 00000000000..f09e5409192 --- /dev/null +++ b/erpnext/healthcare/web_form/patient_appointments/patient_appointments.js @@ -0,0 +1,3 @@ +frappe.ready(function() { + // bind events here +}); diff --git a/erpnext/healthcare/web_form/patient_appointments/patient_appointments.json b/erpnext/healthcare/web_form/patient_appointments/patient_appointments.json new file mode 100644 index 00000000000..613e3673e38 --- /dev/null +++ b/erpnext/healthcare/web_form/patient_appointments/patient_appointments.json @@ -0,0 +1,111 @@ +{ + "accept_payment": 0, + "allow_comments": 0, + "allow_delete": 0, + "allow_edit": 1, + "allow_incomplete": 0, + "allow_multiple": 1, + "allow_print": 1, + "amount": 0.0, + "amount_based_on_field": 0, + "creation": "2017-06-07 15:30:44.984832", + "currency": "INR", + "doc_type": "Patient Appointment", + "docstatus": 0, + "doctype": "Web Form", + "idx": 0, + "introduction_text": "Patient Appointments", + "is_standard": 1, + "login_required": 1, + "max_attachment_size": 0, + "modified": "2017-06-07 15:57:27.196428", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "patient-appointments", + "owner": "Administrator", + "payment_button_label": "Buy Now", + "published": 1, + "route": "patient-appointments", + "show_sidebar": 1, + "sidebar_items": [], + "success_url": "/patient-appointments", + "title": "Patient Appointments", + "web_form_fields": [ + { + "fieldname": "patient", + "fieldtype": "Link", + "hidden": 0, + "label": "Patient", + "max_length": 0, + "max_value": 0, + "options": "Patient", + "read_only": 0, + "reqd": 1 + }, + { + "fieldname": "physician", + "fieldtype": "Link", + "hidden": 0, + "label": "Physician", + "max_length": 0, + "max_value": 0, + "options": "Physician", + "read_only": 0, + "reqd": 1 + }, + { + "fieldname": "appointment_date", + "fieldtype": "Date", + "hidden": 0, + "label": "Date", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1 + }, + { + "fieldname": "appointment_time", + "fieldtype": "Data", + "hidden": 0, + "label": "Time", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0 + }, + { + "fieldname": "department", + "fieldtype": "Link", + "hidden": 0, + "label": "Department", + "max_length": 0, + "max_value": 0, + "options": "Medical Department", + "read_only": 0, + "reqd": 0 + }, + { + "fieldname": "appointment_type", + "fieldtype": "Link", + "hidden": 0, + "label": "Type", + "max_length": 0, + "max_value": 0, + "options": "Appointment Type", + "read_only": 0, + "reqd": 0 + }, + { + "default": "Scheduled", + "fieldname": "status", + "fieldtype": "Select", + "hidden": 0, + "label": "Status", + "max_length": 0, + "max_value": 0, + "options": "\nScheduled\nOpen\nClosed\nPending\nCancelled", + "read_only": 1, + "reqd": 0 + } + ] +} \ No newline at end of file diff --git a/erpnext/healthcare/web_form/patient_appointments/patient_appointments.py b/erpnext/healthcare/web_form/patient_appointments/patient_appointments.py new file mode 100644 index 00000000000..09bcb42b579 --- /dev/null +++ b/erpnext/healthcare/web_form/patient_appointments/patient_appointments.py @@ -0,0 +1,25 @@ +from __future__ import unicode_literals + +import frappe + +def get_context(context): + context.read_only = 1 + +def get_list_context(context): + context.row_template = "erpnext/templates/includes/healthcare/appointment_row_template.html" + context.get_list = get_appointment_list + +def get_appointment_list(doctype, txt, filters, limit_start, limit_page_length = 20, order_by='modified desc'): + patient = get_patient() + lab_tests = frappe.db.sql("""select * from `tabPatient Appointment` + where patient = %s and (status = 'Open' or status = 'Scheduled') order by appointment_date""", patient, as_dict = True) + return lab_tests + +def get_patient(): + return frappe.get_value("Patient",{"email": frappe.session.user}, "name") + +def has_website_permission(doc, ptype, user, verbose=False): + if doc.patient == get_patient(): + return True + else: + return False diff --git a/erpnext/healthcare/web_form/prescription/__init__.py b/erpnext/healthcare/web_form/prescription/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/web_form/prescription/prescription.js b/erpnext/healthcare/web_form/prescription/prescription.js new file mode 100644 index 00000000000..f09e5409192 --- /dev/null +++ b/erpnext/healthcare/web_form/prescription/prescription.js @@ -0,0 +1,3 @@ +frappe.ready(function() { + // bind events here +}); diff --git a/erpnext/healthcare/web_form/prescription/prescription.json b/erpnext/healthcare/web_form/prescription/prescription.json new file mode 100644 index 00000000000..5f251a31e85 --- /dev/null +++ b/erpnext/healthcare/web_form/prescription/prescription.json @@ -0,0 +1,112 @@ +{ + "accept_payment": 0, + "allow_comments": 0, + "allow_delete": 0, + "allow_edit": 1, + "allow_incomplete": 0, + "allow_multiple": 1, + "allow_print": 1, + "amount": 0.0, + "amount_based_on_field": 0, + "creation": "2017-06-06 17:13:19.101374", + "currency": "INR", + "doc_type": "Consultation", + "docstatus": 0, + "doctype": "Web Form", + "idx": 0, + "introduction_text": "Patient Prescriptions", + "is_standard": 1, + "login_required": 1, + "max_attachment_size": 0, + "modified": "2017-06-20 11:56:05.132154", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "prescription", + "owner": "Administrator", + "payment_button_label": "Buy Now", + "print_format": "Consultation Print", + "published": 1, + "route": "prescription", + "show_sidebar": 1, + "sidebar_items": [], + "success_url": "/prescription", + "title": "Prescription", + "web_form_fields": [ + { + "fieldname": "physician", + "fieldtype": "Link", + "hidden": 0, + "label": "Doctor", + "max_length": 0, + "max_value": 0, + "options": "Physician", + "read_only": 0, + "reqd": 1 + }, + { + "fieldname": "visit_department", + "fieldtype": "Link", + "hidden": 0, + "label": "Department", + "max_length": 0, + "max_value": 0, + "options": "Medical Department", + "read_only": 0, + "reqd": 0 + }, + { + "default": "Today", + "fieldname": "consultation_date", + "fieldtype": "Date", + "hidden": 0, + "label": "Consultation Date", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1 + }, + { + "default": "", + "fieldname": "consultation_time", + "fieldtype": "Data", + "hidden": 0, + "label": "Consultation Time", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1 + }, + { + "fieldname": "drug_prescription", + "fieldtype": "Table", + "hidden": 0, + "label": "Drug Prescription", + "max_length": 0, + "max_value": 0, + "options": "Drug Prescription", + "read_only": 0, + "reqd": 0 + }, + { + "fieldname": "test_prescription", + "fieldtype": "Table", + "hidden": 0, + "label": "Investigations", + "max_length": 0, + "max_value": 0, + "options": "Lab Prescription", + "read_only": 0, + "reqd": 0 + }, + { + "fieldname": "consultation_comment", + "fieldtype": "Small Text", + "hidden": 0, + "label": "Review Details", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0 + } + ] +} \ No newline at end of file diff --git a/erpnext/healthcare/web_form/prescription/prescription.py b/erpnext/healthcare/web_form/prescription/prescription.py new file mode 100644 index 00000000000..73b035071c1 --- /dev/null +++ b/erpnext/healthcare/web_form/prescription/prescription.py @@ -0,0 +1,25 @@ +from __future__ import unicode_literals + +import frappe + +def get_context(context): + context.read_only = 1 + +def get_list_context(context): + context.row_template = "erpnext/templates/includes/healthcare/prescription_row_template.html" + context.get_list = get_consultation_list + +def get_consultation_list(doctype, txt, filters, limit_start, limit_page_length = 20, order_by='modified desc'): + patient = get_patient() + consultations = frappe.db.sql("""select * from `tabConsultation` + where patient = %s order by creation desc""", patient, as_dict = True) + return consultations + +def get_patient(): + return frappe.get_value("Patient",{"email": frappe.session.user}, "name") + +def has_website_permission(doc, ptype, user, verbose=False): + if doc.patient == get_patient(): + return True + else: + return False diff --git a/erpnext/hooks.py b/erpnext/hooks.py index adcf5dec94a..8306290f723 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -27,6 +27,8 @@ doctype_js = { # setup wizard setup_wizard_requires = "assets/erpnext/js/setup_wizard.js" setup_wizard_complete = "erpnext.setup.setup_wizard.setup_wizard.setup_complete" +setup_wizard_success = "erpnext.setup.setup_wizard.setup_wizard.setup_success" +setup_wizard_test = "erpnext.setup.setup_wizard.test_setup_wizard.run_setup_wizard_test" before_install = "erpnext.setup.install.check_setup_wizard_not_completed" after_install = "erpnext.setup.install.after_install" @@ -34,6 +36,8 @@ after_install = "erpnext.setup.install.after_install" boot_session = "erpnext.startup.boot.boot_session" notification_config = "erpnext.startup.notifications.get_notification_config" get_help_messages = "erpnext.utilities.activation.get_help_messages" +get_user_progress_slides = "erpnext.utilities.user_progress.get_user_progress_slides" +update_and_get_user_progress = "erpnext.utilities.user_progress_utils.update_default_domain_actions_and_get_state" on_session_creation = "erpnext.shopping_cart.utils.set_cart_count" on_logout = "erpnext.shopping_cart.utils.clear_cart_count" @@ -124,8 +128,13 @@ standard_portal_menu_items = [ {"title": _("Shipments"), "route": "/shipments", "reference_doctype": "Delivery Note", "role":"Customer"}, {"title": _("Issues"), "route": "/issues", "reference_doctype": "Issue", "role":"Customer"}, {"title": _("Addresses"), "route": "/addresses", "reference_doctype": "Address"}, + {"title": _("Timesheets"), "route": "/timesheets", "reference_doctype": "Timesheet", "role":"Customer"}, + {"title": _("Timesheets"), "route": "/timesheets", "reference_doctype": "Timesheet", "role":"Customer"}, + {"title": _("Lab Test"), "route": "/lab-test", "reference_doctype": "Lab Test", "role":"Patient"}, + {"title": _("Prescription"), "route": "/prescription", "reference_doctype": "Consultation", "role":"Patient"}, + {"title": _("Patient Appointment"), "route": "/patient-appointments", "reference_doctype": "Patient Appointment", "role":"Patient"}, {"title": _("Fees"), "route": "/fees", "reference_doctype": "Fees", "role":"Student"}, - {"title": _("Timesheets"), "route": "/timesheets", "reference_doctype": "Timesheet", "role":"Customer"} + {"title": _("Newsletter"), "route": "/newsletters", "reference_doctype": "Newsletter"} ] default_roles = [ @@ -141,7 +150,10 @@ has_website_permission = { "Supplier Quotation": "erpnext.controllers.website_list_for_contact.has_website_permission", "Delivery Note": "erpnext.controllers.website_list_for_contact.has_website_permission", "Issue": "erpnext.support.doctype.issue.issue.has_website_permission", - "Timesheet": "erpnext.controllers.website_list_for_contact.has_website_permission" + "Timesheet": "erpnext.controllers.website_list_for_contact.has_website_permission", + "Lab Test": "erpnext.healthcare.web_form.lab_test.lab_test.has_website_permission", + "Consultation": "erpnext.healthcare.web_form.prescription.prescription.has_website_permission", + "Patient Appointment": "erpnext.healthcare.web_form.patient_appointments.patient_appointments.has_website_permission" } dump_report_map = "erpnext.startup.report_data_map.data_map" @@ -180,7 +192,7 @@ doc_events = { scheduler_events = { "hourly": [ - "erpnext.controllers.recurring_document.create_recurring_documents", + "erpnext.accounts.doctype.subscription.subscription.make_subscription_entry", 'erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.trigger_emails' ], "daily": [ diff --git a/erpnext/hr/doctype/appraisal/test_appraisal.js b/erpnext/hr/doctype/appraisal/test_appraisal.js index 91da7d36248..9ca17e2e226 100644 --- a/erpnext/hr/doctype/appraisal/test_appraisal.js +++ b/erpnext/hr/doctype/appraisal/test_appraisal.js @@ -29,7 +29,7 @@ QUnit.test("Test: Expense Claim [HR]", function (assert) { () => frappe.timeout(1), () => cur_frm.set_value('employee',employee_name), () => cur_frm.set_value('employee_name','Test Employee 1'), - () => cur_frm.set_value('company','Test Company'), + () => cur_frm.set_value('company','For Testing'), () => frappe.click_button('Calculate Total Score'), () => frappe.timeout(1), () => cur_frm.save(), diff --git a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js index 4e245c7117d..0403cad0683 100644 --- a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js +++ b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js @@ -20,7 +20,7 @@ QUnit.test("Test: Appraisal Template [HR]", function (assert) { ]}, ]); }, - () => frappe.timeout(5), + () => frappe.timeout(10), () => { assert.equal('Test Appraisal 1',cur_frm.doc.kra_title, 'Appraisal name correctly set'); }, diff --git a/erpnext/hr/doctype/attendance/test_attendance.js b/erpnext/hr/doctype/attendance/test_attendance.js index 82347ad5672..8f30e8cc161 100644 --- a/erpnext/hr/doctype/attendance/test_attendance.js +++ b/erpnext/hr/doctype/attendance/test_attendance.js @@ -13,9 +13,13 @@ QUnit.test("Test: Attendance [HR]", function (assert) { () => assert.equal("Attendance", cur_frm.doctype, "Form for new Attendance opened successfully."), // set values in form - () => cur_frm.set_value("company", "Test Company"), - () => frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name'), - (employee) => cur_frm.set_value("employee", employee.message.name), + () => cur_frm.set_value("company", "For Testing"), + () => { + frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name', function(r) { + cur_frm.set_value("employee", r.name) + }); + }, + () => frappe.timeout(1), () => cur_frm.save(), () => frappe.timeout(1), // check docstatus of attendance before submit [Draft] diff --git a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py index f03c6fa491d..9ac74aae155 100644 --- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py +++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py @@ -51,6 +51,25 @@ class DailyWorkSummary(Document): d.sender_name = emp[0].employee_name if emp else d.sender d.image = emp[0].image if emp and emp[0].image else None + + original_image = d.image + # make thumbnail image + try: + if original_image: + file_name = frappe.get_list('File', {'file_url': original_image}) + + if file_name: + file_name = file_name[0].name + file_doc = frappe.get_doc('File', file_name) + thumbnail_image = file_doc.make_thumbnail( + set_as_thumbnail=False, + width=100, + height=100, + crop=True + ) + d.image = thumbnail_image + except: + d.image = original_image if d.sender in did_not_reply: did_not_reply.remove(d.sender) diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index a078ef2033b..03626cdc5cf 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -289,3 +289,15 @@ def create_user(employee, user = None): }) user.insert() return user.name + +def get_employee_emails(employee_list): + '''Returns list of employee emails either based on user_id or company_email''' + employee_emails = [] + for employee in employee_list: + if not employee: + continue + user, email = frappe.db.get_value('Employee', employee, ['user_id', 'company_email']) + if user or email: + employee_emails.append(user or email) + + return employee_emails \ No newline at end of file diff --git a/erpnext/hr/doctype/employee/test_employee.js b/erpnext/hr/doctype/employee/test_employee.js index b7f510506bf..200dcd79666 100644 --- a/erpnext/hr/doctype/employee/test_employee.js +++ b/erpnext/hr/doctype/employee/test_employee.js @@ -4,14 +4,14 @@ QUnit.test("Test: Employee [HR]", function (assert) { assert.expect(4); let done = assert.async(); // let today_date = frappe.datetime.nowdate(); - let employee_creation = (name,joining_date,birth_date) => { + let employee_creation = (name, joining_date, birth_date) => { frappe.run_serially([ // test employee creation () => { frappe.tests.make('Employee', [ { employee_name: name}, { salutation: 'Mr'}, - { company: 'Test Company'}, + { company: 'For Testing'}, { date_of_joining: joining_date}, { date_of_birth: birth_date}, { employment_type: 'Test Employment Type'}, @@ -32,9 +32,9 @@ QUnit.test("Test: Employee [HR]", function (assert) { }; frappe.run_serially([ () => employee_creation('Test Employee 1','2017-04-01','1992-02-02'), - () => frappe.timeout(6), + () => frappe.timeout(10), () => employee_creation('Test Employee 3','2017-04-01','1992-02-02'), - () => frappe.timeout(4), + () => frappe.timeout(10), () => done() ]); }); \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js b/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js index 3ec8ac0a593..2827d4ba289 100644 --- a/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js +++ b/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js @@ -12,7 +12,7 @@ QUnit.test("Test: Employee attendance tool [HR]", function (assert) { return frappe.tests.make('Employee', [ {salutation: "Mr"}, {employee_name: "Test Employee 2"}, - {company: "Test Company"}, + {company: "For Testing"}, {date_of_joining: frappe.datetime.add_months(today_date, -2)}, // joined 2 month from now {date_of_birth: frappe.datetime.add_months(today_date, -240)}, // age is 20 years {employment_type: "Test Employment type"}, @@ -30,7 +30,7 @@ QUnit.test("Test: Employee attendance tool [HR]", function (assert) { () => cur_frm.set_value("date", date_of_attendance), () => cur_frm.set_value("branch", "Test Branch"), () => cur_frm.set_value("department", "Test Department"), - () => cur_frm.set_value("company", "Test Company"), + () => cur_frm.set_value("company", "For Testing"), () => frappe.timeout(1), () => frappe.click_button('Check all'), () => frappe.click_button('Mark Present'), @@ -38,9 +38,23 @@ QUnit.test("Test: Employee attendance tool [HR]", function (assert) { () => frappe.set_route("List", "Attendance", "List"), () => frappe.timeout(1), () => { - let marked_attendance = cur_list.data.filter(d => d.attendance_date == date_of_attendance); - assert.equal(marked_attendance.length, 3, - 'all the attendance are marked for correct date'); + return frappe.call({ + method: "frappe.client.get_list", + args: { + doctype: "Employee", + filters: { + "branch": "Test Branch", + "department": "Test Department", + "company": "For Testing", + "status": "Active" + } + }, + callback: function(r) { + let marked_attendance = cur_list.data.filter(d => d.attendance_date == date_of_attendance); + assert.equal(marked_attendance.length, r.message.length, + 'all the attendance are marked for correct date'); + } + }); }, () => done() ]); diff --git a/erpnext/hr/doctype/employee_loan/test_employee_loan.js b/erpnext/hr/doctype/employee_loan/test_employee_loan.js index 90393397737..41aad04c2ce 100644 --- a/erpnext/hr/doctype/employee_loan/test_employee_loan.js +++ b/erpnext/hr/doctype/employee_loan/test_employee_loan.js @@ -15,14 +15,14 @@ QUnit.test("Test Loan [HR]", function(assert) { (r) => { // Creating loan for an employee return frappe.tests.make('Employee Loan', [ - { company: 'Test Company'}, + { company: 'For Testing'}, { posting_date: '2017-08-26'}, { employee: employee_name}, { employee_loan_application: r.message.name}, { disbursement_date: '2018-08-26'}, { mode_of_payment: 'Cash'}, - { employee_loan_account: 'Temporary Opening - TC'}, - { interest_income_account: 'Service - TC'} + { employee_loan_account: 'Temporary Opening - FT'}, + { interest_income_account: 'Service - FT'} ]); }, () => frappe.timeout(3), diff --git a/erpnext/hr/doctype/employee_loan_application/test_employee_loan_application.js b/erpnext/hr/doctype/employee_loan_application/test_employee_loan_application.js index 72ad915f7d5..3e2977ee9a8 100644 --- a/erpnext/hr/doctype/employee_loan_application/test_employee_loan_application.js +++ b/erpnext/hr/doctype/employee_loan_application/test_employee_loan_application.js @@ -13,7 +13,7 @@ QUnit.test("Test: Employee Loan Application [HR]", function (assert) { }, () => { frappe.tests.make('Employee Loan Application', [ - { company: 'Test Company'}, + { company: 'For Testing'}, { employee: employee_name}, { employee_name: 'Test Employee 1'}, { status: 'Approved'}, diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.js b/erpnext/hr/doctype/expense_claim/test_expense_claim.js index c7c764cab52..c89eef4a6f9 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.js @@ -30,9 +30,9 @@ QUnit.test("Test: Expense Claim [HR]", function (assert) { () => frappe.timeout(1), () => cur_frm.set_value('employee',employee_name), () => cur_frm.set_value('employee_name','Test Employee 1'), - () => cur_frm.set_value('company','Test Company'), - () => cur_frm.set_value('payable_account','Creditors - TC'), - () => cur_frm.set_value('cost_center','Main - TC'), + () => cur_frm.set_value('company','For Testing'), + () => cur_frm.set_value('payable_account','Creditors - FT'), + () => cur_frm.set_value('cost_center','Main - FT'), () => cur_frm.set_value('mode_of_payment','Cash'), () => cur_frm.save(), () => frappe.timeout(1), diff --git a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js b/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js index 595454fca0a..3f9d486349e 100644 --- a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js +++ b/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js @@ -11,8 +11,8 @@ QUnit.test("Test: Expense Claim Type [HR]", function (assert) { { description:'This is just a test'}, { accounts: [ [ - { company: 'Test Company'}, - { default_account: 'Round Off - TC'} + { company: 'For Testing'}, + { default_account: 'Round Off - FT'} ] ]}, ]); diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js index 5d189d2cf27..b8f4fafa6d8 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js @@ -10,8 +10,12 @@ QUnit.test("Test: Leave allocation [HR]", function (assert) { () => frappe.set_route("List", "Leave Allocation", "List"), () => frappe.new_doc("Leave Allocation"), () => frappe.timeout(1), - () => frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name'), - (employee) => cur_frm.set_value("employee", employee.message.name), + () => { + frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name', function(r) { + cur_frm.set_value("employee", r.name) + }); + }, + () => frappe.timeout(1), () => cur_frm.set_value("leave_type", "Test Leave type"), () => cur_frm.set_value("to_date", frappe.datetime.add_months(today_date, 2)), // for two months () => cur_frm.set_value("description", "This is just for testing"), diff --git a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js b/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js index 1241d3d5b6c..453787865c7 100644 --- a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js +++ b/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js @@ -11,7 +11,7 @@ QUnit.test("Test: Leave block list [HR]", function (assert) { () => frappe.new_doc("Leave Block List"), () => frappe.timeout(1), () => cur_frm.set_value("leave_block_list_name", "Test Leave block list"), - () => cur_frm.set_value("company", "Test Company"), + () => cur_frm.set_value("company", "For Testing"), () => frappe.click_button('Add Row'), () => { cur_frm.fields_dict.leave_block_list_dates.grid.grid_rows[0].doc.block_date = today_date; diff --git a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js index 5133c0c2829..2b5cec1c1e1 100644 --- a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js +++ b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js @@ -10,7 +10,7 @@ QUnit.test("Test: Leave control panel [HR]", function (assert) { () => frappe.set_route("Form", "Leave Control Panel"), () => frappe.timeout(1), () => cur_frm.set_value("leave_type", "Test Leave type"), - () => cur_frm.set_value("company", "Test Company"), + () => cur_frm.set_value("company", "For Testing"), () => cur_frm.set_value("employment_type", "Test Employment Type"), () => cur_frm.set_value("branch", "Test Branch"), () => cur_frm.set_value("department", "Test Department"), @@ -21,15 +21,29 @@ QUnit.test("Test: Leave control panel [HR]", function (assert) { // allocate leaves () => frappe.click_button('Allocate'), () => frappe.timeout(1), - () => assert.equal("Message", cur_dialog.title, - "leave alloction message shown"), + () => assert.equal("Message", cur_dialog.title, "leave alloction message shown"), () => frappe.click_button('Close'), () => frappe.set_route("List", "Leave Allocation", "List"), () => frappe.timeout(1), () => { - let leave_allocated = cur_list.data.filter(d => d.leave_type == "Test Leave type"); - assert.equal(3, leave_allocated.length, - 'leave allocation successfully done for all the employees'); + return frappe.call({ + method: "frappe.client.get_list", + args: { + doctype: "Employee", + filters: { + "branch": "Test Branch", + "department": "Test Department", + "company": "For Testing", + "designation": "Test Designation", + "status": "Active" + } + }, + callback: function(r) { + let leave_allocated = cur_list.data.filter(d => d.leave_type == "Test Leave type"); + assert.equal(r.message.length, leave_allocated.length, + 'leave allocation successfully done for all the employees'); + } + }); }, () => done() ]); diff --git a/erpnext/hr/doctype/loan_type/test_loan_type.js b/erpnext/hr/doctype/loan_type/test_loan_type.js index 8b5032b04e2..71354be48d0 100644 --- a/erpnext/hr/doctype/loan_type/test_loan_type.js +++ b/erpnext/hr/doctype/loan_type/test_loan_type.js @@ -15,9 +15,9 @@ QUnit.test("Test: Loan Type [HR]", function (assert) { 'This is just a test.'} ]); }, - () => frappe.timeout(3), + () => frappe.timeout(7), () => frappe.set_route('List','Loan Type','List'), - () => frappe.timeout(2), + () => frappe.timeout(4), // Checking if the fields are correctly set () => { diff --git a/erpnext/hr/doctype/process_payroll/test_process_payroll.js b/erpnext/hr/doctype/process_payroll/test_process_payroll.js index 7e23fb9c987..bc611504636 100644 --- a/erpnext/hr/doctype/process_payroll/test_process_payroll.js +++ b/erpnext/hr/doctype/process_payroll/test_process_payroll.js @@ -39,7 +39,7 @@ QUnit.test("Test: Process Payroll [HR]", function (assert) { // Creating Process Payroll for specific company () => frappe.set_route('Form','Process Payroll'), () => { - cur_frm.set_value('company','Test Company'), + cur_frm.set_value('company','For Testing'), frappe.timeout(1), cur_frm.set_value('payroll_frequency','Monthly'), cur_frm.set_value('start_date','2017-08-01'), diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.js b/erpnext/hr/doctype/salary_structure/test_salary_structure.js index 23b52f6a1d7..8276a82cb79 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.js +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.js @@ -3,17 +3,17 @@ QUnit.test("test Salary Structure", function(assert) { let done = assert.async(); let employee_name1; - let salary_structure = (ename1,ename2) => { - frappe.run_serially([ - () => frappe.db.get_value('Employee', {'employee_name': ename1}, 'name'), + frappe.run_serially([ + () => frappe.db.get_value('Employee', {'employee_name': "Test Employee 1"}, 'name', (r) => { - employee_name1 = r.message.name; - }, - () => frappe.db.get_value('Employee', {'employee_name': ename2}, 'name'), + employee_name1 = r.name; + } + ), + () => frappe.db.get_value('Employee', {'employee_name': "Test Employee 3"}, 'name', (r) => { // Creating Salary Structure for employees); - frappe.tests.make('Salary Structure', [ - { company: 'Test Company'}, + return frappe.tests.make('Salary Structure', [ + { company: 'For Testing'}, { payroll_frequency: 'Monthly'}, { employees: [ [ @@ -22,7 +22,7 @@ QUnit.test("test Salary Structure", function(assert) { {base: 25000} ], [ - {employee: r.message.name}, + {employee: r.name}, {from_date: '2017-07-01'}, {base: 30000} ] @@ -43,39 +43,36 @@ QUnit.test("test Salary Structure", function(assert) { {formula: '(B+LE) * .20'} ] ]}, - { payment_account: 'CASH - TC'}, + { payment_account: 'CASH - FT'}, ]); - }, - () => frappe.timeout(10), - () => cur_dialog.set_value('value','Test Salary Structure'), - () => frappe.timeout(2), - () => frappe.click_button('Create'), - () => { - // To check if all the fields are correctly set - assert.ok(cur_frm.doc.employees[0].employee_name.includes('Test Employee 1') && - cur_frm.doc.employees[1].employee_name.includes('Test Employee 3'), - 'Employee names are correctly set'); + } + ), + () => frappe.timeout(18), + () => cur_dialog.set_value('value','Test Salary Structure'), + () => frappe.timeout(1), + () => frappe.click_button('Create'), + () => frappe.timeout(1), + () => { + // To check if all the fields are correctly set + assert.ok(cur_frm.doc.employees[0].employee_name.includes('Test Employee 1') && + cur_frm.doc.employees[1].employee_name.includes('Test Employee 3'), + 'Employee names are correctly set'); - assert.ok(cur_frm.doc.employees[0].base==25000, - 'Base value for first employee is correctly set'); + assert.ok(cur_frm.doc.employees[0].base==25000, + 'Base value for first employee is correctly set'); - assert.ok(cur_frm.doc.employees[1].base==30000, - 'Base value for second employee is correctly set'); + assert.ok(cur_frm.doc.employees[1].base==30000, + 'Base value for second employee is correctly set'); - assert.ok(cur_frm.doc.earnings[0].formula.includes('base * .80'), - 'Formula for earnings as Basic is correctly set'); + assert.ok(cur_frm.doc.earnings[0].formula.includes('base * .80'), + 'Formula for earnings as Basic is correctly set'); - assert.ok(cur_frm.doc.earnings[1].formula.includes('B * .20'), - 'Formula for earnings as Leave Encashment is correctly set'); + assert.ok(cur_frm.doc.earnings[1].formula.includes('B * .20'), + 'Formula for earnings as Leave Encashment is correctly set'); - assert.ok(cur_frm.doc.deductions[0].formula.includes('(B+LE) * .20'), - 'Formula for deductions as Income Tax is correctly set'); - }, - ]); - }; - frappe.run_serially([ - () => salary_structure('Test Employee 1','Test Employee 3'), - () => frappe.timeout(16), + assert.ok(cur_frm.doc.deductions[0].formula.includes('(B+LE) * .20'), + 'Formula for deductions as Income Tax is correctly set'); + }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/training_event/test_training_event.js b/erpnext/hr/doctype/training_event/tests/test_training_event.js similarity index 100% rename from erpnext/hr/doctype/training_event/test_training_event.js rename to erpnext/hr/doctype/training_event/tests/test_training_event.js diff --git a/erpnext/hr/doctype/training_event/tests/test_training_event_attendance.js b/erpnext/hr/doctype/training_event/tests/test_training_event_attendance.js new file mode 100644 index 00000000000..6364308f731 --- /dev/null +++ b/erpnext/hr/doctype/training_event/tests/test_training_event_attendance.js @@ -0,0 +1,40 @@ +QUnit.module('hr'); + +QUnit.test("test: Training Event", function (assert) { + // number of asserts + assert.expect(1); + let done = assert.async(); + + frappe.run_serially([ + // insert a new Training Event + () => frappe.set_route("List", "Training Event", "List"), + () => frappe.new_doc("Training Event"), + () => frappe.timeout(1), + () => frappe.click_link('Edit in full page'), + () => cur_frm.set_value("event_name", "Test Event " + frappe.utils.get_random(10)), + () => cur_frm.set_value("start_time", "2017-07-26, 2:00 pm PDT"), + () => cur_frm.set_value("end_time", "2017-07-26, 2:30 pm PDT"), + () => cur_frm.set_value("introduction", "This is a test report"), + () => cur_frm.set_value("location", "Fake office"), + () => frappe.click_button('Add Row'), + () => frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name'), + (r) => { + console.log(r); + return cur_frm.fields_dict.employees.grid.grid_rows[0].doc.employee = r.message.name; + }, + () => { + return cur_frm.fields_dict.employees.grid.grid_rows[0].doc.attendance = "Optional"; + }, + () => frappe.click_button('Save'), + () => frappe.timeout(2), + () => frappe.click_button('Submit'), + () => frappe.timeout(2), + () => frappe.click_button('Yes'), + () => frappe.timeout(1), + () => { + assert.equal(cur_frm.doc.docstatus, 1); + }, + () => done() + ]); + +}); \ No newline at end of file diff --git a/erpnext/hr/doctype/training_event/training_event.js b/erpnext/hr/doctype/training_event/training_event.js index ebe0c7907ce..6a6e8fe0a6a 100644 --- a/erpnext/hr/doctype/training_event/training_event.js +++ b/erpnext/hr/doctype/training_event/training_event.js @@ -18,4 +18,4 @@ frappe.ui.form.on('Training Event', { }); } } -}); +}); \ No newline at end of file diff --git a/erpnext/hr/doctype/training_event/training_event.json b/erpnext/hr/doctype/training_event/training_event.json index 03b58b48021..7be9d974fe6 100644 --- a/erpnext/hr/doctype/training_event/training_event.json +++ b/erpnext/hr/doctype/training_event/training_event.json @@ -25,7 +25,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Event Name", "length": 0, @@ -55,7 +55,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 1, "label": "Event Status", "length": 0, @@ -115,12 +115,12 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 1, "label": "Type", "length": 0, "no_copy": 0, - "options": "Seminar\nTheory\nWorkshop\nConference\nExam\nInternet", + "options": "Seminar\nTheory\nWorkshop\nConference\nExam\nInternet\nSelf-Study", "permlevel": 0, "precision": "", "print_hide": 0, @@ -386,7 +386,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 1, "label": "Location", "length": 0, @@ -581,37 +581,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Will send an email about the event to employees with status 'Open'", - "fieldname": "send_email", - "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": "Send Email", - "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 - }, { "allow_bulk_edit": 0, "allow_on_submit": 1, @@ -672,6 +641,37 @@ "search_index": 0, "set_only_once": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee_emails", + "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": "Employee Emails", + "length": 0, + "no_copy": 0, + "options": "Email", + "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 } ], "has_web_view": 0, @@ -684,7 +684,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-05-29 06:13:38.411039", + "modified": "2017-08-11 03:11:25.768563", "modified_by": "Administrator", "module": "HR", "name": "Training Event", diff --git a/erpnext/hr/doctype/training_event/training_event.py b/erpnext/hr/doctype/training_event/training_event.py index 27ae8cf5491..cc568414a03 100644 --- a/erpnext/hr/doctype/training_event/training_event.py +++ b/erpnext/hr/doctype/training_event/training_event.py @@ -3,24 +3,10 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe -from frappe import _ from frappe.model.document import Document +from erpnext.hr.doctype.employee.employee import get_employee_emails class TrainingEvent(Document): - def on_update(self): - self.invite_employee() - - def on_update_after_submit(self): - self.invite_employee() - - def invite_employee(self): - if self.event_status == "Scheduled" and self.send_email: - subject = _("""You are invited for to attend {0} - {1} scheduled from {2} to {3} at {4}."""\ - .format(self.type, self.event_name, self.start_time, self.end_time, self.location)) - - for emp in self.employees: - if emp.status== "Open": - frappe.sendmail(frappe.db.get_value("Employee", emp.employee, "company_email"), \ - subject=subject, content= self.introduction) - emp.status= "Invited" \ No newline at end of file + def validate(self): + self.employee_emails = ', '.join(get_employee_emails([d.employee + for d in self.employees])) diff --git a/erpnext/hr/doctype/training_event_employee/training_event_employee.json b/erpnext/hr/doctype/training_event_employee/training_event_employee.json index 575d0e75822..a8a72b1a701 100644 --- a/erpnext/hr/doctype/training_event_employee/training_event_employee.json +++ b/erpnext/hr/doctype/training_event_employee/training_event_employee.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "beta": 0, @@ -11,6 +12,7 @@ "editable_grid": 1, "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -21,6 +23,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, "label": "Employee", @@ -40,6 +43,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -50,6 +54,7 @@ "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 Name", @@ -69,6 +74,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -79,6 +85,7 @@ "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, @@ -96,6 +103,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, @@ -107,12 +115,44 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, "label": "Status", "length": 0, "no_copy": 1, - "options": "Open\nInvited\nConfirmed\nAttended\nWithdrawn", + "options": "Open\nInvited\nCompleted\nFeedback Submitted", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "attendance", + "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": "Attendance", + "length": 0, + "no_copy": 0, + "options": "Mandatory\nOptional", "permlevel": 0, "precision": "", "print_hide": 0, @@ -126,17 +166,17 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 0, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-12-14 11:43:40.996578", + "modified": "2017-08-11 03:36:22.738253", "modified_by": "Administrator", "module": "HR", "name": "Training Event Employee", @@ -146,7 +186,9 @@ "quick_entry": 1, "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 } \ No newline at end of file diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.py b/erpnext/hr/doctype/training_feedback/training_feedback.py index 2a0403bd53a..b7eae38ae48 100644 --- a/erpnext/hr/doctype/training_feedback/training_feedback.py +++ b/erpnext/hr/doctype/training_feedback/training_feedback.py @@ -5,6 +5,19 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from frappe import _ class TrainingFeedback(Document): - pass + def validate(self): + training_event = frappe.get_doc("Training Event", self.training_event) + if training_event.docstatus != 1: + frappe.throw(_('{0} must be submitted').format(_('Training Event'))) + + def on_submit(self): + training_event = frappe.get_doc("Training Event", self.training_event) + for e in training_event.employees: + if e.employee == self.employee: + training_event.status = 'Feedback Submitted' + break + + training_event.save() diff --git a/erpnext/hr/doctype/training_result/test_training_result.js b/erpnext/hr/doctype/training_result/test_training_result.js new file mode 100644 index 00000000000..cb1d7fb27a3 --- /dev/null +++ b/erpnext/hr/doctype/training_result/test_training_result.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Training Result", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Training Result + () => frappe.tests.make('Training Result', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/hr/doctype/training_result/training_result.json b/erpnext/hr/doctype/training_result/training_result.json index e5fbb5fd426..41142b59e69 100644 --- a/erpnext/hr/doctype/training_result/training_result.json +++ b/erpnext/hr/doctype/training_result/training_result.json @@ -26,7 +26,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Training Event", "length": 0, @@ -133,6 +133,37 @@ "search_index": 0, "set_only_once": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee_emails", + "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": "Employee Emails", + "length": 0, + "no_copy": 0, + "options": "Email", + "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 } ], "has_web_view": 0, @@ -145,7 +176,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-06-15 08:16:01.566531", + "modified": "2017-08-11 03:53:21.283968", "modified_by": "Administrator", "module": "HR", "name": "Training Result", diff --git a/erpnext/hr/doctype/training_result/training_result.py b/erpnext/hr/doctype/training_result/training_result.py index 36c3cb93bc1..7cdc51f8010 100644 --- a/erpnext/hr/doctype/training_result/training_result.py +++ b/erpnext/hr/doctype/training_result/training_result.py @@ -6,19 +6,27 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document +from erpnext.hr.doctype.employee.employee import get_employee_emails class TrainingResult(Document): + def validate(self): + training_event = frappe.get_doc("Training Event", self.training_event) + if training_event.docstatus != 1: + frappe.throw(_('{0} must be submitted').format(_('Training Event'))) + + self.employee_emails = ', '.join(get_employee_emails([d.employee + for d in self.employees])) + def on_submit(self): - self.send_result() - - def send_result(self): - for emp in self.employees: - message = "Thank You for attending {0}.".format(self.training_event) - if emp.grade: - message = message + "Your grade: {0}".format(emp.grade) - frappe.sendmail(frappe.db.get_value("Employee", emp.employee, "company_email"), \ - subject=_("{0} Results".format(self.training_event)), \ - content=message) + training_event = frappe.get_doc("Training Event", self.training_event) + training_event.status = 'Completed' + for e in self.employees: + for e1 in training_event.employees: + if e1.employee == e.employee: + e1.status = 'Completed' + break + + training_event.save() @frappe.whitelist() def get_employees(training_event): diff --git a/erpnext/hr/email_alert/__init__.py b/erpnext/hr/email_alert/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/email_alert/training_feedback/__init__.py b/erpnext/hr/email_alert/training_feedback/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/email_alert/training_feedback/training_feedback.html b/erpnext/hr/email_alert/training_feedback/training_feedback.html new file mode 100644 index 00000000000..fd8fef9e82c --- /dev/null +++ b/erpnext/hr/email_alert/training_feedback/training_feedback.html @@ -0,0 +1,6 @@ +

{{ _("Hello") }},

+ +

You attended training {{ frappe.utils.get_link_to_form( + "Training Event", doc.training_event) }}

+ +

{{ _("Please share your feedback to the training by clicking on 'Training Feedback' and then 'New'") }}

\ No newline at end of file diff --git a/erpnext/hr/email_alert/training_feedback/training_feedback.json b/erpnext/hr/email_alert/training_feedback/training_feedback.json new file mode 100644 index 00000000000..51f6cedfecd --- /dev/null +++ b/erpnext/hr/email_alert/training_feedback/training_feedback.json @@ -0,0 +1,24 @@ +{ + "attach_print": 0, + "creation": "2017-08-11 03:17:11.769210", + "days_in_advance": 0, + "docstatus": 0, + "doctype": "Email Alert", + "document_type": "Training Result", + "enabled": 1, + "event": "Submit", + "idx": 0, + "is_standard": 1, + "message": "

{{_(\"Training Event\")}}

\n

{{ message }}

\n\n

{{_(\"Details\")}}

\n{{_(\"Event Name\")}}: {{ name }}\n
{{_(\"Event Location\")}}: {{ location }}\n
{{_(\"Start Time\")}}: {{ start_time }}\n
{{_(\"End Time\")}}: {{ end_time }}\n
{{_(\"Attendance\")}}: {{ attendance }}\n", + "modified": "2017-08-11 04:26:58.194793", + "modified_by": "Administrator", + "module": "HR", + "name": "Training Feedback", + "owner": "Administrator", + "recipients": [ + { + "email_by_document_field": "employee_emails" + } + ], + "subject": "Please Share your Feedback For {{ doc.training_event }}" +} \ No newline at end of file diff --git a/erpnext/hr/email_alert/training_feedback/training_feedback.md b/erpnext/hr/email_alert/training_feedback/training_feedback.md new file mode 100644 index 00000000000..bcadf7df590 --- /dev/null +++ b/erpnext/hr/email_alert/training_feedback/training_feedback.md @@ -0,0 +1,9 @@ +

{{_("Training Event")}}

+

{{ message }}

+ +

{{_("Details")}}

+{{_("Event Name")}}: {{ name }} +
{{_("Event Location")}}: {{ location }} +
{{_("Start Time")}}: {{ start_time }} +
{{_("End Time")}}: {{ end_time }} +
{{_("Attendance")}}: {{ attendance }} diff --git a/erpnext/hr/email_alert/training_feedback/training_feedback.py b/erpnext/hr/email_alert/training_feedback/training_feedback.py new file mode 100644 index 00000000000..2334f8b26d8 --- /dev/null +++ b/erpnext/hr/email_alert/training_feedback/training_feedback.py @@ -0,0 +1,7 @@ +from __future__ import unicode_literals + +import frappe + +def get_context(context): + # do your magic here + pass diff --git a/erpnext/hr/email_alert/training_scheduled/__init__.py b/erpnext/hr/email_alert/training_scheduled/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/email_alert/training_scheduled/training_scheduled.html b/erpnext/hr/email_alert/training_scheduled/training_scheduled.html new file mode 100644 index 00000000000..b1aeb2c8739 --- /dev/null +++ b/erpnext/hr/email_alert/training_scheduled/training_scheduled.html @@ -0,0 +1,9 @@ +

{{_("Training Event")}}

+ +

{{ doc.introduction }}

+ +

{{_("Details")}}

+{{_("Event Name")}}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }} +
{{_("Event Location")}}: {{ doc.location }} +
{{_("Start Time")}}: {{ doc.start_time }} +
{{_("End Time")}}: {{ doc.end_time }} diff --git a/erpnext/hr/email_alert/training_scheduled/training_scheduled.json b/erpnext/hr/email_alert/training_scheduled/training_scheduled.json new file mode 100644 index 00000000000..e1631f86839 --- /dev/null +++ b/erpnext/hr/email_alert/training_scheduled/training_scheduled.json @@ -0,0 +1,24 @@ +{ + "attach_print": 0, + "creation": "2017-08-11 03:13:40.519614", + "days_in_advance": 0, + "docstatus": 0, + "doctype": "Email Alert", + "document_type": "Training Event", + "enabled": 1, + "event": "Submit", + "idx": 0, + "is_standard": 1, + "message": "

{{_(\"Training Event\")}}

\n\n

{{ doc.introduction }}

\n\n

{{_(\"Details\")}}

\n{{_(\"Event Name\")}}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}\n
{{_(\"Event Location\")}}: {{ doc.location }}\n
{{_(\"Start Time\")}}: {{ doc.start_time }}\n
{{_(\"End Time\")}}: {{ doc.end_time }}\n", + "modified": "2017-08-13 22:49:42.338881", + "modified_by": "Administrator", + "module": "HR", + "name": "Training Scheduled", + "owner": "Administrator", + "recipients": [ + { + "email_by_document_field": "employee_emails" + } + ], + "subject": "Training Scheduled: {{ doc.name }}" +} \ No newline at end of file diff --git a/erpnext/hr/email_alert/training_scheduled/training_scheduled.md b/erpnext/hr/email_alert/training_scheduled/training_scheduled.md new file mode 100644 index 00000000000..bcadf7df590 --- /dev/null +++ b/erpnext/hr/email_alert/training_scheduled/training_scheduled.md @@ -0,0 +1,9 @@ +

{{_("Training Event")}}

+

{{ message }}

+ +

{{_("Details")}}

+{{_("Event Name")}}: {{ name }} +
{{_("Event Location")}}: {{ location }} +
{{_("Start Time")}}: {{ start_time }} +
{{_("End Time")}}: {{ end_time }} +
{{_("Attendance")}}: {{ attendance }} diff --git a/erpnext/hr/email_alert/training_scheduled/training_scheduled.py b/erpnext/hr/email_alert/training_scheduled/training_scheduled.py new file mode 100644 index 00000000000..2334f8b26d8 --- /dev/null +++ b/erpnext/hr/email_alert/training_scheduled/training_scheduled.py @@ -0,0 +1,7 @@ +from __future__ import unicode_literals + +import frappe + +def get_context(context): + # do your magic here + pass diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 94a21130812..531da8a1c33 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -5,7 +5,10 @@ frappe.provide("erpnext.bom"); frappe.ui.form.on("BOM", { setup: function(frm) { - frm.add_fetch('buying_price_list', 'currency', 'currency'); + frm.add_fetch("item", "description", "description"); + frm.add_fetch("item", "image", "image"); + frm.add_fetch("item", "item_name", "item_name"); + frm.add_fetch("item", "stock_uom", "uom"); frm.set_query("bom_no", "items", function() { return { @@ -23,6 +26,38 @@ frappe.ui.form.on("BOM", { } }; }); + + frm.set_query("item", function() { + return { + query: "erpnext.controllers.queries.item_query" + }; + }); + + frm.set_query("project", function() { + return{ + filters:[ + ['Project', 'status', 'not in', 'Completed, Cancelled'] + ] + }; + }); + + frm.set_query("item_code", "items", function() { + return { + query: "erpnext.controllers.queries.item_query", + filters: [["Item", "name", "!=", cur_frm.doc.item]] + }; + }); + + frm.set_query("bom_no", "items", function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + 'item': d.item_code, + 'is_active': 1, + 'docstatus': 1 + } + }; + }); }, onload_post_render: function(frm) { @@ -69,14 +104,14 @@ frappe.ui.form.on("BOM", { }); erpnext.bom.BomController = erpnext.TransactionController.extend({ - conversion_rate: function(doc, cdt, cdn) { + conversion_rate: function(doc) { if(this.frm.doc.currency === this.get_company_currency()) { this.frm.set_value("conversion_rate", 1.0); } else { erpnext.bom.update_cost(doc); } }, - + item_code: function(doc, cdt, cdn){ var scrap_items = false; var child = locals[cdt][cdn]; @@ -90,39 +125,34 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({ get_bom_material_detail(doc, cdt, cdn, scrap_items); }, - conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) { + conversion_factor: function(doc, cdt, cdn) { if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { var item = frappe.get_doc(cdt, cdn); frappe.model.round_floats_in(item, ["qty", "conversion_factor"]); item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item)); refresh_field("stock_qty", item.name, item.parentfield); this.toggle_conversion_factor(item); + this.frm.events.update_cost(this.frm); } }, -}) +}); $.extend(cur_frm.cscript, new erpnext.bom.BomController({frm: cur_frm})); -cur_frm.add_fetch("item", "description", "description"); -cur_frm.add_fetch("item", "image", "image"); -cur_frm.add_fetch("item", "item_name", "item_name"); -cur_frm.add_fetch("item", "stock_uom", "uom"); - - -cur_frm.cscript.hour_rate = function(doc, dt, dn) { +cur_frm.cscript.hour_rate = function(doc) { erpnext.bom.calculate_op_cost(doc); erpnext.bom.calculate_total(doc); -} +}; cur_frm.cscript.time_in_mins = cur_frm.cscript.hour_rate; cur_frm.cscript.bom_no = function(doc, cdt, cdn) { get_bom_material_detail(doc, cdt, cdn, false); -} +}; cur_frm.cscript.is_default = function(doc) { if (doc.is_default) cur_frm.set_value("is_active", 1); -} +}; var get_bom_material_detail= function(doc, cdt, cdn, scrap_items) { var d = locals[cdt][cdn]; @@ -141,6 +171,7 @@ var get_bom_material_detail= function(doc, cdt, cdn, scrap_items) { $.extend(d, r.message); refresh_field("items"); refresh_field("scrap_items"); + doc = locals[doc.doctype][doc.name]; erpnext.bom.calculate_rm_cost(doc); erpnext.bom.calculate_scrap_materials_cost(doc); @@ -149,13 +180,13 @@ var get_bom_material_detail= function(doc, cdt, cdn, scrap_items) { freeze: true }); } -} +}; -cur_frm.cscript.qty = function(doc, cdt, cdn) { +cur_frm.cscript.qty = function(doc) { erpnext.bom.calculate_rm_cost(doc); erpnext.bom.calculate_scrap_materials_cost(doc); erpnext.bom.calculate_total(doc); -} +}; cur_frm.cscript.rate = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; @@ -173,14 +204,14 @@ cur_frm.cscript.rate = function(doc, cdt, cdn) { erpnext.bom.calculate_scrap_materials_cost(doc); erpnext.bom.calculate_total(doc); } -} +}; erpnext.bom.update_cost = function(doc) { erpnext.bom.calculate_op_cost(doc); erpnext.bom.calculate_rm_cost(doc); erpnext.bom.calculate_scrap_materials_cost(doc); erpnext.bom.calculate_total(doc); -} +}; erpnext.bom.calculate_op_cost = function(doc) { var op = doc.operations || []; @@ -189,7 +220,7 @@ erpnext.bom.calculate_op_cost = function(doc) { for(var i=0;i { let element = $(`.btn-primary:contains("Make"):visible`); @@ -19,22 +19,17 @@ QUnit.test("test: production order", function (assert) { () => frappe.timeout(0.5), // Create a laptop production order - () => frappe.new_doc("Production Order"), - () => frappe.timeout(1), - () => cur_frm.set_value("production_item", "Laptop"), + () => { + return frappe.tests.make('Production Order', [ + {production_item: 'Laptop'}, + {company: 'For Testing'}, + {qty: laptop_quantity}, + {scrap_warehouse: "Laptop Scrap Warehouse - FT"}, + {wip_warehouse: "Work In Progress - FT"}, + {fg_warehouse: "Finished Goods - FT"} + ]); + }, () => frappe.timeout(2), - () => cur_frm.set_value("company", "Razer Blade"), - () => frappe.timeout(2), - () => cur_frm.set_value("qty", laptop_quantity), - () => frappe.timeout(2), - () => cur_frm.set_value("scrap_warehouse", "Laptop Scrap Warehouse - RB"), - () => frappe.timeout(1), - () => cur_frm.set_value("wip_warehouse", "Work In Progress - RB"), - () => frappe.timeout(1), - () => cur_frm.set_value("fg_warehouse", "Finished Goods - RB"), - () => cur_frm.save(), - () => frappe.timeout(2), - () => { assert.equal(cur_frm.doc.planned_operating_cost, cur_frm.doc.total_operating_cost, "Total and Planned Cost is equal"); @@ -43,7 +38,7 @@ QUnit.test("test: production order", function (assert) { items.forEach(function(item, index) { assert.equal(item, cur_frm.doc.required_items[index].item_code, `Required item ${item} added`); - assert.equal("Stores - RB", cur_frm.doc.required_items[index].source_warehouse, `Item ${item} warhouse verified`); + assert.equal("Stores - FT", cur_frm.doc.required_items[index].source_warehouse, `Item ${item} warhouse verified`); assert.equal("5", cur_frm.doc.required_items[index].required_qty, `Item ${item} quantity verified`); }); @@ -73,7 +68,7 @@ QUnit.test("test: production order", function (assert) { // Start the production order process () => frappe.set_route("List", "Production Order"), - () => frappe.timeout(2), + () => frappe.timeout(.5), () => frappe.set_route("List", "Production Order"), () => frappe.timeout(2), () => frappe.click_link("Laptop"), @@ -81,6 +76,7 @@ QUnit.test("test: production order", function (assert) { () => frappe.click_button("Start"), () => frappe.timeout(0.5), () => click_make(), + () => frappe.timeout(1), () => frappe.click_button("Save"), () => frappe.timeout(0.5), diff --git a/erpnext/manufacturing/doctype/production_order/test_production_order.py b/erpnext/manufacturing/doctype/production_order/test_production_order.py index 1d555f7c0cb..002f03b9201 100644 --- a/erpnext/manufacturing/doctype/production_order/test_production_order.py +++ b/erpnext/manufacturing/doctype/production_order/test_production_order.py @@ -92,7 +92,8 @@ class TestProductionOrder(unittest.TestCase): self.assertEqual(prod_order.name, time_sheet_doc.production_order) - self.assertEqual((prod_order.qty - d.completed_qty), sum([d.completed_qty for d in time_sheet_doc.time_logs])) + self.assertEqual((prod_order.qty - d.completed_qty), + sum([d.completed_qty for d in time_sheet_doc.time_logs])) manufacturing_settings = frappe.get_doc({ "doctype": "Manufacturing Settings", @@ -106,7 +107,7 @@ class TestProductionOrder(unittest.TestCase): self.assertEqual(prod_order.operations[0].completed_qty, prod_order.qty) self.assertEqual(prod_order.operations[0].actual_operation_time, 60) - self.assertEqual(prod_order.operations[0].actual_operating_cost, 100) + self.assertEqual(prod_order.operations[0].actual_operating_cost, 6000) time_sheet_doc1 = make_timesheet(prod_order.name, prod_order.company) self.assertEqual(len(time_sheet_doc1.get('time_logs')), 0) diff --git a/erpnext/modules.txt b/erpnext/modules.txt index 1edff10ca50..6449a4ad5ce 100644 --- a/erpnext/modules.txt +++ b/erpnext/modules.txt @@ -14,4 +14,5 @@ Hub Node Portal Maintenance Schools -Regional \ No newline at end of file +Regional +Healthcare diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1fea30875c1..19d713e5c7e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -435,6 +435,12 @@ erpnext.patches.v8_5.remove_project_type_property_setter erpnext.patches.v8_7.add_more_gst_fields erpnext.patches.v8_7.fix_purchase_receipt_status erpnext.patches.v8_6.rename_bom_update_tool +erpnext.patches.v8_7.set_offline_in_pos_settings #11-09-17 +erpnext.patches.v8_9.add_setup_progress_actions #08-09-2017 #26-09-2017 +erpnext.patches.v8_9.rename_company_sales_target_field +erpnext.patches.v8_8.set_bom_rate_as_per_uom +erpnext.patches.v9_0.remove_subscription_module +erpnext.patches.v8_7.make_subscription_from_recurring_data erpnext.patches.v8_9.set_print_zero_amount_taxes erpnext.patches.v8_9.set_default_customer_group erpnext.patches.v8_9.remove_employee_from_salary_structure_parent diff --git a/erpnext/patches/v8_7/make_subscription_from_recurring_data.py b/erpnext/patches/v8_7/make_subscription_from_recurring_data.py new file mode 100644 index 00000000000..ab0fc121fc9 --- /dev/null +++ b/erpnext/patches/v8_7/make_subscription_from_recurring_data.py @@ -0,0 +1,43 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import today + +def execute(): + frappe.reload_doc('accounts', 'doctype', 'subscription') + frappe.reload_doc('selling', 'doctype', 'sales_order') + frappe.reload_doc('buying', 'doctype', 'purchase_order') + frappe.reload_doc('accounts', 'doctype', 'sales_invoice') + frappe.reload_doc('accounts', 'doctype', 'purchase_invoice') + + for doctype in ['Sales Order', 'Sales Invoice', + 'Purchase Invoice', 'Purchase Invoice']: + for data in get_data(doctype): + make_subscription(doctype, data) + +def get_data(doctype): + return frappe.db.sql(""" select name, from_date, end_date, recurring_type,recurring_id, + next_date, notify_by_email, notification_email_address, recurring_print_format, + repeat_on_day_of_month, submit_on_creation, docstatus + from `tab{0}` where is_recurring = 1 and next_date >= %s and docstatus < 2 + """.format(doctype), today(), as_dict=1) + +def make_subscription(doctype, data): + doc = frappe.get_doc({ + 'doctype': 'Subscription', + 'reference_doctype': doctype, + 'reference_document': data.name, + 'start_date': data.from_date, + 'end_date': data.end_date, + 'frequency': data.recurring_type, + 'repeat_on_day': data.repeat_on_day_of_month, + 'notify_by_email': data.notify_by_email, + 'recipients': data.notification_email_address, + 'next_schedule_date': data.next_date, + 'submit_on_creation': data.submit_on_creation + }).insert(ignore_permissions=True) + + if data.docstatus == 1: + doc.submit() \ No newline at end of file diff --git a/erpnext/patches/v8_7/set_offline_in_pos_settings.py b/erpnext/patches/v8_7/set_offline_in_pos_settings.py new file mode 100644 index 00000000000..b24fe37a285 --- /dev/null +++ b/erpnext/patches/v8_7/set_offline_in_pos_settings.py @@ -0,0 +1,12 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc('accounts', 'doctype', 'pos_settings') + + doc = frappe.get_doc('POS Settings') + doc.use_pos_in_offline_mode = 1 + doc.save() \ No newline at end of file diff --git a/erpnext/patches/v8_8/__init__.py b/erpnext/patches/v8_8/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/patches/v8_8/set_bom_rate_as_per_uom.py b/erpnext/patches/v8_8/set_bom_rate_as_per_uom.py new file mode 100644 index 00000000000..5b169cdff2b --- /dev/null +++ b/erpnext/patches/v8_8/set_bom_rate_as_per_uom.py @@ -0,0 +1,13 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.db.sql(""" + update `tabBOM Item` + set rate = rate * conversion_factor + where uom != stock_uom and docstatus < 2 + and conversion_factor not in (0, 1) + """) \ No newline at end of file diff --git a/erpnext/patches/v8_9/add_setup_progress_actions.py b/erpnext/patches/v8_9/add_setup_progress_actions.py new file mode 100644 index 00000000000..dbd0face7b7 --- /dev/null +++ b/erpnext/patches/v8_9/add_setup_progress_actions.py @@ -0,0 +1,42 @@ + +from __future__ import unicode_literals +import frappe +from frappe import _ + +def execute(): + """Add setup progress actions""" + frappe.reload_doc("setup", "doctype", "setup_progress") + frappe.reload_doc("setup", "doctype", "setup_progress_action") + + actions = [ + {"action_name": "Add Company", "action_doctype": "Company", "min_doc_count": 1, "is_completed": 1, + "domains": '[]' }, + {"action_name": "Set Sales Target", "action_doctype": "Company", "min_doc_count": 99, + "action_document": frappe.defaults.get_defaults().get("company") or '', + "action_field": "monthly_sales_target", "is_completed": 0, + "domains": '["Manufacturing", "Services", "Retail", "Distribution"]' }, + {"action_name": "Add Customers", "action_doctype": "Customer", "min_doc_count": 1, "is_completed": 0, + "domains": '["Manufacturing", "Services", "Retail", "Distribution"]' }, + {"action_name": "Add Suppliers", "action_doctype": "Supplier", "min_doc_count": 1, "is_completed": 0, + "domains": '["Manufacturing", "Services", "Retail", "Distribution"]' }, + {"action_name": "Add Products", "action_doctype": "Item", "min_doc_count": 1, "is_completed": 0, + "domains": '["Manufacturing", "Services", "Retail", "Distribution"]' }, + {"action_name": "Add Programs", "action_doctype": "Program", "min_doc_count": 1, "is_completed": 0, + "domains": '["Education"]' }, + {"action_name": "Add Instructors", "action_doctype": "Instructor", "min_doc_count": 1, "is_completed": 0, + "domains": '["Education"]' }, + {"action_name": "Add Courses", "action_doctype": "Course", "min_doc_count": 1, "is_completed": 0, + "domains": '["Education"]' }, + {"action_name": "Add Rooms", "action_doctype": "Room", "min_doc_count": 1, "is_completed": 0, + "domains": '["Education"]' }, + {"action_name": "Add Users", "action_doctype": "User", "min_doc_count": 4, "is_completed": 0, + "domains": '[]' } + ] + + setup_progress = frappe.get_doc("Setup Progress", "Setup Progress") + setup_progress.actions = [] + for action in actions: + setup_progress.append("actions", action) + + setup_progress.save(ignore_permissions=True) + diff --git a/erpnext/patches/v8_9/rename_company_sales_target_field.py b/erpnext/patches/v8_9/rename_company_sales_target_field.py new file mode 100644 index 00000000000..5433eb673e4 --- /dev/null +++ b/erpnext/patches/v8_9/rename_company_sales_target_field.py @@ -0,0 +1,8 @@ +from __future__ import unicode_literals +import frappe +from frappe.model.utils.rename_field import rename_field + +def execute(): + frappe.reload_doc("setup", "doctype", "company") + if frappe.db.has_column('Company', 'sales_target'): + rename_field("Company", "sales_target", "monthly_sales_target") diff --git a/erpnext/patches/v9_0/__init__.py b/erpnext/patches/v9_0/__init__.py new file mode 100644 index 00000000000..baffc488252 --- /dev/null +++ b/erpnext/patches/v9_0/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/erpnext/patches/v9_0/remove_subscription_module.py b/erpnext/patches/v9_0/remove_subscription_module.py new file mode 100644 index 00000000000..493873f3e80 --- /dev/null +++ b/erpnext/patches/v9_0/remove_subscription_module.py @@ -0,0 +1,9 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + if frappe.db.exists('Module Def', 'Subscription'): + frappe.db.sql(""" delete from `tabModule Def` where name = 'Subscription'""") \ No newline at end of file diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index f16cf07d2a8..5af8410dce3 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -2,7 +2,7 @@ // License: GNU General Public License v3. See license.txt frappe.ui.form.on("Project", { - onload: function(frm) { + setup: function(frm) { frm.set_indicator_formatter('title', function(doc) { let indicator = 'orange'; @@ -18,7 +18,9 @@ frappe.ui.form.on("Project", { return indicator; } ); + }, + onload: function(frm) { var so = frappe.meta.get_docfield("Project", "sales_order"); so.get_route_options_for_new_doc = function(field) { if(frm.is_new()) return; @@ -113,4 +115,3 @@ frappe.ui.form.on("Project Task", { frm.trigger('tasks_refresh'); }, }); - diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py index 955a2b0fe34..2588d566d56 100644 --- a/erpnext/projects/doctype/timesheet/test_timesheet.py +++ b/erpnext/projects/doctype/timesheet/test_timesheet.py @@ -54,19 +54,18 @@ class TestTimesheet(unittest.TestCase): def test_sales_invoice_from_timesheet(self): timesheet = make_timesheet("_T-Employee-0001", simulate=True, billable=1) - sales_invoice = make_sales_invoice(timesheet.name) - sales_invoice.customer = "_Test Customer" + sales_invoice = make_sales_invoice(timesheet.name, '_Test Item', '_Test Customer') sales_invoice.due_date = nowdate() - - item = sales_invoice.append('items', {}) - item.item_code = '_Test Item' - item.qty = 2 - item.rate = 100 - sales_invoice.submit() timesheet = frappe.get_doc('Timesheet', timesheet.name) self.assertEquals(sales_invoice.total_billing_amount, 100) self.assertEquals(timesheet.status, 'Billed') + self.assertEquals(sales_invoice.customer, '_Test Customer') + + item = sales_invoice.items[0] + self.assertEquals(item.item_code, '_Test Item') + self.assertEquals(item.qty, 2.00) + self.assertEquals(item.rate, 50.00) def test_timesheet_billing_based_on_project(self): timesheet = make_timesheet("_T-Employee-0001", simulate=True, billable=1, project = '_Test Project', company='_Test Company') diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index 9b330e7811b..ba1414cf2a6 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -57,10 +57,37 @@ frappe.ui.form.on("Timesheet", { }, make_invoice: function(frm) { - frappe.model.open_mapped_doc({ - method: "erpnext.projects.doctype.timesheet.timesheet.make_sales_invoice", - frm: frm + let dialog = new frappe.ui.Dialog({ + title: __("Select Item (optional)"), + fields: [ + {"fieldtype": "Link", "label": __("Item Code"), "fieldname": "item_code", "options":"Item"}, + {"fieldtype": "Link", "label": __("Customer"), "fieldname": "customer", "options":"Customer"} + ] }); + + dialog.set_primary_action(__("Make Sales Invoice"), () => { + var args = dialog.get_values(); + if(!args) return; + dialog.hide(); + return frappe.call({ + type: "GET", + method: "erpnext.projects.doctype.timesheet.timesheet.make_sales_invoice", + args: { + "source_name": frm.doc.name, + "item_code": args.item_code, + "customer": args.customer + }, + freeze: true, + callback: function(r) { + if(!r.exc) { + frappe.model.sync(r.message); + frappe.set_route("Form", r.message.doctype, r.message.name); + } + } + }) + }) + + dialog.show(); }, make_salary_slip: function(frm) { diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 16abd2499cc..ad566d5ac11 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -323,17 +323,32 @@ def get_timesheet_data(name, project): } @frappe.whitelist() -def make_sales_invoice(source_name, target=None): +def make_sales_invoice(source_name, item_code=None, customer=None): target = frappe.new_doc("Sales Invoice") timesheet = frappe.get_doc('Timesheet', source_name) + hours = flt(timesheet.total_billable_hours) - flt(timesheet.total_billed_hours) + billing_amount = flt(timesheet.total_billable_amount) - flt(timesheet.total_billed_amount) + billing_rate = billing_amount / hours + + if customer: + target.customer = customer + + if item_code: + target.append('items', { + 'item_code': item_code, + 'qty': hours, + 'rate': billing_rate + }) + target.append('timesheets', { 'time_sheet': timesheet.name, - 'billing_hours': flt(timesheet.total_billable_hours) - flt(timesheet.total_billed_hours), - 'billing_amount': flt(timesheet.total_billable_amount) - flt(timesheet.total_billed_amount) + 'billing_hours': hours, + 'billing_amount': billing_amount }) target.run_method("calculate_billing_amount_for_timesheet") + target.run_method("set_missing_values") return target diff --git a/erpnext/public/css/erpnext.css b/erpnext/public/css/erpnext.css index 0660b392086..460efbfe9fe 100644 --- a/erpnext/public/css/erpnext.css +++ b/erpnext/public/css/erpnext.css @@ -308,16 +308,6 @@ body[data-route="pos"] .item-list .image-field .placeholder-text { body[data-route="pos"] .item-list .pos-item-wrapper { position: relative; } -body[data-route="pos"] .item-list .price-info { - position: absolute; - left: 0; - bottom: 0; - margin: 0 0 15px 15px; - background-color: rgba(141, 153, 166, 0.6); - padding: 5px 9px; - border-radius: 3px; - color: #fff; -} body[data-route="pos"] .pos-bill-toolbar { margin-top: 10px; } @@ -356,3 +346,22 @@ body[data-route="pos"] .btn-more { body[data-route="pos"] .collapse-btn { cursor: pointer; } +.price-info { + position: absolute; + left: 0; + bottom: 0; + margin: 0 0 15px 15px; + background-color: rgba(141, 153, 166, 0.6); + padding: 5px 9px; + border-radius: 3px; + color: #fff; +} +.leaderboard .result { + border-top: 1px solid #d1d8dd; +} +.leaderboard .list-item { + padding-left: 45px; +} +.leaderboard .list-item_content { + padding-right: 45px; +} diff --git a/erpnext/public/css/pos.css b/erpnext/public/css/pos.css new file mode 100644 index 00000000000..f66abc80816 --- /dev/null +++ b/erpnext/public/css/pos.css @@ -0,0 +1,174 @@ +[data-route="point-of-sale"] .layout-main-section-wrapper { + margin-bottom: 0; +} +[data-route="point-of-sale"] .pos-items-wrapper { + max-height: calc(100vh - 210px); +} +.pos { + padding: 15px; +} +.list-item { + min-height: 40px; + height: auto; +} +.cart-container { + padding: 0 15px; + display: inline-block; + width: 39%; + vertical-align: top; +} +.item-container { + padding: 0 15px; + display: inline-block; + width: 60%; + vertical-align: top; +} +.search-field { + width: 60%; +} +.search-field input::placeholder { + font-size: 12px; +} +.item-group-field { + width: 40%; + margin-left: 15px; +} +.cart-wrapper { + margin-bottom: 10px; +} +.cart-wrapper .list-item__content:not(:first-child) { + justify-content: flex-end; +} +.cart-wrapper .list-item--head .list-item__content:nth-child(2) { + flex: 1.5; +} +.cart-items { + height: 150px; + overflow: auto; +} +.cart-items .list-item.current-item { + background-color: #fffce7; +} +.cart-items .list-item.current-item.qty input { + border: 1px solid #5E64FF; + font-weight: bold; +} +.cart-items .list-item.current-item.disc .discount { + font-weight: bold; +} +.cart-items .list-item.current-item.rate .rate { + font-weight: bold; +} +.cart-items .list-item .quantity { + flex: 1.5; +} +.cart-items input { + text-align: right; + height: 22px; + font-size: 12px; +} +.fields { + display: flex; +} +.pos-items-wrapper { + max-height: 480px; + overflow-y: auto; +} +.pos-items { + overflow: hidden; +} +.pos-item-wrapper { + display: flex; + flex-direction: column; + position: relative; + width: 25%; +} +.image-view-container { + display: block; +} +.image-view-container .image-field { + height: auto; +} +.empty-state { + height: 100%; + position: relative; +} +.empty-state span { + position: absolute; + color: #8D99A6; + font-size: 12px; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} +@keyframes yellow-fade { + 0% { + background-color: #fffce7; + } + 100% { + background-color: transparent; + } +} +.highlight { + animation: yellow-fade 1s ease-in 1; +} +input[type=number]::-webkit-inner-spin-button, +input[type=number]::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} +.number-pad { + border-collapse: collapse; + cursor: pointer; + display: table; + margin: auto; +} +.num-row { + display: table-row; +} +.num-col { + display: table-cell; + border: 1px solid #d1d8dd; +} +.num-col > div { + width: 50px; + height: 50px; + text-align: center; + line-height: 50px; +} +.num-col.active { + background-color: #fffce7; +} +.num-col.brand-primary { + background-color: #5E64FF; + color: #ffffff; +} +.discount-amount .discount-inputs { + display: flex; + flex-direction: column; + padding: 15px 0; +} +.discount-amount input:first-child { + margin-bottom: 10px; +} +.taxes-and-totals { + border-top: 1px solid #d1d8dd; +} +.taxes-and-totals .taxes { + display: flex; + flex-direction: column; + padding: 15px 0; + align-items: flex-end; +} +.taxes-and-totals .taxes > div:first-child { + margin-bottom: 10px; +} +.grand-total { + border-top: 1px solid #d1d8dd; +} +.grand-total .list-item { + height: 60px; +} +.grand-total .grand-total-value { + font-size: 24px; +} diff --git a/erpnext/public/images/illustrations/shop.jpg b/erpnext/public/images/illustrations/shop.jpg new file mode 100644 index 00000000000..f92f7dbd4eb Binary files /dev/null and b/erpnext/public/images/illustrations/shop.jpg differ diff --git a/erpnext/public/images/illustrations/shop2.jpg b/erpnext/public/images/illustrations/shop2.jpg new file mode 100644 index 00000000000..62e464956ba Binary files /dev/null and b/erpnext/public/images/illustrations/shop2.jpg differ diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index d1eb534b3b7..12ab7326769 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -155,7 +155,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ }); if(cumulated_tax_fraction && !me.discount_amount_applied) { - item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction), precision("net_amount", item)); + item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction)); item.net_rate = flt(item.net_amount / item.qty, precision("net_rate", item)); me.set_in_company_currency(item, ["net_rate", "net_amount"]); @@ -211,6 +211,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ calculate_taxes: function() { var me = this; + this.frm.doc.rounding_adjustment = 0; var actual_tax_dict = {}; // maintain actual tax rate based on idx @@ -277,8 +278,10 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ // adjust Discount Amount loss in last tax iteration if ((i == me.frm.doc["taxes"].length - 1) && me.discount_amount_applied - && me.frm.doc.apply_discount_on == "Grand Total" && me.frm.doc.discount_amount) - me.adjust_discount_amount_loss(tax); + && me.frm.doc.apply_discount_on == "Grand Total" && me.frm.doc.discount_amount) { + me.frm.doc.rounding_adjustment = flt(me.frm.doc.grand_total - + flt(me.frm.doc.discount_amount) - tax.total, precision("rounding_adjustment")); + } } }); }); @@ -341,45 +344,41 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, precision("tax_amount", tax)); }, - adjust_discount_amount_loss: function(tax) { - var discount_amount_loss = this.frm.doc.grand_total - flt(this.frm.doc.discount_amount) - tax.total; - tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount + - discount_amount_loss, precision("tax_amount", tax)); - tax.total = flt(tax.total + discount_amount_loss, precision("total", tax)); - - this.set_in_company_currency(tax, ["total", "tax_amount_after_discount_amount"]); - }, - manipulate_grand_total_for_inclusive_tax: function() { var me = this; // if fully inclusive taxes and diff if (this.frm.doc["taxes"] && this.frm.doc["taxes"].length) { - var all_inclusive = frappe.utils.all(this.frm.doc["taxes"].map(function(d) { - return cint(d.included_in_print_rate); - })); - - if (all_inclusive) { + var any_inclusive_tax = false; + $.each(this.frm.doc.taxes || [], function(i, d) { + if(cint(d.included_in_print_rate)) any_inclusive_tax = true; + }); + if (any_inclusive_tax) { var last_tax = me.frm.doc["taxes"].slice(-1)[0]; + var non_inclusive_tax_amount = frappe.utils.sum($.map(this.frm.doc.taxes || [], + function(d) { + if(!d.included_in_print_rate) { + return flt(d.tax_amount_after_discount_amount); + } + } + )); + var diff = me.frm.doc.total + non_inclusive_tax_amount + - flt(last_tax.total, precision("grand_total")); - var diff = me.frm.doc.total - flt(last_tax.total, precision("grand_total")); - - if ( diff && Math.abs(diff) <= (2.0 / Math.pow(10, precision("tax_amount", last_tax))) ) { - last_tax.tax_amount += diff; - last_tax.tax_amount_after_discount += diff; - last_tax.total += diff; - - this.set_in_company_currency(last_tax, - ["total", "tax_amount", "tax_amount_after_discount_amount"]); + if ( diff && Math.abs(diff) <= (5.0 / Math.pow(10, precision("tax_amount", last_tax))) ) { + this.frm.doc.rounding_adjustment = flt(flt(this.frm.doc.rounding_adjustment) + diff, + precision("rounding_adjustment")); } } } }, calculate_totals: function() { - // Changing sequence can cause roundiing issue and on-screen discrepency + // Changing sequence can cause rounding_adjustmentng issue and on-screen discrepency var me = this; var tax_count = this.frm.doc["taxes"] ? this.frm.doc["taxes"].length : 0; - this.frm.doc.grand_total = flt(tax_count ? this.frm.doc["taxes"][tax_count - 1].total : this.frm.doc.net_total); + this.frm.doc.grand_total = flt(tax_count + ? this.frm.doc["taxes"][tax_count - 1].total + flt(this.frm.doc.rounding_adjustment) + : this.frm.doc.net_total); if(in_list(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"], this.frm.doc.doctype)) { this.frm.doc.base_grand_total = (this.frm.doc.total_taxes_and_charges) ? @@ -398,24 +397,30 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ } }); - frappe.model.round_floats_in(this.frm.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]); + frappe.model.round_floats_in(this.frm.doc, + ["taxes_and_charges_added", "taxes_and_charges_deducted"]); } this.frm.doc.base_grand_total = flt((this.frm.doc.taxes_and_charges_added || this.frm.doc.taxes_and_charges_deducted) ? flt(this.frm.doc.grand_total * this.frm.doc.conversion_rate) : this.frm.doc.base_net_total); - this.set_in_company_currency(this.frm.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]); + this.set_in_company_currency(this.frm.doc, + ["taxes_and_charges_added", "taxes_and_charges_deducted"]); } - this.frm.doc.total_taxes_and_charges = flt(this.frm.doc.grand_total - this.frm.doc.net_total, - precision("total_taxes_and_charges")); + this.frm.doc.total_taxes_and_charges = flt(this.frm.doc.grand_total - this.frm.doc.net_total + - flt(this.frm.doc.rounding_adjustment), precision("total_taxes_and_charges")); - this.set_in_company_currency(this.frm.doc, ["total_taxes_and_charges"]); + this.set_in_company_currency(this.frm.doc, ["total_taxes_and_charges", "rounding_adjustment"]); // Round grand total as per precision frappe.model.round_floats_in(this.frm.doc, ["grand_total", "base_grand_total"]); // rounded totals + this.set_rounded_total() + }, + + set_rounded_total: function() { if(frappe.meta.get_docfield(this.frm.doc.doctype, "rounded_total", this.frm.doc.name)) { this.frm.doc.rounded_total = round_based_on_smallest_currency_fraction(this.frm.doc.grand_total, this.frm.doc.currency, precision("rounded_total")); diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 3a8ddb59278..a5ef15e60bf 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -4,6 +4,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ setup: function() { this._super(); + frappe.flags.hide_serial_batch_dialog = false; frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); var has_margin_field = frappe.meta.has_field(cdt, 'margin_type'); @@ -314,12 +315,15 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if(!r.exc) { me.frm.script_manager.trigger("price_list_rate", cdt, cdn); me.toggle_conversion_factor(item); - if(show_batch_dialog) { + if(show_batch_dialog && !frappe.flags.hide_serial_batch_dialog) { var d = locals[cdt][cdn]; $.each(r.message, function(k, v) { if(!d[k]) d[k] = v; }); - erpnext.show_serial_batch_selector(me.frm, d); + + erpnext.show_serial_batch_selector(me.frm, d, (item) => { + me.frm.script_manager.trigger('qty', item.doctype, item.name); + }); } } } @@ -527,12 +531,23 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if(this.frm.doc.ignore_pricing_rule) { this.calculate_taxes_and_totals(); } else if (!this.in_apply_price_list){ + this.set_actual_charges_based_on_currency(); this.apply_price_list(); } } }, + set_actual_charges_based_on_currency: function() { + var me = this; + $.each(this.frm.doc.taxes || [], function(i, d) { + if(d.charge_type == "Actual") { + frappe.model.set_value(d.doctype, d.name, "tax_amount", + flt(d.tax_amount) / flt(me.frm.doc.conversion_rate)); + } + }); + }, + get_exchange_rate: function(transaction_date, from_currency, to_currency, callback) { if (!transaction_date || !from_currency || !to_currency) return; return frappe.call({ @@ -645,30 +660,32 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ "base_discount_amount", "base_grand_total", "base_rounded_total", "base_in_words", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", "total_amount_to_pay", "base_paid_amount", "base_write_off_amount", "base_change_amount", "base_operating_cost", - "base_raw_material_cost", "base_total_cost", "base_scrap_material_cost" - ], company_currency); + "base_raw_material_cost", "base_total_cost", "base_scrap_material_cost", + "base_rounding_adjustment"], company_currency); this.frm.set_currency_labels(["total", "net_total", "total_taxes_and_charges", "discount_amount", "grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted", - "rounded_total", "in_words", "paid_amount", "write_off_amount", "operating_cost", "scrap_material_cost", - "raw_material_cost", "total_cost"], this.frm.doc.currency); + "rounded_total", "in_words", "paid_amount", "write_off_amount", "operating_cost", + "scrap_material_cost", "rounding_adjustment", "raw_material_cost", + "total_cost"], this.frm.doc.currency); - this.frm.set_currency_labels(["outstanding_amount", "total_advance"], this.frm.doc.party_account_currency); + this.frm.set_currency_labels(["outstanding_amount", "total_advance"], + this.frm.doc.party_account_currency); cur_frm.set_df_property("conversion_rate", "description", "1 " + this.frm.doc.currency - + " = [?] " + company_currency) + + " = [?] " + company_currency); if(this.frm.doc.price_list_currency && this.frm.doc.price_list_currency!=company_currency) { - cur_frm.set_df_property("plc_conversion_rate", "description", "1 " + this.frm.doc.price_list_currency - + " = [?] " + company_currency) + cur_frm.set_df_property("plc_conversion_rate", "description", "1 " + + this.frm.doc.price_list_currency + " = [?] " + company_currency); } // toggle fields - this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total", "base_total_taxes_and_charges", - "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", + this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total", + "base_total_taxes_and_charges", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", "base_grand_total", "base_rounded_total", "base_in_words", "base_discount_amount", - "base_paid_amount", "base_write_off_amount", "base_operating_cost", - "base_raw_material_cost", "base_total_cost", "base_scrap_material_cost"], + "base_paid_amount", "base_write_off_amount", "base_operating_cost", "base_raw_material_cost", + "base_total_cost", "base_scrap_material_cost", "base_rounding_adjustment"], this.frm.doc.currency != company_currency); this.frm.toggle_display(["plc_conversion_rate", "price_list_currency"], @@ -1102,7 +1119,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, }); -erpnext.show_serial_batch_selector = function(frm, d) { +erpnext.show_serial_batch_selector = function(frm, d, callback, show_dialog) { frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() { new erpnext.SerialNoBatchSelector({ frm: frm, @@ -1111,6 +1128,7 @@ erpnext.show_serial_batch_selector = function(frm, d) { type: "Warehouse", name: d.warehouse }, - }); + callback: callback + }, show_dialog); }); } diff --git a/erpnext/public/js/pos/clusterize.js b/erpnext/public/js/pos/clusterize.js new file mode 100644 index 00000000000..075c9ca4ae6 --- /dev/null +++ b/erpnext/public/js/pos/clusterize.js @@ -0,0 +1,330 @@ +/* eslint-disable */ +/*! Clusterize.js - v0.17.6 - 2017-03-05 +* http://NeXTs.github.com/Clusterize.js/ +* Copyright (c) 2015 Denis Lukov; Licensed GPLv3 */ + +;(function(name, definition) { + if (typeof module != 'undefined') module.exports = definition(); + else if (typeof define == 'function' && typeof define.amd == 'object') define(definition); + else this[name] = definition(); +}('Clusterize', function() { + "use strict" + + // detect ie9 and lower + // https://gist.github.com/padolsey/527683#comment-786682 + var ie = (function(){ + for( var v = 3, + el = document.createElement('b'), + all = el.all || []; + el.innerHTML = '', + all[0]; + ){} + return v > 4 ? v : document.documentMode; + }()), + is_mac = navigator.platform.toLowerCase().indexOf('mac') + 1; + var Clusterize = function(data) { + if( ! (this instanceof Clusterize)) + return new Clusterize(data); + var self = this; + + var defaults = { + rows_in_block: 50, + blocks_in_cluster: 4, + tag: null, + show_no_data_row: true, + no_data_class: 'clusterize-no-data', + no_data_text: 'No data', + keep_parity: true, + callbacks: {} + } + + // public parameters + self.options = {}; + var options = ['rows_in_block', 'blocks_in_cluster', 'show_no_data_row', 'no_data_class', 'no_data_text', 'keep_parity', 'tag', 'callbacks']; + for(var i = 0, option; option = options[i]; i++) { + self.options[option] = typeof data[option] != 'undefined' && data[option] != null + ? data[option] + : defaults[option]; + } + + var elems = ['scroll', 'content']; + for(var i = 0, elem; elem = elems[i]; i++) { + self[elem + '_elem'] = data[elem + 'Id'] + ? document.getElementById(data[elem + 'Id']) + : data[elem + 'Elem']; + if( ! self[elem + '_elem']) + throw new Error("Error! Could not find " + elem + " element"); + } + + // tabindex forces the browser to keep focus on the scrolling list, fixes #11 + if( ! self.content_elem.hasAttribute('tabindex')) + self.content_elem.setAttribute('tabindex', 0); + + // private parameters + var rows = isArray(data.rows) + ? data.rows + : self.fetchMarkup(), + cache = {}, + scroll_top = self.scroll_elem.scrollTop; + + // append initial data + self.insertToDOM(rows, cache); + + // restore the scroll position + self.scroll_elem.scrollTop = scroll_top; + + // adding scroll handler + var last_cluster = false, + scroll_debounce = 0, + pointer_events_set = false, + scrollEv = function() { + // fixes scrolling issue on Mac #3 + if (is_mac) { + if( ! pointer_events_set) self.content_elem.style.pointerEvents = 'none'; + pointer_events_set = true; + clearTimeout(scroll_debounce); + scroll_debounce = setTimeout(function () { + self.content_elem.style.pointerEvents = 'auto'; + pointer_events_set = false; + }, 50); + } + if (last_cluster != (last_cluster = self.getClusterNum())) + self.insertToDOM(rows, cache); + if (self.options.callbacks.scrollingProgress) + self.options.callbacks.scrollingProgress(self.getScrollProgress()); + }, + resize_debounce = 0, + resizeEv = function() { + clearTimeout(resize_debounce); + resize_debounce = setTimeout(self.refresh, 100); + } + on('scroll', self.scroll_elem, scrollEv); + on('resize', window, resizeEv); + + // public methods + self.destroy = function(clean) { + off('scroll', self.scroll_elem, scrollEv); + off('resize', window, resizeEv); + self.html((clean ? self.generateEmptyRow() : rows).join('')); + } + self.refresh = function(force) { + if(self.getRowsHeight(rows) || force) self.update(rows); + } + self.update = function(new_rows) { + rows = isArray(new_rows) + ? new_rows + : []; + var scroll_top = self.scroll_elem.scrollTop; + // fixes #39 + if(rows.length * self.options.item_height < scroll_top) { + self.scroll_elem.scrollTop = 0; + last_cluster = 0; + } + self.insertToDOM(rows, cache); + self.scroll_elem.scrollTop = scroll_top; + } + self.clear = function() { + self.update([]); + } + self.getRowsAmount = function() { + return rows.length; + } + self.getScrollProgress = function() { + return this.options.scroll_top / (rows.length * this.options.item_height) * 100 || 0; + } + + var add = function(where, _new_rows) { + var new_rows = isArray(_new_rows) + ? _new_rows + : []; + if( ! new_rows.length) return; + rows = where == 'append' + ? rows.concat(new_rows) + : new_rows.concat(rows); + self.insertToDOM(rows, cache); + } + self.append = function(rows) { + add('append', rows); + } + self.prepend = function(rows) { + add('prepend', rows); + } + } + + Clusterize.prototype = { + constructor: Clusterize, + // fetch existing markup + fetchMarkup: function() { + var rows = [], rows_nodes = this.getChildNodes(this.content_elem); + while (rows_nodes.length) { + rows.push(rows_nodes.shift().outerHTML); + } + return rows; + }, + // get tag name, content tag name, tag height, calc cluster height + exploreEnvironment: function(rows, cache) { + var opts = this.options; + opts.content_tag = this.content_elem.tagName.toLowerCase(); + if( ! rows.length) return; + if(ie && ie <= 9 && ! opts.tag) opts.tag = rows[0].match(/<([^>\s/]*)/)[1].toLowerCase(); + if(this.content_elem.children.length <= 1) cache.data = this.html(rows[0] + rows[0] + rows[0]); + if( ! opts.tag) opts.tag = this.content_elem.children[0].tagName.toLowerCase(); + this.getRowsHeight(rows); + }, + getRowsHeight: function(rows) { + var opts = this.options, + prev_item_height = opts.item_height; + opts.cluster_height = 0; + if( ! rows.length) return; + var nodes = this.content_elem.children; + var node = nodes[Math.floor(nodes.length / 2)]; + opts.item_height = node.offsetHeight; + // consider table's border-spacing + if(opts.tag == 'tr' && getStyle('borderCollapse', this.content_elem) != 'collapse') + opts.item_height += parseInt(getStyle('borderSpacing', this.content_elem), 10) || 0; + // consider margins (and margins collapsing) + if(opts.tag != 'tr') { + var marginTop = parseInt(getStyle('marginTop', node), 10) || 0; + var marginBottom = parseInt(getStyle('marginBottom', node), 10) || 0; + opts.item_height += Math.max(marginTop, marginBottom); + } + opts.block_height = opts.item_height * opts.rows_in_block; + opts.rows_in_cluster = opts.blocks_in_cluster * opts.rows_in_block; + opts.cluster_height = opts.blocks_in_cluster * opts.block_height; + return prev_item_height != opts.item_height; + }, + // get current cluster number + getClusterNum: function () { + this.options.scroll_top = this.scroll_elem.scrollTop; + return Math.floor(this.options.scroll_top / (this.options.cluster_height - this.options.block_height)) || 0; + }, + // generate empty row if no data provided + generateEmptyRow: function() { + var opts = this.options; + if( ! opts.tag || ! opts.show_no_data_row) return []; + var empty_row = document.createElement(opts.tag), + no_data_content = document.createTextNode(opts.no_data_text), td; + empty_row.className = opts.no_data_class; + if(opts.tag == 'tr') { + td = document.createElement('td'); + // fixes #53 + td.colSpan = 100; + td.appendChild(no_data_content); + } + empty_row.appendChild(td || no_data_content); + return [empty_row.outerHTML]; + }, + // generate cluster for current scroll position + generate: function (rows, cluster_num) { + var opts = this.options, + rows_len = rows.length; + if (rows_len < opts.rows_in_block) { + return { + top_offset: 0, + bottom_offset: 0, + rows_above: 0, + rows: rows_len ? rows : this.generateEmptyRow() + } + } + var items_start = Math.max((opts.rows_in_cluster - opts.rows_in_block) * cluster_num, 0), + items_end = items_start + opts.rows_in_cluster, + top_offset = Math.max(items_start * opts.item_height, 0), + bottom_offset = Math.max((rows_len - items_end) * opts.item_height, 0), + this_cluster_rows = [], + rows_above = items_start; + if(top_offset < 1) { + rows_above++; + } + for (var i = items_start; i < items_end; i++) { + rows[i] && this_cluster_rows.push(rows[i]); + } + return { + top_offset: top_offset, + bottom_offset: bottom_offset, + rows_above: rows_above, + rows: this_cluster_rows + } + }, + renderExtraTag: function(class_name, height) { + var tag = document.createElement(this.options.tag), + clusterize_prefix = 'clusterize-'; + tag.className = [clusterize_prefix + 'extra-row', clusterize_prefix + class_name].join(' '); + height && (tag.style.height = height + 'px'); + return tag.outerHTML; + }, + // if necessary verify data changed and insert to DOM + insertToDOM: function(rows, cache) { + // explore row's height + if( ! this.options.cluster_height) { + this.exploreEnvironment(rows, cache); + } + var data = this.generate(rows, this.getClusterNum()), + this_cluster_rows = data.rows.join(''), + this_cluster_content_changed = this.checkChanges('data', this_cluster_rows, cache), + top_offset_changed = this.checkChanges('top', data.top_offset, cache), + only_bottom_offset_changed = this.checkChanges('bottom', data.bottom_offset, cache), + callbacks = this.options.callbacks, + layout = []; + + if(this_cluster_content_changed || top_offset_changed) { + if(data.top_offset) { + this.options.keep_parity && layout.push(this.renderExtraTag('keep-parity')); + layout.push(this.renderExtraTag('top-space', data.top_offset)); + } + layout.push(this_cluster_rows); + data.bottom_offset && layout.push(this.renderExtraTag('bottom-space', data.bottom_offset)); + callbacks.clusterWillChange && callbacks.clusterWillChange(); + this.html(layout.join('')); + this.options.content_tag == 'ol' && this.content_elem.setAttribute('start', data.rows_above); + callbacks.clusterChanged && callbacks.clusterChanged(); + } else if(only_bottom_offset_changed) { + this.content_elem.lastChild.style.height = data.bottom_offset + 'px'; + } + }, + // unfortunately ie <= 9 does not allow to use innerHTML for table elements, so make a workaround + html: function(data) { + var content_elem = this.content_elem; + if(ie && ie <= 9 && this.options.tag == 'tr') { + var div = document.createElement('div'), last; + div.innerHTML = '' + data + '
'; + while((last = content_elem.lastChild)) { + content_elem.removeChild(last); + } + var rows_nodes = this.getChildNodes(div.firstChild.firstChild); + while (rows_nodes.length) { + content_elem.appendChild(rows_nodes.shift()); + } + } else { + content_elem.innerHTML = data; + } + }, + getChildNodes: function(tag) { + var child_nodes = tag.children, nodes = []; + for (var i = 0, ii = child_nodes.length; i < ii; i++) { + nodes.push(child_nodes[i]); + } + return nodes; + }, + checkChanges: function(type, value, cache) { + var changed = value != cache[type]; + cache[type] = value; + return changed; + } + } + + // support functions + function on(evt, element, fnc) { + return element.addEventListener ? element.addEventListener(evt, fnc, false) : element.attachEvent("on" + evt, fnc); + } + function off(evt, element, fnc) { + return element.removeEventListener ? element.removeEventListener(evt, fnc, false) : element.detachEvent("on" + evt, fnc); + } + function isArray(arr) { + return Object.prototype.toString.call(arr) === '[object Array]'; + } + function getStyle(prop, elem) { + return window.getComputedStyle ? window.getComputedStyle(elem)[prop] : elem.currentStyle[prop]; + } + + return Clusterize; +})); \ No newline at end of file diff --git a/erpnext/public/js/schools/assessment_result_tool.html b/erpnext/public/js/schools/assessment_result_tool.html index 3c09ccd6eea..9fc17f7be1d 100644 --- a/erpnext/public/js/schools/assessment_result_tool.html +++ b/erpnext/public/js/schools/assessment_result_tool.html @@ -1,44 +1,72 @@ - - - - - {% for c in criteria %} - - {% endfor %} - - - - - {% for c in criteria %} - - {% endfor %} - - - - - {% for s in students %} - - - - {% for c in criteria %} - - {% endfor %} - - - {% endfor %} - + + + + + {% for c in criteria %} + + {% endfor %} + + + + + + {% for c in criteria %} + + {% endfor %} + + + + + {% for s in students %} + + + + + {% for c in criteria %} + + {% endfor %} + + + {% endfor %} +
StudentStudent Name{{ c.assessment_criteria }}Total Marks
{{ c.maximum_score }}{{max_total_score}}
{{ s.student }}{{ s.student_name }} - - - {% if(s.assessment_details) { %} {{s.assessment_details.total_score}} {% } %} -
StudentStudent Name{{ c.assessment_criteria }}CommentsTotal Marks
Score ({{ c.maximum_score }})Score ({{max_total_score}})
{{ s.student }}{{ s.student_name }} + + {% if(s.assessment_details) { %} + {{s.assessment_details[c.assessment_criteria][1]}} + {% } %} + + + + + + + {% if(s.assessment_details) { %} + {{s.assessment_details.total_score[1]}} + {% } %} + + + {% if(s.assessment_details) { %} + {{s.assessment_details.total_score[0]}} + {% } %} + + + + + + +
\ No newline at end of file diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js index d5518857000..88178f42ce4 100644 --- a/erpnext/public/js/setup_wizard.js +++ b/erpnext/public/js/setup_wizard.js @@ -7,7 +7,11 @@ frappe.pages['setup-wizard'].on_page_load = function(wrapper) { } }; -var erpnext_slides = [ +frappe.setup.on("before_load", function () { + erpnext.setup.slides_settings.map(frappe.setup.add_slide); +}); + +erpnext.setup.slides_settings = [ { // Domain name: 'domain', @@ -18,14 +22,15 @@ var erpnext_slides = [ fieldname: 'domain', label: __('Domain'), fieldtype: 'Select', options: [ { "label": __("Distribution"), "value": "Distribution" }, - { "label": __("Education"), "value": "Education" }, { "label": __("Manufacturing"), "value": "Manufacturing" }, { "label": __("Retail"), "value": "Retail" }, - { "label": __("Services"), "value": "Services" } + { "label": __("Services"), "value": "Services" }, + { "label": __("Education (beta)"), "value": "Education" }, + {"label": __("Healthcare (beta)"), "value": "Healthcare"} ], reqd: 1 }, ], - help: __('Select the nature of your business.'), + // help: __('Select the nature of your business.'), onload: function (slide) { slide.get_input("domain").on("change", function () { frappe.setup.domain = $(this).val(); @@ -40,7 +45,7 @@ var erpnext_slides = [ domains: ["all"], icon: "fa fa-bookmark", title: __("The Brand"), - help: __('Upload your letter head and logo. (you can edit them later).'), + // help: __('Upload your letter head and logo. (you can edit them later).'), fields: [ { fieldtype: "Attach Image", fieldname: "attach_logo", @@ -79,6 +84,12 @@ var erpnext_slides = [ slide.get_field("company_abbr").set_value(""); } }); + }, + validate: function() { + if (!this.values.company_abbr) { + return false; + } + return true; } }, { @@ -87,9 +98,9 @@ var erpnext_slides = [ domains: ["all"], title: __("Your Organization"), icon: "fa fa-building", - help: (frappe.setup.domain === 'Education' ? - __('The name of the institute for which you are setting up this system.') : - __('The name of your company for which you are setting up this system.')), + // help: (frappe.setup.domain === 'Education' ? + // __('The name of the institute for which you are setting up this system.') : + // __('The name of your company for which you are setting up this system.')), fields: [ { fieldname: 'company_tagline', @@ -124,12 +135,10 @@ var erpnext_slides = [ frappe.msgprint(__("Please enter valid Financial Year Start and End Dates")); return false; } - if ((this.values.company_name || "").toLowerCase() == "company") { frappe.msgprint(__("Company Name cannot be Company")); return false; } - return true; }, @@ -153,7 +162,6 @@ var erpnext_slides = [ slide.get_field("fy_start_date").set_value(current_year + '-' + fy[0]); slide.get_field("fy_end_date").set_value(next_year + '-' + fy[1]); } - }, @@ -189,213 +197,6 @@ var erpnext_slides = [ slide.form.fields_dict.fy_end_date.set_value(year_end_date); }); } - }, - - { - // Users - name: 'users', - domains: ["all"], - title: __("Add Users"), - help: __("Add users to your organization, other than yourself"), - add_more: 1, - max_count: 3, - fields: [ - {fieldtype:"Section Break"}, - {fieldtype:"Data", fieldname:"user_fullname", - label:__("Full Name"), static: 1}, - {fieldtype:"Data", fieldname:"user_email", label:__("Email ID"), - placeholder:__("user@example.com"), options: "Email", static: 1}, - {fieldtype:"Column Break"}, - {fieldtype: "Check", fieldname: "user_sales", - label:__("Sales"), "default": 1, static: 1, - hidden: frappe.setup.domain==='Education' ? 1 : 0}, - {fieldtype: "Check", fieldname: "user_purchaser", - label:__("Purchaser"), "default": 1, static: 1, - hidden: frappe.setup.domain==='Education' ? 1 : 0}, - {fieldtype: "Check", fieldname: "user_accountant", - label:__("Accountant"), "default": 1, static: 1, - hidden: frappe.setup.domain==='Education' ? 1 : 0}, - ] - }, - - { - // Sales Target - name: 'Goals', - domains: ['manufacturing', 'services', 'retail', 'distribution'], - title: __("Set your Target"), - help: __("Set a sales target you'd like to achieve."), - fields: [ - {fieldtype:"Currency", fieldname:"sales_target", label:__("Monthly Sales Target")}, - ] - }, - - { - // Taxes - name: 'taxes', - domains: ['manufacturing', 'services', 'retail', 'distribution'], - icon: "fa fa-money", - title: __("Add Taxes"), - help: __("List your tax heads (e.g. VAT, Customs etc; they should have unique names) and their standard rates. This will create a standard template, which you can edit and add more later."), - add_more: 1, - max_count: 3, - mandatory_entry: 0, - fields: [ - {fieldtype:"Section Break"}, - {fieldtype:"Data", fieldname:"tax", label:__("Tax"), - placeholder:__("e.g. VAT")}, - {fieldtype:"Column Break"}, - {fieldtype:"Float", fieldname:"tax_rate", label:__("Rate (%)"), placeholder:__("e.g. 5")} - ] - }, - - { - // Customers - name: 'customers', - domains: ['manufacturing', 'services', 'retail', 'distribution'], - icon: "fa fa-group", - title: __("Add Customers"), - help: __("List a few of your customers. They could be organizations or individuals."), - add_more: 1, - max_count: 5, - mandatory_entry: 1, - fields: [ - {fieldtype:"Section Break"}, - {fieldtype:"Data", fieldname:"customer", label:__("Customer"), - placeholder:__("Customer Name")}, - {fieldtype:"Column Break"}, - {fieldtype:"Data", fieldname:"customer_contact", - label:__("Contact Name"), placeholder:__("Contact Name")} - ], - }, - - { - // Suppliers - name: 'suppliers', - domains: ['manufacturing', 'services', 'retail', 'distribution'], - icon: "fa fa-group", - title: __("Your Suppliers"), - help: __("List a few of your suppliers. They could be organizations or individuals."), - add_more: 1, - max_count: 5, - mandatory_entry: 1, - fields: [ - {fieldtype:"Section Break"}, - {fieldtype:"Data", fieldname:"supplier", label:__("Supplier"), - placeholder:__("Supplier Name")}, - {fieldtype:"Column Break"}, - {fieldtype:"Data", fieldname:"supplier_contact", - label:__("Contact Name"), placeholder:__("Contact Name")}, - ] - }, - - { - // Products - name: 'products', - domains: ['manufacturing', 'services', 'retail', 'distribution'], - icon: "fa fa-barcode", - title: __("Your Products or Services"), - help: __("List your products or services that you buy or sell. Make sure to check the Item Group, Unit of Measure and other properties when you start."), - add_more: 1, - max_count: 5, - mandatory_entry: 1, - fields: [ - {fieldtype:"Section Break", show_section_border: true}, - {fieldtype:"Data", fieldname:"item", label:__("Item"), - placeholder:__("A Product or Service")}, - {fieldtype:"Select", label:__("Group"), fieldname:"item_group", - options:[__("Products"), __("Services"), - __("Raw Material"), __("Consumable"), __("Sub Assemblies")], - "default": __("Products"), static: 1}, - {fieldtype:"Select", fieldname:"item_uom", label:__("UOM"), - options:[__("Unit"), __("Nos"), __("Box"), __("Pair"), __("Kg"), __("Set"), - __("Hour"), __("Minute"), __("Litre"), __("Meter"), __("Gram")], - "default": __("Unit"), static: 1}, - {fieldtype: "Check", fieldname: "is_sales_item", - label:__("We sell this Item"), default: 1, static: 1}, - {fieldtype: "Check", fieldname: "is_purchase_item", - label:__("We buy this Item"), default: 1, static: 1}, - {fieldtype:"Column Break"}, - {fieldtype:"Currency", fieldname:"item_price", label:__("Rate"), static: 1}, - {fieldtype:"Attach Image", fieldname:"item_img", label:__("Attach Image"), is_private: 0, static: 1}, - ], - get_item_count: function() { - return this.item_count; - } - }, - - { - // Program - name: 'program', - domains: ["education"], - title: __("Program"), - help: __("Example: Masters in Computer Science"), - add_more: 1, - max_count: 5, - mandatory_entry: 1, - fields: [ - {fieldtype:"Section Break", show_section_border: true}, - {fieldtype:"Data", fieldname:"program", label:__("Program"), placeholder: __("Program Name")}, - ], - }, - - { - // Course - name: 'course', - domains: ["education"], - title: __("Course"), - help: __("Example: Basic Mathematics"), - add_more: 1, - max_count: 5, - mandatory_entry: 1, - fields: [ - {fieldtype:"Section Break", show_section_border: true}, - {fieldtype:"Data", fieldname:"course", label:__("Course"), placeholder: __("Course Name")}, - ] - }, - - { - // Instructor - name: 'instructor', - domains: ["education"], - title: __("Instructor"), - help: __("People who teach at your organisation"), - add_more: 1, - max_count: 5, - mandatory_entry: 1, - fields: [ - {fieldtype:"Section Break", show_section_border: true}, - {fieldtype:"Data", fieldname:"instructor", label:__("Instructor"), placeholder: __("Instructor Name")}, - ] - }, - - { - // Room - name: 'room', - domains: ["education"], - title: __("Room"), - help: __("Classrooms/ Laboratories etc where lectures can be scheduled."), - add_more: 1, - max_count: 3, - mandatory_entry: 1, - fields: [ - {fieldtype:"Section Break", show_section_border: true}, - {fieldtype:"Data", fieldname:"room", label:__("Room")}, - {fieldtype:"Column Break"}, - {fieldtype:"Int", fieldname:"room_capacity", label:__("Room") + " Capacity", static: 1}, - ] - }, - - { - // last slide: Sample Data - name: 'bootstrap', - domains: ["all"], - title: __("Sample Data"), - fields: [{fieldtype: "Section Break"}, - {fieldtype: "Check", fieldname: "add_sample_data", - label: __("Add a few sample records"), "default": 1}, - {fieldtype: "Check", fieldname: "setup_website", - label: __("Setup a simple website for my organization"), "default": 1} - ] } ]; @@ -422,23 +223,19 @@ erpnext.setup.fiscal_years = { "United Kingdom": ["04-01", "03-31"], }; -frappe.setup.on("before_load", function () { - erpnext_slides.map(frappe.setup.add_slide); -}); - -var test_values_edu = { - "language": "english", - "domain": "Education", - "country": "India", - "timezone": "Asia/Kolkata", - "currency": "INR", - "first_name": "Tester", - "email": "test@example.com", - "password": "test", - "company_name": "Hogwarts", - "company_abbr": "HS", - "company_tagline": "School for magicians", - "bank_account": "Gringotts Wizarding Bank", - "fy_start_date": "2016-04-01", - "fy_end_date": "2017-03-31" -} +// var test_values_edu = { +// "language": "english", +// "domain": "Education", +// "country": "India", +// "timezone": "Asia/Kolkata", +// "currency": "INR", +// "first_name": "Tester", +// "email": "test@example.com", +// "password": "test", +// "company_name": "Hogwarts", +// "company_abbr": "HS", +// "company_tagline": "School for magicians", +// "bank_account": "Gringotts Wizarding Bank", +// "fy_start_date": "2016-04-01", +// "fy_end_date": "2017-03-31" +// } diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index a333ca82d68..721f216888b 100644 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -127,6 +127,20 @@ $.extend(erpnext.utils, { } }, + make_subscription: function(doctype, docname) { + frappe.call({ + method: "erpnext.accounts.doctype.subscription.subscription.make_subscription", + args: { + doctype: doctype, + docname: docname + }, + callback: function(r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }) + }, + /** * Checks if the first row of a given child table is empty * @param child_table - Child table Doctype diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 08630e59984..3e2414e665b 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -1,15 +1,16 @@ erpnext.SerialNoBatchSelector = Class.extend({ - init: function(opts) { + init: function(opts, show_dialog) { $.extend(this, opts); + this.show_dialog = show_dialog; // frm, item, warehouse_details, has_batch, oldest let d = this.item; // Don't show dialog if batch no or serial no already set - if(d && d.has_batch_no && !d.batch_no) { + if(d && d.has_batch_no && (!d.batch_no || this.show_dialog)) { this.has_batch = 1; this.setup(); - } else if(d && d.has_serial_no && !d.serial_no) { + } else if(d && d.has_serial_no && (!d.serial_no || this.show_dialog)) { this.has_batch = 0; this.setup(); } @@ -93,6 +94,11 @@ erpnext.SerialNoBatchSelector = Class.extend({ } }); + if(this.show_dialog) { + let d = this.item; + this.dialog.set_value('serial_no', d.serial_no); + } + this.dialog.show(); }, @@ -140,6 +146,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ this.map_row_values(this.item, this.values, 'serial_no', 'qty'); } refresh_field("items"); + this.callback && this.callback(this.item); }, map_row_values: function(row, values, number, qty_field, warehouse) { diff --git a/erpnext/public/less/erpnext.less b/erpnext/public/less/erpnext.less index 6c616c9e32a..d0c4841cc4f 100644 --- a/erpnext/public/less/erpnext.less +++ b/erpnext/public/less/erpnext.less @@ -364,17 +364,6 @@ body[data-route="pos"] { .pos-item-wrapper { position: relative; } - - .price-info { - position: absolute; - left: 0; - bottom: 0; - margin: 0 0 15px 15px; - background-color: rgba(141, 153, 166, 0.6); - padding: 5px 9px; - border-radius: 3px; - color: #fff; - } } .pos-bill-toolbar { @@ -423,4 +412,30 @@ body[data-route="pos"] { .collapse-btn { cursor: pointer; } +} + +.price-info { + position: absolute; + left: 0; + bottom: 0; + margin: 0 0 15px 15px; + background-color: rgba(141, 153, 166, 0.6); + padding: 5px 9px; + border-radius: 3px; + color: #fff; + +} + +// Leaderboard + +.leaderboard { + .result { + border-top: 1px solid #d1d8dd; + } + .list-item { + padding-left: 45px; + } + .list-item_content { + padding-right: 45px; + } } \ No newline at end of file diff --git a/erpnext/public/less/pos.less b/erpnext/public/less/pos.less new file mode 100644 index 00000000000..9653a826585 --- /dev/null +++ b/erpnext/public/less/pos.less @@ -0,0 +1,222 @@ +@import "../../../../frappe/frappe/public/less/variables.less"; + +[data-route="point-of-sale"] { + .layout-main-section-wrapper { + margin-bottom: 0; + } + + .pos-items-wrapper { + max-height: ~"calc(100vh - 210px)"; + } +} + +.pos { + // display: flex; + padding: 15px; +} + +.list-item { + min-height: 40px; + height: auto; +} + +.cart-container { + padding: 0 15px; + // flex: 2; + display: inline-block; + width: 39%; + vertical-align: top; +} + +.item-container { + padding: 0 15px; + // flex: 3; + display: inline-block; + width: 60%; + vertical-align: top; +} + +.search-field { + width: 60%; + + input::placeholder { + font-size: @text-medium; + } +} + +.item-group-field { + width: 40%; + margin-left: 15px; +} + +.cart-wrapper { + margin-bottom: 10px; + .list-item__content:not(:first-child) { + justify-content: flex-end; + } + + .list-item--head .list-item__content:nth-child(2) { + flex: 1.5; + } +} + +.cart-items { + height: 150px; + overflow: auto; + + .list-item.current-item { + background-color: @light-yellow; + } + + .list-item.current-item.qty input { + border: 1px solid @brand-primary; + font-weight: bold; + } + + .list-item.current-item.disc .discount { + font-weight: bold; + } + + .list-item.current-item.rate .rate { + font-weight: bold; + } + + .list-item .quantity { + flex: 1.5; + } + + input { + text-align: right; + height: 22px; + font-size: @text-medium; + } +} + +.fields { + display: flex; +} + +.pos-items-wrapper { + max-height: 480px; + overflow-y: auto; +} + +.pos-items { + overflow: hidden; +} + +.pos-item-wrapper { + display: flex; + flex-direction: column; + position: relative; + width: 25%; +} + +.image-view-container { + display: block; +} + +.image-view-container .image-field { + height: auto; +} + +.empty-state { + height: 100%; + position: relative; + + span { + position: absolute; + color: @text-muted; + font-size: @text-medium; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } +} + +@keyframes yellow-fade { + 0% {background-color: @light-yellow;} + 100% {background-color: transparent;} +} + +.highlight { + animation: yellow-fade 1s ease-in 1; +} + +input[type=number]::-webkit-inner-spin-button, +input[type=number]::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} + +// number pad + +.number-pad { + border-collapse: collapse; + cursor: pointer; + display: table; + margin: auto; +} +.num-row { + display: table-row; +} +.num-col { + display: table-cell; + border: 1px solid @border-color; + + & > div { + width: 50px; + height: 50px; + text-align: center; + line-height: 50px; + } + + &.active { + background-color: @light-yellow; + } + + &.brand-primary { + background-color: @brand-primary; + color: #ffffff; + } +} + +// taxes, totals and discount area +.discount-amount { + .discount-inputs { + display: flex; + flex-direction: column; + padding: 15px 0; + } + + input:first-child { + margin-bottom: 10px; + } +} + +.taxes-and-totals { + border-top: 1px solid @border-color; + + .taxes { + display: flex; + flex-direction: column; + padding: 15px 0; + align-items: flex-end; + + & > div:first-child { + margin-bottom: 10px; + } + } +} + +.grand-total { + border-top: 1px solid @border-color; + + .list-item { + height: 60px; + } + + .grand-total-value { + font-size: 24px; + } +} \ No newline at end of file diff --git a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.json b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.json index 23cf0823628..7b3a8d6b724 100644 --- a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.json +++ b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.json @@ -84,13 +84,34 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-06-30 20:12:57.903983", - "modified_by": "Administrator", + "modified": "2017-08-31 14:38:52.220743", + "modified_by": "ewdszx@ed.ews", "module": "Regional", "name": "GST HSN Code", "name_case": "", "owner": "Administrator", - "permissions": [], + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "write": 1 + } + ], "quick_entry": 1, "read_only": 0, "read_only_onload": 0, diff --git a/erpnext/regional/doctype/gst_hsn_code/test_gst_hsn_code.js b/erpnext/regional/doctype/gst_hsn_code/test_gst_hsn_code.js new file mode 100644 index 00000000000..24c5fd355ff --- /dev/null +++ b/erpnext/regional/doctype/gst_hsn_code/test_gst_hsn_code.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: GST HSN Code", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new GST HSN Code + () => frappe.tests.make('GST HSN Code', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.json b/erpnext/regional/doctype/gst_settings/gst_settings.json index 61af138e440..04065e29dfa 100644 --- a/erpnext/regional/doctype/gst_settings/gst_settings.json +++ b/erpnext/regional/doctype/gst_settings/gst_settings.json @@ -83,13 +83,34 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2017-06-28 16:20:21.206397", - "modified_by": "Administrator", + "modified": "2017-08-31 14:39:15.625952", + "modified_by": "ewdszx@ed.ews", "module": "Regional", "name": "GST Settings", "name_case": "", "owner": "Administrator", - "permissions": [], + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 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, + "write": 1 + } + ], "quick_entry": 1, "read_only": 0, "read_only_onload": 0, diff --git a/erpnext/regional/doctype/gst_settings/test_gst_settings.js b/erpnext/regional/doctype/gst_settings/test_gst_settings.js new file mode 100644 index 00000000000..00fcca6f326 --- /dev/null +++ b/erpnext/regional/doctype/gst_settings/test_gst_settings.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: GST Settings", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new GST Settings + () => frappe.tests.make('GST Settings', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 59e23a78823..106a3d57400 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, os, json -from frappe.custom.doctype.custom_field.custom_field import create_custom_field +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from frappe.permissions import add_permission from erpnext.regional.india import states @@ -39,12 +39,12 @@ def add_hsn_sac_codes(): hsn_codes = json.loads(f.read()) create_hsn_codes(hsn_codes, code_field="hsn_code") - + # SAC Codes with open(os.path.join(os.path.dirname(__file__), 'sac_code_data.json'), 'r') as f: sac_codes = json.loads(f.read()) create_hsn_codes(sac_codes, code_field="sac_code") - + def create_hsn_codes(data, code_field): for d in data: if not frappe.db.exists("GST HSN Code", d[code_field]): @@ -100,7 +100,7 @@ def make_custom_fields(): dict(fieldname='ecommerce_gstin', label='E-commerce GSTIN', fieldtype='Data', insert_after='export_type', print_hide=1) ] - + purchase_invoice_gst_fields = [ dict(fieldname='supplier_gstin', label='Supplier GSTIN', fieldtype='Data', insert_after='supplier_address', @@ -109,7 +109,7 @@ def make_custom_fields(): fieldtype='Data', insert_after='shipping_address', options='shipping_address.gstin', print_hide=1) ] - + sales_invoice_gst_fields = [ dict(fieldname='customer_gstin', label='Customer GSTIN', fieldtype='Data', insert_after='shipping_address', @@ -121,7 +121,7 @@ def make_custom_fields(): fieldtype='Data', insert_after='company_address', options='company_address.gstin', print_hide=1) ] - + custom_fields = { 'Address': [ dict(fieldname='gstin', label='Party GSTIN', fieldtype='Data', @@ -148,15 +148,7 @@ def make_custom_fields(): 'Purchase Invoice Item': [hsn_sac_field] } - for doctype, fields in custom_fields.items(): - for df in fields: - field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": df["fieldname"]}) - if not field: - create_custom_field(doctype, df) - else: - custom_field = frappe.get_doc("Custom Field", field) - custom_field.update(df) - custom_field.save() + create_custom_fields(custom_fields) def make_fixtures(): docs = [ diff --git a/erpnext/schools/api.py b/erpnext/schools/api.py index ff2da07a301..ea4da69504b 100644 --- a/erpnext/schools/api.py +++ b/erpnext/schools/api.py @@ -18,6 +18,7 @@ def get_course(program): (program), as_dict=1) return courses + @frappe.whitelist() def enroll_student(source_name): """Creates a Student Record and returns a Program Enrollment. @@ -40,6 +41,7 @@ def enroll_student(source_name): frappe.publish_realtime('enroll_student_progress', {"progress": [4, 4]}, user=frappe.session.user) return program_enrollment + @frappe.whitelist() def check_attendance_records_exist(course_schedule=None, student_group=None, date=None): """Check if Attendance Records are made against the specified Course Schedule or Student Group for given date. @@ -53,6 +55,7 @@ def check_attendance_records_exist(course_schedule=None, student_group=None, dat else: return frappe.get_list("Student Attendance", filters={"student_group": student_group, "date": date}) + @frappe.whitelist() def mark_attendance(students_present, students_absent, course_schedule=None, student_group=None, date=None): """Creates Multiple Attendance Records. @@ -76,6 +79,7 @@ def mark_attendance(students_present, students_absent, course_schedule=None, stu frappe.db.commit() frappe.msgprint(_("Attendance has been marked successfully.")) + def make_attendance_records(student, student_name, status, course_schedule=None, student_group=None, date=None): """Creates/Update Attendance Record. @@ -103,6 +107,7 @@ def make_attendance_records(student, student_name, status, course_schedule=None, student_attendance.status = status student_attendance.save() + @frappe.whitelist() def get_student_guardians(student): """Returns List of Guardians of a Student. @@ -113,6 +118,7 @@ def get_student_guardians(student): filters={"parent": student}) return guardians + @frappe.whitelist() def get_student_group_students(student_group, include_inactive=0): """Returns List of student, student_name in Student Group. @@ -127,6 +133,7 @@ def get_student_group_students(student_group, include_inactive=0): filters={"parent": student_group, "active": 1}, order_by= "group_roll_number") return students + @frappe.whitelist() def get_fee_structure(program, academic_term=None): """Returns Fee Structure. @@ -138,6 +145,7 @@ def get_fee_structure(program, academic_term=None): "academic_term": academic_term}, 'name', as_dict=True) return fee_structure[0].name if fee_structure else None + @frappe.whitelist() def get_fee_components(fee_structure): """Returns Fee Components. @@ -148,6 +156,7 @@ def get_fee_components(fee_structure): fs = frappe.get_list("Fee Component", fields=["fees_category", "amount"] , filters={"parent": fee_structure}, order_by= "idx") return fs + @frappe.whitelist() def get_fee_schedule(program, student_category=None): """Returns Fee Schedule. @@ -159,6 +168,7 @@ def get_fee_schedule(program, student_category=None): filters={"parent": program, "student_category": student_category }, order_by= "idx") return fs + @frappe.whitelist() def collect_fees(fees, amt): paid_amount = flt(amt) + flt(frappe.db.get_value("Fees", fees, "paid_amount")) @@ -167,6 +177,7 @@ def collect_fees(fees, amt): frappe.db.set_value("Fees", fees, "outstanding_amount", (total_amount - paid_amount)) return paid_amount + @frappe.whitelist() def get_course_schedule_events(start, end, filters=None): """Returns events for Course Schedule Calendar view rendering. @@ -191,6 +202,7 @@ def get_course_schedule_events(start, end, filters=None): return data + @frappe.whitelist() def get_assessment_criteria(course): """Returns Assessmemt Criteria and their Weightage from Course Master. @@ -200,22 +212,30 @@ def get_assessment_criteria(course): return frappe.get_list("Course Assessment Criteria", \ fields=["assessment_criteria", "weightage"], filters={"parent": course}, order_by= "idx") + @frappe.whitelist() def get_assessment_students(assessment_plan, student_group): - student_list = get_student_group_students(student_group) for i, student in enumerate(student_list): result = get_result(student.student, assessment_plan) if result: student_result = {} for d in result.details: - student_result.update({d.assessment_criteria: cstr(d.score) + " ("+ d.grade + ")"}) - student_result.update({"total_score": cstr(result.total_score) + " (" + result.grade + ")"}) - student.update({'assessment_details': student_result}) + student_result.update({d.assessment_criteria: [cstr(d.score), d.grade]}) + student_result.update({ + "total_score": [cstr(result.total_score), result.grade], + "comment": result.comment + }) + student.update({ + "assessment_details": student_result, + "docstatus": result.docstatus, + "name": result.name + }) else: student.update({'assessment_details': None}) return student_list + @frappe.whitelist() def get_assessment_details(assessment_plan): """Returns Assessment Criteria and Maximum Score from Assessment Plan Master. @@ -223,7 +243,8 @@ def get_assessment_details(assessment_plan): :param Assessment Plan: Assessment Plan """ return frappe.get_list("Assessment Plan Criteria", \ - fields=["assessment_criteria", "maximum_score"], filters={"parent": assessment_plan}, order_by= "idx") + fields=["assessment_criteria", "maximum_score", "docstatus"], filters={"parent": assessment_plan}, order_by= "idx") + @frappe.whitelist() def get_result(student, assessment_plan): @@ -232,12 +253,14 @@ def get_result(student, assessment_plan): :param Student: Student :param Assessment Plan: Assessment Plan """ - results = frappe.get_all("Assessment Result", filters={"student": student, "assessment_plan": assessment_plan, "docstatus": 1}) + results = frappe.get_all("Assessment Result", filters={"student": student, + "assessment_plan": assessment_plan, "docstatus": ("!=", 2)}) if results: return frappe.get_doc("Assessment Result", results[0]) else: return None + @frappe.whitelist() def get_grade(grading_scale, percentage): """Returns Grade based on the Grading Scale and Score. @@ -257,25 +280,63 @@ def get_grade(grading_scale, percentage): grade = "" return grade + @frappe.whitelist() -def mark_assessment_result(student, assessment_plan, scores): - student_score = json.loads(scores) - details = [] - for s in student_score.keys(): - details.append({ - "assessment_criteria": s, - "score": flt(student_score[s]) +def mark_assessment_result(assessment_plan, scores): + student_score = json.loads(scores); + assessment_details = [] + for criteria in student_score.get("assessment_details"): + assessment_details.append({ + "assessment_criteria": criteria, + "score": flt(student_score["assessment_details"][criteria]) }) - assessment_result = frappe.new_doc("Assessment Result") + assessment_result = get_assessment_result_doc(student_score["student"], assessment_plan) assessment_result.update({ - "student": student, - "student_name": frappe.db.get_value("Student", student, "title"), + "student": student_score.get("student"), "assessment_plan": assessment_plan, - "details": details + "comment": student_score.get("comment"), + "total_score":student_score.get("total_score"), + "details": assessment_details }) assessment_result.save() - assessment_result.submit() - return assessment_result + details = {} + for d in assessment_result.details: + details.update({d.assessment_criteria: d.grade}) + assessment_result_dict = { + "name": assessment_result.name, + "student": assessment_result.student, + "total_score": assessment_result.total_score, + "grade": assessment_result.grade, + "details": details + } + return assessment_result_dict + + +@frappe.whitelist() +def submit_assessment_results(assessment_plan, student_group): + total_result = 0 + student_list = get_student_group_students(student_group) + for i, student in enumerate(student_list): + doc = get_result(student.student, assessment_plan) + if doc and doc.docstatus==0: + total_result += 1 + doc.submit() + return total_result + + +def get_assessment_result_doc(student, assessment_plan): + assessment_result = frappe.get_all("Assessment Result", filters={"student": student, + "assessment_plan": assessment_plan, "docstatus": ("!=", 2)}) + if assessment_result: + doc = frappe.get_doc("Assessment Result", assessment_result[0]) + if doc.docstatus == 0: + return doc + elif doc.docstatus == 1: + frappe.msgprint("Result already Submitted") + return None + else: + return frappe.new_doc("Assessment Result") + @frappe.whitelist() def update_email_group(doctype, name): @@ -293,3 +354,21 @@ def update_email_group(doctype, name): if email: email_list.append(email) add_subscribers(name, email_list) + +@frappe.whitelist() +def get_current_enrollment(student, academic_year=None): + current_academic_year = academic_year or frappe.defaults.get_defaults().academic_year + program_enrollment_list = frappe.db.sql(''' + select + name as program_enrollment, student_name, program, student_batch_name as student_batch, + student_category, academic_term, academic_year + from + `tabProgram Enrollment` + where + student = %s and academic_year = %s + order by creation''', (student, current_academic_year), as_dict=1) + + if program_enrollment_list: + return program_enrollment_list[0] + else: + return None diff --git a/erpnext/schools/doctype/academic_term/test_records.json b/erpnext/schools/doctype/academic_term/test_records.json index 2d84383b1d3..6bd365561f1 100644 --- a/erpnext/schools/doctype/academic_term/test_records.json +++ b/erpnext/schools/doctype/academic_term/test_records.json @@ -13,5 +13,15 @@ "doctype": "Academic Term", "academic_year": "2014-2015", "term_name": "_Test Academic Term 2" + }, + { + "doctype": "Academic Term", + "academic_year": "2017-2018", + "term_name": "_Test AT1" + }, + { + "doctype": "Academic Term", + "academic_year": "2017-2018", + "term_name": "_Test AT2" } ] \ No newline at end of file diff --git a/erpnext/schools/doctype/academic_year/test_records.json b/erpnext/schools/doctype/academic_year/test_records.json index dba4f1285d6..5eb5e2e3607 100644 --- a/erpnext/schools/doctype/academic_year/test_records.json +++ b/erpnext/schools/doctype/academic_year/test_records.json @@ -1,11 +1,18 @@ [ { + "doctype": "Academic Year", "academic_year_name": "2014-2015" }, { + "doctype": "Academic Year", "academic_year_name": "2015-2016" }, { + "doctype": "Academic Year", "academic_year_name": "2016-2017" + }, + { + "doctype": "Academic Year", + "academic_year_name": "2017-2018" } ] \ No newline at end of file diff --git a/erpnext/schools/doctype/assessment_plan/assessment_plan.js b/erpnext/schools/doctype/assessment_plan/assessment_plan.js index be628b83c35..e83c4d3c70e 100644 --- a/erpnext/schools/doctype/assessment_plan/assessment_plan.js +++ b/erpnext/schools/doctype/assessment_plan/assessment_plan.js @@ -27,6 +27,14 @@ frappe.ui.form.on("Assessment Plan", { frappe.set_route("Form", "Assessment Result Tool"); }); } + + frm.set_query('grading_scale', function(){ + return { + filters: { + docstatus: 1 + } + } + }); }, course: function(frm) { diff --git a/erpnext/schools/doctype/assessment_result/assessment_result.json b/erpnext/schools/doctype/assessment_result/assessment_result.json index c6b3c44579e..13b927c367b 100644 --- a/erpnext/schools/doctype/assessment_result/assessment_result.json +++ b/erpnext/schools/doctype/assessment_result/assessment_result.json @@ -410,7 +410,7 @@ "collapsible": 0, "columns": 0, "fieldname": "comment", - "fieldtype": "Long Text", + "fieldtype": "Small Text", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -474,7 +474,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-06-30 08:21:46.875594", + "modified": "2017-08-31 15:39:24.813328", "modified_by": "Administrator", "module": "Schools", "name": "Assessment Result", diff --git a/erpnext/schools/doctype/assessment_result/assessment_result.py b/erpnext/schools/doctype/assessment_result/assessment_result.py index c878ec301aa..3c036dd56ff 100644 --- a/erpnext/schools/doctype/assessment_result/assessment_result.py +++ b/erpnext/schools/doctype/assessment_result/assessment_result.py @@ -9,13 +9,18 @@ from frappe.utils import flt from frappe.model.document import Document from erpnext.schools.api import get_grade from erpnext.schools.api import get_assessment_details +from frappe.utils.csvutils import getlink + class AssessmentResult(Document): def validate(self): + if self.student and not self.student_name: + self.student_name = frappe.db.get_value("Student", self.student, "title") self.grading_scale = frappe.db.get_value("Assessment Plan", self.assessment_plan, "grading_scale") self.validate_maximum_score() self.validate_grade() - + self.validate_duplicate() + def validate_maximum_score(self): self.maximum_score = frappe.db.get_value("Assessment Plan", self.assessment_plan, "maximum_assessment_score") assessment_details = get_assessment_details(self.assessment_plan) @@ -34,3 +39,13 @@ class AssessmentResult(Document): d.grade = get_grade(self.grading_scale, (flt(d.score)/d.maximum_score)*100) self.total_score += d.score self.grade = get_grade(self.grading_scale, (self.total_score/self.maximum_score)*100) + + def validate_duplicate(self): + assessment_result = frappe.get_list("Assessment Result", filters={"name": ("not in", [self.name]), + "student":self.student, "assessment_plan":self.assessment_plan, "docstatus":("!=", 2)}) + if assessment_result: + frappe.throw(_("Assessment Result record {0} already exists.".format(getlink("Assessment Result",assessment_result[0].name)))) + + + + diff --git a/erpnext/schools/doctype/assessment_result/test_assessment_result.js b/erpnext/schools/doctype/assessment_result/test_assessment_result.js index 83aca6bcba4..1ed249a75ba 100644 --- a/erpnext/schools/doctype/assessment_result/test_assessment_result.js +++ b/erpnext/schools/doctype/assessment_result/test_assessment_result.js @@ -20,8 +20,9 @@ QUnit.test('Test: Assessment Result', function(assert){ () => cur_frm.refresh(), () => frappe.timeout(1), () => { - for(i = 0; i < $('tbody tr').size() * 4; i = (i + 4)) - student_list.push($(`tbody td:eq("${i}")`).text()); + $("tbody tr").each( function(i, input){ + student_list.push($(input).data().student); + }); }, // Looping through each student in the list and setting up their score diff --git a/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.js b/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.js index a2eeceff25f..dfa7b142f70 100644 --- a/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.js +++ b/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.js @@ -1,12 +1,13 @@ - // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -cur_frm.add_fetch("assessment_plan", "student_group", "student_group"); frappe.ui.form.on('Assessment Result Tool', { + setup: function(frm) { + frm.add_fetch("assessment_plan", "student_group", "student_group"); + }, + refresh: function(frm) { - frm.trigger("assessment_plan"); if (frappe.route_options) { frm.set_value("student_group", frappe.route_options.student_group); frm.set_value("assessment_plan", frappe.route_options.assessment_plan); @@ -14,98 +15,145 @@ frappe.ui.form.on('Assessment Result Tool', { } frm.disable_save(); frm.page.clear_indicator(); + frm.trigger("assessment_plan"); }, assessment_plan: function(frm) { - if(!frm.doc.student_group) return; - frappe.call({ - method: "erpnext.schools.api.get_assessment_students", - args: { - "assessment_plan": frm.doc.assessment_plan, - "student_group": frm.doc.student_group - }, - callback: function(r) { - frm.events.render_table(frm, r.message); - } - }); + frm.doc.show_submit = false; + if(frm.doc.assessment_plan) { + if (!frm.doc.student_group) + return + frappe.call({ + method: "erpnext.schools.api.get_assessment_students", + args: { + "assessment_plan": frm.doc.assessment_plan, + "student_group": frm.doc.student_group + }, + callback: function(r) { + frm.doc.students = r.message; + frm.events.render_table(frm); + for (let value of r.message) { + if (!value.docstatus) { + frm.doc.show_submit = true; + break; + } + } + frm.events.submit_result(frm); + } + }); + } }, - render_table: function(frm, students) { + render_table: function(frm) { $(frm.fields_dict.result_html.wrapper).empty(); - var assessment_plan = frm.doc.assessment_plan; - var student_scores = {}; - students.forEach(function(stu) { - student_scores[stu.student] = {} - }); - + let assessment_plan = frm.doc.assessment_plan; frappe.call({ method: "erpnext.schools.api.get_assessment_details", args: { assessment_plan: assessment_plan }, callback: function(r) { - var criteria_list = r.message; - var max_total_score = 0; - criteria_list.forEach(function(c) { - max_total_score += c.maximum_score - }); - var result_table = $(frappe.render_template('assessment_result_tool', { - frm: frm, - students: students, - criteria: criteria_list, - max_total_score: max_total_score - })); - result_table.appendTo(frm.fields_dict.result_html.wrapper) - - result_table.on('change', 'input', function(e) { - var $input = $(e.target); - var max_score = $input.data().maxScore; - var student = $input.data().student; - var criteria = $input.data().criteria; - var value = $input.val(); - if(value < 0) { - $input.val(0); - value = 0; - } - if(value > max_score) { - $input.val(max_score); - value = max_score; - } - student_scores[student][criteria] = value; - if(Object.keys(student_scores[student]).length == criteria_list.length) { - console.log("ok"); - frappe.call(({ - method: "erpnext.schools.api.mark_assessment_result", - args: { - "student": student, - "assessment_plan": assessment_plan, - "scores": student_scores[student] - }, - callback: function(r) { - var doc = r.message; - var student = doc.student; - result_table.find(`[data-student=${student}].total-score`) - .html(doc.total_score + ' ('+ doc.grade + ')'); - var details = doc.details; - result_table.find(`tr[data-student=${student}]`).addClass('text-muted'); - result_table.find(`input[data-student=${student}]`).each(function(el, input) { - var $input = $(input); - var criteria = $input.data().criteria; - var value = $input.val(); - var grade = details.find(function(d) { - return d.assessment_criteria === criteria; - }).grade; - $input.val(`${value} (${grade})`); - $input.attr('disabled', true); - }); - - } - })) - } - }); - + frm.events.get_marks(frm, r.message); } }); }, + get_marks: function(frm, criteria_list) { + let max_total_score = 0; + criteria_list.forEach(function(c) { + max_total_score += c.maximum_score + }); + var result_table = $(frappe.render_template('assessment_result_tool', { + frm: frm, + students: frm.doc.students, + criteria: criteria_list, + max_total_score: max_total_score + })); + result_table.appendTo(frm.fields_dict.result_html.wrapper); + + result_table.on('change', 'input', function(e) { + let $input = $(e.target); + let student = $input.data().student; + let max_score = $input.data().maxScore; + let value = $input.val(); + if(value < 0) { + $input.val(0); + } else if(value > max_score) { + $input.val(max_score); + } + let total_score = 0; + let student_scores = {}; + student_scores["assessment_details"] = {} + result_table.find(`input[data-student=${student}].student-result-data`) + .each(function(el, input) { + let $input = $(input); + let criteria = $input.data().criteria; + let value = parseFloat($input.val()); + if (value) { + student_scores["assessment_details"][criteria] = value; + } + total_score += value; + }); + if(!Number.isNaN(total_score)) { + result_table.find(`span[data-student=${student}].total-score`).html(total_score); + } + if (Object.keys(student_scores["assessment_details"]).length === criteria_list.length) { + student_scores["student"] = student; + student_scores["total_score"] = total_score; + result_table.find(`[data-student=${student}].result-comment`) + .each(function(el, input){ + student_scores["comment"] = $(input).val(); + }); + frappe.call({ + method: "erpnext.schools.api.mark_assessment_result", + args: { + "assessment_plan": frm.doc.assessment_plan, + "scores": student_scores + }, + callback: function(r) { + let assessment_result = r.message; + if (!frm.doc.show_submit) { + frm.doc.show_submit = true; + frm.events.submit_result; + } + for (var criteria of Object.keys(assessment_result.details)) { + result_table.find(`[data-criteria=${criteria}][data-student=${assessment_result + .student}].student-result-grade`).each(function(e1, input) { + $(input).html(assessment_result.details[criteria]); + }); + } + result_table.find(`span[data-student=${assessment_result.student}].total-score-grade`).html(assessment_result.grade); + let link_span = result_table.find(`span[data-student=${assessment_result.student}].total-result-link`); + $(link_span).css("display", "block"); + $(link_span).find("a").attr("href", "#Form/Assessment Result/"+assessment_result.name); + } + }); + } + }); + }, + + submit_result: function(frm) { + if (frm.doc.show_submit) { + frm.page.set_primary_action(__("Submit"), function() { + frappe.call({ + method: "erpnext.schools.api.submit_assessment_results", + args: { + "assessment_plan": frm.doc.assessment_plan, + "student_group": frm.doc.student_group + }, + callback: function(r) { + if (r.message) { + frappe.msgprint(__("{0} Result submittted", [r.message])); + } else { + frappe.msgprint(__("No Result to submit")); + } + frm.events.assessment_plan(frm); + } + }); + }); + } + else { + frm.page.clear_primary_action(); + } + } }); diff --git a/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.py b/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.py index a0d286ccbe9..649f420d41f 100644 --- a/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.py +++ b/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class AssessmentResultTool(Document): - pass + pass \ No newline at end of file diff --git a/erpnext/schools/doctype/course/course.js b/erpnext/schools/doctype/course/course.js index c667eca2b7b..e31ba726861 100644 --- a/erpnext/schools/doctype/course/course.js +++ b/erpnext/schools/doctype/course/course.js @@ -28,4 +28,12 @@ frappe.ui.form.on("Course", "refresh", function(frm) { frappe.set_route("List", "Assessment Plan"); }); } + + frm.set_query('default_grading_scale', function(){ + return { + filters: { + docstatus: 1 + } + } + }); }); \ No newline at end of file diff --git a/erpnext/schools/doctype/fee_category/test_records.json b/erpnext/schools/doctype/fee_category/test_records.json new file mode 100644 index 00000000000..598c1ed75ba --- /dev/null +++ b/erpnext/schools/doctype/fee_category/test_records.json @@ -0,0 +1,11 @@ +[ + { + "category_name": "Admission Fee" + }, + { + "category_name": "Tuition Fee" + }, + { + "category_name": "Transportation Fee" + } +] \ No newline at end of file diff --git a/erpnext/schools/doctype/fee_component/fee_component.json b/erpnext/schools/doctype/fee_component/fee_component.json index 2b4e00220fb..ccf1f659db6 100644 --- a/erpnext/schools/doctype/fee_component/fee_component.json +++ b/erpnext/schools/doctype/fee_component/fee_component.json @@ -45,6 +45,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 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": 1, + "in_standard_filter": 0, + "label": "Description", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -120,7 +150,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-06-30 08:21:47.947269", + "modified": "2017-09-11 16:48:07.810959", "modified_by": "Administrator", "module": "Schools", "name": "Fee Component", diff --git a/erpnext/schools/doctype/fee_schedule/__init__.py b/erpnext/schools/doctype/fee_schedule/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/schools/doctype/fee_schedule/fee_schedule.js b/erpnext/schools/doctype/fee_schedule/fee_schedule.js new file mode 100644 index 00000000000..d834b887d94 --- /dev/null +++ b/erpnext/schools/doctype/fee_schedule/fee_schedule.js @@ -0,0 +1,113 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Fee Schedule', { + setup: function(frm) { + frm.add_fetch("fee_structure", "receivable_account", "receivable_account"); + frm.add_fetch("fee_structure", "income_account", "income_account"); + frm.add_fetch("fee_structure", "cost_center", "cost_center"); + }, + + onload: function(frm) { + frm.set_query("receivable_account", function(doc) { + return { + filters: { + 'account_type': 'Receivable', + 'is_group': 0, + 'company': doc.company + } + }; + }); + frm.set_query("income_account", function(doc) { + return { + filters: { + 'account_type': 'Income Account', + 'is_group': 0, + 'company': doc.company + } + }; + }); + frm.set_query("student_group", "student_groups", function() { + return { + "program": frm.doc.program, + "academic_year": frm.doc.academic_year + }; + }); + frappe.realtime.on("fee_schedule_progress", function(data) { + if (data.reload && data.reload === 1) { + frm.reload_doc(); + } + if (data.progress && data.progress === "0") { + frappe.msgprint(__("Fee records will be created in the background. In case of any error the error message will be updated in the Schedule.")); + } + if (data.progress) { + let progress_bar = $(cur_frm.dashboard.progress_area).find(".progress-bar"); + if (progress_bar) { + $(progress_bar).removeClass("progress-bar-danger").addClass("progress-bar-success progress-bar-striped"); + $(progress_bar).css("width", data.progress+"%"); + } + } + }); + }, + + refresh: function(frm) { + if(!frm.doc.__islocal && frm.doc.__onload && frm.doc.__onload.dashboard_info && + frm.doc.fee_creation_status=="Successful") { + var info = frm.doc.__onload.dashboard_info; + frm.dashboard.add_indicator(__('Total Collected: {0}', [format_currency(info.total_paid, + info.currency)]), 'blue'); + frm.dashboard.add_indicator(__('Total Outstanding: {0}', [format_currency(info.total_unpaid, + info.currency)]), info.total_unpaid ? 'orange' : 'green'); + } + if (frm.doc.fee_creation_status=="In Process") { + frm.dashboard.add_progress("Fee Creation Status", "0"); + } + if (frm.doc.docstatus==1 && !frm.doc.fee_creation_status || frm.doc.fee_creation_status == "Failed") { + frm.add_custom_button(__('Create Fees'), function() { + frappe.call({ + method: "create_fees", + doc: frm.doc, + callback: function() { + frm.refresh(); + } + }); + }, "fa fa-play", "btn-success"); + } + }, + + fee_structure: function(frm) { + if (frm.doc.fee_structure) { + frappe.call({ + method: "erpnext.schools.doctype.fee_schedule.fee_schedule.get_fee_structure", + args: { + "target_doc": frm.doc.name, + "source_name": frm.doc.fee_structure + }, + callback: function(r) { + var doc = frappe.model.sync(r.message); + frappe.set_route("Form", doc[0].doctype, doc[0].name); + } + }); + } + } +}); + +frappe.ui.form.on("Fee Schedule Student Group", { + student_group: function(frm, cdt, cdn) { + var row = locals[cdt][cdn]; + frappe.call({ + method: "erpnext.schools.doctype.fee_schedule.fee_schedule.get_total_students", + args: { + "student_group": row.student_group, + "academic_year": frm.doc.academic_year, + "academic_term": frm.doc.academic_term, + "student_category": frm.doc.student_category + }, + callback: function(r) { + if(!r.exc) { + frappe.model.set_value(cdt, cdn, "total_students", r.message); + } + } + }); + } +}) \ No newline at end of file diff --git a/erpnext/schools/doctype/fee_schedule/fee_schedule.json b/erpnext/schools/doctype/fee_schedule/fee_schedule.json new file mode 100644 index 00000000000..d2b5c522279 --- /dev/null +++ b/erpnext/schools/doctype/fee_schedule/fee_schedule.json @@ -0,0 +1,1069 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "naming_series:", + "beta": 1, + "creation": "2017-07-18 15:21:21.527136", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 0, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "fee_structure", + "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": "Fee Structure", + "length": 0, + "no_copy": 0, + "options": "Fee Structure", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "due_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": "Due 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": "Naming Series", + "length": 0, + "no_copy": 1, + "options": "FRQ.", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "fee_creation_status", + "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": "Fee Creation Status", + "length": 0, + "no_copy": 1, + "options": "\nIn Process\nFailed\nSuccessful", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "send_email", + "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": "Send Payment Request Email", + "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 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "student_category", + "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": "Student Category", + "length": 0, + "no_copy": 0, + "options": "Student Category", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "program", + "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": "Program", + "length": 0, + "no_copy": 0, + "options": "Program", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "academic_year", + "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": "Academic Year", + "length": 0, + "no_copy": 0, + "options": "Academic Year", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "academic_term", + "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": "Academic Term", + "length": 0, + "no_copy": 0, + "options": "Academic Term", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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": "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fieldname": "student_groups", + "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": "", + "length": 0, + "no_copy": 0, + "options": "Fee Schedule Student 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_14", + "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": "Fee Breakup for each student", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "components", + "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": "", + "length": 0, + "no_copy": 0, + "options": "Fee Component", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fieldname": "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": "Total Amount per Student", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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": "Grand Total", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "grand_total_in_words", + "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": "In Words", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "edit_printing_settings", + "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": "Printing Settings", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "letter_head", + "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": "Letter Head", + "length": 0, + "no_copy": 0, + "options": "Letter Head", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_32", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "select_print_heading", + "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": "Print Heading", + "length": 0, + "no_copy": 1, + "options": "Print Heading", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "account", + "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": "Accounting", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "receivable_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": "Receivable Account", + "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": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "income_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": "Income Account", + "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": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_39", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "cost_center", + "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": "Cost Center", + "length": 0, + "no_copy": 0, + "options": "Cost Center", + "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 + }, + { + "allow_bulk_edit": 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": "Institution", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 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": "Fee Schedule", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "collapsible_depends_on": "", + "columns": 0, + "depends_on": "error_log", + "fieldname": "section_break_31", + "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": "Error Log", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "error_log", + "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": "Error Log", + "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 + } + ], + "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": "2017-09-19 16:24:17.266071", + "modified_by": "Administrator", + "module": "Schools", + "name": "Fee Schedule", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "apply_user_permissions": 0, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "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", + "title_field": "", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/schools/doctype/fee_schedule/fee_schedule.py b/erpnext/schools/doctype/fee_schedule/fee_schedule.py new file mode 100644 index 00000000000..fc2907aaaf2 --- /dev/null +++ b/erpnext/schools/doctype/fee_schedule/fee_schedule.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, erpnext +from frappe.model.document import Document +from frappe.model.mapper import get_mapped_doc +from frappe.utils import money_in_words +from frappe.utils import cint, flt, cstr +from frappe.utils.background_jobs import enqueue + + +class FeeSchedule(Document): + def onload(self): + info = self.get_dashboard_info() + self.set_onload('dashboard_info', info) + + def get_dashboard_info(self): + info = { + "total_paid": 0, + "total_unpaid": 0, + "currency": erpnext.get_company_currency(self.company) + } + + fees_amount = frappe.db.sql("""select sum(grand_total), sum(outstanding_amount) from tabFees + where fee_schedule=%s and docstatus=1""", (self.name)) + + if fees_amount: + info["total_paid"] = flt(fees_amount[0][0]) - flt(fees_amount[0][1]) + info["total_unpaid"] = flt(fees_amount[0][1]) + + return info + + def validate(self): + self.calculate_total() + + def calculate_total(self): + no_of_students = 0 + for d in self.student_groups: + # if not d.total_students: + d.total_students = get_total_students(d.student_group, self.academic_year, + self.academic_term, self.student_category) + no_of_students += cint(d.total_students) + self.grand_total = no_of_students*self.total_amount + self.grand_total_in_words = money_in_words(self.grand_total) + + def create_fees(self): + self.db_set("fee_creation_status", "In Process") + frappe.publish_realtime("fee_schedule_progress", + {"progress": "0", "reload": 1}, user=frappe.session.user) + enqueue(generate_fee, queue='default', timeout=6000, event='generate_fee', + fee_schedule=self.name) + +def generate_fee(fee_schedule): + doc = frappe.get_doc("Fee Schedule", fee_schedule) + error = False + total_records = sum([int(d.total_students) for d in doc.student_groups]) + created_records = 0 + for d in doc.student_groups: + students = frappe.db.sql(""" select sg.program, sg.batch, sgs.student, sgs.student_name + from `tabStudent Group` sg, `tabStudent Group Student` sgs + where sg.name=%s and sg.name=sgs.parent and sgs.active=1""", d.student_group, as_dict=1) + + for student in students: + try: + fees_doc = get_mapped_doc("Fee Schedule", fee_schedule, { + "Fee Schedule": { + "doctype": "Fees", + "field_map": { + "name": "Fee Schedule" + } + } + }) + fees_doc.student = student.student + fees_doc.student_name = student.student_name + fees_doc.program = student.program + fees_doc.student_batch = student.batch + fees_doc.send_payment_request = doc.send_email + fees_doc.save() + fees_doc.submit() + created_records += 1 + frappe.publish_realtime("fee_schedule_progress", {"progress": str(int(created_records * 100/total_records))}, user=frappe.session.user) + + except Exception as e: + error = True + err_msg = frappe.local.message_log and "\n\n".join(frappe.local.message_log) or cstr(e) + + if error: + frappe.db.rollback() + frappe.db.set_value("Fee Schedule", fee_schedule, "fee_creation_status", "Failed") + frappe.db.set_value("Fee Schedule", fee_schedule, "error_log", err_msg) + + else: + frappe.db.set_value("Fee Schedule", fee_schedule, "fee_creation_status", "Successful") + frappe.db.set_value("Fee Schedule", fee_schedule, "error_log", None) + + frappe.publish_realtime("fee_schedule_progress", + {"progress": "100", "reload": 1}, user=frappe.session.user) + + +@frappe.whitelist() +def get_fee_structure(source_name,target_doc=None): + fee_request = get_mapped_doc("Fee Structure", source_name, + {"Fee Structure": { + "doctype": "Fee Schedule" + }}, ignore_permissions=True) + return fee_request + +@frappe.whitelist() +def get_total_students(student_group, academic_year, academic_term=None, student_category=None): + conditions = "" + if student_category: + conditions = " and pe.student_category='{}'".format(frappe.db.escape(student_category)) + if academic_term: + conditions = " and pe.academic_term='{}'".format(frappe.db.escape(academic_term)) + + + return frappe.db.sql(""" + select count(pe.name) + from `tabStudent Group Student` sgs, `tabProgram Enrollment` pe + where + pe.student = sgs.student + and pe.academic_year = %s + and sgs.parent = %s + and sgs.active = 1 + {conditions} + """.format(conditions=conditions), (academic_year, student_group))[0][0] diff --git a/erpnext/schools/doctype/fee_schedule/fee_schedule_list.js b/erpnext/schools/doctype/fee_schedule/fee_schedule_list.js new file mode 100644 index 00000000000..3039c51d780 --- /dev/null +++ b/erpnext/schools/doctype/fee_schedule/fee_schedule_list.js @@ -0,0 +1,14 @@ +frappe.listview_settings['Fee Schedule'] = { + add_fields: ["fee_creation_status", "due_date", "grand_total"], + get_indicator: function(doc) { + if (doc.fee_creation_status=="Successful") { + return [__("Fee Created"), "blue", "fee_creation_status,=,Successful"]; + } else if(doc.fee_creation_status == "In Process") { + return [__("Creating Fees"), "orange", "fee_creation_status,=,In Process"]; + } else if(doc.fee_creation_status == "Failed") { + return [__("Fee Creation Failed"), "red", "fee_creation_status,=,Failed"]; + } else { + return [__("Fee Creation Pending"), "green", "fee_creation_status,=,"]; + } + } +}; diff --git a/erpnext/schools/doctype/fee_schedule/test_fee_schedule.js b/erpnext/schools/doctype/fee_schedule/test_fee_schedule.js new file mode 100644 index 00000000000..d495b4ce7b1 --- /dev/null +++ b/erpnext/schools/doctype/fee_schedule/test_fee_schedule.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Fee Schedule", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially('Fee Schedule', [ + // insert a new Fee Schedule + () => frappe.tests.make([ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/schools/doctype/fee_schedule/test_fee_schedule.py b/erpnext/schools/doctype/fee_schedule/test_fee_schedule.py new file mode 100644 index 00000000000..44e0756d64d --- /dev/null +++ b/erpnext/schools/doctype/fee_schedule/test_fee_schedule.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import unittest + +class TestFeeSchedule(unittest.TestCase): + pass diff --git a/erpnext/schools/doctype/fee_schedule_program/__init__.py b/erpnext/schools/doctype/fee_schedule_program/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/schools/doctype/fee_schedule_program/fee_schedule_program.json b/erpnext/schools/doctype/fee_schedule_program/fee_schedule_program.json new file mode 100644 index 00000000000..42cc7bfcd89 --- /dev/null +++ b/erpnext/schools/doctype/fee_schedule_program/fee_schedule_program.json @@ -0,0 +1,133 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2017-03-23 17:46:55.712169", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "program", + "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": "Program", + "length": 0, + "no_copy": 0, + "options": "Program", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "student_batch", + "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": "Student Batch", + "length": 0, + "no_copy": 0, + "options": "Student Batch Name", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "total_students", + "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": "Total Students", + "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, + "unique": 0 + } + ], + "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-08-22 16:24:40.547517", + "modified_by": "Administrator", + "module": "Schools", + "name": "Fee Schedule Program", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "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 +} \ No newline at end of file diff --git a/erpnext/schools/doctype/fee_schedule_program/fee_schedule_program.py b/erpnext/schools/doctype/fee_schedule_program/fee_schedule_program.py new file mode 100644 index 00000000000..11d56973808 --- /dev/null +++ b/erpnext/schools/doctype/fee_schedule_program/fee_schedule_program.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class FeeScheduleProgram(Document): + pass diff --git a/erpnext/schools/doctype/fee_schedule_student_group/__init__.py b/erpnext/schools/doctype/fee_schedule_student_group/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/schools/doctype/fee_schedule_student_group/fee_schedule_student_group.json b/erpnext/schools/doctype/fee_schedule_student_group/fee_schedule_student_group.json new file mode 100644 index 00000000000..c80e32087c9 --- /dev/null +++ b/erpnext/schools/doctype/fee_schedule_student_group/fee_schedule_student_group.json @@ -0,0 +1,102 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2017-03-23 17:55:52.476822", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "student_group", + "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": "Student Group", + "length": 0, + "no_copy": 0, + "options": "Student 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "total_students", + "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": "Total Students", + "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, + "unique": 0 + } + ], + "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-08-22 16:23:12.337294", + "modified_by": "Administrator", + "module": "Schools", + "name": "Fee Schedule Student Group", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "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 +} \ No newline at end of file diff --git a/erpnext/schools/doctype/fee_schedule_student_group/fee_schedule_student_group.py b/erpnext/schools/doctype/fee_schedule_student_group/fee_schedule_student_group.py new file mode 100644 index 00000000000..776534d6faf --- /dev/null +++ b/erpnext/schools/doctype/fee_schedule_student_group/fee_schedule_student_group.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class FeeScheduleStudentGroup(Document): + pass diff --git a/erpnext/schools/doctype/fee_structure/fee_structure.js b/erpnext/schools/doctype/fee_structure/fee_structure.js index fa46278859f..300bdc869f3 100644 --- a/erpnext/schools/doctype/fee_structure/fee_structure.js +++ b/erpnext/schools/doctype/fee_structure/fee_structure.js @@ -1,3 +1,50 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Fee Structure', { + setup: function(frm) { + frm.add_fetch("company", "default_receivable_account", "receivable_account"); + frm.add_fetch("company", "default_income_account", "income_account"); + frm.add_fetch("company", "cost_center", "cost_center"); + }, + + onload: function(frm) { + frm.set_query("receivable_account", function(doc) { + return { + filters: { + 'account_type': 'Receivable', + 'is_group': 0, + 'company': doc.company + } + }; + }); + frm.set_query("income_account", function(doc) { + return { + filters: { + 'account_type': 'Income Account', + 'is_group': 0, + 'company': doc.company + } + }; + }); + }, + + refresh: function(frm) { + if(frm.doc.docstatus === 1) { + frm.add_custom_button(__("Make Fee Schedule"), function() { + frm.events.make_fee_schedule(frm); + }); + } + }, + + make_fee_schedule: function(frm) { + frappe.model.open_mapped_doc({ + method: "erpnext.schools.doctype.fee_structure.fee_structure.make_fee_schedule", + frm: frm + }); + } +}); + frappe.ui.form.on("Fee Component", { amount: function(frm) { var total_amount = 0; diff --git a/erpnext/schools/doctype/fee_structure/fee_structure.json b/erpnext/schools/doctype/fee_structure/fee_structure.json index 79d48cfff66..d93a667bd30 100644 --- a/erpnext/schools/doctype/fee_structure/fee_structure.json +++ b/erpnext/schools/doctype/fee_structure/fee_structure.json @@ -13,39 +13,6 @@ "editable_grid": 0, "engine": "InnoDB", "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Program", - "length": 0, - "no_copy": 0, - "oldfieldname": "earning_name", - "oldfieldtype": "Data", - "options": "Program", - "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, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -77,6 +44,70 @@ "set_only_once": 1, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "student_category", + "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": "Student Category", + "length": 0, + "no_copy": 0, + "options": "Student Category", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "program", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Program", + "length": 0, + "no_copy": 0, + "oldfieldname": "earning_name", + "oldfieldtype": "Data", + "options": "Program", + "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 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -146,7 +177,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "student_category", + "fieldname": "academic_year", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -155,10 +186,10 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Student Category", + "label": "Academic Year", "length": 0, "no_copy": 0, - "options": "Student Category", + "options": "Academic Year", "permlevel": 0, "precision": "", "print_hide": 0, @@ -260,6 +291,35 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -289,6 +349,220 @@ "search_index": 0, "set_only_once": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "accounts", + "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": "Accounts", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", + "fieldname": "receivable_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": "Receivable Account", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "income_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": "Income Account", + "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": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_16", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "cost_center", + "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": "Cost Center", + "length": 0, + "no_copy": 0, + "options": "Cost Center", + "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 + }, + { + "allow_bulk_edit": 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": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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": "Fee Structure", + "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, + "unique": 0 } ], "has_web_view": 0, @@ -298,12 +572,12 @@ "idx": 0, "image_view": 0, "in_create": 0, - "is_submittable": 0, + "is_submittable": 1, "issingle": 0, "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-06-30 08:21:48.057298", + "modified": "2017-09-11 15:18:27.975666", "modified_by": "Administrator", "module": "Schools", "name": "Fee Structure", @@ -311,15 +585,15 @@ "owner": "Administrator", "permissions": [ { - "amend": 0, + "amend": 1, "apply_user_permissions": 0, - "cancel": 0, + "cancel": 1, "create": 1, "delete": 1, "email": 1, "export": 1, "if_owner": 0, - "import": 0, + "import": 1, "permlevel": 0, "print": 1, "read": 1, @@ -327,7 +601,7 @@ "role": "Academics User", "set_user_permissions": 0, "share": 1, - "submit": 0, + "submit": 1, "write": 1 } ], diff --git a/erpnext/schools/doctype/fee_structure/fee_structure.py b/erpnext/schools/doctype/fee_structure/fee_structure.py index b71c507dd48..781382b51be 100644 --- a/erpnext/schools/doctype/fee_structure/fee_structure.py +++ b/erpnext/schools/doctype/fee_structure/fee_structure.py @@ -5,6 +5,8 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from frappe.model.mapper import get_mapped_doc + class FeeStructure(Document): def validate(self): @@ -16,3 +18,17 @@ class FeeStructure(Document): for d in self.components: self.total_amount += d.amount + +@frappe.whitelist() +def make_fee_schedule(source_name, target_doc=None): + return get_mapped_doc("Fee Structure", source_name, { + "Fee Structure": { + "doctype": "Fee Schedule", + "validation": { + "docstatus": ["=", 1], + } + }, + "Fee Component": { + "doctype": "Fee Component" + } + }, target_doc) \ No newline at end of file diff --git a/erpnext/schools/doctype/fee_structure/test_fee_structure.js b/erpnext/schools/doctype/fee_structure/test_fee_structure.js new file mode 100644 index 00000000000..61f41354c34 --- /dev/null +++ b/erpnext/schools/doctype/fee_structure/test_fee_structure.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Fee Structure", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Fee Structure + () => frappe.tests.make('Fee Structure', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/schools/doctype/fee_structure/test_records.json b/erpnext/schools/doctype/fee_structure/test_records.json new file mode 100644 index 00000000000..cdd00be3ff9 --- /dev/null +++ b/erpnext/schools/doctype/fee_structure/test_records.json @@ -0,0 +1,42 @@ +[ + { + "doctype": "Fee Structure", + "academic_year": "2017-2018", + "academic_term": "2017-2018 (_Test AT1)", + "components": [ + { + "fees_category": "Tuition Fee", + "amount": 40000 + }, + { + "fees_category": "Transportation Fee", + "amount": 10000 + } + ], + "total_amount": 50000, + "receivable_account": "_Test Receivable - _TC", + "income_account": "Sales - _TC", + "cost_center": "_Test Cost Center - _TC", + "company": "_Test Company" + }, + { + "doctype": "Fee Structure", + "academic_year": "2017-2018", + "academic_term": "2017-2018 (_Test AT2)", + "components": [ + { + "fees_category": "Tuition Fee", + "amount": 40000 + }, + { + "fees_category": "Transportation Fee", + "amount": 10000 + } + ], + "total_amount": 50000, + "receivable_account": "_Test Receivable - _TC", + "income_account": "Sales - _TC", + "cost_center": "_Test Cost Center - _TC", + "company": "_Test Company" + } +] \ No newline at end of file diff --git a/erpnext/schools/doctype/fees/fees.js b/erpnext/schools/doctype/fees/fees.js index e5a840a3b22..4347308740c 100644 --- a/erpnext/schools/doctype/fees/fees.js +++ b/erpnext/schools/doctype/fees/fees.js @@ -1,65 +1,144 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt -cur_frm.add_fetch("student", "title", "student_name"); frappe.ui.form.on("Fees", { + setup: function(frm) { + frm.add_fetch("fee_structure", "receivable_account", "receivable_account"); + frm.add_fetch("fee_structure", "income_account", "income_account"); + frm.add_fetch("fee_structure", "cost_center", "cost_center"); + }, onload: function(frm){ - cur_frm.set_query("academic_term",function(){ + frm.set_query("academic_term",function(){ return{ "filters":{ "academic_year": (frm.doc.academic_year) } }; }); - - cur_frm.set_query("fee_structure",function(){ + frm.set_query("fee_structure",function(){ return{ "filters":{ - "academic_term": (frm.doc.academic_term) + "academic_year": (frm.doc.academic_year) } }; }); - }, - - refresh: function(frm) { - if (frm.doc.docstatus === 1 && (frm.doc.total_amount > frm.doc.paid_amount)) { - frm.add_custom_button(__("Collect Fees"), function() { - frappe.prompt({fieldtype:"Float", label: __("Amount Paid"), fieldname:"amt"}, - function(data) { - frappe.call({ - method:"erpnext.schools.api.collect_fees", - args: { - "fees": frm.doc.name, - "amt": data.amt - }, - callback: function(r) { - frm.doc.paid_amount = r.message - frm.doc.outstanding_amount = frm.doc.total_amount - r.message - frm.refresh() - } - }); - }, __("Enter Paid Amount"), __("Collect")); - }); + frm.set_query("receivable_account", function(doc) { + return { + filters: { + 'account_type': 'Receivable', + 'is_group': 0, + 'company': doc.company + } + }; + }); + frm.set_query("income_account", function(doc) { + return { + filters: { + 'account_type': 'Income Account', + 'is_group': 0, + 'company': doc.company + } + }; + }); + if (!frm.doc.posting_date) { + frm.doc.posting_date = frappe.datetime.get_today(); } }, - program: function(frm) { - if (frm.doc.program && frm.doc.academic_term) { + refresh: function(frm) { + if(frm.doc.docstatus == 0 && frm.doc.set_posting_time) { + frm.set_df_property('posting_date', 'read_only', 0); + frm.set_df_property('posting_time', 'read_only', 0); + } else { + frm.set_df_property('posting_date', 'read_only', 1); + frm.set_df_property('posting_time', 'read_only', 1); + } + if(frm.doc.docstatus===1) { + frm.add_custom_button(__('Accounting Ledger'), function() { + frappe.route_options = { + voucher_no: frm.doc.name, + from_date: frm.doc.posting_date, + to_date: frm.doc.posting_date, + company: frm.doc.company, + group_by_voucher: false + }; + frappe.set_route("query-report", "General Ledger"); + }, __("View")); + } + if(frm.doc.docstatus===1 && frm.doc.outstanding_amount>0) { + frm.add_custom_button(__("Payment Request"), function() { + frm.events.make_payment_request(frm); + }, __("Make")); + frm.page.set_inner_btn_group_as_primary(__("Make")); + } + if(frm.doc.docstatus===1 && frm.doc.outstanding_amount!=0) { + frm.add_custom_button(__("Payment"), function() { + frm.events.make_payment_entry(frm); + }, __("Make")); + frm.page.set_inner_btn_group_as_primary(__("Make")); + } + }, + + student: function(frm) { + if (frm.doc.student) { frappe.call({ - method: "erpnext.schools.api.get_fee_structure", + method:"erpnext.schools.api.get_current_enrollment", args: { - "program": frm.doc.program, - "academic_term": frm.doc.academic_term + "student": frm.doc.student, + "academic_year": frm.doc.academic_year }, callback: function(r) { - if(r.message) { - frm.set_value("fee_structure" ,r.message); + if(r){ + $.each(r.message, function(i, d) { + frm.set_value(i,d); + }); } } }); } }, + make_payment_request: function(frm) { + if (!frm.doc.student_email) { + frappe.msgprint(__("Please set the Email ID for the Student to send the Payment Request")); + } else { + frappe.call({ + method:"erpnext.accounts.doctype.payment_request.payment_request.make_payment_request", + args: { + "dt": frm.doc.doctype, + "dn": frm.doc.name, + "recipient_id": frm.doc.student_email + }, + callback: function(r) { + if(!r.exc){ + var doc = frappe.model.sync(r.message); + frappe.set_route("Form", doc[0].doctype, doc[0].name); + } + } + }); + } + }, + + make_payment_entry: function(frm) { + return frappe.call({ + method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry", + args: { + "dt": frm.doc.doctype, + "dn": frm.doc.name + }, + callback: function(r) { + var doc = frappe.model.sync(r.message); + frappe.set_route("Form", doc[0].doctype, doc[0].name); + } + }); + }, + + set_posting_time: function(frm) { + frm.refresh(); + }, + academic_term: function() { frappe.ui.form.trigger("Fees", "program"); }, @@ -88,14 +167,15 @@ frappe.ui.form.on("Fees", { }, calculate_total_amount: function(frm) { - var total_amount = 0; + var grand_total = 0; for(var i=0;i 0: + self.indicator_color = "orange" + self.indicator_title = _("Unpaid") + else: + self.indicator_color = "green" + self.indicator_title = _("Paid") -class Fees(Document): def validate(self): self.calculate_total() - + self.set_missing_accounts_and_fields() + + def set_missing_accounts_and_fields(self): + if not self.company: + self.company = frappe.defaults.get_defaults().company + if not self.currency: + self.currency = erpnext.get_company_currency(self.company) + if not (self.receivable_account and self.income_account and self.cost_center): + accounts_details = frappe.get_all("Company", + fields=["default_receivable_account", "default_income_account", "cost_center"], + filters={"name": self.company})[0] + if not self.receivable_account: + self.receivable_account = accounts_details.default_receivable_account + if not self.income_account: + self.income_account = accounts_details.default_income_account + if not self.cost_center: + self.cost_center = accounts_details.cost_center + if not self.student_email: + self.student_email = self.get_student_emails() + + def get_student_emails(self): + student_emails = frappe.db.sql_list(""" + select g.email_address + from `tabGuardian` g, `tabStudent Guardian` sg + where g.name = sg.guardian and sg.parent = %s and sg.parenttype = 'Student' + """, self.student) + + student_email_id = frappe.db.get_value("Student", self.student, "student_email_id") + if student_email_id: + student_emails.append(student_email_id) + if student_emails: + return ", ".join(list(set(student_emails))) + else: + return None + + def calculate_total(self): """Calculates total amount.""" - self.total_amount = 0 + self.grand_total = 0 for d in self.components: - self.total_amount += d.amount - self.outstanding_amount = self.total_amount + self.grand_total += d.amount + self.outstanding_amount = self.grand_total + self.grand_total_in_words = money_in_words(self.grand_total) + + def on_submit(self): + + self.make_gl_entries() + + if self.send_payment_request and self.student_email: + pr = make_payment_request(dt="Fees", dn=self.name, recipient_id=self.student_email, + submit_doc=True, use_dummy_message=True) + frappe.msgprint(_("Payment request {0} created").format(getlink("Payment Request", pr.name))) + + def on_cancel(self): + delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name) + # frappe.db.set(self, 'status', 'Cancelled') + + + def make_gl_entries(self): + if not self.grand_total: + return + student_gl_entries = self.get_gl_dict({ + "account": self.receivable_account, + "party_type": "Student", + "party": self.student, + "against": self.income_account, + "debit": self.grand_total, + "debit_in_account_currency": self.grand_total, + "against_voucher": self.name, + "against_voucher_type": self.doctype + }) + fee_gl_entry = self.get_gl_dict({ + "account": self.income_account, + "against": self.student, + "credit": self.grand_total, + "credit_in_account_currency": self.grand_total, + "cost_center": self.cost_center + }) + from erpnext.accounts.general_ledger import make_gl_entries + make_gl_entries([student_gl_entries, fee_gl_entry], cancel=(self.docstatus == 2), + update_outstanding="Yes", merge_entries=False) def get_fee_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"): user = frappe.session.user diff --git a/erpnext/schools/doctype/fees/fees_list.js b/erpnext/schools/doctype/fees/fees_list.js index 2b5c9f6d8f1..52e1c4beb5a 100644 --- a/erpnext/schools/doctype/fees/fees_list.js +++ b/erpnext/schools/doctype/fees/fees_list.js @@ -1,15 +1,12 @@ frappe.listview_settings['Fees'] = { - add_fields: [ "total_amount", "paid_amount", "due_date"], + add_fields: ["grand_total", "outstanding_amount", "due_date"], get_indicator: function(doc) { - var { get_today } = frappe.datetime; - if ((doc.total_amount > doc.paid_amount) && doc.due_date < get_today()) { - return [__("Overdue"), "red", ["due_date,<," + get_today()], ["due_date,<," + get_today()]]; - } - else if (doc.total_amount > doc.paid_amount) { - return [__("Pending"), "orange"]; - } - else { - return [__("Paid"), "green"]; + if(flt(doc.outstanding_amount)==0) { + return [__("Paid"), "green", "outstanding_amount,=,0"]; + } else if (flt(doc.outstanding_amount) > 0 && doc.due_date >= frappe.datetime.get_today()) { + return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>,Today"]; + } else if (flt(doc.outstanding_amount) > 0 && doc.due_date < frappe.datetime.get_today()) { + return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<=,Today"]; } } }; \ No newline at end of file diff --git a/erpnext/schools/doctype/fees/test_fees.js b/erpnext/schools/doctype/fees/test_fees.js new file mode 100644 index 00000000000..22e987e8c25 --- /dev/null +++ b/erpnext/schools/doctype/fees/test_fees.js @@ -0,0 +1,31 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Fees", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially('Fees', [ + + // insert a new Fees + () => { + return frappe.tests.make('Fees', [ + {student: 'STUD00001'}, + {due_date: frappe.datetime.get_today()}, + {fee_structure: 'FS00001'} + ]); + }, + () => { + assert.equal(cur_frm.doc.grand_total===cur_frm.doc.outstanding_amount); + }, + () => frappe.timeout(0.3), + () => cur_frm.save(), + () => frappe.tests.click_button('Submit'), + () => frappe.tests.click_button('Yes'), + () => done() + ]); + +}); diff --git a/erpnext/schools/doctype/fees/test_fees.py b/erpnext/schools/doctype/fees/test_fees.py index 3ea83ee039e..50e1539cf15 100644 --- a/erpnext/schools/doctype/fees/test_fees.py +++ b/erpnext/schools/doctype/fees/test_fees.py @@ -5,8 +5,49 @@ from __future__ import unicode_literals import frappe import unittest +from frappe.utils import nowdate +from frappe.utils.make_random import get_random + # test_records = frappe.get_test_records('Fees') class TestFees(unittest.TestCase): - pass + + def test_fees(self): + student = get_random("Student") + fee = frappe.new_doc("Fees") + fee.posting_date = nowdate() + fee.due_date = nowdate() + fee.student = student + fee.receivable_account = "_Test Receivable - _TC" + fee.income_account = "Sales - _TC" + fee.cost_center = "_Test Cost Center - _TC" + fee.company = "_Test Company" + + fee.extend("components", [ + { + "fees_category": "Tuition Fee", + "amount": 40000 + }, + { + "fees_category": "Transportation Fee", + "amount": 10000 + }]) + fee.save() + fee.submit() + + gl_entries = frappe.db.sql(""" + select account, posting_date, party_type, party, cost_center, fiscal_year, voucher_type, + voucher_no, against_voucher_type, against_voucher, cost_center, company, credit, debit + from `tabGL Entry` where voucher_type=%s and voucher_no=%s""", ("Fees", fee.name), as_dict=True) + + if gl_entries[0].account == "_Test Receivable - _TC": + self.assertEquals(gl_entries[0].debit, 50000) + self.assertEquals(gl_entries[0].credit, 0) + self.assertEquals(gl_entries[1].debit, 0) + self.assertEquals(gl_entries[1].credit, 50000) + else: + self.assertEquals(gl_entries[0].credit, 50000) + self.assertEquals(gl_entries[0].debit, 0) + self.assertEquals(gl_entries[1].credit, 0) + self.assertEquals(gl_entries[1].debit, 50000) diff --git a/erpnext/schools/doctype/student/student.json b/erpnext/schools/doctype/student/student.json index 75cb758b069..4961c2db8a8 100644 --- a/erpnext/schools/doctype/student/student.json +++ b/erpnext/schools/doctype/student/student.json @@ -1114,7 +1114,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-07-07 16:30:08.930882", + "modified": "2017-07-26 19:46:26.893441", "modified_by": "Administrator", "module": "Schools", "name": "Student", diff --git a/erpnext/schools/doctype/student_applicant/student_applicant.js b/erpnext/schools/doctype/student_applicant/student_applicant.js index 40a6ac3a3de..fdf16bae0ac 100644 --- a/erpnext/schools/doctype/student_applicant/student_applicant.js +++ b/erpnext/schools/doctype/student_applicant/student_applicant.js @@ -2,6 +2,10 @@ // For license information, please see license.txt frappe.ui.form.on("Student Applicant", { + setup: function(frm) { + frm.add_fetch("guardian", "guardian_name", "guardian_name"); + }, + refresh: function(frm) { if(frm.doc.application_status== "Applied" && frm.doc.docstatus== 1 ) { frm.add_custom_button(__("Approve"), function() { @@ -39,10 +43,11 @@ frappe.ui.form.on("Student Applicant", { method: "erpnext.schools.api.enroll_student", frm: frm }) - }, + } +}); +frappe.ui.form.on('Student Sibling', { setup: function(frm) { - frm.add_fetch("guardian", "guardian_name", "guardian_name"); frm.add_fetch("student", "title", "full_name"); frm.add_fetch("student", "gender", "gender"); frm.add_fetch("student", "date_of_birth", "date_of_birth"); diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 52dcb73c038..64cd190d210 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -18,7 +18,7 @@ class Customer(TransactionBase): def onload(self): """Load address and contacts in `__onload`""" - load_address_and_contact(self, "customer") + load_address_and_contact(self) self.load_dashboard_info() def load_dashboard_info(self): diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 3e5e52fe2af..1863fb2a5e2 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -10,12 +10,15 @@ frappe.ui.form.on('Quotation', { 'Sales Order': 'Make Sales Order' } }, + refresh: function(frm) { frm.trigger("set_label"); }, + quotation_to: function(frm) { frm.trigger("set_label"); }, + set_label: function(frm) { frm.fields_dict.customer_address.set_label(__(frm.doc.quotation_to + " Address")); } @@ -44,14 +47,22 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ if(doc.docstatus == 1 && doc.status!=='Lost') { if(!doc.valid_till || frappe.datetime.get_diff(doc.valid_till, frappe.datetime.get_today()) > 0) { - cur_frm.add_custom_button(__('Make Sales Order'), - cur_frm.cscript['Make Sales Order']); + cur_frm.add_custom_button(__('Sales Order'), + cur_frm.cscript['Make Sales Order'], __("Make")); } if(doc.status!=="Ordered") { cur_frm.add_custom_button(__('Set as Lost'), cur_frm.cscript['Declare Order Lost']); } + + if(!doc.subscription) { + cur_frm.add_custom_button(__('Subscription'), function() { + erpnext.utils.make_subscription(doc.doctype, doc.name) + }, __("Make")) + } + + cur_frm.page.set_inner_btn_group_as_primary(__("Make")); } if (this.frm.doc.docstatus===0) { @@ -161,7 +172,7 @@ cur_frm.cscript['Make Sales Order'] = function() { cur_frm.cscript['Declare Order Lost'] = function(){ var dialog = new frappe.ui.Dialog({ - title: "Set as Lost", + title: __('Set as Lost'), fields: [ {"fieldtype": "Text", "label": __("Reason for losing"), "fieldname": "reason", "reqd": 1 }, diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 0afc5ca3d07..fe41426c744 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -1895,6 +1895,37 @@ "unique": 0, "width": "200px" }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "base_rounding_adjustment", + "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": "Rounding Adjustment (Company Currency)", + "length": 0, + "no_copy": 1, + "options": "Company:company:default_currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2024,6 +2055,37 @@ "unique": 0, "width": "200px" }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "rounding_adjustment", + "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": "Rounding Adjustment", + "length": 0, + "no_copy": 1, + "options": "currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2337,6 +2399,67 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription", + "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": "Subscription", + "length": 0, + "no_copy": 1, + "options": "Subscription", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2633,7 +2756,7 @@ "istable": 0, "max_attachments": 1, "menu_index": 0, - "modified": "2017-08-09 06:35:48.691648", + "modified": "2017-09-19 11:22:15.268846", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index f3ebe810565..1cdd8404288 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -108,6 +108,9 @@ class Quotation(SellingController): print_lst.append(lst1) return print_lst + def on_recurring(self, reference_doc, subscription_doc): + self.valid_till = None + def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) diff --git a/erpnext/selling/doctype/quotation/quotation_dashboard.py b/erpnext/selling/doctype/quotation/quotation_dashboard.py index f1c41e560a1..c6297e22ab5 100644 --- a/erpnext/selling/doctype/quotation/quotation_dashboard.py +++ b/erpnext/selling/doctype/quotation/quotation_dashboard.py @@ -3,9 +3,17 @@ from frappe import _ def get_data(): return { 'fieldname': 'prevdoc_docname', + 'non_standard_fieldnames': { + 'Subscription': 'reference_document', + }, 'transactions': [ { + 'label': _('Sales Order'), 'items': ['Sales Order'] }, + { + 'label': _('Subscription'), + 'items': ['Subscription'] + }, ] } \ No newline at end of file diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation.js b/erpnext/selling/doctype/quotation/tests/test_quotation.js index 44173cc0e1f..1683fa56883 100644 --- a/erpnext/selling/doctype/quotation/tests/test_quotation.js +++ b/erpnext/selling/doctype/quotation/tests/test_quotation.js @@ -30,7 +30,7 @@ QUnit.test("test: quotation", function (assert) { () => cur_frm.doc.items[0].rate = 200, () => frappe.timeout(0.3), () => cur_frm.set_value("tc_name", "Test Term 1"), - () => frappe.timeout(0.3), + () => frappe.timeout(0.5), () => cur_frm.save(), () => { // Check Address and Contact Info @@ -39,7 +39,7 @@ QUnit.test("test: quotation", function (assert) { assert.ok(cur_frm.doc.contact_display == "Contact 1", "Contact info changed"); // Check Currency - assert.ok(cur_frm.doc_currency == "USD", "Currency Changed"); + assert.ok(cur_frm.doc.currency == "USD", "Currency Changed"); assert.ok(cur_frm.doc.selling_price_list == "Test-Selling-USD", "Price List Changed"); assert.ok(cur_frm.doc.items[0].rate == 200, "Price Changed Manually"); assert.equal(cur_frm.doc.total, 1000, "New Total Calculated"); @@ -50,4 +50,4 @@ QUnit.test("test: quotation", function (assert) { }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 901e236bd2e..00d2121897a 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -141,6 +141,12 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( function() { me.make_project() }, __("Make")); } + if(!doc.subscription) { + this.frm.add_custom_button(__('Subscription'), function() { + erpnext.utils.make_subscription(doc.doctype, doc.name) + }, __("Make")) + } + } else { if (this.frm.has_perm("submit")) { // un-close diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index b69b3fda77b..b57895ad7ce 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -1980,6 +1980,37 @@ "unique": 0, "width": "150px" }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "base_rounding_adjustment", + "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": "Rounding Adjustment (Company Currency)", + "length": 0, + "no_copy": 1, + "options": "Company:company:default_currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2109,6 +2140,38 @@ "unique": 0, "width": "150px" }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fieldname": "rounding_adjustment", + "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": "Rounding Adjustment", + "length": 0, + "no_copy": 1, + "options": "currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -3179,6 +3242,67 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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": 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": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription", + "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": "Subscription", + "length": 0, + "no_copy": 0, + "options": "Subscription", + "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 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -3189,7 +3313,7 @@ "depends_on": "eval:doc.docstatus<2 && !doc.__islocal", "fieldname": "recurring_order", "fieldtype": "Section Break", - "hidden": 0, + "hidden": 1, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -3659,7 +3783,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-08-07 21:27:10.073581", + "modified": "2017-09-19 11:21:36.332326", "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 5f904c2e3d5..8720482549d 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -11,9 +11,9 @@ from frappe.model.utils import get_fetch_values from frappe.model.mapper import get_mapped_doc from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty from frappe.desk.notifications import clear_doctype_notifications -from erpnext.controllers.recurring_document import month_map, get_next_date from frappe.contacts.doctype.address.address import get_company_address from erpnext.controllers.selling_controller import SellingController +from erpnext.accounts.doctype.subscription.subscription import get_next_schedule_date form_grid_templates = { "items": "templates/form_grid/item_grid.html" @@ -346,17 +346,16 @@ class SalesOrder(SellingController): return items - def on_recurring(self, reference_doc): - mcount = month_map[reference_doc.recurring_type] - self.set("delivery_date", get_next_date(reference_doc.delivery_date, mcount, - cint(reference_doc.repeat_on_day_of_month))) + def on_recurring(self, reference_doc, subscription_doc): + self.set("delivery_date", get_next_schedule_date(reference_doc.delivery_date, subscription_doc.frequency, + cint(subscription_doc.repeat_on_day))) for d in self.get("items"): reference_delivery_date = frappe.db.get_value("Sales Order Item", {"parent": reference_doc.name, "item_code": d.item_code, "idx": d.idx}, "delivery_date") d.set("delivery_date", - get_next_date(reference_delivery_date, mcount, cint(reference_doc.repeat_on_day_of_month))) + get_next_schedule_date(reference_delivery_date, subscription_doc.frequency, cint(subscription_doc.repeat_on_day))) def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context diff --git a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py index a0ed034313a..ffce7ce102d 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py +++ b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py @@ -7,7 +7,8 @@ def get_data(): 'Delivery Note': 'against_sales_order', 'Journal Entry': 'reference_name', 'Payment Entry': 'reference_name', - 'Payment Request': 'reference_name' + 'Payment Request': 'reference_name', + 'Subscription': 'reference_document', }, 'internal_links': { 'Quotation': ['items', 'prevdoc_docname'] @@ -31,7 +32,7 @@ def get_data(): }, { 'label': _('Reference'), - 'items': ['Quotation'] + 'items': ['Quotation', 'Subscription'] }, { 'label': _('Payment'), diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order.js index 3eceb89ca26..daa81318818 100644 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order.js +++ b/erpnext/selling/doctype/sales_order/tests/test_sales_order.js @@ -1,7 +1,7 @@ QUnit.module('Sales Order'); QUnit.test("test sales order", function(assert) { - assert.expect(8); + assert.expect(10); let done = assert.async(); frappe.run_serially([ () => { @@ -10,11 +10,8 @@ QUnit.test("test sales order", function(assert) { {items: [ [ {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 4'}, - {'uom': 'unit'}, - {'margin_type': 'Percentage'}, - {'discount_percentage': 10}, + {'qty': 5.123}, + {'item_code': 'Test Product 3'}, ] ]}, {customer_address: 'Test1-Billing'}, @@ -28,29 +25,31 @@ QUnit.test("test sales order", function(assert) { () => { return frappe.tests.set_form_values(cur_frm, [ {selling_price_list:'Test-Selling-USD'}, - {currency: 'USD'}, - {apply_discount_on:'Grand Total'}, - {additional_discount_percentage:10} + {currency: 'USD'} ]); }, - () => cur_frm.save(), + () => frappe.timeout(1), () => { // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); + assert.ok(cur_frm.doc.items[0].item_name=='Test Product 3', "Item name correct"); // get tax details assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST', "Tax details correct"); // get tax account head details assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); // calculate totals - assert.ok(cur_frm.doc.items[0].price_list_rate==1000, "Item 1 price_list_rate"); - assert.ok(cur_frm.doc.total== 4500, "total correct "); - assert.ok(cur_frm.doc.rounded_total== 4414.5, "rounded total correct "); - + assert.ok(cur_frm.doc.items[0].price_list_rate==250, "Item 1 price_list_rate"); + assert.ok(cur_frm.doc.net_total== 1280.75, "net total correct "); + assert.ok(cur_frm.doc.base_grand_total== flt(1511.29* cur_frm.doc.conversion_rate, precision('base_grand_total')), "base round total correct "); + assert.ok(cur_frm.doc.grand_total== 1511.29 , "grand total correct "); + assert.ok(cur_frm.doc.rounded_total== 1511.30, "rounded total correct "); }, + () => cur_frm.save(), + () => frappe.timeout(1), () => cur_frm.print_doc(), () => frappe.timeout(1), () => { assert.ok($('.btn-print-print').is(':visible'), "Print Format Available"); + frappe.timeout(1); assert.ok($(".section-break+ .section-break .column-break:nth-child(1) .data-field:nth-child(1) .value").text().includes("Billing Street 1"), "Print Preview Works As Expected"); }, () => cur_frm.print_doc(), diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multi_uom.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multi_uom.js index 74f51ca72cf..84301f5a86b 100644 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multi_uom.js +++ b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multi_uom.js @@ -12,7 +12,7 @@ QUnit.test("test sales order", function(assert) { {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, {'qty': 5}, {'item_code': 'Test Product 4'}, - {'uom': 'unit'}, + {'uom': 'Unit'}, ] ]}, {customer_address: 'Test1-Billing'}, diff --git a/erpnext/selling/page/point_of_sale/__init__.py b/erpnext/selling/page/point_of_sale/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js new file mode 100644 index 00000000000..ec1a2926218 --- /dev/null +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -0,0 +1,1290 @@ +/* global Clusterize */ +frappe.provide('erpnext.pos'); + +frappe.pages['point-of-sale'].on_page_load = function(wrapper) { + frappe.ui.make_app_page({ + parent: wrapper, + title: 'Point of Sale', + single_column: true + }); + + frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'is_online', (r) => { + if (r && r.use_pos_in_offline_mode && !cint(r.use_pos_in_offline_mode)) { + // online + wrapper.pos = new erpnext.pos.PointOfSale(wrapper); + window.cur_pos = wrapper.pos; + } else { + // offline + frappe.set_route('pos'); + } + }); +}; + +frappe.pages['point-of-sale'].refresh = function(wrapper) { + cur_frm = wrapper.pos.frm; +} + +erpnext.pos.PointOfSale = class PointOfSale { + constructor(wrapper) { + this.wrapper = $(wrapper).find('.layout-main-section'); + this.page = wrapper.page; + + const assets = [ + 'assets/erpnext/js/pos/clusterize.js', + 'assets/erpnext/css/pos.css' + ]; + + frappe.require(assets, () => { + this.make(); + }); + } + + make() { + return frappe.run_serially([ + () => { + this.prepare_dom(); + this.prepare_menu(); + this.set_online_status(); + }, + () => this.setup_pos_profile(), + () => { + this.make_items(); + this.bind_events(); + }, + () => this.make_new_invoice(), + () => this.page.set_title(__('Point of Sale')) + ]); + } + + set_online_status() { + this.connection_status = false; + this.page.set_indicator(__("Offline"), "grey"); + frappe.call({ + method: "frappe.handler.ping", + callback: r => { + if (r.message) { + this.connection_status = true; + this.page.set_indicator(__("Online"), "green"); + } + } + }); + } + + prepare_dom() { + this.wrapper.append(` +
+
+ +
+
+ +
+
+ `); + } + + make_cart() { + this.cart = new POSCart({ + frm: this.frm, + wrapper: this.wrapper.find('.cart-container'), + events: { + on_customer_change: (customer) => this.frm.set_value('customer', customer), + on_field_change: (item_code, field, value) => { + this.update_item_in_cart(item_code, field, value); + }, + on_numpad: (value) => { + if (value == 'Pay') { + if (!this.payment) { + this.make_payment_modal(); + } + this.payment.open_modal(); + } + }, + on_select_change: () => { + this.cart.numpad.set_inactive(); + } + } + }); + } + + toggle_editing(flag) { + let disabled; + if (flag !== undefined) { + disabled = !flag; + } else { + disabled = this.frm.doc.docstatus == 1 ? true: false; + } + const pointer_events = disabled ? 'none' : 'inherit'; + + this.wrapper.find('input, button, select').prop("disabled", disabled); + this.wrapper.find('.number-pad-container').toggleClass("hide", disabled); + + this.wrapper.find('.cart-container').css('pointer-events', pointer_events); + this.wrapper.find('.item-container').css('pointer-events', pointer_events); + + this.page.clear_actions(); + } + + make_items() { + this.items = new POSItems({ + wrapper: this.wrapper.find('.item-container'), + pos_profile: this.pos_profile, + events: { + update_cart: (item, field, value) => { + if(!this.frm.doc.customer) { + frappe.throw(__('Please select a customer')); + } + this.update_item_in_cart(item, field, value); + this.cart && this.cart.unselect_all(); + } + } + }); + } + + update_item_in_cart(item_code, field='qty', value=1) { + if(this.cart.exists(item_code)) { + const item = this.frm.doc.items.find(i => i.item_code === item_code); + frappe.flags.hide_serial_batch_dialog = false; + + if (typeof value === 'string' && !in_list(['serial_no', 'batch_no'], field)) { + // value can be of type '+1' or '-1' + value = item[field] + flt(value); + } + + if(field === 'serial_no') { + value = item.serial_no + '\n'+ value; + } + + if(field === 'qty' && (item.serial_no || item.batch_no)) { + this.select_batch_and_serial_no(item); + } else { + this.update_item_in_frm(item, field, value) + .then(() => { + // update cart + this.update_cart_data(item); + }); + } + return; + } + + let args = { item_code: item_code }; + 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; + this.frm.script_manager + .trigger('item_code', item.doctype, item.name) + .then(() => { + const show_dialog = item.has_serial_no || item.has_batch_no; + if (show_dialog && field == 'qty') { + // check has serial no/batch no and update cart + this.select_batch_and_serial_no(item); + } else { + // update cart + this.update_cart_data(item); + } + }); + } + + select_batch_and_serial_no(item) { + erpnext.show_serial_batch_selector(this.frm, item, () => { + this.update_item_in_frm(item) + .then(() => { + // update cart + this.update_cart_data(item); + }); + }, true); + } + + update_cart_data(item) { + this.cart.add_item(item); + this.cart.update_taxes_and_totals(); + this.cart.update_grand_total(); + } + + update_item_in_frm(item, field, value) { + if (field) { + frappe.model.set_value(item.doctype, item.name, field, value); + } + + return this.frm.script_manager + .trigger('qty', item.doctype, item.name) + .then(() => { + if (field === 'qty' && value === 0) { + frappe.model.clear_doc(item.doctype, item.name); + } + }); + } + + make_payment_modal() { + this.payment = new Payment({ + frm: this.frm, + events: { + submit_form: () => { + this.submit_sales_invoice(); + } + } + }); + } + + submit_sales_invoice() { + + frappe.confirm(__("Permanently Submit {0}?", [this.frm.doc.name]), () => { + frappe.call({ + method: 'erpnext.selling.page.point_of_sale.point_of_sale.submit_invoice', + freeze: true, + args: { + doc: this.frm.doc + } + }).then(r => { + if(r.message) { + this.frm.doc = r.message; + frappe.show_alert({ + indicator: 'green', + message: __(`Sales invoice ${r.message.name} created succesfully`) + }); + + this.toggle_editing(); + this.set_form_action(); + } + }); + }); + } + + bind_events() { + + } + + setup_pos_profile() { + return frappe.call({ + method: 'erpnext.stock.get_item_details.get_pos_profile', + args: { + company: frappe.sys_defaults.company + } + }).then(r => { + this.pos_profile = r.message; + + if (!this.pos_profile) { + this.pos_profile = { + currency: frappe.defaults.get_default('currency'), + selling_price_list: frappe.defaults.get_default('selling_price_list') + }; + } + }); + } + + make_new_invoice() { + return frappe.run_serially([ + () => this.make_sales_invoice_frm(), + () => { + if (this.cart) { + this.cart.frm = this.frm; + this.cart.reset(); + } else { + this.make_cart(); + } + this.toggle_editing(true); + } + ]); + } + + make_sales_invoice_frm() { + const doctype = 'Sales Invoice'; + return new Promise(resolve => { + if (this.frm) { + this.frm = get_frm(this.frm); + resolve(); + } else { + frappe.model.with_doctype(doctype, () => { + this.frm = get_frm(); + resolve(); + }); + } + }); + + function get_frm(_frm) { + const page = $('
'); + const frm = _frm || new _f.Frm(doctype, page, false); + const name = frappe.model.make_new_doc_and_get_name(doctype, true); + frm.refresh(name); + frm.doc.items = []; + frm.set_value('is_pos', 1); + frm.meta.default_print_format = 'POS Invoice'; + return frm; + } + } + + prepare_menu() { + var me = this; + this.page.clear_menu(); + + // for mobile + // this.page.add_menu_item(__("Pay"), function () { + // + // }).addClass('visible-xs'); + + this.page.add_menu_item(__("Form View"), function () { + frappe.model.sync(me.frm.doc); + frappe.set_route("Form", me.frm.doc.doctype, me.frm.doc.name); + }); + + this.page.add_menu_item(__("POS Profile"), function () { + frappe.set_route('List', 'POS Profile'); + }); + + this.page.add_menu_item(__('POS Settings'), function() { + frappe.set_route('Form', 'POS Settings'); + }); + } + + set_form_action() { + if(this.frm.doc.docstatus !== 1) return; + + this.page.set_secondary_action(__("Print"), () => { + if (this.pos_profile && this.pos_profile.print_format_for_online) { + this.frm.meta.default_print_format = this.pos_profile.print_format_for_online; + } + this.frm.print_preview.printit(true); + }); + + this.page.set_primary_action(__("New"), () => { + this.make_new_invoice(); + }); + + this.page.add_menu_item(__("Email"), () => { + this.frm.email_doc(); + }); + } +}; + +class POSCart { + constructor({frm, wrapper, events}) { + this.frm = frm; + this.wrapper = wrapper; + this.events = events; + this.make(); + this.bind_events(); + } + + make() { + this.make_dom(); + this.make_customer_field(); + this.make_numpad(); + } + + make_dom() { + this.wrapper.append(` +
+
+
+
+
+
+
${__('Item Name')}
+
${__('Quantity')}
+
${__('Discount')}
+
${__('Rate')}
+
+
+
+ No Items added to cart +
+
+
+ ${this.get_taxes_and_totals()} +
+
+ ${this.get_discount_amount()} +
+
+ ${this.get_grand_total()} +
+
+
+
+
+
+ `); + this.$cart_items = this.wrapper.find('.cart-items'); + this.$empty_state = this.wrapper.find('.cart-items .empty-state'); + this.$taxes_and_totals = this.wrapper.find('.taxes-and-totals'); + this.$discount_amount = this.wrapper.find('.discount-amount'); + this.$grand_total = this.wrapper.find('.grand-total'); + + this.toggle_taxes_and_totals(false); + this.$grand_total.on('click', () => { + this.toggle_taxes_and_totals(); + }); + } + + reset() { + this.$cart_items.find('.list-item').remove(); + this.$empty_state.show(); + this.$taxes_and_totals.html(this.get_taxes_and_totals()); + this.numpad && this.numpad.reset_value(); + this.customer_field.set_value(""); + } + + get_grand_total() { + return ` +
+
${__('Grand Total')}
+
0.00
+
+ `; + } + + get_discount_amount() { + const get_currency_symbol = window.get_currency_symbol; + + return ` +
+
${__('Discount')}
+
+ + +
+
+ `; + } + + get_taxes_and_totals() { + return ` +
+
${__('Net Total')}
+
0.00
+
+
+
${__('Taxes')}
+
0.00
+
+ `; + } + + toggle_taxes_and_totals(flag) { + if (flag !== undefined) { + this.tax_area_is_shown = flag; + } else { + this.tax_area_is_shown = !this.tax_area_is_shown; + } + + this.$taxes_and_totals.toggle(this.tax_area_is_shown); + this.$discount_amount.toggle(this.tax_area_is_shown); + } + + update_taxes_and_totals() { + if (!this.frm.doc.taxes) { return; } + + const currency = this.frm.doc.currency; + this.frm.refresh_field('taxes'); + + // Update totals + this.$taxes_and_totals.find('.net-total') + .html(format_currency(this.frm.doc.net_total, currency)); + + // Update taxes + const taxes_html = this.frm.doc.taxes.map(tax => { + return ` +
+ ${tax.description} + + ${format_currency(tax.tax_amount, currency)} + +
+ `; + }).join(""); + this.$taxes_and_totals.find('.taxes').html(taxes_html); + } + + update_grand_total() { + this.$grand_total.find('.grand-total-value').text( + format_currency(this.frm.doc.grand_total, this.frm.currency) + ); + } + + make_customer_field() { + this.customer_field = frappe.ui.form.make_control({ + df: { + fieldtype: 'Link', + label: 'Customer', + fieldname: 'customer', + options: 'Customer', + reqd: 1, + default: this.frm.doc.customer, + onchange: () => { + this.events.on_customer_change(this.customer_field.get_value()); + } + }, + parent: this.wrapper.find('.customer-field'), + render_input: true + }); + } + + make_numpad() { + this.numpad = new NumberPad({ + button_array: [ + [1, 2, 3, 'Qty'], + [4, 5, 6, 'Disc'], + [7, 8, 9, 'Rate'], + ['Del', 0, '.', 'Pay'] + ], + add_class: { + 'Pay': 'brand-primary' + }, + disable_highlight: ['Qty', 'Disc', 'Rate', 'Pay'], + reset_btns: ['Qty', 'Disc', 'Rate', 'Pay'], + del_btn: 'Del', + wrapper: this.wrapper.find('.number-pad-container'), + onclick: (btn_value) => { + // on click + if (!this.selected_item && btn_value !== 'Pay') { + frappe.show_alert({ + indicator: 'red', + message: __('Please select an item in the cart') + }); + return; + } + if (['Qty', 'Disc', 'Rate'].includes(btn_value)) { + this.set_input_active(btn_value); + } else if (btn_value !== 'Pay') { + if (!this.selected_item.active_field) { + frappe.show_alert({ + indicator: 'red', + message: __('Please select a field to edit from numpad') + }); + return; + } + + const item_code = this.selected_item.attr('data-item-code'); + const field = this.selected_item.active_field; + const value = this.numpad.get_value(); + + this.events.on_field_change(item_code, field, value); + } + + this.events.on_numpad(btn_value); + } + }); + } + + set_input_active(btn_value) { + this.selected_item.removeClass('qty disc rate'); + + this.numpad.set_active(btn_value); + if (btn_value === 'Qty') { + this.selected_item.addClass('qty'); + this.selected_item.active_field = 'qty'; + } else if (btn_value == 'Disc') { + this.selected_item.addClass('disc'); + this.selected_item.active_field = 'discount_percentage'; + } else if (btn_value == 'Rate') { + this.selected_item.addClass('rate'); + this.selected_item.active_field = 'rate'; + } + } + + add_item(item) { + this.$empty_state.hide(); + + if (this.exists(item.item_code)) { + // update quantity + this.update_item(item); + } else { + // add to cart + const $item = $(this.get_item_html(item)); + $item.appendTo(this.$cart_items); + } + this.highlight_item(item.item_code); + this.scroll_to_item(item.item_code); + } + + update_item(item) { + const $item = this.$cart_items.find(`[data-item-code="${item.item_code}"]`); + + if(item.qty > 0) { + const indicator_class = item.actual_qty >= item.qty ? 'green' : 'red'; + const remove_class = indicator_class == 'green' ? 'red' : 'green'; + + $item.find('.quantity input').val(item.qty); + $item.find('.discount').text(item.discount_percentage + '%'); + $item.find('.rate').text(format_currency(item.rate, this.frm.doc.currency)); + $item.addClass(indicator_class); + $item.removeClass(remove_class); + } else { + $item.remove(); + } + } + + get_item_html(item) { + const rate = format_currency(item.rate, this.frm.doc.currency); + const indicator_class = item.actual_qty >= item.qty ? 'green' : 'red'; + return ` +
+
+ ${item.item_name} +
+
+ ${get_quantity_html(item.qty)} +
+
+ ${item.discount_percentage}% +
+
+ ${rate} +
+
+ `; + + function get_quantity_html(value) { + return ` +
+ + + + + + + + + +
+ `; + } + } + + exists(item_code) { + let $item = this.$cart_items.find(`[data-item-code="${item_code}"]`); + return $item.length > 0; + } + + highlight_item(item_code) { + const $item = this.$cart_items.find(`[data-item-code="${item_code}"]`); + $item.addClass('highlight'); + setTimeout(() => $item.removeClass('highlight'), 1000); + } + + scroll_to_item(item_code) { + const $item = this.$cart_items.find(`[data-item-code="${item_code}"]`); + if ($item.length === 0) return; + const scrollTop = $item.offset().top - this.$cart_items.offset().top + this.$cart_items.scrollTop(); + this.$cart_items.animate({ scrollTop }); + } + + bind_events() { + const me = this; + const events = this.events; + + // quantity change + this.$cart_items.on('click', + '[data-action="increment"], [data-action="decrement"]', function() { + const $btn = $(this); + const $item = $btn.closest('.list-item[data-item-code]'); + const item_code = $item.attr('data-item-code'); + const action = $btn.attr('data-action'); + + if(action === 'increment') { + events.on_field_change(item_code, 'qty', '+1'); + } else if(action === 'decrement') { + events.on_field_change(item_code, 'qty', '-1'); + } + }); + + // this.$cart_items.on('focus', '.quantity input', function(e) { + // const $input = $(this); + // const $item = $input.closest('.list-item[data-item-code]'); + // me.set_selected_item($item); + // me.set_input_active('Qty'); + // e.preventDefault(); + // e.stopPropagation(); + // return false; + // }); + + this.$cart_items.on('change', '.quantity input', function() { + const $input = $(this); + const $item = $input.closest('.list-item[data-item-code]'); + const item_code = $item.attr('data-item-code'); + events.on_field_change(item_code, 'qty', flt($input.val())); + }); + + // current item + this.$cart_items.on('click', '.list-item', function() { + me.set_selected_item($(this)); + }); + + // disable current item + // $('body').on('click', function(e) { + // console.log(e); + // if($(e.target).is('.list-item')) { + // return; + // } + // me.$cart_items.find('.list-item').removeClass('current-item qty disc rate'); + // me.selected_item = null; + // }); + + this.wrapper.find('.additional_discount_percentage').on('change', (e) => { + frappe.model.set_value(this.frm.doctype, this.frm.docname, + 'additional_discount_percentage', e.target.value) + .then(() => { + let discount_wrapper = this.wrapper.find('.discount_amount'); + discount_wrapper.val(this.frm.doc.discount_amount); + discount_wrapper.trigger('change'); + }); + }); + + this.wrapper.find('.discount_amount').on('change', (e) => { + frappe.model.set_value(this.frm.doctype, this.frm.docname, + 'discount_amount', e.target.value); + this.frm.trigger('discount_amount') + .then(() => { + let discount_wrapper = this.wrapper.find('.additional_discount_percentage'); + discount_wrapper.val(this.frm.doc.additional_discount_percentage); + this.update_taxes_and_totals(); + this.update_grand_total(); + }); + }); + } + + set_selected_item($item) { + this.selected_item = $item; + this.$cart_items.find('.list-item').removeClass('current-item qty disc rate'); + this.selected_item.addClass('current-item'); + this.events.on_select_change(); + } + + unselect_all() { + this.$cart_items.find('.list-item').removeClass('current-item qty disc rate'); + this.selected_item = null; + this.events.on_select_change(); + } +} + +class POSItems { + constructor({wrapper, pos_profile, events}) { + this.wrapper = wrapper; + this.pos_profile = pos_profile; + this.items = {}; + this.events = events; + this.currency = this.pos_profile.currency; + + this.make_dom(); + this.make_fields(); + + this.init_clusterize(); + this.bind_events(); + + // bootstrap with 20 items + this.get_items() + .then(({ items }) => { + this.all_items = items; + this.items = items; + this.render_items(items); + }); + } + + make_dom() { + this.wrapper.html(` +
+
+
+
+
+
+
+
+ `); + + this.items_wrapper = this.wrapper.find('.items-wrapper'); + this.items_wrapper.append(` +
+
+
+
+ `); + } + + make_fields() { + // Search field + this.search_field = frappe.ui.form.make_control({ + df: { + fieldtype: 'Data', + label: 'Search Item (Ctrl + I)', + placeholder: 'Search by item code, serial number, batch no or barcode' + }, + parent: this.wrapper.find('.search-field'), + render_input: true, + }); + + frappe.ui.keys.on('ctrl+i', () => { + this.search_field.set_focus(); + }); + + this.search_field.$input.on('input', (e) => { + clearTimeout(this.last_search); + this.last_search = setTimeout(() => { + const search_term = e.target.value; + this.filter_items({ search_term }); + }, 300); + }); + + this.item_group_field = frappe.ui.form.make_control({ + df: { + fieldtype: 'Link', + label: 'Item Group', + options: 'Item Group', + default: 'All Item Groups', + onchange: () => { + const item_group = this.item_group_field.get_value(); + if (item_group) { + this.filter_items({ item_group: item_group }); + } + }, + }, + parent: this.wrapper.find('.item-group-field'), + render_input: true + }); + } + + init_clusterize() { + this.clusterize = new Clusterize({ + scrollElem: this.wrapper.find('.pos-items-wrapper')[0], + contentElem: this.wrapper.find('.pos-items')[0], + rows_in_block: 6 + }); + } + + render_items(items) { + let _items = items || this.items; + + const all_items = Object.values(_items).map(item => this.get_item_html(item)); + let row_items = []; + + const row_container = '
'; + let curr_row = row_container; + + for (let i=0; i < all_items.length; i++) { + // wrap 4 items in a div to emulate + // a row for clusterize + if(i % 4 === 0 && i !== 0) { + curr_row += '
'; + row_items.push(curr_row); + curr_row = row_container; + } + curr_row += all_items[i]; + + if(i == all_items.length - 1 && all_items.length % 4 !== 0) { + row_items.push(curr_row); + } + } + + this.clusterize.update(row_items); + } + + filter_items({ search_term='', item_group='All Item Groups' }={}) { + if (search_term) { + search_term = search_term.toLowerCase(); + + // memoize + this.search_index = this.search_index || {}; + if (this.search_index[search_term]) { + const items = this.search_index[search_term]; + this.render_items(items); + return; + } + } else if (item_group == "All Item Groups") { + return this.render_items(this.all_items); + } + + this.get_items({search_value: search_term, item_group }) + .then(({ items, serial_no, batch_no }) => { + if (search_term) { + this.search_index[search_term] = items; + } + + this.render_items(items); + if(serial_no) { + this.events.update_cart(items[0].item_code, + 'serial_no', serial_no); + this.search_field.set_value(''); + } + if(batch_no) { + this.events.update_cart(items[0].item_code, + 'batch_no', serial_no); + this.search_field.set_value(''); + } + }); + } + + bind_events() { + var me = this; + this.wrapper.on('click', '.pos-item-wrapper', function() { + const $item = $(this); + const item_code = $item.attr('data-item-code'); + me.events.update_cart(item_code, 'qty', '+1'); + }); + } + + get(item_code) { + return this.items[item_code]; + } + + get_all() { + return this.items; + } + + get_item_html(item) { + const price_list_rate = format_currency(item.price_list_rate, this.currency); + const { item_code, item_name, item_image} = item; + const item_title = item_name || item_code; + + const template = ` + + `; + + return template; + } + + get_items({start = 0, page_length = 40, search_value='', item_group="All Item Groups"}={}) { + return new Promise(res => { + frappe.call({ + method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items", + args: { + start, + page_length, + 'price_list': this.pos_profile.selling_price_list, + item_group, + search_value + } + }).then(r => { + // const { items, serial_no, batch_no } = r.message; + + // this.serial_no = serial_no || ""; + res(r.message); + }); + }); + } +} + +class NumberPad { + constructor({ + wrapper, onclick, button_array, + add_class={}, disable_highlight=[], + reset_btns=[], del_btn='', + }) { + this.wrapper = wrapper; + this.onclick = onclick; + this.button_array = button_array; + this.add_class = add_class; + this.disable_highlight = disable_highlight; + this.reset_btns = reset_btns; + this.del_btn = del_btn; + this.make_dom(); + this.bind_events(); + this.value = ''; + } + + make_dom() { + if (!this.button_array) { + this.button_array = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ['', 0, ''] + ]; + } + + this.wrapper.html(` +
+ ${this.button_array.map(get_row).join("")} +
+ `); + + function get_row(row) { + return '
' + row.map(get_col).join("") + '
'; + } + + function get_col(col) { + return `
${col}
`; + } + + this.set_class(); + } + + set_class() { + for (const btn in this.add_class) { + const class_name = this.add_class[btn]; + this.get_btn(btn).addClass(class_name); + } + } + + bind_events() { + // bind click event + const me = this; + this.wrapper.on('click', '.num-col', function() { + const $btn = $(this); + const btn_value = $btn.attr('data-value'); + if (!me.disable_highlight.includes(btn_value)) { + me.highlight_button($btn); + } + if (me.reset_btns.includes(btn_value)) { + me.reset_value(); + } else { + if (btn_value === me.del_btn) { + me.value = me.value.substr(0, me.value.length - 1); + } else { + me.value += btn_value; + } + } + me.onclick(btn_value); + }); + } + + reset_value() { + this.value = ''; + } + + get_value() { + return flt(this.value); + } + + get_btn(btn_value) { + return this.wrapper.find(`.num-col[data-value="${btn_value}"]`); + } + + highlight_button($btn) { + $btn.addClass('highlight'); + setTimeout(() => $btn.removeClass('highlight'), 1000); + } + + set_active(btn_value) { + const $btn = this.get_btn(btn_value); + this.wrapper.find('.num-col').removeClass('active'); + $btn.addClass('active'); + } + + set_inactive() { + this.wrapper.find('.num-col').removeClass('active'); + } +} + +class Payment { + constructor({frm, events}) { + this.frm = frm; + this.events = events; + this.make(); + this.bind_events(); + this.set_primary_action(); + } + + open_modal() { + this.dialog.show(); + } + + make() { + this.set_flag(); + + let title = __('Total Amount {0}', + [format_currency(this.frm.doc.grand_total, this.frm.doc.currency)]); + + this.dialog = new frappe.ui.Dialog({ + title: title, + fields: this.get_fields(), + width: 800 + }); + + this.$body = this.dialog.body; + + this.numpad = new NumberPad({ + wrapper: $(this.$body).find('[data-fieldname="numpad"]'), + button_array: [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ['Del', 0, '.'], + ], + onclick: () => { + if(this.fieldname) { + this.dialog.set_value(this.fieldname, this.numpad.get_value()); + } + } + }); + } + + bind_events() { + var me = this; + $(this.dialog.body).find('.input-with-feedback').focusin(function() { + me.numpad.reset_value(); + me.fieldname = $(this).prop('dataset').fieldname; + }); + } + + set_primary_action() { + var me = this; + + this.dialog.set_primary_action(__("Submit"), function() { + me.dialog.hide(); + me.events.submit_form(); + }); + } + + get_fields() { + const me = this; + + let fields = this.frm.doc.payments.map(p => { + return { + fieldtype: 'Currency', + label: __(p.mode_of_payment), + options: me.frm.doc.currency, + fieldname: p.mode_of_payment, + default: p.amount, + onchange: () => { + const value = this.dialog.get_value(this.fieldname); + me.update_payment_value(this.fieldname, value); + } + }; + }); + + fields = fields.concat([ + { + fieldtype: 'Column Break', + }, + { + fieldtype: 'HTML', + fieldname: 'numpad' + }, + { + fieldtype: 'Section Break', + }, + { + fieldtype: 'Currency', + label: __("Write off Amount"), + options: me.frm.doc.currency, + fieldname: "write_off_amount", + default: me.frm.doc.write_off_amount, + onchange: () => { + me.update_cur_frm_value('write_off_amount', () => { + frappe.flags.change_amount = false; + me.update_change_amount(); + }); + } + }, + { + fieldtype: 'Column Break', + }, + { + fieldtype: 'Currency', + label: __("Change Amount"), + options: me.frm.doc.currency, + fieldname: "change_amount", + default: me.frm.doc.change_amount, + onchange: () => { + me.update_cur_frm_value('change_amount', () => { + frappe.flags.write_off_amount = false; + me.update_write_off_amount(); + }); + } + }, + { + fieldtype: 'Section Break', + }, + { + fieldtype: 'Currency', + label: __("Paid Amount"), + options: me.frm.doc.currency, + fieldname: "paid_amount", + default: me.frm.doc.paid_amount, + read_only: 1 + }, + { + fieldtype: 'Column Break', + }, + { + fieldtype: 'Currency', + label: __("Outstanding Amount"), + options: me.frm.doc.currency, + fieldname: "outstanding_amount", + default: me.frm.doc.outstanding_amount, + read_only: 1 + }, + ]); + + return fields; + } + + set_flag() { + frappe.flags.write_off_amount = true; + frappe.flags.change_amount = true; + } + + update_cur_frm_value(fieldname, callback) { + if (frappe.flags[fieldname]) { + const value = this.dialog.get_value(fieldname); + this.frm.set_value(fieldname, value) + .then(() => { + callback(); + }); + } + + frappe.flags[fieldname] = true; + } + + update_payment_value(fieldname, value) { + var me = this; + $.each(this.frm.doc.payments, function(i, data) { + if (__(data.mode_of_payment) == __(fieldname)) { + frappe.model.set_value('Sales Invoice Payment', data.name, 'amount', value) + .then(() => { + me.update_change_amount(); + me.update_write_off_amount(); + }); + } + }); + } + + update_change_amount() { + this.dialog.set_value("change_amount", this.frm.doc.change_amount); + this.show_paid_amount(); + } + + update_write_off_amount() { + this.dialog.set_value("write_off_amount", this.frm.doc.write_off_amount); + } + + show_paid_amount() { + this.dialog.set_value("paid_amount", this.frm.doc.paid_amount); + this.dialog.set_value("outstanding_amount", this.frm.doc.outstanding_amount); + } +} diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.json b/erpnext/selling/page/point_of_sale/point_of_sale.json new file mode 100644 index 00000000000..6d2f5f2f8d5 --- /dev/null +++ b/erpnext/selling/page/point_of_sale/point_of_sale.json @@ -0,0 +1,33 @@ +{ + "content": null, + "creation": "2017-08-07 17:08:56.737947", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2017-09-11 13:49:05.415211", + "modified_by": "Administrator", + "module": "Selling", + "name": "point-of-sale", + "owner": "Administrator", + "page_name": "Point of Sale", + "restrict_to_domain": "Retail", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Sales User" + }, + { + "role": "Sales Manager" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0, + "title": "Point of Sale" +} \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py new file mode 100644 index 00000000000..8ed288b6e9d --- /dev/null +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -0,0 +1,71 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe, json + +@frappe.whitelist() +def get_items(start, page_length, price_list, item_group, search_value=""): + serial_no = "" + batch_no = "" + item_code = search_value + + if search_value: + # search serial no + serial_no_data = frappe.db.get_value('Serial No', search_value, ['name', 'item_code']) + if serial_no_data: + serial_no, item_code = serial_no_data + + if not serial_no: + batch_no_data = frappe.db.get_value('Batch', search_value, ['name', 'item']) + if batch_no_data: + batch_no, item_code = batch_no_data + + 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 + res = frappe.db.sql("""select i.name as item_code, i.item_name, i.image as item_image, + item_det.price_list_rate, item_det.currency + from `tabItem` i LEFT JOIN + (select item_code, price_list_rate, currency from + `tabItem Price` where price_list=%(price_list)s) item_det + ON + (item_det.item_code=i.name or item_det.item_code=i.variant_of) + where + i.disabled = 0 and i.has_variants = 0 + and i.item_group in (select name from `tabItem Group` where lft >= {lft} and rgt <= {rgt}) + and (i.item_code like %(item_code)s + or i.item_name like %(item_code)s or i.barcode like %(item_code)s) + limit {start}, {page_length}""".format(start=start, page_length=page_length, lft=lft, rgt=rgt), + { + 'item_code': '%%%s%%'%(frappe.db.escape(item_code)), + 'price_list': price_list + } , as_dict=1) + + res = { + 'items': res + } + + if serial_no: + res.update({ + 'serial_no': serial_no + }) + + if batch_no: + res.update({ + 'batch_no': batch_no + }) + + return res + +@frappe.whitelist() +def submit_invoice(doc): + if isinstance(doc, basestring): + args = json.loads(doc) + + doc = frappe.new_doc('Sales Invoice') + doc.update(args) + doc.run_method("set_missing_values") + doc.run_method("calculate_taxes_and_totals") + doc.submit() + + return doc diff --git a/erpnext/selling/page/point_of_sale/tests/test_point_of_sale.js b/erpnext/selling/page/point_of_sale/tests/test_point_of_sale.js new file mode 100644 index 00000000000..c70d076c70a --- /dev/null +++ b/erpnext/selling/page/point_of_sale/tests/test_point_of_sale.js @@ -0,0 +1,38 @@ +QUnit.test("test:Point of Sales", function(assert) { + assert.expect(1); + let done = assert.async(); + + frappe.run_serially([ + () => frappe.set_route('point-of-sale'), + () => frappe.timeout(2), + () => frappe.set_control('customer', 'Test Customer 1'), + () => frappe.timeout(0.2), + () => cur_frm.set_value('customer', 'Test Customer 1'), + () => frappe.timeout(2), + () => frappe.click_link('Test Product 2'), + () => frappe.timeout(0.2), + () => frappe.click_element(`.cart-items [data-item-code="Test Product 2"]`), + () => frappe.timeout(0.2), + () => frappe.click_element(`.number-pad [data-value="Rate"]`), + () => frappe.timeout(0.2), + () => frappe.click_element(`.number-pad [data-value="2"]`), + () => frappe.timeout(0.2), + () => frappe.click_element(`.number-pad [data-value="5"]`), + () => frappe.timeout(0.2), + () => frappe.click_element(`.number-pad [data-value="0"]`), + () => frappe.timeout(0.2), + () => frappe.click_element(`.number-pad [data-value="Pay"]`), + () => frappe.timeout(0.2), + () => frappe.click_element(`.frappe-control [data-value="4"]`), + () => frappe.timeout(0.2), + () => frappe.click_element(`.frappe-control [data-value="5"]`), + () => frappe.timeout(0.2), + () => frappe.click_element(`.frappe-control [data-value="0"]`), + () => frappe.timeout(0.2), + () => frappe.click_button('Submit'), + () => frappe.click_button('Yes'), + () => frappe.timeout(3), + () => assert.ok(cur_frm.doc.docstatus==1, "Sales invoice created successfully"), + () => done() + ]); +}); \ No newline at end of file diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 2822dd0fa04..34ecf26316f 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -8,6 +8,16 @@ frappe.ui.form.on("Company", { erpnext.company.setup_queries(frm); }, + company_name: function(frm) { + if(frm.doc.__islocal) { + let parts = frm.doc.company_name.split(); + let abbr = $.map(parts, function (p) { + return p? p.substr(0, 1) : null; + }).join(""); + frm.set_value("abbr", abbr); + } + }, + refresh: function(frm) { if(frm.doc.abbr && !frm.doc.__islocal) { frm.set_df_property("abbr", "read_only", 1); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index c338a81a8ed..ec83705a43b 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -1,26 +1,207 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:company_name", - "beta": 0, - "creation": "2013-04-10 08:35:39", - "custom": 0, - "description": "Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Organization.", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "engine": "InnoDB", + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:company_name", + "beta": 0, + "creation": "2013-04-10 08:35:39", + "custom": 0, + "description": "Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Organization.", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "engine": "InnoDB", "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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": "", + "length": 0, + "no_copy": 0, + "oldfieldtype": "Section Break", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "company_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": "Company", + "length": 0, + "no_copy": 0, + "oldfieldname": "company_name", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "", + "fieldname": "abbr", + "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": "Abbr", + "length": 0, + "no_copy": 0, + "oldfieldname": "abbr", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")", + "fieldname": "change_abbr", + "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": "Change Abbreviation", + "length": 0, + "no_copy": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "cb0", + "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, + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "domain", + "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": "Domain", + "length": 0, + "no_copy": 0, + "options": "Distribution\nManufacturing\nRetail\nServices\nEducation\nHealthcare\nOther", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "details", + "fieldname": "sales_settings", "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, @@ -29,11 +210,11 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "", + "label": "Sales", "length": 0, "no_copy": 0, - "oldfieldtype": "Section Break", "permlevel": 0, + "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, @@ -50,27 +231,26 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "company_name", - "fieldtype": "Data", - "hidden": 0, + "fieldname": "sales_monthly_history", + "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": "Company", + "label": "Sales Monthly History", "length": 0, - "no_copy": 0, - "oldfieldname": "company_name", - "oldfieldtype": "Data", + "no_copy": 1, "permlevel": 0, + "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -81,9 +261,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "description": "", - "fieldname": "abbr", - "fieldtype": "Data", + "fieldname": "monthly_sales_target", + "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -91,42 +270,12 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Abbr", - "length": 0, - "no_copy": 0, - "oldfieldname": "abbr", - "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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")", - "fieldname": "change_abbr", - "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": "Change Abbreviation", + "label": "Monthly Sales Target", "length": 0, "no_copy": 0, + "options": "default_currency", "permlevel": 0, + "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, @@ -143,7 +292,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "cb0", + "fieldname": "column_break_goals", "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, @@ -155,6 +304,7 @@ "length": 0, "no_copy": 0, "permlevel": 0, + "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, @@ -171,8 +321,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "domain", - "fieldtype": "Select", + "fieldname": "total_monthly_sales", + "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -180,14 +330,15 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Domain", + "label": "Total Monthly Sales", "length": 0, - "no_copy": 0, - "options": "Distribution\nManufacturing\nRetail\nServices\nEducation\nOther", + "no_copy": 1, + "options": "default_currency", "permlevel": 0, + "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, @@ -499,1483 +650,1332 @@ "search_index": 0, "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_settings", - "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": "Sales", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "default_settings", + "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": "Accounts Settings", + "length": 0, + "no_copy": 0, + "oldfieldtype": "Section Break", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_monthly_history", - "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": "Sales Monthly History", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_bank_account", + "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": "Default Bank Account", + "length": 0, + "no_copy": 1, + "oldfieldname": "default_bank_account", + "oldfieldtype": "Link", + "options": "Account", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_target", - "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": "Sales Target", - "length": 0, - "no_copy": 1, - "options": "default_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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_cash_account", + "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": "Default Cash Account", + "length": 0, + "no_copy": 1, + "options": "Account", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_goals", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_receivable_account", + "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": "Default Receivable Account", + "length": 0, + "no_copy": 1, + "oldfieldname": "receivables_group", + "oldfieldtype": "Link", + "options": "Account", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_monthly_sales", - "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": "Total Monthly Sales", - "length": 0, - "no_copy": 1, - "options": "default_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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "round_off_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": "Round Off Account", + "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": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_settings", - "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": "Accounts Settings", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "write_off_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": "Write Off Account", + "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": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_bank_account", - "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": "Default Bank Account", - "length": 0, - "no_copy": 1, - "oldfieldname": "default_bank_account", - "oldfieldtype": "Link", - "options": "Account", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "exchange_gain_loss_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": "Exchange Gain / Loss Account", + "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": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_cash_account", - "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": "Default Cash Account", - "length": 0, - "no_copy": 1, - "options": "Account", - "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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_receivable_account", - "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": "Default Receivable Account", - "length": 0, - "no_copy": 1, - "oldfieldname": "receivables_group", - "oldfieldtype": "Link", - "options": "Account", - "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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "round_off_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": "Round Off Account", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "write_off_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": "Write Off Account", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "exchange_gain_loss_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": "Exchange Gain / Loss Account", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break0", - "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, - "oldfieldtype": "Column Break", - "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, - "unique": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break0", + "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, + "oldfieldtype": "Column Break", + "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, + "unique": 0, "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_payable_account", - "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": "Default Payable Account", - "length": 0, - "no_copy": 1, - "oldfieldname": "payables_group", - "oldfieldtype": "Link", - "options": "Account", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_payable_account", + "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": "Default Payable Account", + "length": 0, + "no_copy": 1, + "oldfieldname": "payables_group", + "oldfieldtype": "Link", + "options": "Account", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_expense_account", - "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": "Default Cost of Goods Sold Account", - "length": 0, - "no_copy": 1, - "options": "Account", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_expense_account", + "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": "Default Cost of Goods Sold Account", + "length": 0, + "no_copy": 1, + "options": "Account", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_income_account", - "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": "Default Income Account", - "length": 0, - "no_copy": 1, - "options": "Account", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_income_account", + "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": "Default Income Account", + "length": 0, + "no_copy": 1, + "options": "Account", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_payroll_payable_account", - "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": "Default Payroll Payable Account", - "length": 0, - "no_copy": 1, - "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": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_payroll_payable_account", + "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": "Default Payroll Payable Account", + "length": 0, + "no_copy": 1, + "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": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "round_off_cost_center", - "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": "Round Off Cost Center", - "length": 0, - "no_copy": 0, - "options": "Cost Center", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "round_off_cost_center", + "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": "Round Off Cost Center", + "length": 0, + "no_copy": 0, + "options": "Cost Center", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_22", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_22", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "fieldname": "cost_center", - "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": "Default Cost Center", - "length": 0, - "no_copy": 1, - "options": "Cost Center", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal", + "fieldname": "cost_center", + "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": "Default Cost Center", + "length": 0, + "no_copy": 1, + "options": "Cost Center", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "fieldname": "credit_limit", - "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": "Credit Limit", - "length": 0, - "no_copy": 0, - "oldfieldname": "credit_limit", - "oldfieldtype": "Currency", - "options": "default_currency", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal", + "fieldname": "credit_limit", + "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": "Credit Limit", + "length": 0, + "no_copy": 0, + "oldfieldname": "credit_limit", + "oldfieldtype": "Currency", + "options": "default_currency", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 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, + "allow_bulk_edit": 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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "credit_days_based_on", - "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": "Credit Days Based On", - "length": 0, - "no_copy": 0, - "options": "\nFixed Days\nLast Day of the Next Month", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "credit_days_based_on", + "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": "Credit Days Based On", + "length": 0, + "no_copy": 0, + "options": "\nFixed Days\nLast Day of the Next Month", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:(!doc.__islocal && doc.credit_days_based_on=='Fixed Days')", - "fieldname": "credit_days", - "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": "Credit Days", - "length": 0, - "no_copy": 0, - "oldfieldname": "credit_days", - "oldfieldtype": "Int", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:(!doc.__islocal && doc.credit_days_based_on=='Fixed Days')", + "fieldname": "credit_days", + "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": "Credit Days", + "length": 0, + "no_copy": 0, + "oldfieldname": "credit_days", + "oldfieldtype": "Int", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "fieldname": "auto_accounting_for_stock_settings", - "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": "Stock Settings", - "length": 0, - "no_copy": 0, - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal", + "fieldname": "auto_accounting_for_stock_settings", + "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": "Stock Settings", + "length": 0, + "no_copy": 0, + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fieldname": "enable_perpetual_inventory", - "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 Perpetual Inventory", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "fieldname": "enable_perpetual_inventory", + "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 Perpetual Inventory", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_inventory_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": "Default Inventory Account", - "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": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "default_inventory_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": "Default Inventory Account", + "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": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stock_adjustment_account", - "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": "Stock Adjustment Account", - "length": 0, - "no_copy": 1, - "options": "Account", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "stock_adjustment_account", + "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": "Stock Adjustment Account", + "length": 0, + "no_copy": 1, + "options": "Account", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_32", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_32", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stock_received_but_not_billed", - "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": "Stock Received But Not Billed", - "length": 0, - "no_copy": 1, - "options": "Account", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "stock_received_but_not_billed", + "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": "Stock Received But Not Billed", + "length": 0, + "no_copy": 1, + "options": "Account", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expenses_included_in_valuation", - "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": "Expenses Included In Valuation", - "length": 0, - "no_copy": 1, - "options": "Account", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "expenses_included_in_valuation", + "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": "Expenses Included In Valuation", + "length": 0, + "no_copy": 1, + "options": "Account", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "fixed_asset_depreciation_settings", - "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": "Fixed Asset Depreciation Settings", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "fixed_asset_depreciation_settings", + "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": "Fixed Asset Depreciation Settings", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "accumulated_depreciation_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": "Accumulated Depreciation Account", - "length": 0, - "no_copy": 1, - "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": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "accumulated_depreciation_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": "Accumulated Depreciation Account", + "length": 0, + "no_copy": 1, + "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": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "depreciation_expense_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": "Depreciation Expense Account", - "length": 0, - "no_copy": 1, - "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": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "depreciation_expense_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": "Depreciation Expense Account", + "length": 0, + "no_copy": 1, + "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": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "series_for_depreciation_entry", - "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": "Series for Asset Depreciation Entry (Journal Entry)", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "series_for_depreciation_entry", + "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": "Series for Asset Depreciation Entry (Journal Entry)", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_40", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_40", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "disposal_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": "Gain/Loss Account on Asset Disposal", - "length": 0, - "no_copy": 1, - "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": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "disposal_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": "Gain/Loss Account on Asset Disposal", + "length": 0, + "no_copy": 1, + "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": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "depreciation_cost_center", - "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": "Asset Depreciation Cost Center", - "length": 0, - "no_copy": 1, - "options": "Cost Center", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "depreciation_cost_center", + "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": "Asset Depreciation Cost Center", + "length": 0, + "no_copy": 1, + "options": "Cost Center", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "For reference only.", - "fieldname": "company_info", - "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": "Company Info", - "length": 0, - "no_copy": 0, - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "For reference only.", + "fieldname": "company_info", + "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": "Company Info", + "length": 0, + "no_copy": 0, + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "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, - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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, + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break1", - "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, - "oldfieldtype": "Column Break", - "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, - "unique": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break1", + "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, + "oldfieldtype": "Column Break", + "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, + "unique": 0, "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "phone_no", - "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 No", - "length": 0, - "no_copy": 0, - "oldfieldname": "phone_no", - "oldfieldtype": "Data", - "options": "Phone", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "phone_no", + "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 No", + "length": 0, + "no_copy": 0, + "oldfieldname": "phone_no", + "oldfieldtype": "Data", + "options": "Phone", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "fax", - "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": "Fax", - "length": 0, - "no_copy": 0, - "oldfieldname": "fax", - "oldfieldtype": "Data", - "options": "Phone", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "fax", + "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": "Fax", + "length": 0, + "no_copy": 0, + "oldfieldname": "fax", + "oldfieldtype": "Data", + "options": "Phone", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "email", - "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": "Email", - "length": 0, - "no_copy": 0, - "oldfieldname": "email", - "oldfieldtype": "Data", - "options": "Email", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "email", + "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": "Email", + "length": 0, + "no_copy": 0, + "oldfieldname": "email", + "oldfieldtype": "Data", + "options": "Email", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "website", - "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": "Website", - "length": 0, - "no_copy": 0, - "oldfieldname": "website", - "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": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "website", + "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": "Website", + "length": 0, + "no_copy": 0, + "oldfieldname": "website", + "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": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "registration_info", - "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": "", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "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, - "unique": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "", + "fieldname": "registration_info", + "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": "", + "length": 0, + "no_copy": 0, + "oldfieldtype": "Section Break", + "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, + "unique": 0, "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Company registration numbers for your reference. Tax numbers etc.", - "fieldname": "registration_details", - "fieldtype": "Code", - "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": "Registration Details", - "length": 0, - "no_copy": 0, - "oldfieldname": "registration_details", - "oldfieldtype": "Code", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "Company registration numbers for your reference. Tax numbers etc.", + "fieldname": "registration_details", + "fieldtype": "Code", + "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": "Registration Details", + "length": 0, + "no_copy": 0, + "oldfieldname": "registration_details", + "oldfieldtype": "Code", + "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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "delete_company_transactions", - "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": "Delete Company Transactions", - "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, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "delete_company_transactions", + "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": "Delete Company Transactions", + "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 } ], @@ -1991,158 +1991,158 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-08-03 16:17:31.206886", + "modified": "2017-08-31 11:48:56.278568", "modified_by": "Administrator", "module": "Setup", "name": "Company", "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Accounts User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Employee", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "Employee", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "Sales User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "Purchase User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Stock User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "Stock User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Projects User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "Projects User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 1, - "sort_order": "ASC", - "track_changes": 1, + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 1, + "sort_order": "ASC", + "track_changes": 1, "track_seen": 0 -} \ No newline at end of file +} diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 849e6f9886f..0459e84b252 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -76,7 +76,10 @@ class Company(Document): self.create_default_accounts() self.create_default_warehouses() - self.install_country_fixtures() + if cint(frappe.db.get_single_value('System Settings', 'setup_complete')): + # In the case of setup, fixtures should be installed after setup_success + # This also prevents db commits before setup is successful + install_country_fixtures(self.name) if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": self.name}): self.create_default_cost_center() @@ -95,12 +98,6 @@ class Company(Document): frappe.clear_cache() - def install_country_fixtures(self): - path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(self.country)) - if os.path.exists(path.encode("utf-8")): - frappe.get_attr("erpnext.regional.{0}.setup.setup" - .format(self.country.lower()))(self) - def create_default_warehouses(self): for wh_detail in [ {"warehouse_name": _("All Warehouses"), "is_group": 1}, @@ -281,6 +278,8 @@ class Company(Document): frappe.db.sql("""update `tabSingles` set value="" where doctype='Global Defaults' and field='default_company' and value=%s""", self.name) + # delete mode of payment account + frappe.db.sql("delete from `tabMode of Payment Account` where company=%s", self.name) @frappe.whitelist() @@ -324,6 +323,13 @@ def get_name_with_abbr(name, company): return " - ".join(parts) +def install_country_fixtures(company): + company_doc = frappe.get_doc("Company", company) + path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(company_doc.country)) + if os.path.exists(path.encode("utf-8")): + frappe.get_attr("erpnext.regional.{0}.setup.setup" + .format(company_doc.country.lower()))(company_doc) + def update_company_current_month_sales(company): current_month_year = formatdate(today(), "MM-yyyy") diff --git a/erpnext/setup/doctype/company/company_dashboard.py b/erpnext/setup/doctype/company/company_dashboard.py index da7f2b582a7..7526dd6c547 100644 --- a/erpnext/setup/doctype/company/company_dashboard.py +++ b/erpnext/setup/doctype/company/company_dashboard.py @@ -8,8 +8,8 @@ def get_data(): 'graph': True, 'graph_method': "frappe.utils.goal.get_monthly_goal_graph_data", 'graph_method_args': { - 'title': 'Sales', - 'goal_value_field': 'sales_target', + 'title': _('Sales'), + 'goal_value_field': 'monthly_sales_target', 'goal_total_field': 'total_monthly_sales', 'goal_history_field': 'sales_monthly_history', 'goal_doctype': 'Sales Invoice', diff --git a/erpnext/setup/doctype/company/tests/test_company.js b/erpnext/setup/doctype/company/tests/test_company.js index afa3296e896..8c0b609775a 100644 --- a/erpnext/setup/doctype/company/tests/test_company.js +++ b/erpnext/setup/doctype/company/tests/test_company.js @@ -1,7 +1,7 @@ QUnit.module('setup'); QUnit.test("Test: Company [SetUp]", function (assert) { - assert.expect(1); + assert.expect(2); let done = assert.async(); frappe.run_serially([ @@ -16,8 +16,10 @@ QUnit.test("Test: Company [SetUp]", function (assert) { // save form () => cur_frm.save(), () => frappe.timeout(1), - () => assert.equal("Test Company", cur_frm.doc.company_name, - 'name of company correctly saved'), + () => assert.equal("Debtors - TC", cur_frm.doc.default_receivable_account, + 'chart of acounts created'), + () => assert.equal("Main - TC", cur_frm.doc.cost_center, + 'chart of cost centers created'), () => done() ]); }); \ No newline at end of file diff --git a/erpnext/setup/doctype/company/tests/test_company_production.js b/erpnext/setup/doctype/company/tests/test_company_production.js index b73af1dd98b..37a3d744d06 100644 --- a/erpnext/setup/doctype/company/tests/test_company_production.js +++ b/erpnext/setup/doctype/company/tests/test_company_production.js @@ -8,7 +8,7 @@ QUnit.test("Test: Company", function (assert) { () => frappe.set_route("List", "Company"), () => frappe.new_doc("Company"), () => frappe.timeout(1), - () => cur_frm.set_value("company_name", "Razer Blade"), + () => cur_frm.set_value("company_name", "For Testing"), () => cur_frm.set_value("abbr", "RB"), () => cur_frm.set_value("default_currency", "INR"), () => cur_frm.save(), diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.py b/erpnext/setup/doctype/sales_partner/sales_partner.py index e6b26eee555..675f9ca560b 100644 --- a/erpnext/setup/doctype/sales_partner/sales_partner.py +++ b/erpnext/setup/doctype/sales_partner/sales_partner.py @@ -16,7 +16,7 @@ class SalesPartner(WebsiteGenerator): def onload(self): """Load address and contacts in `__onload`""" - load_address_and_contact(self, "sales_partner") + load_address_and_contact(self) def autoname(self): self.name = self.partner_name diff --git a/erpnext/setup/doctype/setup_progress/__init__.py b/erpnext/setup/doctype/setup_progress/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/setup/doctype/setup_progress/setup_progress.js b/erpnext/setup/doctype/setup_progress/setup_progress.js new file mode 100644 index 00000000000..5c78bd50758 --- /dev/null +++ b/erpnext/setup/doctype/setup_progress/setup_progress.js @@ -0,0 +1,8 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Setup Progress', { + refresh: function() { + + } +}); diff --git a/erpnext/setup/doctype/setup_progress/setup_progress.json b/erpnext/setup/doctype/setup_progress/setup_progress.json new file mode 100644 index 00000000000..09072d4665b --- /dev/null +++ b/erpnext/setup/doctype/setup_progress/setup_progress.json @@ -0,0 +1,123 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2017-08-27 21:01:42.032109", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "actions_sb", + "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": "Actions", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "actions", + "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": "Actions", + "length": 0, + "no_copy": 0, + "options": "Setup Progress Action", + "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 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 1, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2017-09-21 11:52:56.106659", + "modified_by": "Administrator", + "module": "Setup", + "name": "Setup Progress", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "All", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 1, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/setup/doctype/setup_progress/setup_progress.py b/erpnext/setup/doctype/setup_progress/setup_progress.py new file mode 100644 index 00000000000..9187eb79962 --- /dev/null +++ b/erpnext/setup/doctype/setup_progress/setup_progress.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, json +from frappe.model.document import Document + +class SetupProgress(Document): + pass + +def get_setup_progress(): + if not getattr(frappe.local, "setup_progress", None): + frappe.local.setup_progress = frappe.get_doc("Setup Progress", "Setup Progress") + + return frappe.local.setup_progress + +def get_action_completed_state(action_name): + return [d.is_completed for d in get_setup_progress().actions + if d.action_name == action_name][0] + +def update_action_completed_state(action_name): + action_table_doc = [d for d in get_setup_progress().actions + if d.action_name == action_name][0] + update_action(action_table_doc) + +def update_action(doc): + doctype = doc.action_doctype + docname = doc.action_document + field = doc.action_field + + if not doc.is_completed: + if doc.min_doc_count: + if frappe.db.count(doctype) >= doc.min_doc_count: + doc.is_completed = 1 + doc.save() + if docname and field: + d = frappe.get_doc(doctype, docname) + if d.get(field): + doc.is_completed = 1 + doc.save() + +def update_domain_actions(domain): + for d in get_setup_progress().actions: + domains = json.loads(d.domains) + if domains == [] or domain in domains: + update_action(d) + +def get_domain_actions_state(domain): + state = {} + for d in get_setup_progress().actions: + domains = json.loads(d.domains) + if domains == [] or domain in domains: + state[d.action_name] = d.is_completed + return state + +@frappe.whitelist() +def set_action_completed_state(action_name): + action_table_doc = [d for d in get_setup_progress().actions + if d.action_name == action_name][0] + action_table_doc.is_completed = 1 + action_table_doc.save() diff --git a/erpnext/setup/doctype/setup_progress/test_setup_progress.js b/erpnext/setup/doctype/setup_progress/test_setup_progress.js new file mode 100644 index 00000000000..9e84e0cb154 --- /dev/null +++ b/erpnext/setup/doctype/setup_progress/test_setup_progress.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Setup Progress", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Setup Progress + () => frappe.tests.make('Setup Progress', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/setup/doctype/setup_progress/test_setup_progress.py b/erpnext/setup/doctype/setup_progress/test_setup_progress.py new file mode 100644 index 00000000000..89262191436 --- /dev/null +++ b/erpnext/setup/doctype/setup_progress/test_setup_progress.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import unittest + +class TestSetupProgress(unittest.TestCase): + pass diff --git a/erpnext/setup/doctype/setup_progress_action/__init__.py b/erpnext/setup/doctype/setup_progress_action/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/setup/doctype/setup_progress_action/setup_progress_action.json b/erpnext/setup/doctype/setup_progress_action/setup_progress_action.json new file mode 100644 index 00000000000..e9abcbcd1ac --- /dev/null +++ b/erpnext/setup/doctype/setup_progress_action/setup_progress_action.json @@ -0,0 +1,253 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2017-08-27 21:00:40.715360", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "action_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": "Action 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "action_doctype", + "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": "Action 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": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "action_document", + "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": "Action Document", + "length": 0, + "no_copy": 0, + "options": "action_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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "action_field", + "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": "Action Field", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "min_doc_count", + "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": "Min Doc Count", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "domains", + "fieldtype": "Code", + "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": "Domains", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "is_completed", + "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": "Is Completed", + "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 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 1, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2017-09-01 14:34:59.685730", + "modified_by": "Administrator", + "module": "Setup", + "name": "Setup Progress Action", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 1, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/setup/doctype/setup_progress_action/setup_progress_action.py b/erpnext/setup/doctype/setup_progress_action/setup_progress_action.py new file mode 100644 index 00000000000..24af94347e5 --- /dev/null +++ b/erpnext/setup/doctype/setup_progress_action/setup_progress_action.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class SetupProgressAction(Document): + pass diff --git a/erpnext/setup/setup_wizard/domainify.py b/erpnext/setup/setup_wizard/domainify.py index 7f333cc8150..ddb2a80afb1 100644 --- a/erpnext/setup/setup_wizard/domainify.py +++ b/erpnext/setup/setup_wizard/domainify.py @@ -12,7 +12,8 @@ def get_domain(domain): 'desktop_icons': ['Item', 'BOM', 'Customer', 'Supplier', 'Sales Order', 'Production Order', 'Stock Entry', 'Purchase Order', 'Task', 'Buying', 'Selling', 'Accounts', 'HR', 'ToDo'], - 'remove_roles': ['Academics User', 'Instructor'], + 'remove_roles': ['Academics User', 'Instructor', 'Physician', 'Nursing User', + 'Laboratory user', 'LabTest Approver', 'Healthcare Administrator'], 'properties': [ {'doctype': 'Item', 'fieldname': 'manufacturing', 'property': 'collapsible_depends_on', 'value': 'is_stock_item'}, ], @@ -25,7 +26,9 @@ def get_domain(domain): 'Retail': { 'desktop_icons': ['POS', 'Item', 'Customer', 'Sales Invoice', 'Purchase Order', 'Warranty Claim', 'Accounts', 'Task', 'Buying', 'ToDo'], - 'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Academics User', 'Instructor'], + 'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Academics User', 'Instructor', + 'Physician', 'Nursing User', 'Laboratory user', + 'LabTest Approver', 'Healthcare Administrator'], 'properties': [ {'doctype': 'Item', 'fieldname': 'manufacturing', 'property': 'hidden', 'value': 1}, {'doctype': 'Customer', 'fieldname': 'credit_limit_section', 'property': 'hidden', 'value': 1}, @@ -39,7 +42,9 @@ def get_domain(domain): 'Distribution': { 'desktop_icons': ['Item', 'Customer', 'Supplier', 'Lead', 'Sales Order', 'Task', 'Sales Invoice', 'CRM', 'Selling', 'Buying', 'Stock', 'Accounts', 'HR', 'ToDo'], - 'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Academics User', 'Instructor'], + 'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Academics User', 'Instructor', + 'Physician', 'Nursing User', 'Laboratory user', + 'LabTest Approver', 'Healthcare Administrator'], 'set_value': [ ['Stock Settings', None, 'show_barcode_field', 1] ], @@ -49,7 +54,9 @@ def get_domain(domain): 'Services': { 'desktop_icons': ['Project', 'Timesheet', 'Customer', 'Sales Order', 'Sales Invoice', 'Lead', 'Opportunity', 'Task', 'Expense Claim', 'Employee', 'HR', 'ToDo'], - 'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Academics User', 'Instructor'], + 'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Academics User', 'Instructor', + 'Physician', 'Nursing User', 'Laboratory user', + 'LabTest Approver', 'Healthcare Administrator'], 'properties': [ {'doctype': 'Item', 'fieldname': 'is_stock_item', 'property': 'default', 'value': 0}, ], @@ -66,6 +73,13 @@ def get_domain(domain): 'Student', 'Projects User', 'Instructor'], 'default_portal_role': 'Student' }, + 'Healthcare': { + 'desktop_icons': ['Patient', 'Patient Appointment', 'Consultation', 'Lab Test', 'Healthcare', + 'Accounts', 'Buying', 'Stock', 'HR', 'ToDo'], + 'remove_roles': ['Manufacturing User', 'Manufacturing Manager', 'Projects User', 'Projects Manager', + 'Academics User', 'Instructor'], + 'default_portal_role': 'Patient' + }, } if not domain in data: raise 'Invalid Domain {0}'.format(domain) diff --git a/erpnext/setup/setup_wizard/healthcare.py b/erpnext/setup/setup_wizard/healthcare.py new file mode 100644 index 00000000000..ebc644e87b7 --- /dev/null +++ b/erpnext/setup/setup_wizard/healthcare.py @@ -0,0 +1,281 @@ +from __future__ import unicode_literals +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields + +from frappe import _ + +def setup_healthcare(): + create_medical_departments() + create_antibiotics() + create_test_uom() + create_duration() + create_dosage() + create_healthcare_item_groups() + create_lab_test_items() + create_lab_test_template() + create_sensitivity() + make_custom_fields() + +def make_custom_fields(): + custom_fields = { + 'Sales Invoice': [ + dict(fieldname='appointment', label='Patient Appointment', + fieldtype='Link', options='Patient Appointment', + insert_after='customer') + ] + } + + create_custom_fields(custom_fields) + + +def create_medical_departments(): + depts = ["Accident and emergency care" ,"Anaesthetics", "Biochemistry", "Cardiology", "Dermatology", + "Diagnostic imaging", "ENT", "Gastroenterology", "General Surgery", "Gynaecology", + "Haematology", "Maternity", "Microbiology", "Nephrology", "Neurology", "Oncology", + "Orthopaedics", "Pathology", "Physiotherapy", "Rheumatology", "Serology", "Urology"] + for d in depts: + mediacal_department = frappe.new_doc("Medical Department") + mediacal_department.department = d + try: + mediacal_department.save() + except frappe.DuplicateEntryError: + pass + +def create_antibiotics(): + abt = ["Amoxicillin", "Ampicillin", "Bacampicillin", "Carbenicillin", "Cloxacillin", "Dicloxacillin", + "Flucloxacillin", "Mezlocillin", "Nafcillin", "Oxacillin", "Penicillin G", "Penicillin V", + "Piperacillin", "Pivampicillin", "Pivmecillinam", "Ticarcillin", "Cefacetrile (cephacetrile)", + "Cefadroxil (cefadroxyl)", "Cefalexin (cephalexin)", "Cefaloglycin (cephaloglycin)", + "Cefalonium (cephalonium)", "Cefaloridine (cephaloradine)", "Cefalotin (cephalothin)", + "Cefapirin (cephapirin)", "Cefatrizine", "Cefazaflur", "Cefazedone", "Cefazolin (cephazolin)", + "Cefradine (cephradine)", "Cefroxadine", "Ceftezole", "Cefaclor", "Cefamandole", "Cefmetazole", + "Cefonicid", "Cefotetan", "Cefoxitin", "Cefprozil (cefproxil)", "Cefuroxime", "Cefuzonam", + "Cefcapene", "Cefdaloxime", "Cefdinir", "Cefditoren", "Cefetamet", "Cefixime", "Cefmenoxime", + "Cefodizime", "Cefotaxime", "Cefpimizole", "Cefpodoxime", "Cefteram", "Ceftibuten", "Ceftiofur", + "Ceftiolene", "Ceftizoxime", "Ceftriaxone", "Cefoperazone", "Ceftazidime", "Cefclidine", "Cefepime", + "Cefluprenam", "Cefoselis", "Cefozopran", "Cefpirome", "Cefquinome", "Ceftobiprole", "Ceftaroline", + "Cefaclomezine","Cefaloram", "Cefaparole", "Cefcanel", "Cefedrolor", "Cefempidone", "Cefetrizole", + "Cefivitril", "Cefmatilen", "Cefmepidium", "Cefovecin", "Cefoxazole", "Cefrotil", "Cefsumide", + "Cefuracetime", "Ceftioxide", "Ceftazidime/Avibactam", "Ceftolozane/Tazobactam", "Aztreonam", + "Imipenem", "Imipenem/cilastatin", "Doripenem", "Meropenem", "Ertapenem", "Azithromycin", + "Erythromycin", "Clarithromycin", "Dirithromycin", "Roxithromycin", "Telithromycin", "Clindamycin", + "Lincomycin", "Pristinamycin", "Quinupristin/dalfopristin", "Amikacin", "Gentamicin", "Kanamycin", + "Neomycin", "Netilmicin", "Paromomycin", "Streptomycin", "Tobramycin", "Flumequine", "Nalidixic acid", + "Oxolinic acid", "Piromidic acid", "Pipemidic acid", "Rosoxacin", "Ciprofloxacin", "Enoxacin", + "Lomefloxacin", "Nadifloxacin", "Norfloxacin", "Ofloxacin", "Pefloxacin", "Rufloxacin", "Balofloxacin", + "Gatifloxacin", "Grepafloxacin", "Levofloxacin", "Moxifloxacin", "Pazufloxacin", "Sparfloxacin", + "Temafloxacin", "Tosufloxacin", "Besifloxacin", "Clinafloxacin", "Gemifloxacin", + "Sitafloxacin", "Trovafloxacin", "Prulifloxacin", "Sulfamethizole", "Sulfamethoxazole", + "Sulfisoxazole", "Trimethoprim-Sulfamethoxazole", "Demeclocycline", "Doxycycline", "Minocycline", + "Oxytetracycline", "Tetracycline", "Tigecycline", "Chloramphenicol", "Metronidazole", + "Tinidazole", "Nitrofurantoin", "Vancomycin", "Teicoplanin", "Telavancin", "Linezolid", + "Cycloserine 2", "Rifampin", "Rifabutin", "Rifapentine", "Rifalazil", "Bacitracin", "Polymyxin B", + "Viomycin", "Capreomycin"] + for a in abt: + antibiotic = frappe.new_doc("Antibiotic") + antibiotic.antibiotic_name = a + try: + antibiotic.save() + except frappe.DuplicateEntryError: + pass + +def create_test_uom(): + records = [ + {"doctype": "Lab Test UOM", "name": "umol/L", "test_uom": "umol/L", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "mg/L", "test_uom": "mg/L", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "mg / dl", "test_uom": "mg / dl", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "pg / ml", "test_uom": "pg / ml", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "U/ml", "test_uom": "U/ml", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "/HPF", "test_uom": "/HPF", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "Million Cells / cumm", "test_uom": "Million Cells / cumm", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "Lakhs Cells / cumm", "test_uom": "Lakhs Cells / cumm", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "U / L", "test_uom": "U / L", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "g / L", "test_uom": "g / L", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "IU / ml", "test_uom": "IU / ml", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "gm %", "test_uom": "gm %", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "Microgram", "test_uom": "Microgram", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "Micron", "test_uom": "Micron", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "Cells / cumm", "test_uom": "Cells / cumm", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "%", "test_uom": "%", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "mm / dl", "test_uom": "mm / dl", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "mm / hr", "test_uom": "mm / hr", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "ulU / ml", "test_uom": "ulU / ml", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "ng / ml", "test_uom": "ng / ml", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "ng / dl", "test_uom": "ng / dl", "uom_description": None }, + {"doctype": "Lab Test UOM", "name": "ug / dl", "test_uom": "ug / dl", "uom_description": None } + ] + + insert_record(records) + +def create_duration(): + records = [ + {"doctype": "Prescription Duration", "name": "3 Month", "number": "3", "period": "Month" }, + {"doctype": "Prescription Duration", "name": "2 Month", "number": "2", "period": "Month" }, + {"doctype": "Prescription Duration", "name": "1 Month", "number": "1", "period": "Month" }, + {"doctype": "Prescription Duration", "name": "12 Hour", "number": "12", "period": "Hour" }, + {"doctype": "Prescription Duration", "name": "11 Hour", "number": "11", "period": "Hour" }, + {"doctype": "Prescription Duration", "name": "10 Hour", "number": "10", "period": "Hour" }, + {"doctype": "Prescription Duration", "name": "9 Hour", "number": "9", "period": "Hour" }, + {"doctype": "Prescription Duration", "name": "8 Hour", "number": "8", "period": "Hour" }, + {"doctype": "Prescription Duration", "name": "7 Hour", "number": "7", "period": "Hour" }, + {"doctype": "Prescription Duration", "name": "6 Hour", "number": "6", "period": "Hour" }, + {"doctype": "Prescription Duration", "name": "5 Hour", "number": "5", "period": "Hour" }, + {"doctype": "Prescription Duration", "name": "4 Hour", "number": "4", "period": "Hour" }, + {"doctype": "Prescription Duration", "name": "3 Hour", "number": "3", "period": "Hour" }, + {"doctype": "Prescription Duration", "name": "2 Hour", "number": "2", "period": "Hour" }, + {"doctype": "Prescription Duration", "name": "1 Hour", "number": "1", "period": "Hour" }, + {"doctype": "Prescription Duration", "name": "5 Week", "number": "5", "period": "Week" }, + {"doctype": "Prescription Duration", "name": "4 Week", "number": "4", "period": "Week" }, + {"doctype": "Prescription Duration", "name": "3 Week", "number": "3", "period": "Week" }, + {"doctype": "Prescription Duration", "name": "2 Week", "number": "2", "period": "Week" }, + {"doctype": "Prescription Duration", "name": "1 Week", "number": "1", "period": "Week" }, + {"doctype": "Prescription Duration", "name": "6 Day", "number": "6", "period": "Day" }, + {"doctype": "Prescription Duration", "name": "5 Day", "number": "5", "period": "Day" }, + {"doctype": "Prescription Duration", "name": "4 Day", "number": "4", "period": "Day" }, + {"doctype": "Prescription Duration", "name": "3 Day", "number": "3", "period": "Day" }, + {"doctype": "Prescription Duration", "name": "2 Day", "number": "2", "period": "Day" }, + {"doctype": "Prescription Duration", "name": "1 Day", "number": "1", "period": "Day" } + ] + insert_record(records) + +def create_dosage(): + records = [ + {"doctype": "Prescription Dosage", "name": "1-1-1-1", "dosage": "1-1-1-1","dosage_strength": + [{"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "13:00:00"},{"strength": "1.0","strength_time": "17:00:00"},{"strength": "1.0","strength_time": "21:00:00"}] + }, + {"doctype": "Prescription Dosage", "name": "0-0-1", "dosage": "0-0-1","dosage_strength": + [{"strength": "1.0","strength_time": "21:00:00"}] + }, + {"doctype": "Prescription Dosage", "name": "1-0-0", "dosage": "1-0-0","dosage_strength": + [{"strength": "1.0","strength_time": "9:00:00"}] + }, + {"doctype": "Prescription Dosage", "name": "0-1-0", "dosage": "0-1-0","dosage_strength": + [{"strength": "1.0","strength_time": "14:00:00"}] + }, + {"doctype": "Prescription Dosage", "name": "1-1-1", "dosage": "1-1-1","dosage_strength": + [{"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "14:00:00"},{"strength": "1.0","strength_time": "21:00:00"}] + }, + {"doctype": "Prescription Dosage", "name": "1-0-1", "dosage": "1-0-1","dosage_strength": + [{"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "21:00:00"}] + }, + {"doctype": "Prescription Dosage", "name": "Once Bedtime", "dosage": "Once Bedtime","dosage_strength": + [{"strength": "1.0","strength_time": "21:00:00"}] + }, + {"doctype": "Prescription Dosage", "name": "5 times a day", "dosage": "5 times a day","dosage_strength": + [{"strength": "1.0","strength_time": "5:00:00"}, {"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "13:00:00"},{"strength": "1.0","strength_time": "17:00:00"},{"strength": "1.0","strength_time": "21:00:00"}] + }, + {"doctype": "Prescription Dosage", "name": "QID", "dosage": "QID","dosage_strength": + [{"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "13:00:00"},{"strength": "1.0","strength_time": "17:00:00"},{"strength": "1.0","strength_time": "21:00:00"}] + }, + {"doctype": "Prescription Dosage", "name": "TID", "dosage": "TID","dosage_strength": + [{"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "14:00:00"},{"strength": "1.0","strength_time": "21:00:00"}] + }, + {"doctype": "Prescription Dosage", "name": "BID", "dosage": "BID","dosage_strength": + [{"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "21:00:00"}] + }, + {"doctype": "Prescription Dosage", "name": "Once Daily", "dosage": "Once Daily","dosage_strength": + [{"strength": "1.0","strength_time": "9:00:00"}] + } + ] + insert_record(records) + +def create_healthcare_item_groups(): + records = [ + {'doctype': 'Item Group', 'item_group_name': _('Laboratory'), + 'is_group': 0, 'parent_item_group': _('All Item Groups') }, + {'doctype': 'Item Group', 'item_group_name': _('Drug'), + 'is_group': 0, 'parent_item_group': _('All Item Groups') } + ] + insert_record(records) + +def create_lab_test_items(): + records = [ + {"doctype": "Item", "item_code": "MCH", "item_name": "MCH", "item_group": "Laboratory", + "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + {"doctype": "Item", "item_code": "LDL", "item_name": "LDL", "item_group": "Laboratory", + "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + {"doctype": "Item", "item_code": "GTT", "item_name": "GTT", "item_group": "Laboratory", + "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + {"doctype": "Item", "item_code": "HDL", "item_name": "HDL", "item_group": "Laboratory", + "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + {"doctype": "Item", "item_code": "BILT", "item_name": "BILT", "item_group": "Laboratory", + "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + {"doctype": "Item", "item_code": "BILD", "item_name": "BILD", "item_group": "Laboratory", + "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + {"doctype": "Item", "item_code": "BP", "item_name": "BP", "item_group": "Laboratory", + "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + {"doctype": "Item", "item_code": "BS", "item_name": "BS", "item_group": "Laboratory", + "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1} + ] + insert_record(records) + +def create_lab_test_template(): + records = [ + {"doctype": "Lab Test Template", "name": "MCH","test_name": "MCH","test_code": "MCH", + "test_group": "Laboratory","department": "Haematology","item": "MCH", + "test_template_type": "Single","is_billable": 1,"test_rate": 0.0,"test_uom": "Microgram", + "test_normal_range": "27 - 32 Microgram", + "sensitivity": 0,"test_description": "Mean Corpuscular Hemoglobin"}, + {"doctype": "Lab Test Template", "name": "LDL","test_name": "LDL (Serum)","test_code": "LDL", + "test_group": "Laboratory","department": "Biochemistry", + "item": "LDL","test_template_type": "Single", + "is_billable": 1,"test_rate": 0.0,"test_uom": "mg / dl","test_normal_range": "70 - 160 mg/dlLow-density Lipoprotein (LDL)", + "sensitivity": 0,"test_description": "Low-density Lipoprotein (LDL)"}, + {"doctype": "Lab Test Template", "name": "GTT","test_name": "GTT","test_code": "GTT", + "test_group": "Laboratory","department": "Haematology", + "item": "GTT","test_template_type": "Single", + "is_billable": 1,"test_rate": 0.0,"test_uom": "mg / dl","test_normal_range": "Less than 85 mg/dl", + "sensitivity": 0,"test_description": "Glucose Tolerance Test"}, + {"doctype": "Lab Test Template", "name": "HDL","test_name": "HDL (Serum)","test_code": "HDL", + "test_group": "Laboratory","department": "Biochemistry", + "item": "HDL","test_template_type": "Single", + "is_billable": 1,"test_rate": 0.0,"test_uom": "mg / dl","test_normal_range": "35 - 65 mg/dl", + "sensitivity": 0,"test_description": "High-density Lipoprotein (HDL)"}, + {"doctype": "Lab Test Template", "name": "BILT","test_name": "Bilirubin Total","test_code": "BILT", + "test_group": "Laboratory","department": "Biochemistry", + "item": "BILT","test_template_type": "Single", + "is_billable": 1,"test_rate": 0.0,"test_uom": "mg / dl","test_normal_range": "0.2 - 1.2 mg / dl", + "sensitivity": 0,"test_description": "Bilirubin Total"}, + {"doctype": "Lab Test Template", "name": "BILD","test_name": "Bilirubin Direct","test_code": "BILD", + "test_group": "Laboratory","department": "Biochemistry", + "item": "BILD","test_template_type": "Single", + "is_billable": 1,"test_rate": 0.0,"test_uom": "mg / dl","test_normal_range": "0.4 mg / dl", + "sensitivity": 0,"test_description": "Bilirubin Direct"}, + + {"doctype": "Lab Test Template", "name": "BP","test_name": "Bile Pigment","test_code": "BP", + "test_group": "Laboratory","department": "Pathology", + "item": "BP","test_template_type": "Single", + "is_billable": 1,"test_rate": 0.0,"test_uom": "","test_normal_range": "", + "sensitivity": 0,"test_description": "Bile Pigment"}, + {"doctype": "Lab Test Template", "name": "BS","test_name": "Bile Salt","test_code": "BS", + "test_group": "Laboratory","department": "Pathology", + "item": "BS","test_template_type": "Single", + "is_billable": 1,"test_rate": 0.0,"test_uom": "","test_normal_range": "", + "sensitivity": 0,"test_description": "Bile Salt"} + ] + insert_record(records) + +def create_sensitivity(): + records = [ + {"doctype": "Sensitivity", "sensitivity": "Low Sensitivity"}, + {"doctype": "Sensitivity", "sensitivity": "High Sensitivity"}, + {"doctype": "Sensitivity", "sensitivity": "Moderate Sensitivity"}, + {"doctype": "Sensitivity", "sensitivity": "Susceptible"}, + {"doctype": "Sensitivity", "sensitivity": "Resistant"}, + {"doctype": "Sensitivity", "sensitivity": "Intermediate"} + ] + insert_record(records) + +def insert_record(records): + for r in records: + doc = frappe.new_doc(r.get("doctype")) + doc.update(r) + try: + doc.insert(ignore_permissions=True) + except frappe.DuplicateEntryError, e: + # pass DuplicateEntryError and continue + if e.args and e.args[0]==doc.doctype and e.args[1]==doc.name: + # make sure DuplicateEntryError is for the exact same doc and not a related doc + pass + else: + raise diff --git a/erpnext/setup/setup_wizard/install_fixtures.py b/erpnext/setup/setup_wizard/install_fixtures.py index ea6da04641a..e6a4a81959e 100644 --- a/erpnext/setup/setup_wizard/install_fixtures.py +++ b/erpnext/setup/setup_wizard/install_fixtures.py @@ -19,6 +19,33 @@ def install(country=None): { 'doctype': 'Domain', 'domain': _('Retail')}, { 'doctype': 'Domain', 'domain': _('Services')}, { 'doctype': 'Domain', 'domain': _('Education')}, + { 'doctype': 'Domain', 'domain': _('Healthcare')}, + + # Setup Progress + {'doctype': "Setup Progress", "actions": [ + {"action_name": "Add Company", "action_doctype": "Company", "min_doc_count": 1, "is_completed": 1, + "domains": '[]' }, + {"action_name": "Set Sales Target", "action_doctype": "Company", "min_doc_count": 99, + "action_document": frappe.defaults.get_defaults().get("company") or '', + "action_field": "monthly_sales_target", "is_completed": 0, + "domains": '["Manufacturing", "Services", "Retail", "Distribution"]' }, + {"action_name": "Add Customers", "action_doctype": "Customer", "min_doc_count": 1, "is_completed": 0, + "domains": '["Manufacturing", "Services", "Retail", "Distribution"]' }, + {"action_name": "Add Suppliers", "action_doctype": "Supplier", "min_doc_count": 1, "is_completed": 0, + "domains": '["Manufacturing", "Services", "Retail", "Distribution"]' }, + {"action_name": "Add Products", "action_doctype": "Item", "min_doc_count": 1, "is_completed": 0, + "domains": '["Manufacturing", "Services", "Retail", "Distribution"]' }, + {"action_name": "Add Programs", "action_doctype": "Program", "min_doc_count": 1, "is_completed": 0, + "domains": '["Education"]' }, + {"action_name": "Add Instructors", "action_doctype": "Instructor", "min_doc_count": 1, "is_completed": 0, + "domains": '["Education"]' }, + {"action_name": "Add Courses", "action_doctype": "Course", "min_doc_count": 1, "is_completed": 0, + "domains": '["Education"]' }, + {"action_name": "Add Rooms", "action_doctype": "Room", "min_doc_count": 1, "is_completed": 0, + "domains": '["Education"]' }, + {"action_name": "Add Users", "action_doctype": "User", "min_doc_count": 4, "is_completed": 0, + "domains": '[]' } + ]}, # address template {'doctype':"Address Template", "country": country}, @@ -240,3 +267,8 @@ def install(country=None): pass else: raise + + # set default customer group and territory + selling_settings = frappe.get_doc("Selling Settings") + selling_settings.set_default_customer_group_and_territory() + selling_settings.save() diff --git a/erpnext/setup/setup_wizard/sample_data.py b/erpnext/setup/setup_wizard/sample_data.py index cfc6726d1ca..bc26e09677c 100644 --- a/erpnext/setup/setup_wizard/sample_data.py +++ b/erpnext/setup/setup_wizard/sample_data.py @@ -10,26 +10,27 @@ import random, os, json from frappe import _ from markdown2 import markdown -def make_sample_data(args): +def make_sample_data(domain, make_dependent = False): """Create a few opportunities, quotes, material requests, issues, todos, projects to help the user get started""" - items = frappe.get_all("Item", {'is_sales_item': 1}) - customers = frappe.get_all("Customer") - warehouses = frappe.get_all("Warehouse") + if make_dependent: + items = frappe.get_all("Item", {'is_sales_item': 1}) + customers = frappe.get_all("Customer") + warehouses = frappe.get_all("Warehouse") - if items and customers: - for i in range(3): - customer = random.choice(customers).name - make_opportunity(items, customer) - make_quote(items, customer) + if items and customers: + for i in range(3): + customer = random.choice(customers).name + make_opportunity(items, customer) + make_quote(items, customer) - make_projects(args.get('domain')) + if items and warehouses: + make_material_request(frappe.get_all("Item")) + + make_projects(domain) import_email_alert() - if items and warehouses: - make_material_request(frappe.get_all("Item")) - frappe.db.commit() def make_opportunity(items, customer): diff --git a/erpnext/setup/setup_wizard/setup_wizard.py b/erpnext/setup/setup_wizard/setup_wizard.py index c40ad55f8bd..d3e4a084f53 100644 --- a/erpnext/setup/setup_wizard/setup_wizard.py +++ b/erpnext/setup/setup_wizard/setup_wizard.py @@ -10,11 +10,13 @@ from frappe.utils import cstr, flt, getdate from frappe import _ from frappe.utils.file_manager import save_file from .default_website import website_maker +from .healthcare import setup_healthcare import install_fixtures from .sample_data import make_sample_data from erpnext.accounts.doctype.account.account import RootNotEditable from frappe.core.doctype.communication.comment import add_info_comment from erpnext.setup.setup_wizard.domainify import setup_domain +from erpnext.setup.doctype.company.company import install_country_fixtures def setup_complete(args=None): if frappe.db.sql("select name from tabCompany"): @@ -25,24 +27,19 @@ def setup_complete(args=None): create_price_lists(args) create_fiscal_year_and_company(args) create_sales_tax(args) - create_users(args) + create_employee_for_self(args) set_defaults(args) create_territories() create_feed_and_todo() create_email_digest() create_letter_head(args) - create_taxes(args) - create_items(args) - create_customers(args) - create_suppliers(args) if args.get('domain').lower() == 'education': create_academic_year() create_academic_term() - create_program(args) - create_course(args) - create_instructor(args) - create_room(args) + + if args.domain.lower() == 'healthcare': + setup_healthcare() if args.get('setup_website'): website_maker(args) @@ -58,16 +55,19 @@ def setup_complete(args=None): frappe.db.commit() frappe.clear_cache() - if args.get("add_sample_data"): - try: - make_sample_data(args) - frappe.clear_cache() - except: - # clear message - if frappe.message_log: - frappe.message_log.pop() + try: + make_sample_data(args.get('domain')) + frappe.clear_cache() + except: + # clear message + if frappe.message_log: + frappe.message_log.pop() - pass + pass + +def setup_success(args=None): + company = frappe.db.sql("select name from tabCompany", as_dict=True)[0]["name"] + install_country_fixtures(company) def create_fiscal_year_and_company(args): if (args.get('fy_start_date')): @@ -91,8 +91,7 @@ def create_fiscal_year_and_company(args): 'country': args.get('country'), 'create_chart_of_accounts_based_on': 'Standard Template', 'chart_of_accounts': args.get('chart_of_accounts'), - 'domain': args.get('domain'), - 'sales_target': args.get('sales_target') + 'domain': args.get('domain') }).insert() #Enable shopping cart @@ -159,7 +158,9 @@ def set_defaults(args): global_defaults.save() - frappe.db.set_value("System Settings", None, "email_footer_address", args.get("company")) + system_settings = frappe.get_doc("System Settings") + system_settings.email_footer_address = args.get("company") + system_settings.save() stock_settings = frappe.get_doc("Stock Settings") stock_settings.item_naming_by = "Item Code" @@ -176,7 +177,6 @@ def set_defaults(args): selling_settings.so_required = "No" selling_settings.dn_required = "No" selling_settings.allow_multiple_items = 1 - selling_settings.set_default_customer_group_and_territory() selling_settings.save() buying_settings = frappe.get_doc("Buying Settings") @@ -260,22 +260,7 @@ def create_sales_tax(args): tax_data.get('account_name'), tax_data.get('tax_rate'), sales_tax) -def get_country_wise_tax(country): - data = {} - with open (os.path.join(os.path.dirname(__file__), "data", "country_wise_tax.json")) as countrywise_tax: - data = json.load(countrywise_tax).get(country) - - return data - -def create_taxes(args): - for i in xrange(1,6): - if args.get("tax_" + str(i)): - # replace % in case someone also enters the % symbol - tax_rate = cstr(args.get("tax_rate_" + str(i)) or "").replace("%", "") - account_name = args.get("tax_" + str(i)) - - make_tax_account_and_template(args.get("company_name") , account_name, tax_rate) - +# Tax utils start def make_tax_account_and_template(company, account_name, tax_rate, template_name=None): try: if not isinstance(account_name, (list, tuple)): @@ -293,15 +278,6 @@ def make_tax_account_and_template(company, account_name, tax_rate, template_name except RootNotEditable: pass -def get_tax_account_group(company): - tax_group = frappe.db.get_value("Account", - {"account_name": "Duties and Taxes", "is_group": 1, "company": company}) - if not tax_group: - tax_group = frappe.db.get_value("Account", {"is_group": 1, "root_type": "Liability", - "account_type": "Tax", "company": company}) - - return tax_group - def make_tax_account(company, account_name, tax_rate): tax_group = get_tax_account_group(company) if tax_group: @@ -346,115 +322,23 @@ def make_sales_and_purchase_tax_templates(accounts, template_name=None): doc = frappe.get_doc(purchase_tax_template) doc.insert(ignore_permissions=True) -def create_items(args): - for i in xrange(1,6): - item = args.get("item_" + str(i)) - if item: - item_group = _(args.get("item_group_" + str(i))) - is_sales_item = args.get("is_sales_item_" + str(i)) - is_purchase_item = args.get("is_purchase_item_" + str(i)) - is_stock_item = item_group!=_("Services") - default_warehouse = "" - if is_stock_item: - default_warehouse = frappe.db.get_value("Warehouse", filters={ - "warehouse_name": _("Finished Goods") if is_sales_item else _("Stores"), - "company": args.get("company_name") - }) +def get_tax_account_group(company): + tax_group = frappe.db.get_value("Account", + {"account_name": "Duties and Taxes", "is_group": 1, "company": company}) + if not tax_group: + tax_group = frappe.db.get_value("Account", {"is_group": 1, "root_type": "Liability", + "account_type": "Tax", "company": company}) - try: - frappe.get_doc({ - "doctype":"Item", - "item_code": item, - "item_name": item, - "description": item, - "show_in_website": 1, - "is_sales_item": is_sales_item, - "is_purchase_item": is_purchase_item, - "is_stock_item": is_stock_item and 1 or 0, - "item_group": item_group, - "stock_uom": _(args.get("item_uom_" + str(i))), - "default_warehouse": default_warehouse - }).insert() + return tax_group - if args.get("item_img_" + str(i)): - item_image = args.get("item_img_" + str(i)).split(",") - if len(item_image)==3: - filename, filetype, content = item_image - fileurl = save_file(filename, content, "Item", item, decode=True).file_url - frappe.db.set_value("Item", item, "image", fileurl) +# Tax utils end - if args.get("item_price_" + str(i)): - item_price = flt(args.get("item_price_" + str(i))) +def get_country_wise_tax(country): + data = {} + with open (os.path.join(os.path.dirname(__file__), "data", "country_wise_tax.json")) as countrywise_tax: + data = json.load(countrywise_tax).get(country) - if is_sales_item: - price_list_name = frappe.db.get_value("Price List", {"selling": 1}) - make_item_price(item, price_list_name, item_price) - - if is_purchase_item: - price_list_name = frappe.db.get_value("Price List", {"buying": 1}) - make_item_price(item, price_list_name, item_price) - - except frappe.NameError: - pass - -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() - - -def create_customers(args): - for i in xrange(1,6): - customer = args.get("customer_" + str(i)) - if customer: - try: - doc = frappe.get_doc({ - "doctype":"Customer", - "customer_name": customer, - "customer_type": "Company", - "customer_group": _("Commercial"), - "territory": args.get("country"), - "company": args.get("company_name") - }).insert() - - if args.get("customer_contact_" + str(i)): - create_contact(args.get("customer_contact_" + str(i)), - "Customer", doc.name) - except frappe.NameError: - pass - -def create_suppliers(args): - for i in xrange(1,6): - supplier = args.get("supplier_" + str(i)) - if supplier: - try: - doc = frappe.get_doc({ - "doctype":"Supplier", - "supplier_name": supplier, - "supplier_type": _("Local"), - "company": args.get("company_name") - }).insert() - - if args.get("supplier_contact_" + str(i)): - create_contact(args.get("supplier_contact_" + str(i)), - "Supplier", doc.name) - except frappe.NameError: - pass - -def create_contact(contact, party_type, party): - """Create contact based on given contact name""" - contact = contact .split(" ") - - contact = frappe.get_doc({ - "doctype":"Contact", - "first_name":contact[0], - "last_name": len(contact) > 1 and contact[1] or "" - }) - contact.append('links', dict(link_doctype=party_type, link_name=party)) - contact.insert() + return data def create_letter_head(args): if args.get("attach_letterhead"): @@ -498,7 +382,7 @@ def login_as_first_user(args): if args.get("email") and hasattr(frappe.local, "login_manager"): frappe.local.login_manager.login_as(args.get("email")) -def create_users(args): +def create_employee_for_self(args): if frappe.session.user == 'Administrator': return @@ -513,50 +397,7 @@ def create_users(args): emp.flags.ignore_mandatory = True emp.insert(ignore_permissions = True) - for i in xrange(1,5): - email = args.get("user_email_" + str(i)) - fullname = args.get("user_fullname_" + str(i)) - if email: - if not fullname: - fullname = email.split("@")[0] - - parts = fullname.split(" ", 1) - - user = frappe.get_doc({ - "doctype": "User", - "email": email, - "first_name": parts[0], - "last_name": parts[1] if len(parts) > 1 else "", - "enabled": 1, - "user_type": "System User" - }) - - # default roles - user.append_roles("Projects User", "Stock User", "Support Team") - - if args.get("user_sales_" + str(i)): - user.append_roles("Sales User", "Sales Manager", "Accounts User") - if args.get("user_purchaser_" + str(i)): - user.append_roles("Purchase User", "Purchase Manager", "Accounts User") - if args.get("user_accountant_" + str(i)): - user.append_roles("Accounts Manager", "Accounts User") - - user.flags.delay_emails = True - - if not frappe.db.get_value("User", email): - user.insert(ignore_permissions=True) - - # create employee - emp = frappe.get_doc({ - "doctype": "Employee", - "employee_name": fullname, - "user_id": email, - "status": "Active", - "company": args.get("company_name") - }) - emp.flags.ignore_mandatory = True - emp.insert(ignore_permissions = True) - +# Schools def create_academic_term(): at = ["Semester 1", "Semester 2", "Semester 3"] ay = ["2013-14", "2014-15", "2015-16", "2016-17", "2017-18"] @@ -580,45 +421,3 @@ def create_academic_year(): except frappe.DuplicateEntryError: pass -def create_program(args): - for i in xrange(1,6): - if args.get("program_" + str(i)): - program = frappe.new_doc("Program") - program.program_code = args.get("program_" + str(i)) - program.program_name = args.get("program_" + str(i)) - try: - program.save() - except frappe.DuplicateEntryError: - pass - -def create_course(args): - for i in xrange(1,6): - if args.get("course_" + str(i)): - course = frappe.new_doc("Course") - course.course_code = args.get("course_" + str(i)) - course.course_name = args.get("course_" + str(i)) - try: - course.save() - except frappe.DuplicateEntryError: - pass - -def create_instructor(args): - for i in xrange(1,6): - if args.get("instructor_" + str(i)): - instructor = frappe.new_doc("Instructor") - instructor.instructor_name = args.get("instructor_" + str(i)) - try: - instructor.save() - except frappe.DuplicateEntryError: - pass - -def create_room(args): - for i in xrange(1,6): - if args.get("room_" + str(i)): - room = frappe.new_doc("Room") - room.room_name = args.get("room_" + str(i)) - room.seating_capacity = args.get("room_capacity_" + str(i)) - try: - room.save() - except frappe.DuplicateEntryError: - pass diff --git a/erpnext/setup/setup_wizard/test_setup_wizard.py b/erpnext/setup/setup_wizard/test_setup_wizard.py new file mode 100644 index 00000000000..57eeeff5b2c --- /dev/null +++ b/erpnext/setup/setup_wizard/test_setup_wizard.py @@ -0,0 +1,65 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe, time +from frappe.utils.selenium_testdriver import TestDriver + +def run_setup_wizard_test(): + driver = TestDriver() + frappe.db.set_default('in_selenium', '1') + frappe.db.commit() + + driver.login('#page-setup-wizard') + print('Running Setup Wizard Test...') + + # Language slide + driver.wait_for_ajax(True) + time.sleep(2) + driver.set_select("language", "English (United States)") + driver.wait_for_ajax(True) + driver.wait_till_clickable(".next-btn").click() + + # Region slide + driver.wait_for_ajax(True) + driver.set_select("country", "India") + driver.wait_for_ajax(True) + driver.wait_till_clickable(".next-btn").click() + + # Profile slide + driver.set_field("full_name", "Great Tester") + driver.set_field("email", "great@example.com") + driver.set_field("password", "test") + driver.wait_till_clickable(".next-btn").click() + + # Brand slide + driver.set_select("domain", "Manufacturing") + time.sleep(5) + driver.wait_till_clickable(".next-btn").click() + + # Org slide + driver.set_field("company_name", "For Testing") + driver.wait_till_clickable(".next-btn").click() + driver.set_field("company_tagline", "Just for GST") + driver.set_field("bank_account", "HDFC") + driver.wait_till_clickable(".complete-btn").click() + + # Wait for desktop + driver.wait_for('#page-desktop', timeout=600) + + console = driver.get_console() + if frappe.flags.tests_verbose: + for line in console: + print(line) + print('-' * 40) + time.sleep(1) + + frappe.db.set_default('in_selenium', None) + frappe.db.set_value("Company", "For Testing", "write_off_account", "Write Off - FT") + frappe.db.set_value("Company", "For Testing", "exchange_gain_loss_account", "Exchange Gain/Loss - FT") + frappe.db.commit() + + driver.close() + + return True diff --git a/erpnext/setup/setup_wizard/utils.py b/erpnext/setup/setup_wizard/utils.py index dc4abd4f502..d821a129899 100644 --- a/erpnext/setup/setup_wizard/utils.py +++ b/erpnext/setup/setup_wizard/utils.py @@ -10,6 +10,3 @@ def complete(): #setup_wizard.create_sales_tax(data) setup_complete(data) - - - diff --git a/erpnext/startup/notifications.py b/erpnext/startup/notifications.py index 5b0ce391a13..eb06f27c70a 100644 --- a/erpnext/startup/notifications.py +++ b/erpnext/startup/notifications.py @@ -55,13 +55,19 @@ def get_notification_config(): }, "Production Order": { "status": ("in", ("Draft", "Not Started", "In Process")) }, "BOM": {"docstatus": 0}, - "Timesheet": {"status": "Draft"} + + "Timesheet": {"status": "Draft"}, + + "Lab Test": {"docstatus": 0}, + "Sample Collection": {"docstatus": 0}, + "Patient Appointment": {"status": "Open"}, + "Consultation": {"docstatus": 0} }, "targets": { "Company": { - "filters" : { "sales_target": ( ">", 0 ) }, - "target_field" : "sales_target", + "filters" : { "monthly_sales_target": ( ">", 0 ) }, + "target_field" : "monthly_sales_target", "value_field" : "total_monthly_sales" } } diff --git a/erpnext/startup/report_data_map.py b/erpnext/startup/report_data_map.py index e4bbd8744e4..3d5a6740214 100644 --- a/erpnext/startup/report_data_map.py +++ b/erpnext/startup/report_data_map.py @@ -269,7 +269,7 @@ data_map = { }, "Purchase Receipt": { "columns": ["name", "supplier", "posting_date", "company"], - "conditions": ["docstatus=1"], + "conditions": ["docstatus=1"], "order_by": "posting_date", "links": { "supplier": ["Supplier", "name"], @@ -278,7 +278,7 @@ data_map = { }, "Purchase Receipt Item[Purchase Analytics]": { "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"], - "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], + "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], "order_by": "parent", "links": { "parent": ["Purchase Receipt", "name"], @@ -297,5 +297,39 @@ data_map = { "columns": ["name","status","creation","planned_start_date","planned_end_date","status","actual_start_date","actual_end_date", "modified"], "conditions": ["docstatus = 1"], "order_by": "creation" + }, + + #Medical + "Patient": { + "columns": ["name", "creation", "owner", "if(patient_name=name, '', patient_name) as patient_name"], + "conditions": ["docstatus < 2"], + "order_by": "name", + "links": { + "owner" : ["User", "name"] + } + }, + "Patient Appointment": { + "columns": ["name", "appointment_type", "patient", "physician", "appointment_date", "department", "status", "company"], + "order_by": "name", + "links": { + "physician": ["Physician", "name"], + "appointment_type": ["Appointment Type", "name"] + } + }, + "Physician": { + "columns": ["name", "department"], + "order_by": "name", + "links": { + "department": ["Department", "name"], + } + + }, + "Appointment Type": { + "columns": ["name"], + "order_by": "name" + }, + "Medical Department": { + "columns": ["name"], + "order_by": "name" } } diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 46d536dacea..e0ee3708f43 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -166,6 +166,12 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend( __("Status")) } erpnext.stock.delivery_note.set_print_hide(doc, dt, dn); + + if(doc.docstatus==1 && !doc.subscription) { + cur_frm.add_custom_button(__('Subscription'), function() { + erpnext.utils.make_subscription(doc.doctype, doc.name) + }, __("Make")) + } }, make_sales_invoice: function() { diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 41f8b8493e5..a41ae680d2a 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -2109,6 +2109,37 @@ "unique": 0, "width": "150px" }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "base_rounding_adjustment", + "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": "Rounding Adjustment (Company Currency)", + "length": 0, + "no_copy": 1, + "options": "Company:company:default_currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2240,6 +2271,37 @@ "unique": 0, "width": "150px" }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "rounding_adjustment", + "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": "Rounding Adjustment", + "length": 0, + "no_copy": 1, + "options": "currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -3251,6 +3313,67 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription", + "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": "Subscription", + "length": 0, + "no_copy": 1, + "options": "Subscription", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -3487,7 +3610,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-08-23 13:25:34.182268", + "modified": "2017-09-19 11:21:59.084183", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py index c296d8cb9f9..2e150f70d00 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py @@ -5,7 +5,8 @@ def get_data(): 'fieldname': 'delivery_note', 'non_standard_fieldnames': { 'Stock Entry': 'delivery_note_no', - 'Quality Inspection': 'reference_name' + 'Quality Inspection': 'reference_name', + 'Subscription': 'reference_document', }, 'internal_links': { 'Sales Order': ['items', 'against_sales_order'], @@ -23,5 +24,9 @@ def get_data(): 'label': _('Returns'), 'items': ['Stock Entry'] }, + { + 'label': _('Subscription'), + 'items': ['Subscription'] + }, ] } \ No newline at end of file diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.js b/erpnext/stock/doctype/delivery_note/test_delivery_note.js index 482f8929974..bbc97b8d402 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.js @@ -34,4 +34,3 @@ QUnit.test("test delivery note", function(assert) { ]); }); - diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 1af08070ecd..7837f8c73e8 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -63,8 +63,8 @@ frappe.ui.form.on("Item", { frm.page.set_inner_btn_group_as_primary(__("Make")); } if (frm.doc.variant_of) { - frm.set_intro(__("This Item is a Variant of {0} (Template).", - [frm.doc.variant_of]), true); + frm.set_intro(__('This Item is a Variant of {0} (Template).', + [`${frm.doc.variant_of}`]), true); } if (frappe.defaults.get_default("item_naming_by")!="Naming Series" || frm.doc.variant_of) { diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 5dc97f623b3..f2ea1d88bca 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -488,6 +488,8 @@ class Item(WebsiteGenerator): def validate_warehouse_for_reorder(self): warehouse = [] for i in self.get("reorder_levels"): + if not i.warehouse_group: + i.warehouse_group = i.warehouse if i.get("warehouse") and i.get("warehouse") not in warehouse: warehouse += [i.get("warehouse")] else: diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json index ed647d6c8be..87cde0d3e2f 100644 --- a/erpnext/stock/doctype/material_request/material_request.json +++ b/erpnext/stock/doctype/material_request/material_request.json @@ -86,7 +86,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 1, "label": "Type", "length": 0, @@ -144,7 +144,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Series", "length": 0, @@ -211,7 +211,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 1, "label": "Company", "length": 0, @@ -369,7 +369,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Transaction Date", "length": 0, @@ -686,7 +686,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-06-13 14:29:18.032657", + "modified": "2017-07-26 19:43:31.823549", "modified_by": "Administrator", "module": "Stock", "name": "Material Request", diff --git a/erpnext/stock/doctype/material_request/test_material_request.js b/erpnext/stock/doctype/material_request/test_material_request.js new file mode 100644 index 00000000000..793cad0f3b6 --- /dev/null +++ b/erpnext/stock/doctype/material_request/test_material_request.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Material Request", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially('Material Request', [ + // insert a new Material Request + () => frappe.tests.make([ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index 19cc44a1191..4447fb8cfab 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -48,7 +48,7 @@ frappe.ui.form.on("Purchase Receipt", { toggle_display_account_head: function(frm) { var enabled = erpnext.is_perpetual_inventory_enabled(frm.doc.company) frm.fields_dict["items"].grid.set_column_disp(["cost_center"], enabled); - } + }, }); erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend({ @@ -98,6 +98,13 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend if(flt(this.frm.doc.per_billed) < 100) { cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice, __("Make")); } + + if(!this.frm.doc.subscription) { + cur_frm.add_custom_button(__('Subscription'), function() { + erpnext.utils.make_subscription(me.frm.doc.doctype, me.frm.doc.name) + }, __("Make")) + } + cur_frm.page.set_inner_btn_group_as_primary(__("Make")); } } diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index adcea6d76cb..113bbc2f11e 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -1939,6 +1939,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "base_rounding_adjustment", + "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": "Rounding Adjustment (Company Currency)", + "length": 0, + "no_copy": 1, + "options": "Company:company:default_currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2064,6 +2095,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "rounding_adjustment", + "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": "Rounding Adjustment", + "length": 0, + "no_copy": 1, + "options": "currency", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2611,6 +2673,67 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription_detail", + "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 Detail", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription", + "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": "Subscription", + "length": 0, + "no_copy": 1, + "options": "Subscription", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2999,7 +3122,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-08-06 19:33:59.822251", + "modified": "2017-09-19 11:02:24.831340", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py index 9722d8791f6..9ade1afd8a0 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py @@ -5,7 +5,8 @@ def get_data(): 'fieldname': 'purchase_receipt_no', 'non_standard_fieldnames': { 'Purchase Invoice': 'purchase_receipt', - 'Landed Cost Voucher': 'receipt_document' + 'Landed Cost Voucher': 'receipt_document', + 'Subscription': 'reference_document' }, 'internal_links': { 'Purchase Order': ['items', 'purchase_order'], @@ -25,5 +26,9 @@ def get_data(): 'label': _('Returns'), 'items': ['Stock Entry'] }, + { + 'label': _('Subscription'), + 'items': ['Subscription'] + }, ] } \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js index b0eae735ef7..d4d1a0a52e0 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js @@ -6,21 +6,21 @@ QUnit.test("test Stock Reconciliation", function(assert) { frappe.run_serially([ () => frappe.set_route('List', 'Stock Reconciliation'), () => frappe.click_button('New'), - () => cur_frm.set_value('company','Razer Blade'), + () => cur_frm.set_value('company','For Testing'), () => frappe.click_button('Items'), - () => {cur_dialog.set_value('warehouse','Stores - RB'); }, + () => {cur_dialog.set_value('warehouse','Stores - FT'); }, () => frappe.timeout(0.5), () => frappe.click_button('Update'), () => { cur_frm.doc.items[0].qty = 150; cur_frm.refresh_fields('items');}, () => frappe.timeout(0.5), - () => cur_frm.set_value('expense_account','Stock Adjustment - RB'), - () => cur_frm.set_value('cost_center','Main - RB'), + () => cur_frm.set_value('expense_account','Stock Adjustment - FT'), + () => cur_frm.set_value('cost_center','Main - FT'), () => cur_frm.save(), () => { // get_item_details - assert.ok(cur_frm.doc.expense_account=='Stock Adjustment - RB', "expense_account correct"); + assert.ok(cur_frm.doc.expense_account=='Stock Adjustment - FT', "expense_account correct"); }, () => frappe.tests.click_button('Submit'), () => frappe.tests.click_button('Yes'), diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.js b/erpnext/stock/doctype/warehouse/test_warehouse.js index f13bc58084d..8ea280cc59e 100644 --- a/erpnext/stock/doctype/warehouse/test_warehouse.js +++ b/erpnext/stock/doctype/warehouse/test_warehouse.js @@ -10,7 +10,7 @@ QUnit.test("test: warehouse", function (assert) { () => frappe.tests.make( "Warehouse", [ {warehouse_name: "Laptop Scrap Warehouse"}, - {company: "Razer Blade"} + {company: "For Testing"} ] ), diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 2a22854677e..72a83c6c1b2 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -79,7 +79,7 @@ def get_item_details(args): and out.warehouse and out.stock_qty > 0: if out.has_serial_no: - out.serial_no = get_serial_no(out) + out.serial_no = get_serial_no(out, args.serial_no) if out.has_batch_no and not args.get("batch_no"): out.batch_no = get_batch_no(out.item_code, out.warehouse, out.qty) @@ -554,7 +554,8 @@ def get_gross_profit(out): return out @frappe.whitelist() -def get_serial_no(args): +def get_serial_no(args, serial_nos=None): + serial_no = None if isinstance(args, basestring): args = json.loads(args) args = frappe._dict(args) @@ -568,4 +569,9 @@ def get_serial_no(args): args = json.dumps({"item_code": args.get('item_code'),"warehouse": args.get('warehouse'),"stock_qty": args.get('stock_qty')}) args = process_args(args) serial_no = get_serial_nos_by_fifo(args) - return serial_no + + if not serial_no and serial_nos: + # For POS + serial_no = serial_nos + + return serial_no diff --git a/erpnext/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.json b/erpnext/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.json index 557847abb6f..ed48a0fbd52 100644 --- a/erpnext/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.json +++ b/erpnext/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.json @@ -7,12 +7,12 @@ "doctype": "Report", "idx": 3, "is_standard": "Yes", - "modified": "2017-07-22 09:24:30.201487", + "modified": "2017-09-18 12:28:49.322622", "modified_by": "Administrator", "module": "Stock", "name": "Ordered Items To Be Delivered", "owner": "Administrator", - "query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`customer_name` as \"Customer Name::150\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project` as \"Project:Link/Project:120\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.qty as \"Qty:Float:140\",\n `tabSales Order Item`.delivered_qty as \"Delivered Qty:Float:140\",\n (`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0)) as \"Qty to Deliver:Float:140\",\n `tabSales Order Item`.base_rate as \"Rate:Float:140\",\n `tabSales Order Item`.base_amount as \"Amount:Float:140\",\n ((`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0))*`tabSales Order Item`.base_rate) as \"Amount to Deliver:Float:140\",\n `tabBin`.actual_qty as \"Available Qty:Float:120\",\n `tabBin`.projected_qty as \"Projected Qty:Float:120\",\n `tabSales Order Item`.`delivery_date` as \"Item Delivery Date:Date:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order Item`.item_group as \"Item Group:Link/Item Group:120\",\n `tabSales Order Item`.warehouse as \"Warehouse:Link/Warehouse:200\"\nfrom\n `tabSales Order` JOIN `tabSales Order Item` \n LEFT JOIN `tabBin` ON (`tabBin`.item_code = `tabSales Order Item`.item_code\n and `tabBin`.warehouse = `tabSales Order Item`.warehouse)\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status not in (\"Stopped\", \"Closed\")\n and ifnull(`tabSales Order Item`.delivered_qty,0) < ifnull(`tabSales Order Item`.qty,0)\norder by `tabSales Order`.transaction_date asc", + "query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`customer_name` as \"Customer Name::150\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project` as \"Project:Link/Project:120\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order Item`.qty as \"Qty:Float:140\",\n `tabSales Order Item`.delivered_qty as \"Delivered Qty:Float:140\",\n (`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0)) as \"Qty to Deliver:Float:140\",\n `tabSales Order Item`.base_rate as \"Rate:Float:140\",\n `tabSales Order Item`.base_amount as \"Amount:Float:140\",\n ((`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0))*`tabSales Order Item`.base_rate) as \"Amount to Deliver:Float:140\",\n `tabBin`.actual_qty as \"Available Qty:Float:120\",\n `tabBin`.projected_qty as \"Projected Qty:Float:120\",\n `tabSales Order Item`.`delivery_date` as \"Item Delivery Date:Date:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.item_group as \"Item Group:Link/Item Group:120\",\n `tabSales Order Item`.warehouse as \"Warehouse:Link/Warehouse:200\"\nfrom\n `tabSales Order` JOIN `tabSales Order Item` \n LEFT JOIN `tabBin` ON (`tabBin`.item_code = `tabSales Order Item`.item_code\n and `tabBin`.warehouse = `tabSales Order Item`.warehouse)\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status not in (\"Stopped\", \"Closed\")\n and ifnull(`tabSales Order Item`.delivered_qty,0) < ifnull(`tabSales Order Item`.qty,0)\norder by `tabSales Order`.transaction_date asc", "ref_doctype": "Delivery Note", "report_name": "Ordered Items To Be Delivered", "report_type": "Query Report", diff --git a/erpnext/templates/emails/recurring_document_failed.html b/erpnext/templates/emails/recurring_document_failed.html index 9b887311787..ea48034f418 100644 --- a/erpnext/templates/emails/recurring_document_failed.html +++ b/erpnext/templates/emails/recurring_document_failed.html @@ -1,12 +1,11 @@ -

Recurring {{ type }} Failed

+

{{_("Recurring")}} {{ type }} {{ _("Failed")}}

An error occured while creating recurring {{ type }} {{ name }} for {{ party }}.

This could be because of some invalid Email Addresses in the {{ type }}.

-

To stop sending repetitive error notifications from the system, we have unchecked -"Convert into Recurring" field in the {{ type }} {{ name }}.

-

Please correct the {{ type }} and make the {{ type }} recurring again.

+

To stop sending repetitive error notifications from the system, we have checked "Disabled" field in the subscription {{ subscription}} for the {{ type }} {{ name }}.

+

Please correct the {{ type }} and unchcked "Disabled" in the {{ subscription }} for making recurring again.


It is necessary to take this action today itself for the above mentioned recurring {{ type }} to be generated. If delayed, you will have to manually change the "Repeat on Day of Month" field -of this {{ type }} for generating the recurring {{ type }}.

+of this {{ type }} for generating the recurring {{ type }} in the subscription {{ subscription }}.

[This email is autogenerated]

diff --git a/erpnext/templates/emails/training_event.html b/erpnext/templates/emails/training_event.html new file mode 100644 index 00000000000..51c232d8e87 --- /dev/null +++ b/erpnext/templates/emails/training_event.html @@ -0,0 +1,21 @@ +

{{_("Training Event")}}

+

{{ message }}

+ +

{{_("Details")}}

+{{_("Event Name")}}: {{ name }} +
{{_("Event Location")}}: {{ location }} +
{{_("Start Time")}}: {{ start_time }} +
{{_("End Time")}}: {{ end_time }} +
{{_("Attendance")}}: {{ attendance }} + +

{{_("Update Response")}}

+{% if not self_study %} +

{{_("Please update your status for this training event")}}:

+
+
+{% else %} +

{{_("Please confirm once you have completed your training")}}:

+
+{% endif %} +

{{_("Thank you")}},
+{{ user_fullname }}

diff --git a/erpnext/templates/includes/healthcare/appointment_row_template.html b/erpnext/templates/includes/healthcare/appointment_row_template.html new file mode 100644 index 00000000000..53be5e67647 --- /dev/null +++ b/erpnext/templates/includes/healthcare/appointment_row_template.html @@ -0,0 +1,23 @@ + diff --git a/erpnext/templates/includes/healthcare/lab_test_row_template.html b/erpnext/templates/includes/healthcare/lab_test_row_template.html new file mode 100644 index 00000000000..d6c9b54b09c --- /dev/null +++ b/erpnext/templates/includes/healthcare/lab_test_row_template.html @@ -0,0 +1,20 @@ + diff --git a/erpnext/templates/includes/healthcare/prescription_row_template.html b/erpnext/templates/includes/healthcare/prescription_row_template.html new file mode 100644 index 00000000000..13ce3bc16b8 --- /dev/null +++ b/erpnext/templates/includes/healthcare/prescription_row_template.html @@ -0,0 +1,15 @@ + diff --git a/erpnext/templates/pages/demo.html b/erpnext/templates/pages/demo.html index f94a7c4591a..10743b82319 100644 --- a/erpnext/templates/pages/demo.html +++ b/erpnext/templates/pages/demo.html @@ -60,7 +60,7 @@ $(document).ready(function() {
-

Sign up for a Free ERPNext.com account here +

Start a free 30-day trial