Merge branch 'develop' into quo-status-fix

This commit is contained in:
Saqib
2020-04-23 15:30:31 +05:30
committed by GitHub
203 changed files with 5294 additions and 2470 deletions

View File

@@ -89,7 +89,7 @@ class Account(NestedSet):
throw(_("Root cannot be edited."), RootNotEditable) throw(_("Root cannot be edited."), RootNotEditable)
if not self.parent_account and not self.is_group: if not self.parent_account and not self.is_group:
frappe.throw(_("Root Account must be a group")) frappe.throw(_("The root account {0} must be a group").format(frappe.bold(self.name)))
def validate_root_company_and_sync_account_to_children(self): def validate_root_company_and_sync_account_to_children(self):
# ignore validation while creating new compnay or while syncing to child companies # ignore validation while creating new compnay or while syncing to child companies

View File

@@ -69,6 +69,7 @@ class TestAccount(unittest.TestCase):
acc.account_name = "Accumulated Depreciation" acc.account_name = "Accumulated Depreciation"
acc.parent_account = "Fixed Assets - _TC" acc.parent_account = "Fixed Assets - _TC"
acc.company = "_Test Company" acc.company = "_Test Company"
acc.account_type = "Accumulated Depreciation"
acc.insert() acc.insert()
doc = frappe.get_doc("Account", "Securities and Deposits - _TC") doc = frappe.get_doc("Account", "Securities and Deposits - _TC")
@@ -149,7 +150,7 @@ def _make_test_records(verbose):
# fixed asset depreciation # fixed asset depreciation
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None], ["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
["_Test Accumulated Depreciations", "Current Assets", 0, None, None], ["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None],
["_Test Depreciations", "Expenses", 0, None, None], ["_Test Depreciations", "Expenses", 0, None, None],
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None], ["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],

View File

@@ -1,134 +0,0 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:account_subtype",
"beta": 0,
"creation": "2018-10-25 15:46:08.054586",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "account_subtype",
"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": "Account Subtype",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-25 15:47:03.841390",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account Subtype",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts 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": 0,
"track_seen": 0,
"track_views": 0
}

View File

@@ -1,23 +0,0 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Account Subtype", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Account Subtype
() => frappe.tests.make('Account Subtype', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@@ -1,8 +0,0 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Account Type', {
refresh: function() {
}
});

View File

@@ -1,134 +0,0 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:account_type",
"beta": 0,
"creation": "2018-10-25 15:45:45.789963",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "account_type",
"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": "Account 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": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-25 15:46:51.042604",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account Type",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts 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": 0,
"track_seen": 0,
"track_views": 0
}

View File

@@ -1,9 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import unittest
class TestAccountType(unittest.TestCase):
pass

View File

@@ -64,13 +64,13 @@
"fieldname": "account_type", "fieldname": "account_type",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Account Type", "label": "Account Type",
"options": "Account Type" "options": "Bank Account Type"
}, },
{ {
"fieldname": "account_subtype", "fieldname": "account_subtype",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Account Subtype", "label": "Account Subtype",
"options": "Account Subtype" "options": "Bank Account Subtype"
}, },
{ {
"fieldname": "column_break_7", "fieldname": "column_break_7",
@@ -200,7 +200,7 @@
} }
], ],
"links": [], "links": [],
"modified": "2020-01-30 20:42:26.458316", "modified": "2020-04-06 21:00:45.379804",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Bank Account", "name": "Bank Account",

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on('Account Subtype', { frappe.ui.form.on('Bank Account Subtype', {
refresh: function() { refresh: function() {
} }

View File

@@ -0,0 +1,134 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:account_subtype",
"beta": 0,
"creation": "2018-10-25 15:46:08.054586",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "account_subtype",
"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": "Account Subtype",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-25 15:47:03.841390",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Account Subtype",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts 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": 0,
"track_seen": 0,
"track_views": 0
}

View File

@@ -5,5 +5,5 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from frappe.model.document import Document from frappe.model.document import Document
class AccountType(Document): class BankAccountSubtype(Document):
pass pass

View File

@@ -2,15 +2,15 @@
// rename this file from _test_[name] to test_[name] to activate // rename this file from _test_[name] to test_[name] to activate
// and remove above this line // and remove above this line
QUnit.test("test: Account Type", function (assert) { QUnit.test("test: Bank Account Subtype", function (assert) {
let done = assert.async(); let done = assert.async();
// number of asserts // number of asserts
assert.expect(1); assert.expect(1);
frappe.run_serially([ frappe.run_serially([
// insert a new Account Type // insert a new Bank Account Subtype
() => frappe.tests.make('Account Type', [ () => frappe.tests.make('Bank Account Subtype', [
// values to be set // values to be set
{key: 'value'} {key: 'value'}
]), ]),

View File

@@ -5,5 +5,5 @@ from __future__ import unicode_literals
import unittest import unittest
class TestAccountSubtype(unittest.TestCase): class TestBankAccountSubtype(unittest.TestCase):
pass pass

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Bank Account Type', {
// refresh: function(frm) {
// }
});

View File

@@ -0,0 +1,68 @@
{
"actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:account_type",
"creation": "2018-10-25 15:45:45.789963",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"account_type"
],
"fields": [
{
"fieldname": "account_type",
"fieldtype": "Data",
"label": "Account Type",
"unique": 1
}
],
"links": [],
"modified": "2020-04-10 21:13:09.137898",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Account Type",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class BankAccountType(Document):
pass

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestBankAccountType(unittest.TestCase):
pass

View File

@@ -9,6 +9,7 @@ from frappe.utils import flt, getdate, add_months, get_last_day, fmt_money, nowd
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
class BudgetError(frappe.ValidationError): pass class BudgetError(frappe.ValidationError): pass
class DuplicateBudgetError(frappe.ValidationError): pass class DuplicateBudgetError(frappe.ValidationError): pass
@@ -98,30 +99,32 @@ def validate_expense_against_budget(args):
if not (args.get('account') and args.get('cost_center')) and args.item_code: if not (args.get('account') and args.get('cost_center')) and args.item_code:
args.cost_center, args.account = get_item_details(args) args.cost_center, args.account = get_item_details(args)
if not (args.cost_center or args.project) and not args.account: if not args.account:
return return
for budget_against in ['project', 'cost_center']: for budget_against in ['project', 'cost_center'] + get_accounting_dimensions():
if (args.get(budget_against) and args.account if (args.get(budget_against) and args.account
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})): and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})):
if args.project and budget_against == 'project': doctype = frappe.unscrub(budget_against)
condition = "and b.project=%s" % frappe.db.escape(args.project)
args.budget_against_field = "Project"
elif args.cost_center and budget_against == 'cost_center': if frappe.get_cached_value('DocType', doctype, 'is_tree'):
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", args.cost_center, ["lft", "rgt"]) lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"])
condition = """and exists(select name from `tabCost Center` condition = """and exists(select name from `tab%s`
where lft<=%s and rgt>=%s and name=b.cost_center)""" % (cc_lft, cc_rgt) where lft<=%s and rgt>=%s and name=b.%s)""" % (doctype, lft, rgt, budget_against) #nosec
args.budget_against_field = "Cost Center" args.is_tree = True
else:
condition = "and b.%s=%s" % (budget_against, frappe.db.escape(args.get(budget_against)))
args.is_tree = False
args.budget_against = args.get(budget_against) args.budget_against_field = budget_against
args.budget_against_doctype = doctype
budget_records = frappe.db.sql(""" budget_records = frappe.db.sql("""
select select
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution, b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
ifnull(b.applicable_on_material_request, 0) as for_material_request, ifnull(b.applicable_on_material_request, 0) as for_material_request,
ifnull(applicable_on_purchase_order,0) as for_purchase_order, ifnull(applicable_on_purchase_order, 0) as for_purchase_order,
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses, ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded, b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded,
b.action_if_annual_budget_exceeded_on_mr, b.action_if_accumulated_monthly_budget_exceeded_on_mr, b.action_if_annual_budget_exceeded_on_mr, b.action_if_accumulated_monthly_budget_exceeded_on_mr,
@@ -132,9 +135,7 @@ def validate_expense_against_budget(args):
b.name=ba.parent and b.fiscal_year=%s b.name=ba.parent and b.fiscal_year=%s
and ba.account=%s and b.docstatus=1 and ba.account=%s and b.docstatus=1
{condition} {condition}
""".format(condition=condition, """.format(condition=condition, budget_against_field=budget_against), (args.fiscal_year, args.account), as_dict=True) #nosec
budget_against_field=frappe.scrub(args.get("budget_against_field"))),
(args.fiscal_year, args.account), as_dict=True)
if budget_records: if budget_records:
validate_budget_records(args, budget_records) validate_budget_records(args, budget_records)
@@ -230,10 +231,10 @@ def get_ordered_amount(args, budget):
def get_other_condition(args, budget, for_doc): def get_other_condition(args, budget, for_doc):
condition = "expense_account = '%s'" % (args.expense_account) condition = "expense_account = '%s'" % (args.expense_account)
budget_against_field = frappe.scrub(args.get("budget_against_field")) budget_against_field = args.get("budget_against_field")
if budget_against_field and args.get(budget_against_field): if budget_against_field and args.get(budget_against_field):
condition += " and child.%s = '%s'" %(budget_against_field, args.get(budget_against_field)) condition += " and child.%s = '%s'" % (budget_against_field, args.get(budget_against_field))
if args.get('fiscal_year'): if args.get('fiscal_year'):
date_field = 'schedule_date' if for_doc == 'Material Request' else 'transaction_date' date_field = 'schedule_date' if for_doc == 'Material Request' else 'transaction_date'
@@ -246,19 +247,30 @@ def get_other_condition(args, budget, for_doc):
return condition return condition
def get_actual_expense(args): def get_actual_expense(args):
if not args.budget_against_doctype:
args.budget_against_doctype = frappe.unscrub(args.budget_against_field)
budget_against_field = args.get('budget_against_field')
condition1 = " and gle.posting_date <= %(month_end_date)s" \ condition1 = " and gle.posting_date <= %(month_end_date)s" \
if args.get("month_end_date") else "" if args.get("month_end_date") else ""
if args.budget_against_field == "Cost Center":
lft_rgt = frappe.db.get_value(args.budget_against_field, if args.is_tree:
args.budget_against, ["lft", "rgt"], as_dict=1) lft_rgt = frappe.db.get_value(args.budget_against_doctype,
args.get(budget_against_field), ["lft", "rgt"], as_dict=1)
args.update(lft_rgt) args.update(lft_rgt)
condition2 = """and exists(select name from `tabCost Center`
where lft>=%(lft)s and rgt<=%(rgt)s and name=gle.cost_center)"""
elif args.budget_against_field == "Project": condition2 = """and exists(select name from `tab{doctype}`
condition2 = "and exists(select name from `tabProject` where name=gle.project and gle.project = %(budget_against)s)" where lft>=%(lft)s and rgt<=%(rgt)s
and name=gle.{budget_against_field})""".format(doctype=args.budget_against_doctype, #nosec
budget_against_field=budget_against_field)
else:
condition2 = """and exists(select name from `tab{doctype}`
where name=gle.{budget_against} and
gle.{budget_against} = %({budget_against})s)""".format(doctype=args.budget_against_doctype,
budget_against = budget_against_field)
return flt(frappe.db.sql(""" amount = flt(frappe.db.sql("""
select sum(gle.debit) - sum(gle.credit) select sum(gle.debit) - sum(gle.credit)
from `tabGL Entry` gle from `tabGL Entry` gle
where gle.account=%(account)s where gle.account=%(account)s
@@ -267,7 +279,9 @@ def get_actual_expense(args):
and gle.company=%(company)s and gle.company=%(company)s
and gle.docstatus=1 and gle.docstatus=1
{condition2} {condition2}
""".format(condition1=condition1, condition2=condition2), (args))[0][0]) """.format(condition1=condition1, condition2=condition2), (args))[0][0]) #nosec
return amount
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget): def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
distribution = {} distribution = {}

View File

@@ -13,7 +13,7 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
class TestBudget(unittest.TestCase): class TestBudget(unittest.TestCase):
def test_monthly_budget_crossed_ignore(self): def test_monthly_budget_crossed_ignore(self):
set_total_expense_zero("2013-02-28", "Cost Center") set_total_expense_zero("2013-02-28", "cost_center")
budget = make_budget(budget_against="Cost Center") budget = make_budget(budget_against="Cost Center")
@@ -26,7 +26,7 @@ class TestBudget(unittest.TestCase):
budget.cancel() budget.cancel()
def test_monthly_budget_crossed_stop1(self): def test_monthly_budget_crossed_stop1(self):
set_total_expense_zero("2013-02-28", "Cost Center") set_total_expense_zero("2013-02-28", "cost_center")
budget = make_budget(budget_against="Cost Center") budget = make_budget(budget_against="Cost Center")
@@ -41,7 +41,7 @@ class TestBudget(unittest.TestCase):
budget.cancel() budget.cancel()
def test_exception_approver_role(self): def test_exception_approver_role(self):
set_total_expense_zero("2013-02-28", "Cost Center") set_total_expense_zero("2013-02-28", "cost_center")
budget = make_budget(budget_against="Cost Center") budget = make_budget(budget_against="Cost Center")
@@ -114,7 +114,7 @@ class TestBudget(unittest.TestCase):
budget.cancel() budget.cancel()
def test_monthly_budget_crossed_stop2(self): def test_monthly_budget_crossed_stop2(self):
set_total_expense_zero("2013-02-28", "Project") set_total_expense_zero("2013-02-28", "project")
budget = make_budget(budget_against="Project") budget = make_budget(budget_against="Project")
@@ -129,7 +129,7 @@ class TestBudget(unittest.TestCase):
budget.cancel() budget.cancel()
def test_yearly_budget_crossed_stop1(self): def test_yearly_budget_crossed_stop1(self):
set_total_expense_zero("2013-02-28", "Cost Center") set_total_expense_zero("2013-02-28", "cost_center")
budget = make_budget(budget_against="Cost Center") budget = make_budget(budget_against="Cost Center")
@@ -141,7 +141,7 @@ class TestBudget(unittest.TestCase):
budget.cancel() budget.cancel()
def test_yearly_budget_crossed_stop2(self): def test_yearly_budget_crossed_stop2(self):
set_total_expense_zero("2013-02-28", "Project") set_total_expense_zero("2013-02-28", "project")
budget = make_budget(budget_against="Project") budget = make_budget(budget_against="Project")
@@ -153,7 +153,7 @@ class TestBudget(unittest.TestCase):
budget.cancel() budget.cancel()
def test_monthly_budget_on_cancellation1(self): def test_monthly_budget_on_cancellation1(self):
set_total_expense_zero("2013-02-28", "Cost Center") set_total_expense_zero("2013-02-28", "cost_center")
budget = make_budget(budget_against="Cost Center") budget = make_budget(budget_against="Cost Center")
@@ -177,7 +177,7 @@ class TestBudget(unittest.TestCase):
budget.cancel() budget.cancel()
def test_monthly_budget_on_cancellation2(self): def test_monthly_budget_on_cancellation2(self):
set_total_expense_zero("2013-02-28", "Project") set_total_expense_zero("2013-02-28", "project")
budget = make_budget(budget_against="Project") budget = make_budget(budget_against="Project")
@@ -201,8 +201,8 @@ class TestBudget(unittest.TestCase):
budget.cancel() budget.cancel()
def test_monthly_budget_against_group_cost_center(self): def test_monthly_budget_against_group_cost_center(self):
set_total_expense_zero("2013-02-28", "Cost Center") set_total_expense_zero("2013-02-28", "cost_center")
set_total_expense_zero("2013-02-28", "Cost Center", "_Test Cost Center 2 - _TC") set_total_expense_zero("2013-02-28", "cost_center", "_Test Cost Center 2 - _TC")
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC") budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
@@ -241,25 +241,30 @@ class TestBudget(unittest.TestCase):
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None): def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
if budget_against_field == "Project": if budget_against_field == "project":
budget_against = "_Test Project" budget_against = "_Test Project"
else: else:
budget_against = budget_against_CC or "_Test Cost Center - _TC" budget_against = budget_against_CC or "_Test Cost Center - _TC"
existing_expense = get_actual_expense(frappe._dict({
args = frappe._dict({
"account": "_Test Account Cost for Goods Sold - _TC", "account": "_Test Account Cost for Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"monthly_end_date": posting_date, "monthly_end_date": posting_date,
"company": "_Test Company", "company": "_Test Company",
"fiscal_year": "_Test Fiscal Year 2013", "fiscal_year": "_Test Fiscal Year 2013",
"budget_against_field": budget_against_field, "budget_against_field": budget_against_field,
"budget_against": budget_against })
}))
if not args.get(budget_against_field):
args[budget_against_field] = budget_against
existing_expense = get_actual_expense(args)
if existing_expense: if existing_expense:
if budget_against_field == "Cost Center": if budget_against_field == "cost_center":
make_journal_entry("_Test Account Cost for Goods Sold - _TC", make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True) "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
elif budget_against_field == "Project": elif budget_against_field == "project":
make_journal_entry("_Test Account Cost for Goods Sold - _TC", make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date="2013-02-28") "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date="2013-02-28")

View File

@@ -152,10 +152,9 @@ def build_forest(data):
return [parent_account] return [parent_account]
elif account_name == child: elif account_name == child:
parent_account_list = return_parent(data, parent_account) parent_account_list = return_parent(data, parent_account)
if not parent_account_list: if not parent_account_list and parent_account:
frappe.throw(_("The parent account {0} does not exists in the uploaded template").format( frappe.throw(_("The parent account {0} does not exists in the uploaded template").format(
frappe.bold(parent_account))) frappe.bold(parent_account)))
return [child] + parent_account_list return [child] + parent_account_list
charts_map, paths = {}, [] charts_map, paths = {}, []

View File

@@ -330,9 +330,9 @@ def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args):
if pr_doc.mixed_conditions: if pr_doc.mixed_conditions:
amt = args.get('qty') * args.get("price_list_rate") amt = args.get('qty') * args.get("price_list_rate")
if args.get("item_code") != row.get("item_code"): if args.get("item_code") != row.get("item_code"):
amt = row.get('qty') * (row.get("price_list_rate") or args.get("rate")) amt = flt(row.get('qty')) * flt(row.get("price_list_rate") or args.get("rate"))
sum_qty += row.get("stock_qty") or args.get("stock_qty") or args.get("qty") sum_qty += flt(row.get("stock_qty")) or flt(args.get("stock_qty")) or flt(args.get("qty"))
sum_amt += amt sum_amt += amt
if pr_doc.is_cumulative: if pr_doc.is_cumulative:

View File

@@ -174,7 +174,8 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
read_only: 0, read_only: 0,
fieldtype:'Date', fieldtype:'Date',
label: __('Release Date'), label: __('Release Date'),
default: me.frm.doc.release_date default: me.frm.doc.release_date,
reqd: 1
}, },
{ {
fieldname: 'hold_comment', fieldname: 'hold_comment',

View File

@@ -13,23 +13,18 @@
"supplier_name", "supplier_name",
"tax_id", "tax_id",
"due_date", "due_date",
"is_paid",
"is_return",
"apply_tds",
"column_break1", "column_break1",
"company", "company",
"posting_date", "posting_date",
"posting_time", "posting_time",
"set_posting_time", "set_posting_time",
"is_paid",
"is_return",
"apply_tds",
"amended_from", "amended_from",
"accounting_dimensions_section", "accounting_dimensions_section",
"cost_center", "cost_center",
"dimension_col_break", "dimension_col_break",
"sb_14",
"on_hold",
"release_date",
"cb_17",
"hold_comment",
"supplier_invoice_details", "supplier_invoice_details",
"bill_no", "bill_no",
"column_break_15", "column_break_15",
@@ -73,9 +68,9 @@
"base_total", "base_total",
"base_net_total", "base_net_total",
"column_break_28", "column_break_28",
"total_net_weight",
"total", "total",
"net_total", "net_total",
"total_net_weight",
"taxes_section", "taxes_section",
"tax_category", "tax_category",
"column_break_49", "column_break_49",
@@ -137,10 +132,15 @@
"terms", "terms",
"printing_settings", "printing_settings",
"letter_head", "letter_head",
"group_same_items",
"column_break_112",
"select_print_heading", "select_print_heading",
"column_break_112",
"group_same_items",
"language", "language",
"sb_14",
"on_hold",
"release_date",
"cb_17",
"hold_comment",
"more_info", "more_info",
"credit_to", "credit_to",
"party_account_currency", "party_account_currency",
@@ -190,6 +190,7 @@
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Supplier", "options": "Supplier",
"print_hide": 1, "print_hide": 1,
"reqd": 1,
"search_index": 1 "search_index": 1
}, },
{ {
@@ -1232,6 +1233,7 @@
"print_hide": 1 "print_hide": 1
}, },
{ {
"collapsible": 1,
"fieldname": "subscription_section", "fieldname": "subscription_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Subscription Section", "label": "Subscription Section",
@@ -1298,7 +1300,7 @@
"idx": 204, "idx": 204,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2019-12-30 19:13:49.610538", "modified": "2020-04-17 13:05:25.199832",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice", "name": "Purchase Invoice",

View File

@@ -12,15 +12,11 @@
"item_name", "item_name",
"description_section", "description_section",
"description", "description",
"item_group",
"brand", "brand",
"image_section", "col_break7",
"item_group",
"image", "image",
"image_view", "image_view",
"manufacture_details",
"manufacturer",
"column_break_13",
"manufacturer_part_no",
"quantity_and_rate", "quantity_and_rate",
"received_qty", "received_qty",
"qty", "qty",
@@ -55,20 +51,19 @@
"item_tax_amount", "item_tax_amount",
"landed_cost_voucher_amount", "landed_cost_voucher_amount",
"rm_supp_cost", "rm_supp_cost",
"item_weight_details",
"weight_per_unit",
"total_weight",
"column_break_38",
"weight_uom",
"warehouse_section", "warehouse_section",
"warehouse", "warehouse",
"rejected_warehouse",
"from_warehouse", "from_warehouse",
"quality_inspection", "quality_inspection",
"batch_no",
"col_br_wh",
"serial_no", "serial_no",
"col_br_wh",
"rejected_warehouse",
"batch_no",
"rejected_serial_no", "rejected_serial_no",
"manufacture_details",
"manufacturer",
"column_break_13",
"manufacturer_part_no",
"accounting", "accounting",
"expense_account", "expense_account",
"col_break5", "col_break5",
@@ -92,6 +87,11 @@
"po_detail", "po_detail",
"purchase_receipt", "purchase_receipt",
"pr_detail", "pr_detail",
"item_weight_details",
"weight_per_unit",
"total_weight",
"column_break_38",
"weight_uom",
"accounting_dimensions_section", "accounting_dimensions_section",
"project", "project",
"dimension_col_break", "dimension_col_break",
@@ -550,23 +550,21 @@
}, },
{ {
"fieldname": "brand", "fieldname": "brand",
"fieldtype": "Data",
"hidden": 1,
"label": "Brand",
"oldfieldname": "brand",
"oldfieldtype": "Data",
"print_hide": 1
},
{
"fieldname": "item_group",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 1,
"label": "Item Group", "label": "Brand",
"oldfieldname": "item_group",
"oldfieldtype": "Link",
"options": "Item Group",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "options": "Brand"
},
{
"fetch_from": "item_code.item_group",
"fetch_if_empty": 1,
"fieldname": "item_group",
"fieldtype": "Link",
"label": "Item Group",
"print_hide": 1,
"read_only": 1,
"options": "Item Group"
}, },
{ {
"description": "Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges", "description": "Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges",
@@ -720,12 +718,6 @@
"fieldname": "section_break_82", "fieldname": "section_break_82",
"fieldtype": "Section Break" "fieldtype": "Section Break"
}, },
{
"collapsible": 1,
"fieldname": "image_section",
"fieldtype": "Section Break",
"label": "Image"
},
{ {
"collapsible": 1, "collapsible": 1,
"fieldname": "accounting_dimensions_section", "fieldname": "accounting_dimensions_section",
@@ -737,6 +729,7 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"collapsible": 1,
"fieldname": "manufacture_details", "fieldname": "manufacture_details",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Manufacture" "label": "Manufacture"
@@ -754,8 +747,7 @@
{ {
"fieldname": "manufacturer_part_no", "fieldname": "manufacturer_part_no",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Manufacturer Part Number", "label": "Manufacturer Part Number"
"read_only": 1
}, },
{ {
"depends_on": "is_fixed_asset", "depends_on": "is_fixed_asset",
@@ -772,12 +764,17 @@
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"label": "Supplier Warehouse", "label": "Supplier Warehouse",
"options": "Warehouse" "options": "Warehouse"
},
{
"collapsible": 1,
"fieldname": "col_break7",
"fieldtype": "Column Break"
} }
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-04-01 14:20:17.297284", "modified": "2020-04-22 10:37:35.103176",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice Item", "name": "Purchase Invoice Item",

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"allow_import": 1, "allow_import": 1,
"autoname": "naming_series:", "autoname": "naming_series:",
"creation": "2013-05-24 19:29:05", "creation": "2013-05-24 19:29:05",
@@ -74,9 +75,9 @@
"base_total", "base_total",
"base_net_total", "base_net_total",
"column_break_32", "column_break_32",
"total_net_weight",
"total", "total",
"net_total", "net_total",
"total_net_weight",
"taxes_section", "taxes_section",
"taxes_and_charges", "taxes_and_charges",
"column_break_38", "column_break_38",
@@ -1577,7 +1578,8 @@
"icon": "fa fa-file-text", "icon": "fa fa-file-text",
"idx": 181, "idx": 181,
"is_submittable": 1, "is_submittable": 1,
"modified": "2020-02-10 04:57:11.221180", "links": [],
"modified": "2020-04-17 12:38:41.435728",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",

View File

@@ -440,11 +440,12 @@ class SalesInvoice(SellingController):
if pos.get("company_address"): if pos.get("company_address"):
self.company_address = pos.get("company_address") self.company_address = pos.get("company_address")
customer_price_list, customer_group = frappe.get_value("Customer", self.customer, ['default_price_list', 'customer_group']) if self.customer:
customer_price_list, customer_group = frappe.get_value("Customer", self.customer, ['default_price_list', 'customer_group'])
customer_group_price_list = frappe.get_value("Customer Group", customer_group, 'default_price_list') customer_group_price_list = frappe.get_value("Customer Group", customer_group, 'default_price_list')
selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list')
selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list') else:
selling_price_list = pos.get('selling_price_list')
if selling_price_list: if selling_price_list:
self.set('selling_price_list', selling_price_list) self.set('selling_price_list', selling_price_list)

View File

@@ -1926,16 +1926,6 @@ class TestSalesInvoice(unittest.TestCase):
item.taxes = [] item.taxes = []
item.save() item.save()
def test_customer_provided_parts_si(self):
create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
si = create_sales_invoice(item_code='CUST-0987', rate=0)
self.assertEqual(si.get("items")[0].allow_zero_valuation_rate, 1)
self.assertEqual(si.get("items")[0].amount, 0)
# test if Sales Invoice with rate is allowed
si2 = create_sales_invoice(item_code='CUST-0987', do_not_save=True)
self.assertRaises(frappe.ValidationError, si2.save)
def create_sales_invoice(**args): def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice") si = frappe.new_doc("Sales Invoice")
args = frappe._dict(args) args = frappe._dict(args)

View File

@@ -2,7 +2,7 @@
<h4 class="text-center"> <h4 class="text-center">
{% if (filters.party_name) { %} {% if (filters.party_name) { %}
{%= filters.party_name %} {%= filters.party_name %}
{% } else if (filters.party) { %} {% } else if (filters.party && filters.party.length) { %}
{%= filters.party %} {%= filters.party %}
{% } else if (filters.account) { %} {% } else if (filters.account) { %}
{%= filters.account %} {%= filters.account %}

View File

@@ -365,6 +365,7 @@ def get_columns(filters):
columns = [ columns = [
{ {
"label": _("GL Entry"),
"fieldname": "gl_entry", "fieldname": "gl_entry",
"fieldtype": "Link", "fieldtype": "Link",
"options": "GL Entry", "options": "GL Entry",

View File

@@ -11,6 +11,7 @@ from frappe.model.document import Document
class AssetCategory(Document): class AssetCategory(Document):
def validate(self): def validate(self):
self.validate_finance_books() self.validate_finance_books()
self.validate_accounts()
def validate_finance_books(self): def validate_finance_books(self):
for d in self.finance_books: for d in self.finance_books:
@@ -18,6 +19,27 @@ class AssetCategory(Document):
if cint(d.get(frappe.scrub(field)))<1: if cint(d.get(frappe.scrub(field)))<1:
frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError) frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError)
def validate_accounts(self):
account_type_map = {
'fixed_asset_account': { 'account_type': 'Fixed Asset' },
'accumulated_depreciation_account': { 'account_type': 'Accumulated Depreciation' },
'depreciation_expense_account': { 'root_type': 'Expense' },
'capital_work_in_progress_account': { 'account_type': 'Capital Work in Progress' }
}
for d in self.accounts:
for fieldname in account_type_map.keys():
if d.get(fieldname):
selected_account = d.get(fieldname)
key_to_match = next(iter(account_type_map.get(fieldname))) # acount_type or root_type
selected_key_type = frappe.db.get_value('Account', selected_account, key_to_match)
expected_key_type = account_type_map[fieldname][key_to_match]
if selected_key_type != expected_key_type:
frappe.throw(_("Row #{}: {} of {} should be {}. Please modify the account or select a different account.")
.format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_type)),
title=_("Invalid Account"))
@frappe.whitelist() @frappe.whitelist()
def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None): def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None):
if item and frappe.db.get_value("Item", item, "is_fixed_asset"): if item and frappe.db.get_value("Item", item, "is_fixed_asset"):

View File

@@ -24,26 +24,6 @@ frappe.ui.form.on('Asset Maintenance', {
return indicator; return indicator;
} }
); );
frm.set_query('select_serial_no', function(doc){
return {
asset: frm.doc.asset_name
}
})
},
select_serial_no: (frm) => {
let serial_nos = frm.doc.serial_no || frm.doc.select_serial_no;
if (serial_nos) {
serial_nos = serial_nos.split('\n');
serial_nos.push(frm.doc.select_serial_no);
const unique_sn = serial_nos.filter(function(elem, index, self) {
return index === self.indexOf(elem);
});
frm.set_value("serial_no", unique_sn.join('\n'));
}
}, },
refresh: (frm) => { refresh: (frm) => {
@@ -93,25 +73,6 @@ frappe.ui.form.on('Asset Maintenance Task', {
}, },
end_date: (frm, cdt, cdn) => { end_date: (frm, cdt, cdn) => {
get_next_due_date(frm, cdt, cdn); get_next_due_date(frm, cdt, cdn);
},
assign_to: (frm, cdt, cdn) => {
var d = locals[cdt][cdn];
if (frm.doc.__islocal) {
frappe.model.set_value(cdt, cdn, "assign_to", "");
frappe.model.set_value(cdt, cdn, "assign_to_name", "");
frappe.throw(__("Please save before assigning task."));
}
if (d.assign_to) {
return frappe.call({
method: 'erpnext.assets.doctype.asset_maintenance.asset_maintenance.assign_tasks',
args: {
asset_maintenance_name: frm.doc.name,
assign_to_member: d.assign_to,
maintenance_task: d.maintenance_task,
next_due_date: d.next_due_date
}
});
}
} }
}); });

View File

@@ -16,12 +16,11 @@ class AssetMaintenance(Document):
throw(_("Start date should be less than end date for task {0}").format(task.maintenance_task)) throw(_("Start date should be less than end date for task {0}").format(task.maintenance_task))
if getdate(task.next_due_date) < getdate(nowdate()): if getdate(task.next_due_date) < getdate(nowdate()):
task.maintenance_status = "Overdue" task.maintenance_status = "Overdue"
if not task.assign_to and self.docstatus == 0:
throw(_("Row #{}: Please asign task to a member.").format(task.idx))
def on_update(self): def on_update(self):
for task in self.get('asset_maintenance_tasks'): for task in self.get('asset_maintenance_tasks'):
if not task.assign_to:
task.db_set("assign_to", self.maintenance_manager)
task.db_set("assign_to_name", self.maintenance_manager_name)
assign_tasks(self.name, task.assign_to, task.maintenance_task, task.next_due_date) assign_tasks(self.name, task.assign_to, task.maintenance_task, task.next_due_date)
self.sync_maintenance_tasks() self.sync_maintenance_tasks()
@@ -108,7 +107,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task):
@frappe.whitelist() @frappe.whitelist()
def get_team_members(doctype, txt, searchfield, start, page_len, filters): def get_team_members(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.get_values('Maintenance Team Member', {'parent':filters.get("maintenance_team")}) return frappe.db.get_values('Maintenance Team Member', { 'parent': filters.get("maintenance_team") })
@frappe.whitelist() @frappe.whitelist()
def get_maintenance_log(asset_name): def get_maintenance_log(asset_name):

View File

@@ -125,13 +125,15 @@ def get_maintenance_tasks():
"start_date": nowdate(), "start_date": nowdate(),
"periodicity": "Monthly", "periodicity": "Monthly",
"maintenance_type": "Preventive Maintenance", "maintenance_type": "Preventive Maintenance",
"maintenance_status": "Planned" "maintenance_status": "Planned",
"assign_to": "marcus@abc.com"
}, },
{"maintenance_task": "Check Gears", {"maintenance_task": "Check Gears",
"start_date": nowdate(), "start_date": nowdate(),
"periodicity": "Yearly", "periodicity": "Yearly",
"maintenance_type": "Calibration", "maintenance_type": "Calibration",
"maintenance_status": "Planned" "maintenance_status": "Planned",
"assign_to": "thalia@abc.com"
} }
] ]

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"allow_import": 1, "allow_import": 1,
"autoname": "naming_series:", "autoname": "naming_series:",
"creation": "2013-05-21 16:16:39", "creation": "2013-05-21 16:16:39",
@@ -63,9 +64,9 @@
"base_total", "base_total",
"base_net_total", "base_net_total",
"column_break_26", "column_break_26",
"total_net_weight",
"total", "total",
"net_total", "net_total",
"total_net_weight",
"taxes_section", "taxes_section",
"tax_category", "tax_category",
"column_break_50", "column_break_50",
@@ -170,8 +171,8 @@
"search_index": 1 "search_index": 1
}, },
{ {
"description": "Fetch items based on Default Supplier.",
"depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))", "depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))",
"description": "Fetch items based on Default Supplier.",
"fieldname": "get_items_from_open_material_requests", "fieldname": "get_items_from_open_material_requests",
"fieldtype": "Button", "fieldtype": "Button",
"label": "Get Items from Open Material Requests" "label": "Get Items from Open Material Requests"
@@ -1054,7 +1055,8 @@
"icon": "fa fa-file-text", "icon": "fa fa-file-text",
"idx": 105, "idx": 105,
"is_submittable": 1, "is_submittable": 1,
"modified": "2020-01-14 18:54:39.694448", "links": [],
"modified": "2020-04-17 13:04:28.185197",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order", "name": "Purchase Order",

View File

@@ -702,8 +702,7 @@
{ {
"fieldname": "manufacturer_part_no", "fieldname": "manufacturer_part_no",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Manufacturer Part Number", "label": "Manufacturer Part Number"
"read_only": 1
}, },
{ {
"default": "0", "default": "0",
@@ -723,7 +722,7 @@
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2019-12-06 13:17:12.142799", "modified": "2020-04-07 18:35:17.558928",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order Item", "name": "Purchase Order Item",

View File

@@ -1,24 +1,31 @@
{ {
"actions": [],
"creation": "2013-02-22 01:27:42", "creation": "2013-02-22 01:27:42",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"main_item_code", "main_item_code",
"rm_item_code",
"description", "description",
"batch_no", "bom_detail_no",
"serial_no",
"col_break1", "col_break1",
"required_qty", "rm_item_code",
"consumed_qty",
"stock_uom", "stock_uom",
"rate",
"amount",
"conversion_factor", "conversion_factor",
"current_stock",
"reference_name", "reference_name",
"bom_detail_no" "secbreak_1",
"rate",
"col_break2",
"amount",
"secbreak_2",
"required_qty",
"col_break3",
"consumed_qty",
"current_stock",
"secbreak_3",
"batch_no",
"col_break4",
"serial_no"
], ],
"fields": [ "fields": [
{ {
@@ -152,11 +159,36 @@
"oldfieldname": "bom_detail_no", "oldfieldname": "bom_detail_no",
"oldfieldtype": "Data", "oldfieldtype": "Data",
"read_only": 1 "read_only": 1
},
{
"fieldname": "secbreak_1",
"fieldtype": "Section Break"
},
{
"fieldname": "col_break2",
"fieldtype": "Column Break"
},
{
"fieldname": "secbreak_2",
"fieldtype": "Section Break"
},
{
"fieldname": "col_break3",
"fieldtype": "Column Break"
},
{
"fieldname": "secbreak_3",
"fieldtype": "Section Break"
},
{
"fieldname": "col_break4",
"fieldtype": "Column Break"
} }
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"modified": "2019-11-21 16:25:29.909112", "links": [],
"modified": "2020-04-10 18:09:33.997618",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Receipt Item Supplied", "name": "Purchase Receipt Item Supplied",

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "hash", "autoname": "hash",
"creation": "2013-05-22 12:43:10", "creation": "2013-05-22 12:43:10",
"doctype": "DocType", "doctype": "DocType",
@@ -522,8 +523,7 @@
{ {
"fieldname": "manufacturer_part_no", "fieldname": "manufacturer_part_no",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Manufacturer Part Number", "label": "Manufacturer Part Number"
"read_only": 1
}, },
{ {
"fieldname": "column_break_15", "fieldname": "column_break_15",
@@ -532,7 +532,8 @@
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"modified": "2019-06-02 05:32:46.019237", "links": [],
"modified": "2020-04-07 18:35:51.175947",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Supplier Quotation Item", "name": "Supplier Quotation Item",

View File

@@ -214,5 +214,41 @@ def get_data():
"label": _("Lab Test Report") "label": _("Lab Test Report")
} }
] ]
},
{
"label": _("Rehabilitation"),
"icon": "icon-cog",
"items": [
{
"type": "doctype",
"name": "Exercise Type",
"label": _("Exercise Type")
},
{
"type": "doctype",
"name": "Exercise Difficulty Level",
"label": _("Exercise Difficulty Level")
},
{
"type": "doctype",
"name": "Therapy Type",
"label": _("Therapy Type")
},
{
"type": "doctype",
"name": "Therapy Plan",
"label": _("Therapy Plan")
},
{
"type": "doctype",
"name": "Therapy Session",
"label": _("Therapy Session")
},
{
"type": "doctype",
"name": "Motor Assessment Scale",
"label": _("Motor Assessment Scale")
}
]
} }
] ]

View File

@@ -1123,36 +1123,39 @@ def get_supplier_block_status(party_name):
} }
return info return info
def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_code): def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
""" """
Returns a Sales Order Item child item containing the default values Returns a Sales Order Item child item containing the default values
""" """
p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname) child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname)
item = frappe.get_doc("Item", item_code) item = frappe.get_doc("Item", trans_item.get('item_code'))
child_item.item_code = item.item_code child_item.item_code = item.item_code
child_item.item_name = item.item_name child_item.item_name = item.item_name
child_item.description = item.description child_item.description = item.description
child_item.reqd_by_date = p_doc.delivery_date child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date
child_item.uom = item.stock_uom child_item.uom = item.stock_uom
child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
if not child_item.warehouse:
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
.format(frappe.bold("default warehouse"), frappe.bold(item.item_code)))
return child_item return child_item
def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_code): def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
""" """
Returns a Purchase Order Item child item containing the default values Returns a Purchase Order Item child item containing the default values
""" """
p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname) child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname)
item = frappe.get_doc("Item", item_code) item = frappe.get_doc("Item", trans_item.get('item_code'))
child_item.item_code = item.item_code child_item.item_code = item.item_code
child_item.item_name = item.item_name child_item.item_name = item.item_name
child_item.description = item.description child_item.description = item.description
child_item.schedule_date = p_doc.schedule_date child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date
child_item.uom = item.stock_uom child_item.uom = item.stock_uom
child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_rate = 1 # Initiallize value will update in parent validation
child_item.base_amount = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation
return child_item return child_item
@@ -1196,9 +1199,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
if not d.get("docname"): if not d.get("docname"):
new_child_flag = True new_child_flag = True
if parent_doctype == "Sales Order": if parent_doctype == "Sales Order":
child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code")) child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d)
if parent_doctype == "Purchase Order": if parent_doctype == "Purchase Order":
child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code")) child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d)
else: else:
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")): if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")):
@@ -1243,6 +1246,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
child_item.flags.ignore_validate_update_after_submit = True child_item.flags.ignore_validate_update_after_submit = True
if new_child_flag: if new_child_flag:
parent.load_from_db()
child_item.idx = len(parent.items) + 1 child_item.idx = len(parent.items) + 1
child_item.insert() child_item.insert()
else: else:

View File

@@ -46,6 +46,7 @@ class SellingController(StockController):
set_default_income_account_for_item(self) set_default_income_account_for_item(self)
self.set_customer_address() self.set_customer_address()
self.validate_for_duplicate_items() self.validate_for_duplicate_items()
self.validate_target_warehouse()
def set_missing_values(self, for_validate=False): def set_missing_values(self, for_validate=False):
@@ -403,6 +404,14 @@ class SellingController(StockController):
else: else:
chk_dupl_itm.append(f) chk_dupl_itm.append(f)
def validate_target_warehouse(self):
items = self.get("items") + (self.get("packed_items") or [])
for d in items:
if d.get("target_warehouse") and d.get("warehouse") == d.get("target_warehouse"):
warehouse = frappe.bold(d.get("target_warehouse"))
frappe.throw(_("Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same")
.format(d.idx, warehouse, warehouse))
def validate_items(self): def validate_items(self):
# validate items to see if they have is_sales_item enabled # validate items to see if they have is_sales_item enabled

View File

@@ -383,9 +383,6 @@ class StockController(AccountsController):
# Customer Provided parts will have zero valuation rate # Customer Provided parts will have zero valuation rate
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
d.allow_zero_valuation_rate = 1 d.allow_zero_valuation_rate = 1
if d.parenttype in ["Delivery Note", "Sales Invoice"] and d.rate:
frappe.throw(_("Row #{0}: {1} cannot have {2} as it is a Customer Provided Item")
.format(d.idx, frappe.bold(d.item_code), frappe.bold("Rate")))
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
warehouse_account=None, company=None): warehouse_account=None, company=None):

View File

@@ -13,7 +13,7 @@ class TestMapper(unittest.TestCase):
'''Test mapping of multiple source docs on a single target doc''' '''Test mapping of multiple source docs on a single target doc'''
make_test_records("Item") make_test_records("Item")
items = frappe.get_all("Item", fields = ["name", "item_code"], filters = {'is_sales_item': 1, 'has_variants': 0}) items = frappe.get_all("Item", fields = ["name", "item_code"], filters = {'is_sales_item': 1, 'has_variants': 0, 'disabled': 0})
customers = frappe.get_all("Customer") customers = frappe.get_all("Customer")
if items and customers: if items and customers:
# Make source docs (quotations) and a target doc (sales order) # Make source docs (quotations) and a target doc (sales order)

View File

@@ -336,3 +336,27 @@ def make_opportunity_from_communication(communication, ignore_communication_link
link_communication_to_document(doc, "Opportunity", opportunity.name, ignore_communication_links) link_communication_to_document(doc, "Opportunity", opportunity.name, ignore_communication_links)
return opportunity.name return opportunity.name
@frappe.whitelist()
def get_events(start, end, filters=None):
"""Returns events for Gantt / Calendar view rendering.
:param start: Start date-time.
:param end: End date-time.
:param filters: Filters (JSON).
"""
from frappe.desk.calendar import get_event_conditions
conditions = get_event_conditions("Opportunity", filters)
data = frappe.db.sql("""
select
distinct `tabOpportunity`.name, `tabOpportunity`.customer_name, `tabOpportunity`.opportunity_amount,
`tabOpportunity`.title, `tabOpportunity`.contact_date
from
`tabOpportunity`
where
(`tabOpportunity`.contact_date between %(start)s and %(end)s)
{conditions}
""".format(conditions=conditions), {
"start": start,
"end": end
}, as_dict=True, update={"allDay": 0})
return data

View File

@@ -0,0 +1,19 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.views.calendar["Opportunity"] = {
field_map: {
"start": "contact_date",
"end": "contact_date",
"id": "name",
"title": "customer_name",
"allDay": "allDay"
},
options: {
header: {
left: 'prev,next today',
center: 'title',
right: 'month'
}
},
get_events_method: 'erpnext.crm.doctype.opportunity.opportunity.get_events'
}

View File

@@ -67,11 +67,11 @@ def add_bank_accounts(response, bank, company):
frappe.throw(_("Please setup a default bank account for company {0}").format(company)) frappe.throw(_("Please setup a default bank account for company {0}").format(company))
for account in response["accounts"]: for account in response["accounts"]:
acc_type = frappe.db.get_value("Account Type", account["type"]) acc_type = frappe.db.get_value("Bank Account Type", account["type"])
if not acc_type: if not acc_type:
add_account_type(account["type"]) add_account_type(account["type"])
acc_subtype = frappe.db.get_value("Account Subtype", account["subtype"]) acc_subtype = frappe.db.get_value("Bank Account Subtype", account["subtype"])
if not acc_subtype: if not acc_subtype:
add_account_subtype(account["subtype"]) add_account_subtype(account["subtype"])
@@ -106,7 +106,7 @@ def add_bank_accounts(response, bank, company):
def add_account_type(account_type): def add_account_type(account_type):
try: try:
frappe.get_doc({ frappe.get_doc({
"doctype": "Account Type", "doctype": "Bank Account Type",
"account_type": account_type "account_type": account_type
}).insert() }).insert()
except Exception: except Exception:
@@ -116,7 +116,7 @@ def add_account_type(account_type):
def add_account_subtype(account_subtype): def add_account_subtype(account_subtype):
try: try:
frappe.get_doc({ frappe.get_doc({
"doctype": "Account Subtype", "doctype": "Bank Account Subtype",
"account_subtype": account_subtype "account_subtype": account_subtype
}).insert() }).insert()
except Exception: except Exception:

View File

@@ -23,11 +23,11 @@ class TestPlaidSettings(unittest.TestCase):
for ba in frappe.get_all("Bank Account"): for ba in frappe.get_all("Bank Account"):
frappe.get_doc("Bank Account", ba.name).delete() frappe.get_doc("Bank Account", ba.name).delete()
for at in frappe.get_all("Account Type"): for at in frappe.get_all("Bank Account Type"):
frappe.get_doc("Account Type", at.name).delete() frappe.get_doc("Bank Account Type", at.name).delete()
for ast in frappe.get_all("Account Subtype"): for ast in frappe.get_all("Bank Account Subtype"):
frappe.get_doc("Account Subtype", ast.name).delete() frappe.get_doc("Bank Account Subtype", ast.name).delete()
def test_plaid_disabled(self): def test_plaid_disabled(self):
frappe.db.set_value("Plaid Settings", None, "enabled", 0) frappe.db.set_value("Plaid Settings", None, "enabled", 0)
@@ -35,11 +35,11 @@ class TestPlaidSettings(unittest.TestCase):
def test_add_account_type(self): def test_add_account_type(self):
add_account_type("brokerage") add_account_type("brokerage")
self.assertEqual(frappe.get_doc("Account Type", "brokerage").name, "brokerage") self.assertEqual(frappe.get_doc("Bank Account Type", "brokerage").name, "brokerage")
def test_add_account_subtype(self): def test_add_account_subtype(self):
add_account_subtype("loan") add_account_subtype("loan")
self.assertEqual(frappe.get_doc("Account Subtype", "loan").name, "loan") self.assertEqual(frappe.get_doc("Bank Account Subtype", "loan").name, "loan")
def test_default_bank_account(self): def test_default_bank_account(self):
if not frappe.db.exists("Bank", "Citi"): if not frappe.db.exists("Bank", "Citi"):

View File

@@ -2,15 +2,40 @@
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on('Tally Migration', { frappe.ui.form.on('Tally Migration', {
onload: function(frm) { onload: function (frm) {
let reload_status = true;
frappe.realtime.on("tally_migration_progress_update", function (data) { frappe.realtime.on("tally_migration_progress_update", function (data) {
if (reload_status) {
frappe.model.with_doc(frm.doc.doctype, frm.doc.name, () => {
frm.refresh_header();
});
reload_status = false;
}
frm.dashboard.show_progress(data.title, (data.count / data.total) * 100, data.message); frm.dashboard.show_progress(data.title, (data.count / data.total) * 100, data.message);
if (data.count == data.total) { let error_occurred = data.count === -1;
window.setTimeout(title => frm.dashboard.hide_progress(title), 1500, data.title); if (data.count == data.total || error_occurred) {
window.setTimeout((title) => {
frm.dashboard.hide_progress(title);
frm.reload_doc();
if (error_occurred) {
frappe.msgprint({
message: __("An error has occurred during {0}. Check {1} for more details",
[
repl("<a href='#Form/Tally Migration/%(tally_document)s' class='variant-click'>%(tally_document)s</a>", {
tally_document: frm.docname
}),
"<a href='#List/Error Log' class='variant-click'>Error Log</a>"
]
),
title: __("Tally Migration Error"),
indicator: "red"
});
}
}, 2000, data.title);
} }
}); });
}, },
refresh: function(frm) { refresh: function (frm) {
if (frm.doc.master_data && !frm.doc.is_master_data_imported) { if (frm.doc.master_data && !frm.doc.is_master_data_imported) {
if (frm.doc.is_master_data_processed) { if (frm.doc.is_master_data_processed) {
if (frm.doc.status != "Importing Master Data") { if (frm.doc.status != "Importing Master Data") {
@@ -34,17 +59,17 @@ frappe.ui.form.on('Tally Migration', {
} }
} }
}, },
add_button: function(frm, label, method) { add_button: function (frm, label, method) {
frm.add_custom_button( frm.add_custom_button(
label, label,
() => frm.call({ () => {
doc: frm.doc, frm.call({
method: method, doc: frm.doc,
freeze: true, method: method,
callback: () => { freeze: true
frm.remove_custom_button(label); });
} frm.reload_doc();
}) }
); );
} }
}); });

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"beta": 1, "beta": 1,
"creation": "2019-02-01 14:27:09.485238", "creation": "2019-02-01 14:27:09.485238",
"doctype": "DocType", "doctype": "DocType",
@@ -14,6 +15,7 @@
"tally_debtors_account", "tally_debtors_account",
"company_section", "company_section",
"tally_company", "tally_company",
"default_uom",
"column_break_8", "column_break_8",
"erpnext_company", "erpnext_company",
"processed_files_section", "processed_files_section",
@@ -43,6 +45,7 @@
"label": "Status" "label": "Status"
}, },
{ {
"description": "Data exported from Tally that consists of the Chart of Accounts, Customers, Suppliers, Addresses, Items and UOMs",
"fieldname": "master_data", "fieldname": "master_data",
"fieldtype": "Attach", "fieldtype": "Attach",
"in_list_view": 1, "in_list_view": 1,
@@ -50,6 +53,7 @@
}, },
{ {
"default": "Sundry Creditors", "default": "Sundry Creditors",
"description": "Creditors Account set in Tally",
"fieldname": "tally_creditors_account", "fieldname": "tally_creditors_account",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Tally Creditors Account", "label": "Tally Creditors Account",
@@ -61,6 +65,7 @@
}, },
{ {
"default": "Sundry Debtors", "default": "Sundry Debtors",
"description": "Debtors Account set in Tally",
"fieldname": "tally_debtors_account", "fieldname": "tally_debtors_account",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Tally Debtors Account", "label": "Tally Debtors Account",
@@ -72,6 +77,7 @@
"fieldtype": "Section Break" "fieldtype": "Section Break"
}, },
{ {
"description": "Company Name as per Imported Tally Data",
"fieldname": "tally_company", "fieldname": "tally_company",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Tally Company", "label": "Tally Company",
@@ -82,9 +88,11 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"description": "Your Company set in ERPNext",
"fieldname": "erpnext_company", "fieldname": "erpnext_company",
"fieldtype": "Data", "fieldtype": "Data",
"label": "ERPNext Company" "label": "ERPNext Company",
"read_only_depends_on": "eval:doc.is_master_data_processed == 1"
}, },
{ {
"fieldname": "processed_files_section", "fieldname": "processed_files_section",
@@ -155,24 +163,28 @@
"options": "Cost Center" "options": "Cost Center"
}, },
{ {
"default": "0",
"fieldname": "is_master_data_processed", "fieldname": "is_master_data_processed",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Master Data Processed", "label": "Is Master Data Processed",
"read_only": 1 "read_only": 1
}, },
{ {
"default": "0",
"fieldname": "is_day_book_data_processed", "fieldname": "is_day_book_data_processed",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Day Book Data Processed", "label": "Is Day Book Data Processed",
"read_only": 1 "read_only": 1
}, },
{ {
"default": "0",
"fieldname": "is_day_book_data_imported", "fieldname": "is_day_book_data_imported",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Day Book Data Imported", "label": "Is Day Book Data Imported",
"read_only": 1 "read_only": 1
}, },
{ {
"default": "0",
"fieldname": "is_master_data_imported", "fieldname": "is_master_data_imported",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Master Data Imported", "label": "Is Master Data Imported",
@@ -188,13 +200,23 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"description": "Day Book Data exported from Tally that consists of all historic transactions",
"fieldname": "day_book_data", "fieldname": "day_book_data",
"fieldtype": "Attach", "fieldtype": "Attach",
"in_list_view": 1, "in_list_view": 1,
"label": "Day Book Data" "label": "Day Book Data"
},
{
"default": "Unit",
"description": "UOM in case unspecified in imported data",
"fieldname": "default_uom",
"fieldtype": "Link",
"label": "Default UOM",
"options": "UOM"
} }
], ],
"modified": "2019-04-29 05:46:54.394967", "links": [],
"modified": "2020-04-16 13:03:28.894919",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "ERPNext Integrations", "module": "ERPNext Integrations",
"name": "Tally Migration", "name": "Tally Migration",

View File

@@ -4,20 +4,23 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from decimal import Decimal
import json import json
import re import re
import traceback import traceback
import zipfile import zipfile
from decimal import Decimal
from bs4 import BeautifulSoup as bs
import frappe import frappe
from erpnext import encode_company_abbr
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts
from frappe import _ from frappe import _
from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from frappe.model.document import Document from frappe.model.document import Document
from frappe.model.naming import getseries, revert_series_if_last from frappe.model.naming import getseries, revert_series_if_last
from frappe.utils.data import format_datetime from frappe.utils.data import format_datetime
from bs4 import BeautifulSoup as bs
from erpnext import encode_company_abbr
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts
PRIMARY_ACCOUNT = "Primary" PRIMARY_ACCOUNT = "Primary"
VOUCHER_CHUNK_SIZE = 500 VOUCHER_CHUNK_SIZE = 500
@@ -39,13 +42,15 @@ class TallyMigration(Document):
return string return string
master_file = frappe.get_doc("File", {"file_url": data_file}) master_file = frappe.get_doc("File", {"file_url": data_file})
master_file_path = master_file.get_full_path()
with zipfile.ZipFile(master_file.get_full_path()) as zf: if zipfile.is_zipfile(master_file_path):
encoded_content = zf.read(zf.namelist()[0]) with zipfile.ZipFile(master_file_path) as zf:
try: encoded_content = zf.read(zf.namelist()[0])
content = encoded_content.decode("utf-8-sig") try:
except UnicodeDecodeError: content = encoded_content.decode("utf-8-sig")
content = encoded_content.decode("utf-16") except UnicodeDecodeError:
content = encoded_content.decode("utf-16")
master = bs(sanitize(emptify(content)), "xml") master = bs(sanitize(emptify(content)), "xml")
collection = master.BODY.IMPORTDATA.REQUESTDATA collection = master.BODY.IMPORTDATA.REQUESTDATA
@@ -58,13 +63,14 @@ class TallyMigration(Document):
"file_name": key + ".json", "file_name": key + ".json",
"attached_to_doctype": self.doctype, "attached_to_doctype": self.doctype,
"attached_to_name": self.name, "attached_to_name": self.name,
"content": json.dumps(value) "content": json.dumps(value),
"is_private": True
}).insert() }).insert()
setattr(self, key, f.file_url) setattr(self, key, f.file_url)
def _process_master_data(self): def _process_master_data(self):
def get_company_name(collection): def get_company_name(collection):
return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string.strip()
def get_coa_customers_suppliers(collection): def get_coa_customers_suppliers(collection):
root_type_map = { root_type_map = {
@@ -97,17 +103,17 @@ class TallyMigration(Document):
# If Ledger doesn't have PARENT field then don't create Account # If Ledger doesn't have PARENT field then don't create Account
# For example "Profit & Loss A/c" # For example "Profit & Loss A/c"
if account.PARENT: if account.PARENT:
yield account.PARENT.string, account["NAME"], 0 yield account.PARENT.string.strip(), account["NAME"], 0
def get_parent(account): def get_parent(account):
if account.PARENT: if account.PARENT:
return account.PARENT.string return account.PARENT.string.strip()
return { return {
("Yes", "No"): "Application of Funds (Assets)", ("Yes", "No"): "Application of Funds (Assets)",
("Yes", "Yes"): "Expenses", ("Yes", "Yes"): "Expenses",
("No", "Yes"): "Income", ("No", "Yes"): "Income",
("No", "No"): "Source of Funds (Liabilities)", ("No", "No"): "Source of Funds (Liabilities)",
}[(account.ISDEEMEDPOSITIVE.string, account.ISREVENUE.string)] }[(account.ISDEEMEDPOSITIVE.string.strip(), account.ISREVENUE.string.strip())]
def get_children_and_parent_dict(accounts): def get_children_and_parent_dict(accounts):
children, parents = {}, {} children, parents = {}, {}
@@ -145,38 +151,38 @@ class TallyMigration(Document):
parties, addresses = [], [] parties, addresses = [], []
for account in collection.find_all("LEDGER"): for account in collection.find_all("LEDGER"):
party_type = None party_type = None
if account.NAME.string in customers: if account.NAME.string.strip() in customers:
party_type = "Customer" party_type = "Customer"
parties.append({ parties.append({
"doctype": party_type, "doctype": party_type,
"customer_name": account.NAME.string, "customer_name": account.NAME.string.strip(),
"tax_id": account.INCOMETAXNUMBER.string if account.INCOMETAXNUMBER else None, "tax_id": account.INCOMETAXNUMBER.string.strip() if account.INCOMETAXNUMBER else None,
"customer_group": "All Customer Groups", "customer_group": "All Customer Groups",
"territory": "All Territories", "territory": "All Territories",
"customer_type": "Individual", "customer_type": "Individual",
}) })
elif account.NAME.string in suppliers: elif account.NAME.string.strip() in suppliers:
party_type = "Supplier" party_type = "Supplier"
parties.append({ parties.append({
"doctype": party_type, "doctype": party_type,
"supplier_name": account.NAME.string, "supplier_name": account.NAME.string.strip(),
"pan": account.INCOMETAXNUMBER.string if account.INCOMETAXNUMBER else None, "pan": account.INCOMETAXNUMBER.string.strip() if account.INCOMETAXNUMBER else None,
"supplier_group": "All Supplier Groups", "supplier_group": "All Supplier Groups",
"supplier_type": "Individual", "supplier_type": "Individual",
}) })
if party_type: if party_type:
address = "\n".join([a.string for a in account.find_all("ADDRESS")]) address = "\n".join([a.string.strip() for a in account.find_all("ADDRESS")])
addresses.append({ addresses.append({
"doctype": "Address", "doctype": "Address",
"address_line1": address[:140].strip(), "address_line1": address[:140].strip(),
"address_line2": address[140:].strip(), "address_line2": address[140:].strip(),
"country": account.COUNTRYNAME.string if account.COUNTRYNAME else None, "country": account.COUNTRYNAME.string.strip() if account.COUNTRYNAME else None,
"state": account.LEDSTATENAME.string if account.LEDSTATENAME else None, "state": account.LEDSTATENAME.string.strip() if account.LEDSTATENAME else None,
"gst_state": account.LEDSTATENAME.string if account.LEDSTATENAME else None, "gst_state": account.LEDSTATENAME.string.strip() if account.LEDSTATENAME else None,
"pin_code": account.PINCODE.string if account.PINCODE else None, "pin_code": account.PINCODE.string.strip() if account.PINCODE else None,
"mobile": account.LEDGERPHONE.string if account.LEDGERPHONE else None, "mobile": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None,
"phone": account.LEDGERPHONE.string if account.LEDGERPHONE else None, "phone": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None,
"gstin": account.PARTYGSTIN.string if account.PARTYGSTIN else None, "gstin": account.PARTYGSTIN.string.strip() if account.PARTYGSTIN else None,
"links": [{"link_doctype": party_type, "link_name": account["NAME"]}], "links": [{"link_doctype": party_type, "link_name": account["NAME"]}],
}) })
return parties, addresses return parties, addresses
@@ -184,41 +190,50 @@ class TallyMigration(Document):
def get_stock_items_uoms(collection): def get_stock_items_uoms(collection):
uoms = [] uoms = []
for uom in collection.find_all("UNIT"): for uom in collection.find_all("UNIT"):
uoms.append({"doctype": "UOM", "uom_name": uom.NAME.string}) uoms.append({"doctype": "UOM", "uom_name": uom.NAME.string.strip()})
items = [] items = []
for item in collection.find_all("STOCKITEM"): for item in collection.find_all("STOCKITEM"):
stock_uom = item.BASEUNITS.string.strip() if item.BASEUNITS else self.default_uom
items.append({ items.append({
"doctype": "Item", "doctype": "Item",
"item_code" : item.NAME.string, "item_code" : item.NAME.string.strip(),
"stock_uom": item.BASEUNITS.string, "stock_uom": stock_uom.strip(),
"is_stock_item": 0, "is_stock_item": 0,
"item_group": "All Item Groups", "item_group": "All Item Groups",
"item_defaults": [{"company": self.erpnext_company}] "item_defaults": [{"company": self.erpnext_company}]
}) })
return items, uoms return items, uoms
try:
self.publish("Process Master Data", _("Reading Uploaded File"), 1, 5)
collection = self.get_collection(self.master_data)
company = get_company_name(collection)
self.tally_company = company
self.erpnext_company = company
self.publish("Process Master Data", _("Reading Uploaded File"), 1, 5) self.publish("Process Master Data", _("Processing Chart of Accounts and Parties"), 2, 5)
collection = self.get_collection(self.master_data) chart_of_accounts, customers, suppliers = get_coa_customers_suppliers(collection)
company = get_company_name(collection) self.publish("Process Master Data", _("Processing Party Addresses"), 3, 5)
self.tally_company = company parties, addresses = get_parties_addresses(collection, customers, suppliers)
self.erpnext_company = company
self.publish("Process Master Data", _("Processing Chart of Accounts and Parties"), 2, 5) self.publish("Process Master Data", _("Processing Items and UOMs"), 4, 5)
chart_of_accounts, customers, suppliers = get_coa_customers_suppliers(collection) items, uoms = get_stock_items_uoms(collection)
self.publish("Process Master Data", _("Processing Party Addresses"), 3, 5) data = {"chart_of_accounts": chart_of_accounts, "parties": parties, "addresses": addresses, "items": items, "uoms": uoms}
parties, addresses = get_parties_addresses(collection, customers, suppliers)
self.publish("Process Master Data", _("Processing Items and UOMs"), 4, 5)
items, uoms = get_stock_items_uoms(collection)
data = {"chart_of_accounts": chart_of_accounts, "parties": parties, "addresses": addresses, "items": items, "uoms": uoms}
self.publish("Process Master Data", _("Done"), 5, 5)
self.dump_processed_data(data) self.publish("Process Master Data", _("Done"), 5, 5)
self.is_master_data_processed = 1 self.dump_processed_data(data)
self.status = ""
self.save() self.is_master_data_processed = 1
except:
self.publish("Process Master Data", _("Process Failed"), -1, 5)
self.log()
finally:
self.set_status()
def publish(self, title, message, count, total): def publish(self, title, message, count, total):
frappe.publish_realtime("tally_migration_progress_update", {"title": title, "message": message, "count": count, "total": total}) frappe.publish_realtime("tally_migration_progress_update", {"title": title, "message": message, "count": count, "total": total})
@@ -256,7 +271,6 @@ class TallyMigration(Document):
except: except:
self.log(address) self.log(address)
def create_items_uoms(items_file_url, uoms_file_url): def create_items_uoms(items_file_url, uoms_file_url):
uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url}) uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url})
for uom in json.loads(uoms_file.get_content()): for uom in json.loads(uoms_file.get_content()):
@@ -273,25 +287,35 @@ class TallyMigration(Document):
except: except:
self.log(item) self.log(item)
self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4) try:
create_company_and_coa(self.chart_of_accounts) self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4)
self.publish("Import Master Data", _("Importing Parties and Addresses"), 2, 4) create_company_and_coa(self.chart_of_accounts)
create_parties_and_addresses(self.parties, self.addresses)
self.publish("Import Master Data", _("Importing Items and UOMs"), 3, 4) self.publish("Import Master Data", _("Importing Parties and Addresses"), 2, 4)
create_items_uoms(self.items, self.uoms) create_parties_and_addresses(self.parties, self.addresses)
self.publish("Import Master Data", _("Done"), 4, 4)
self.status = "" self.publish("Import Master Data", _("Importing Items and UOMs"), 3, 4)
self.is_master_data_imported = 1 create_items_uoms(self.items, self.uoms)
self.save()
self.publish("Import Master Data", _("Done"), 4, 4)
self.is_master_data_imported = 1
except:
self.publish("Import Master Data", _("Process Failed"), -1, 5)
self.log()
finally:
self.set_status()
def _process_day_book_data(self): def _process_day_book_data(self):
def get_vouchers(collection): def get_vouchers(collection):
vouchers = [] vouchers = []
for voucher in collection.find_all("VOUCHER"): for voucher in collection.find_all("VOUCHER"):
if voucher.ISCANCELLED.string == "Yes": if voucher.ISCANCELLED.string.strip() == "Yes":
continue continue
inventory_entries = voucher.find_all("INVENTORYENTRIES.LIST") + voucher.find_all("ALLINVENTORYENTRIES.LIST") + voucher.find_all("INVENTORYENTRIESIN.LIST") + voucher.find_all("INVENTORYENTRIESOUT.LIST") inventory_entries = voucher.find_all("INVENTORYENTRIES.LIST") + voucher.find_all("ALLINVENTORYENTRIES.LIST") + voucher.find_all("INVENTORYENTRIESIN.LIST") + voucher.find_all("INVENTORYENTRIESOUT.LIST")
if voucher.VOUCHERTYPENAME.string not in ["Journal", "Receipt", "Payment", "Contra"] and inventory_entries: if voucher.VOUCHERTYPENAME.string.strip() not in ["Journal", "Receipt", "Payment", "Contra"] and inventory_entries:
function = voucher_to_invoice function = voucher_to_invoice
else: else:
function = voucher_to_journal_entry function = voucher_to_journal_entry
@@ -307,15 +331,15 @@ class TallyMigration(Document):
accounts = [] accounts = []
ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST") ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST")
for entry in ledger_entries: for entry in ledger_entries:
account = {"account": encode_company_abbr(entry.LEDGERNAME.string, self.erpnext_company), "cost_center": self.default_cost_center} account = {"account": encode_company_abbr(entry.LEDGERNAME.string.strip(), self.erpnext_company), "cost_center": self.default_cost_center}
if entry.ISPARTYLEDGER.string == "Yes": if entry.ISPARTYLEDGER.string.strip() == "Yes":
party_details = get_party(entry.LEDGERNAME.string) party_details = get_party(entry.LEDGERNAME.string.strip())
if party_details: if party_details:
party_type, party_account = party_details party_type, party_account = party_details
account["party_type"] = party_type account["party_type"] = party_type
account["account"] = party_account account["account"] = party_account
account["party"] = entry.LEDGERNAME.string account["party"] = entry.LEDGERNAME.string.strip()
amount = Decimal(entry.AMOUNT.string) amount = Decimal(entry.AMOUNT.string.strip())
if amount > 0: if amount > 0:
account["credit_in_account_currency"] = str(abs(amount)) account["credit_in_account_currency"] = str(abs(amount))
else: else:
@@ -324,21 +348,21 @@ class TallyMigration(Document):
journal_entry = { journal_entry = {
"doctype": "Journal Entry", "doctype": "Journal Entry",
"tally_guid": voucher.GUID.string, "tally_guid": voucher.GUID.string.strip(),
"posting_date": voucher.DATE.string, "posting_date": voucher.DATE.string.strip(),
"company": self.erpnext_company, "company": self.erpnext_company,
"accounts": accounts, "accounts": accounts,
} }
return journal_entry return journal_entry
def voucher_to_invoice(voucher): def voucher_to_invoice(voucher):
if voucher.VOUCHERTYPENAME.string in ["Sales", "Credit Note"]: if voucher.VOUCHERTYPENAME.string.strip() in ["Sales", "Credit Note"]:
doctype = "Sales Invoice" doctype = "Sales Invoice"
party_field = "customer" party_field = "customer"
account_field = "debit_to" account_field = "debit_to"
account_name = encode_company_abbr(self.tally_debtors_account, self.erpnext_company) account_name = encode_company_abbr(self.tally_debtors_account, self.erpnext_company)
price_list_field = "selling_price_list" price_list_field = "selling_price_list"
elif voucher.VOUCHERTYPENAME.string in ["Purchase", "Debit Note"]: elif voucher.VOUCHERTYPENAME.string.strip() in ["Purchase", "Debit Note"]:
doctype = "Purchase Invoice" doctype = "Purchase Invoice"
party_field = "supplier" party_field = "supplier"
account_field = "credit_to" account_field = "credit_to"
@@ -351,10 +375,10 @@ class TallyMigration(Document):
invoice = { invoice = {
"doctype": doctype, "doctype": doctype,
party_field: voucher.PARTYNAME.string, party_field: voucher.PARTYNAME.string.strip(),
"tally_guid": voucher.GUID.string, "tally_guid": voucher.GUID.string.strip(),
"posting_date": voucher.DATE.string, "posting_date": voucher.DATE.string.strip(),
"due_date": voucher.DATE.string, "due_date": voucher.DATE.string.strip(),
"items": get_voucher_items(voucher, doctype), "items": get_voucher_items(voucher, doctype),
"taxes": get_voucher_taxes(voucher), "taxes": get_voucher_taxes(voucher),
account_field: account_name, account_field: account_name,
@@ -375,15 +399,15 @@ class TallyMigration(Document):
for entry in inventory_entries: for entry in inventory_entries:
qty, uom = entry.ACTUALQTY.string.strip().split() qty, uom = entry.ACTUALQTY.string.strip().split()
items.append({ items.append({
"item_code": entry.STOCKITEMNAME.string, "item_code": entry.STOCKITEMNAME.string.strip(),
"description": entry.STOCKITEMNAME.string, "description": entry.STOCKITEMNAME.string.strip(),
"qty": qty.strip(), "qty": qty.strip(),
"uom": uom.strip(), "uom": uom.strip(),
"conversion_factor": 1, "conversion_factor": 1,
"price_list_rate": entry.RATE.string.split("/")[0], "price_list_rate": entry.RATE.string.strip().split("/")[0],
"cost_center": self.default_cost_center, "cost_center": self.default_cost_center,
"warehouse": self.default_warehouse, "warehouse": self.default_warehouse,
account_field: encode_company_abbr(entry.find_all("ACCOUNTINGALLOCATIONS.LIST")[0].LEDGERNAME.string, self.erpnext_company), account_field: encode_company_abbr(entry.find_all("ACCOUNTINGALLOCATIONS.LIST")[0].LEDGERNAME.string.strip(), self.erpnext_company),
}) })
return items return items
@@ -391,13 +415,13 @@ class TallyMigration(Document):
ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST") ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST")
taxes = [] taxes = []
for entry in ledger_entries: for entry in ledger_entries:
if entry.ISPARTYLEDGER.string == "No": if entry.ISPARTYLEDGER.string.strip() == "No":
tax_account = encode_company_abbr(entry.LEDGERNAME.string, self.erpnext_company) tax_account = encode_company_abbr(entry.LEDGERNAME.string.strip(), self.erpnext_company)
taxes.append({ taxes.append({
"charge_type": "Actual", "charge_type": "Actual",
"account_head": tax_account, "account_head": tax_account,
"description": tax_account, "description": tax_account,
"tax_amount": entry.AMOUNT.string, "tax_amount": entry.AMOUNT.string.strip(),
"cost_center": self.default_cost_center, "cost_center": self.default_cost_center,
}) })
return taxes return taxes
@@ -408,15 +432,24 @@ class TallyMigration(Document):
elif frappe.db.exists({"doctype": "Customer", "customer_name": party}): elif frappe.db.exists({"doctype": "Customer", "customer_name": party}):
return "Customer", encode_company_abbr(self.tally_debtors_account, self.erpnext_company) return "Customer", encode_company_abbr(self.tally_debtors_account, self.erpnext_company)
self.publish("Process Day Book Data", _("Reading Uploaded File"), 1, 3) try:
collection = self.get_collection(self.day_book_data) self.publish("Process Day Book Data", _("Reading Uploaded File"), 1, 3)
self.publish("Process Day Book Data", _("Processing Vouchers"), 2, 3) collection = self.get_collection(self.day_book_data)
vouchers = get_vouchers(collection)
self.publish("Process Day Book Data", _("Done"), 3, 3) self.publish("Process Day Book Data", _("Processing Vouchers"), 2, 3)
self.dump_processed_data({"vouchers": vouchers}) vouchers = get_vouchers(collection)
self.status = ""
self.is_day_book_data_processed = 1 self.publish("Process Day Book Data", _("Done"), 3, 3)
self.save() self.dump_processed_data({"vouchers": vouchers})
self.is_day_book_data_processed = 1
except:
self.publish("Process Day Book Data", _("Process Failed"), -1, 5)
self.log()
finally:
self.set_status()
def _import_day_book_data(self): def _import_day_book_data(self):
def create_fiscal_years(vouchers): def create_fiscal_years(vouchers):
@@ -454,23 +487,31 @@ class TallyMigration(Document):
"currency": "INR" "currency": "INR"
}).insert() }).insert()
frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable") try:
frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable") frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable")
frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account) frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable")
frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account)
vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers}) vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers})
vouchers = json.loads(vouchers_file.get_content()) vouchers = json.loads(vouchers_file.get_content())
create_fiscal_years(vouchers) create_fiscal_years(vouchers)
create_price_list() create_price_list()
create_custom_fields(["Journal Entry", "Purchase Invoice", "Sales Invoice"]) create_custom_fields(["Journal Entry", "Purchase Invoice", "Sales Invoice"])
total = len(vouchers) total = len(vouchers)
is_last = False is_last = False
for index in range(0, total, VOUCHER_CHUNK_SIZE):
if index + VOUCHER_CHUNK_SIZE >= total: for index in range(0, total, VOUCHER_CHUNK_SIZE):
is_last = True if index + VOUCHER_CHUNK_SIZE >= total:
frappe.enqueue_doc(self.doctype, self.name, "_import_vouchers", queue="long", timeout=3600, start=index+1, total=total, is_last=is_last) is_last = True
frappe.enqueue_doc(self.doctype, self.name, "_import_vouchers", queue="long", timeout=3600, start=index+1, total=total, is_last=is_last)
except:
self.log()
finally:
self.set_status()
def _import_vouchers(self, start, total, is_last=False): def _import_vouchers(self, start, total, is_last=False):
frappe.flags.in_migrate = True frappe.flags.in_migrate = True
@@ -494,25 +535,26 @@ class TallyMigration(Document):
frappe.flags.in_migrate = False frappe.flags.in_migrate = False
def process_master_data(self): def process_master_data(self):
self.status = "Processing Master Data" self.set_status("Processing Master Data")
self.save()
frappe.enqueue_doc(self.doctype, self.name, "_process_master_data", queue="long", timeout=3600) frappe.enqueue_doc(self.doctype, self.name, "_process_master_data", queue="long", timeout=3600)
def import_master_data(self): def import_master_data(self):
self.status = "Importing Master Data" self.set_status("Importing Master Data")
self.save()
frappe.enqueue_doc(self.doctype, self.name, "_import_master_data", queue="long", timeout=3600) frappe.enqueue_doc(self.doctype, self.name, "_import_master_data", queue="long", timeout=3600)
def process_day_book_data(self): def process_day_book_data(self):
self.status = "Processing Day Book Data" self.set_status("Processing Day Book Data")
self.save()
frappe.enqueue_doc(self.doctype, self.name, "_process_day_book_data", queue="long", timeout=3600) frappe.enqueue_doc(self.doctype, self.name, "_process_day_book_data", queue="long", timeout=3600)
def import_day_book_data(self): def import_day_book_data(self):
self.status = "Importing Day Book Data" self.set_status("Importing Day Book Data")
self.save()
frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600) frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600)
def log(self, data=None): def log(self, data=None):
message = "\n".join(["Data", json.dumps(data, default=str, indent=4), "Exception", traceback.format_exc()]) data = data or self.status
message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()])
return frappe.log_error(title="Tally Migration Error", message=message) return frappe.log_error(title="Tally Migration Error", message=message)
def set_status(self, status=""):
self.status = status
self.save()

View File

@@ -1,48 +1,53 @@
{ {
"cards": [ "cards": [
{ {
"icon": "", "hidden": 0,
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient\",\n\t\t\"label\": \"Patient\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Practitioner\",\n\t\t\"label\":\"Healthcare Practitioner\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Practitioner Schedule\",\n\t\t\"label\": \"Practitioner Schedule\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Department\",\n\t\t\"label\": \"Medical Department\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Service Unit Type\",\n\t\t\"label\": \"Healthcare Service Unit Type\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Service Unit\",\n\t\t\"label\": \"Healthcare Service Unit\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Code Standard\",\n\t\t\"label\": \"Medical Code Standard\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Code\",\n\t\t\"label\": \"Medical Code\"\n\t}\n]", "label": "Masters",
"title": "Masters" "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient\",\n\t\t\"label\": \"Patient\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Practitioner\",\n\t\t\"label\":\"Healthcare Practitioner\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Practitioner Schedule\",\n\t\t\"label\": \"Practitioner Schedule\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Department\",\n\t\t\"label\": \"Medical Department\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Service Unit Type\",\n\t\t\"label\": \"Healthcare Service Unit Type\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Service Unit\",\n\t\t\"label\": \"Healthcare Service Unit\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Code Standard\",\n\t\t\"label\": \"Medical Code Standard\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Code\",\n\t\t\"label\": \"Medical Code\"\n\t}\n]"
}, },
{ {
"icon": "", "hidden": 0,
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Appointment Type\",\n\t\t\"label\": \"Appointment Type\"\n },\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Clinical Procedure Template\",\n\t\t\"label\": \"Clinical Procedure Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Prescription Dosage\",\n\t\t\"label\": \"Prescription Dosage\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Prescription Duration\",\n\t\t\"label\": \"Prescription Duration\"\n\t},\n\t{\n\t \"type\": \"doctype\",\n\t\t\"name\": \"Antibiotic\",\n\t\t\"label\": \"Antibiotic\"\n\t}\n]", "label": "Consultation Setup",
"title": "Consultation Setup" "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Appointment Type\",\n\t\t\"label\": \"Appointment Type\"\n },\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Clinical Procedure Template\",\n\t\t\"label\": \"Clinical Procedure Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Prescription Dosage\",\n\t\t\"label\": \"Prescription Dosage\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Prescription Duration\",\n\t\t\"label\": \"Prescription Duration\"\n\t},\n\t{\n\t \"type\": \"doctype\",\n\t\t\"name\": \"Antibiotic\",\n\t\t\"label\": \"Antibiotic\"\n\t}\n]"
}, },
{ {
"icon": "icon-star", "hidden": 0,
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Appointment\",\n\t\t\"label\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Clinical Procedure\",\n\t\t\"label\": \"Clinical Procedure\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Encounter\",\n\t\t\"label\": \"Patient Encounter\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Vital Signs\",\n\t\t\"label\": \"Vital Signs\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Complaint\",\n\t\t\"label\": \"Complaint\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Diagnosis\",\n\t\t\"label\": \"Diagnosis\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Fee Validity\",\n\t\t\"label\": \"Fee Validity\"\n\t}\n]", "label": "Consultation",
"title": "Consultation" "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Appointment\",\n\t\t\"label\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Clinical Procedure\",\n\t\t\"label\": \"Clinical Procedure\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Encounter\",\n\t\t\"label\": \"Patient Encounter\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Vital Signs\",\n\t\t\"label\": \"Vital Signs\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Complaint\",\n\t\t\"label\": \"Complaint\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Diagnosis\",\n\t\t\"label\": \"Diagnosis\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Fee Validity\",\n\t\t\"label\": \"Fee Validity\"\n\t}\n]"
}, },
{ {
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Settings\",\n\t\t\"label\": \"Healthcare Settings\",\n\t\t\"onboard\": 1\n\t}\n]", "hidden": 0,
"title": "Settings" "label": "Settings",
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Settings\",\n\t\t\"label\": \"Healthcare Settings\",\n\t\t\"onboard\": 1\n\t}\n]"
}, },
{ {
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test Template\",\n\t\t\"label\": \"Lab Test Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test Sample\",\n\t\t\"label\": \"Lab Test Sample\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test UOM\",\n\t\t\"label\": \"Lab Test UOM\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sensitivity\",\n\t\t\"label\": \"Sensitivity\"\n\t}\n]", "hidden": 0,
"title": "Laboratory Setup" "label": "Laboratory Setup",
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test Template\",\n\t\t\"label\": \"Lab Test Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test Sample\",\n\t\t\"label\": \"Lab Test Sample\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test UOM\",\n\t\t\"label\": \"Lab Test UOM\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sensitivity\",\n\t\t\"label\": \"Sensitivity\"\n\t}\n]"
}, },
{ {
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test\",\n\t\t\"label\": \"Lab Test\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sample Collection\",\n\t\t\"label\": \"Sample Collection\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Dosage Form\",\n\t\t\"label\": \"Dosage Form\"\n\t}\n]", "hidden": 0,
"title": "Laboratory" "label": "Laboratory",
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test\",\n\t\t\"label\": \"Lab Test\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sample Collection\",\n\t\t\"label\": \"Sample Collection\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Dosage Form\",\n\t\t\"label\": \"Dosage Form\"\n\t}\n]"
}, },
{ {
"links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t}\n]", "hidden": 0,
"title": "Records and History" "label": "Rehabilitation and Physiotherapy",
"links": "[\n {\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Exercise Type\",\n\t\t\"label\": \"Exercise Type\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Therapy Type\",\n\t\t\"label\": \"Therapy Type\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Therapy Plan\",\n\t\t\"label\": \"Therapy Plan\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Therapy Session\",\n\t\t\"label\": \"Therapy Session\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Assessment Template\",\n\t\t\"label\": \"Patient Assessment Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Assessment\",\n\t\t\"label\": \"Patient Assessment\"\n\t}\n]"
}, },
{ {
"links": "[\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Patient Appointment Analytics\",\n\t\t\"doctype\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Lab Test Report\",\n\t\t\"doctype\": \"Lab Test\",\n\t\t\"label\": \"Lab Test Report\"\n\t}\n]", "hidden": 0,
"title": "Reports" "label": "Records and History",
"links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t}\n]"
},
{
"hidden": 0,
"label": "Reports",
"links": "[\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Patient Appointment Analytics\",\n\t\t\"doctype\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Lab Test Report\",\n\t\t\"doctype\": \"Lab Test\",\n\t\t\"label\": \"Lab Test Report\"\n\t}\n]"
} }
], ],
"category": "Domains", "category": "Domains",
"charts": [ "charts": [],
{
"chart_name": "Patient Appointments",
"label": "Patient Appointments"
}
],
"charts_label": "", "charts_label": "",
"creation": "2020-03-02 17:23:17.919682", "creation": "2020-03-02 17:23:17.919682",
"developer_mode_only": 0, "developer_mode_only": 0,
@@ -53,7 +58,7 @@
"idx": 0, "idx": 0,
"is_standard": 1, "is_standard": 1,
"label": "Healthcare", "label": "Healthcare",
"modified": "2020-03-26 16:10:44.629795", "modified": "2020-04-20 11:42:43.889576",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Healthcare", "name": "Healthcare",
@@ -64,32 +69,32 @@
"shortcuts": [ "shortcuts": [
{ {
"format": "{} Open", "format": "{} Open",
"is_query_report": 0, "label": "Patient Appointment",
"link_to": "Patient Appointment", "link_to": "Patient Appointment",
"stats_filter": "{\n \"status\": \"Open\"\n}", "stats_filter": "{\n \"status\": \"Open\"\n}",
"type": "DocType" "type": "DocType"
}, },
{ {
"format": "{} Active", "format": "{} Active",
"is_query_report": 0, "label": "Patient",
"link_to": "Patient", "link_to": "Patient",
"stats_filter": "{\n \"status\": \"Active\"\n}", "stats_filter": "{\n \"status\": \"Active\"\n}",
"type": "DocType" "type": "DocType"
}, },
{ {
"format": "{} Vacant", "format": "{} Vacant",
"is_query_report": 0, "label": "Healthcare Service Unit",
"link_to": "Healthcare Service Unit", "link_to": "Healthcare Service Unit",
"stats_filter": "{\n \"occupancy_status\": \"Vacant\",\n \"is_group\": 0\n}", "stats_filter": "{\n \"occupancy_status\": \"Vacant\",\n \"is_group\": 0\n}",
"type": "DocType" "type": "DocType"
}, },
{ {
"is_query_report": 0, "label": "Healthcare Practitioner",
"link_to": "Healthcare Practitioner", "link_to": "Healthcare Practitioner",
"type": "DocType" "type": "DocType"
}, },
{ {
"is_query_report": 0, "label": "Patient History",
"link_to": "patient_history", "link_to": "patient_history",
"type": "Page" "type": "Page"
} }

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Body Part', {
// refresh: function(frm) {
// }
});

View File

@@ -0,0 +1,45 @@
{
"actions": [],
"autoname": "field:body_part",
"creation": "2020-04-10 12:21:55.036402",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"body_part"
],
"fields": [
{
"fieldname": "body_part",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Body Part",
"reqd": 1,
"unique": 1
}
],
"links": [],
"modified": "2020-04-10 12:26:44.087985",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Body Part",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt # For license information, please see license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document from frappe.model.document import Document
class AccountSubtype(Document): class BodyPart(Document):
pass pass

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestBodyPart(unittest.TestCase):
pass

View File

@@ -0,0 +1,32 @@
{
"actions": [],
"creation": "2020-04-10 12:23:15.259816",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"body_part"
],
"fields": [
{
"fieldname": "body_part",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Body Part",
"options": "Body Part",
"reqd": 1
}
],
"istable": 1,
"links": [],
"modified": "2020-04-10 12:25:23.101749",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Body Part Link",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class BodyPartLink(Document):
pass

View File

@@ -79,6 +79,7 @@ def create_item_from_template(doc):
if doc.is_billable and not doc.disabled: if doc.is_billable and not doc.disabled:
disabled = 0 disabled = 0
uom = frappe.db.exists('UOM', 'Unit') or frappe.db.get_single_value('Stock Settings', 'stock_uom')
item = frappe.get_doc({ item = frappe.get_doc({
'doctype': 'Item', 'doctype': 'Item',
'item_code': doc.template, 'item_code': doc.template,
@@ -92,7 +93,7 @@ def create_item_from_template(doc):
'show_in_website': 0, 'show_in_website': 0,
'is_pro_applicable': 0, 'is_pro_applicable': 0,
'disabled': disabled, 'disabled': disabled,
'stock_uom': 'Unit' 'stock_uom': uom
}).insert(ignore_permissions=True, ignore_mandatory=True) }).insert(ignore_permissions=True, ignore_mandatory=True)
make_item_price(item.name, doc.rate) make_item_price(item.name, doc.rate)

View File

@@ -0,0 +1,61 @@
{
"actions": [],
"creation": "2020-03-11 09:25:00.968572",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"exercise_type",
"difficulty_level",
"counts_target",
"counts_completed",
"assistance_level"
],
"fields": [
{
"fieldname": "exercise_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Exercise Type",
"options": "Exercise Type",
"reqd": 1
},
{
"fetch_from": "exercise_type.difficulty_level",
"fieldname": "difficulty_level",
"fieldtype": "Link",
"label": "Difficulty Level",
"options": "Exercise Difficulty Level"
},
{
"fieldname": "counts_target",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Counts Target"
},
{
"depends_on": "eval:doc.parenttype==\"Therapy\";",
"fieldname": "counts_completed",
"fieldtype": "Int",
"label": "Counts Completed"
},
{
"fieldname": "assistance_level",
"fieldtype": "Select",
"label": "Assistance Level",
"options": "\nPassive\nActive Assist\nActive"
}
],
"istable": 1,
"links": [],
"modified": "2020-04-10 13:41:06.662351",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Exercise",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class Exercise(Document):
pass

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Exercise Difficulty Level', {
// refresh: function(frm) {
// }
});

View File

@@ -0,0 +1,45 @@
{
"actions": [],
"autoname": "field:difficulty_level",
"creation": "2020-03-29 21:12:55.835941",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"difficulty_level"
],
"fields": [
{
"fieldname": "difficulty_level",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Difficulty Level",
"reqd": 1,
"unique": 1
}
],
"links": [],
"modified": "2020-03-31 23:14:33.554066",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Exercise Difficulty Level",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class ExerciseDifficultyLevel(Document):
pass

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestExerciseDifficultyLevel(unittest.TestCase):
pass

View File

@@ -0,0 +1,180 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Exercise Type', {
refresh: function(frm) {
let wrapper = frm.fields_dict.steps_html.wrapper;
frm.ExerciseEditor = new erpnext.ExerciseEditor(frm, wrapper);
}
});
erpnext.ExerciseEditor = Class.extend({
init: function(frm, wrapper) {
this.wrapper = wrapper;
this.frm = frm;
this.make(frm, wrapper);
},
make: function(frm, wrapper) {
$(this.wrapper).empty();
this.exercise_toolbar = $('<p>\
<button class="btn btn-default btn-add btn-xs" style="margin-left: 10px;"></button>').appendTo(this.wrapper);
this.exercise_cards = $('<div class="exercise-cards"></div>').appendTo(this.wrapper);
let me = this;
this.exercise_toolbar.find(".btn-add")
.html(__('Add'))
.on("click", function() {
me.show_add_card_dialog(frm);
});
if (frm.doc.steps_table.length > 0) {
this.make_cards(frm);
this.make_buttons(frm);
}
},
make_cards: function(frm) {
var me = this;
$(me.exercise_cards).empty();
this.row = $('<div class="exercise-row"></div>').appendTo(me.exercise_cards);
$.each(frm.doc.steps_table, function(i, step) {
$(repl(`
<div class="exercise-col col-sm-4" id="%(col_id)s">
<div class="card h-100 exercise-card" id="%(card_id)s">
<div class="card-body exercise-card-body">
<img src=%(image_src)s class="card-img-top" alt="...">
<h4 class="card-title">%(title)s</h4>
<p class="card-text text-truncate">%(description)s</p>
</div>
<div class="card-footer">
<button class="btn btn-default btn-xs btn-edit" data-id="%(id)s"><i class="fa fa-pencil" aria-hidden="true"></i></button>
<button class="btn btn-default btn-xs btn-del" data-id="%(id)s"><i class="fa fa-trash" aria-hidden="true"></i></button>
</div>
</div>
</div>`, {image_src: step.image, title: step.title, description: step.description, col_id: "col-"+i, card_id: "card-"+i, id: i})).appendTo(me.row);
});
},
make_buttons: function(frm) {
let me = this;
$('.btn-edit').on('click', function() {
let id = $(this).attr('data-id');
me.show_edit_card_dialog(frm, id);
});
$('.btn-del').on('click', function() {
let id = $(this).attr('data-id');
$('#card-'+id).addClass("zoomOutDelete");
setTimeout(() => {
// not using grid_rows[id].remove because
// grid_rows is not defined when the table is hidden
frm.doc.steps_table.pop(id);
frm.refresh_field('steps_table');
$('#col-'+id).remove();
}, 300);
});
},
show_add_card_dialog: function(frm) {
let me = this;
let d = new frappe.ui.Dialog({
title: __('Add Exercise Step'),
fields: [
{
"label": "Title",
"fieldname": "title",
"fieldtype": "Data",
"reqd": 1
},
{
"label": "Attach Image",
"fieldname": "image",
"fieldtype": "Attach Image"
},
{
"label": "Step Description",
"fieldname": "step_description",
"fieldtype": "Long Text"
}
],
primary_action: function() {
let data = d.get_values();
let i = frm.doc.steps_table.length;
$(repl(`
<div class="exercise-col col-sm-4" id="%(col_id)s">
<div class="card h-100 exercise-card" id="%(card_id)s">
<div class="card-body exercise-card-body">
<img src=%(image_src)s class="card-img-top" alt="...">
<h4 class="card-title">%(title)s</h4>
<p class="card-text text-truncate">%(description)s</p>
</div>
<div class="card-footer">
<button class="btn btn-default btn-xs btn-edit" data-id="%(id)s"><i class="fa fa-pencil" aria-hidden="true"></i></button>
<button class="btn btn-default btn-xs btn-del" data-id="%(id)s"><i class="fa fa-trash" aria-hidden="true"></i></button>
</div>
</div>
</div>`, {image_src: data.image, title: data.title, description: data.step_description, col_id: "col-"+i, card_id: "card-"+i, id: i})).appendTo(me.row);
let step = frappe.model.add_child(frm.doc, 'Exercise Type Step', 'steps_table');
step.title = data.title;
step.image = data.image;
step.description = data.step_description;
me.make_buttons(frm);
frm.refresh_field('steps_table');
d.hide();
},
primary_action_label: __('Add')
});
d.show();
},
show_edit_card_dialog: function(frm, id) {
let new_dialog = new frappe.ui.Dialog({
title: __("Edit Exercise Step"),
fields: [
{
"label": "Title",
"fieldname": "title",
"fieldtype": "Data",
"reqd": 1
},
{
"label": "Attach Image",
"fieldname": "image",
"fieldtype": "Attach Image"
},
{
"label": "Step Description",
"fieldname": "step_description",
"fieldtype": "Long Text"
}
],
primary_action: () => {
let data = new_dialog.get_values();
$('#card-'+id).find('.card-title').html(data.title);
$('#card-'+id).find('img').attr('src', data.image);
$('#card-'+id).find('.card-text').html(data.step_description);
frm.doc.steps_table[id].title = data.title;
frm.doc.steps_table[id].image = data.image;
frm.doc.steps_table[id].description = data.step_description;
refresh_field('steps_table');
new_dialog.hide();
},
primary_action_label: __("Save"),
});
new_dialog.set_values({
title: frm.doc.steps_table[id].title,
image: frm.doc.steps_table[id].image,
step_description: frm.doc.steps_table[id].description
});
new_dialog.show();
}
});

View File

@@ -0,0 +1,144 @@
{
"actions": [],
"creation": "2020-03-29 21:37:03.366344",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"exercise_name",
"body_parts",
"column_break_3",
"difficulty_level",
"section_break_5",
"description",
"section_break_7",
"exercise_steps",
"column_break_9",
"exercise_video",
"section_break_11",
"steps_html",
"section_break_13",
"steps_table"
],
"fields": [
{
"fieldname": "exercise_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Exercise Name",
"reqd": 1
},
{
"fieldname": "difficulty_level",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Difficulty Level",
"options": "Exercise Difficulty Level"
},
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_5",
"fieldtype": "Section Break"
},
{
"fieldname": "description",
"fieldtype": "Long Text",
"label": "Description"
},
{
"fieldname": "section_break_7",
"fieldtype": "Section Break"
},
{
"fieldname": "exercise_steps",
"fieldtype": "Attach",
"label": "Exercise Instructions"
},
{
"fieldname": "exercise_video",
"fieldtype": "Link",
"label": "Exercise Video",
"options": "Video"
},
{
"fieldname": "column_break_9",
"fieldtype": "Column Break"
},
{
"fieldname": "steps_html",
"fieldtype": "HTML",
"label": "Steps"
},
{
"fieldname": "steps_table",
"fieldtype": "Table",
"hidden": 1,
"label": "Steps Table",
"options": "Exercise Type Step"
},
{
"fieldname": "section_break_11",
"fieldtype": "Section Break",
"label": "Exercise Steps"
},
{
"fieldname": "section_break_13",
"fieldtype": "Section Break"
},
{
"fieldname": "body_parts",
"fieldtype": "Table MultiSelect",
"label": "Body Parts",
"options": "Body Part Link"
}
],
"links": [],
"modified": "2020-04-21 13:05:36.555060",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Exercise Type",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Healthcare Administrator",
"share": 1,
"write": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Physician",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class ExerciseType(Document):
def autoname(self):
if self.difficulty_level:
self.name = ' - '.join(filter(None, [self.exercise_name, self.difficulty_level]))
else:
self.name = self.exercise_name

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestExerciseType(unittest.TestCase):
pass

View File

@@ -0,0 +1,44 @@
{
"actions": [],
"creation": "2020-03-31 23:01:18.761967",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"title",
"image",
"description"
],
"fields": [
{
"fieldname": "title",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Title",
"reqd": 1
},
{
"fieldname": "image",
"fieldtype": "Attach Image",
"label": "Image"
},
{
"fieldname": "description",
"fieldtype": "Long Text",
"in_list_view": 1,
"label": "Description"
}
],
"istable": 1,
"links": [],
"modified": "2020-04-02 20:39:34.258512",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Exercise Type Step",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class ExerciseTypeStep(Document):
pass

View File

@@ -74,26 +74,27 @@ class LabTestTemplate(Document):
def create_item_from_template(doc): def create_item_from_template(doc):
if doc.is_billable: disabled = doc.disabled
if doc.is_billable and not doc.disabled:
disabled = 0 disabled = 0
else:
disabled = 1 uom = frappe.db.exists('UOM', 'Unit') or frappe.db.get_single_value('Stock Settings', 'stock_uom')
# insert item # insert item
item = frappe.get_doc({ item = frappe.get_doc({
"doctype": "Item", "doctype": "Item",
"item_code": doc.lab_test_code, "item_code": doc.lab_test_code,
"item_name":doc.lab_test_name, "item_name":doc.lab_test_name,
"item_group": doc.lab_test_group, "item_group": doc.lab_test_group,
"description":doc.lab_test_description, "description":doc.lab_test_description,
"is_sales_item": 1, "is_sales_item": 1,
"is_service_item": 1, "is_service_item": 1,
"is_purchase_item": 0, "is_purchase_item": 0,
"is_stock_item": 0, "is_stock_item": 0,
"show_in_website": 0, "show_in_website": 0,
"is_pro_applicable": 0, "is_pro_applicable": 0,
"disabled": disabled, "disabled": disabled,
"stock_uom": "Unit" "stock_uom": uom
}).insert(ignore_permissions=True) }).insert(ignore_permissions=True, ignore_mandatory=True)
# insert item price # insert item price
# get item price list to insert item price # get item price list to insert item price

View File

@@ -102,6 +102,13 @@ frappe.ui.form.on('Patient Appointment', {
frm: frm, frm: frm,
}); });
}, __('Create')); }, __('Create'));
} else if (frm.doc.therapy_type) {
frm.add_custom_button(__('Therapy Session'),function(){
frappe.model.open_mapped_doc({
method: 'erpnext.healthcare.doctype.therapy_session.therapy_session.create_therapy_session',
frm: frm,
})
}, 'Create');
} else { } else {
frm.add_custom_button(__('Patient Encounter'), function() { frm.add_custom_button(__('Patient Encounter'), function() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
@@ -123,6 +130,16 @@ frappe.ui.form.on('Patient Appointment', {
} }
}, },
therapy_type: function(frm) {
if (frm.doc.therapy_type) {
frappe.db.get_value('Therapy Type', frm.doc.therapy_type, 'default_duration', (r) => {
if (r.default_duration) {
frm.set_value('duration', r.default_duration)
}
});
}
},
get_procedure_from_encounter: function(frm) { get_procedure_from_encounter: function(frm) {
get_prescribed_procedure(frm); get_prescribed_procedure(frm);
}, },
@@ -148,6 +165,26 @@ frappe.ui.form.on('Patient Appointment', {
} }
} }
}); });
},
get_prescribed_therapies: function(frm) {
if (frm.doc.patient) {
frappe.call({
method: "erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_prescribed_therapies",
args: {patient: frm.doc.patient},
callback: function(r) {
if (r.message) {
show_therapy_types(frm, r.message);
} else {
frappe.msgprint({
title: __('Not Therapies Prescribed'),
message: __('There are no Therapies prescribed for Patient {0}', [frm.doc.patient.bold()]),
indicator: 'blue'
});
}
}
});
}
} }
}); });
@@ -393,6 +430,50 @@ let show_procedure_templates = function(frm, result){
d.show(); d.show();
}; };
let show_therapy_types = function(frm, result) {
var d = new frappe.ui.Dialog({
title: __('Prescribed Therapies'),
fields: [
{
fieldtype: 'HTML', fieldname: 'therapy_type'
}
]
});
var html_field = d.fields_dict.therapy_type.$wrapper;
$.each(result, function(x, y){
var row = $(repl('<div class="col-xs-12" style="padding-top:12px; text-align:center;" >\
<div class="col-xs-5"> %(encounter)s <br> %(practitioner)s <br> %(date)s </div>\
<div class="col-xs-5"> %(therapy)s </div>\
<div class="col-xs-2">\
<a data-therapy="%(therapy)s" data-therapy-plan="%(therapy_plan)s" data-name="%(name)s"\
data-encounter="%(encounter)s" data-practitioner="%(practitioner)s"\
data-date="%(date)s" data-department="%(department)s">\
<button class="btn btn-default btn-xs">Add\
</button></a></div></div><div class="col-xs-12"><hr/><div/>', {therapy:y[0],
name: y[1], encounter:y[2], practitioner:y[3], date:y[4],
department:y[6]? y[6]:'', therapy_plan:y[5]})).appendTo(html_field);
row.find("a").click(function() {
frm.doc.therapy_type = $(this).attr("data-therapy");
frm.doc.practitioner = $(this).attr("data-practitioner");
frm.doc.department = $(this).attr("data-department");
frm.doc.therapy_plan = $(this).attr("data-therapy-plan");
frm.refresh_field("therapy_type");
frm.refresh_field("practitioner");
frm.refresh_field("department");
frm.refresh_field("therapy-plan");
frappe.db.get_value('Therapy Type', frm.doc.therapy_type, 'default_duration', (r) => {
if (r.default_duration) {
frm.set_value('duration', r.default_duration)
}
});
d.hide();
return false;
});
});
d.show();
};
let create_vital_signs = function(frm) { let create_vital_signs = function(frm) {
if (!frm.doc.patient) { if (!frm.doc.patient) {
frappe.throw(__('Please select patient')); frappe.throw(__('Please select patient'));

View File

@@ -20,6 +20,9 @@
"procedure_template", "procedure_template",
"get_procedure_from_encounter", "get_procedure_from_encounter",
"procedure_prescription", "procedure_prescription",
"therapy_type",
"get_prescribed_therapies",
"therapy_plan",
"service_unit", "service_unit",
"section_break_12", "section_break_12",
"practitioner", "practitioner",
@@ -269,6 +272,28 @@
"print_hide": 1, "print_hide": 1,
"report_hide": 1 "report_hide": 1
}, },
{
"depends_on": "eval:doc.patient;",
"fieldname": "therapy_type",
"fieldtype": "Link",
"label": "Therapy",
"options": "Therapy Type",
"set_only_once": 1
},
{
"depends_on": "eval:doc.patient && doc.__islocal;",
"fieldname": "get_prescribed_therapies",
"fieldtype": "Button",
"label": "Get Prescribed Therapies"
},
{
"depends_on": "eval: doc.patient && doc.therapy_type",
"fieldname": "therapy_plan",
"fieldtype": "Link",
"label": "Therapy Plan",
"mandatory_depends_on": "eval: doc.patient && doc.therapy_type",
"options": "Therapy Plan"
},
{ {
"fieldname": "ref_sales_invoice", "fieldname": "ref_sales_invoice",
"fieldtype": "Link", "fieldtype": "Link",
@@ -285,7 +310,7 @@
} }
], ],
"links": [], "links": [],
"modified": "2020-03-27 11:27:33.773195", "modified": "2020-03-31 16:16:32.116865",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Patient Appointment", "name": "Patient Appointment",

View File

@@ -412,11 +412,36 @@ def get_events(start, end, filters=None):
@frappe.whitelist() @frappe.whitelist()
def get_procedure_prescribed(patient): def get_procedure_prescribed(patient):
return frappe.db.sql("""select pp.name, pp.procedure, pp.parent, ct.practitioner, return frappe.db.sql(
ct.encounter_date, pp.practitioner, pp.date, pp.department """
from `tabPatient Encounter` ct, `tabProcedure Prescription` pp SELECT
where ct.patient=%(patient)s and pp.parent=ct.name and pp.appointment_booked=0 pp.name, pp.procedure, pp.parent, ct.practitioner,
order by ct.creation desc""", {'patient': patient}) ct.encounter_date, pp.practitioner, pp.date, pp.department
FROM
`tabPatient Encounter` ct, `tabProcedure Prescription` pp
WHERE
ct.patient=%(patient)s and pp.parent=ct.name and pp.appointment_booked=0
ORDER BY
ct.creation desc
""", {'patient': patient}
)
@frappe.whitelist()
def get_prescribed_therapies(patient):
return frappe.db.sql(
"""
SELECT
t.therapy_type, t.name, t.parent, e.practitioner,
e.encounter_date, e.therapy_plan, e.visit_department
FROM
`tabPatient Encounter` e, `tabTherapy Plan Detail` t
WHERE
e.patient=%(patient)s and t.parent=e.name
ORDER BY
e.creation desc
""", {'patient': patient}
)
def update_appointment_status(): def update_appointment_status():

View File

@@ -0,0 +1,86 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Patient Assessment', {
refresh: function(frm) {
if (frm.doc.assessment_template) {
frm.trigger('set_score_range');
}
if (!frm.doc.__islocal) {
frm.trigger('show_patient_progress');
}
},
assessment_template: function(frm) {
if (frm.doc.assessment_template) {
frappe.call({
'method': 'frappe.client.get',
args: {
doctype: 'Patient Assessment Template',
name: frm.doc.assessment_template
},
callback: function(data) {
frm.doc.assessment_sheet = [];
$.each(data.message.parameters, function(_i, e) {
let entry = frm.add_child('assessment_sheet');
entry.parameter = e.assessment_parameter;
});
frm.set_value('scale_min', data.message.scale_min);
frm.set_value('scale_max', data.message.scale_max);
frm.set_value('assessment_description', data.message.assessment_description);
frm.set_value('total_score', data.message.scale_max * data.message.parameters.length);
frm.trigger('set_score_range');
refresh_field('assessment_sheet');
}
});
}
},
set_score_range: function(frm) {
let options = [];
for(let i = frm.doc.scale_min; i <= frm.doc.scale_max; i++) {
options.push(i);
}
frappe.meta.get_docfield('Patient Assessment Sheet', 'score', frm.doc.name).options = [''].concat(options);
},
calculate_total_score: function(frm, cdt, cdn) {
let row = locals[cdt][cdn];
let total_score = 0;
$.each(frm.doc.assessment_sheet || [], function(_i, item) {
if (item.score) {
total_score += parseInt(item.score);
}
});
frm.set_value('total_score_obtained', total_score);
},
show_patient_progress: function(frm) {
let bars = [];
let message = '';
let added_min = false;
let title = __('{0} out of {1}', [frm.doc.total_score_obtained, frm.doc.total_score]);
bars.push({
'title': title,
'width': (frm.doc.total_score_obtained / frm.doc.total_score * 100) + '%',
'progress_class': 'progress-bar-success'
});
if (bars[0].width == '0%') {
bars[0].width = '0.5%';
added_min = 0.5;
}
message = title;
frm.dashboard.add_progress(__('Status'), bars, message);
},
});
frappe.ui.form.on('Patient Assessment Sheet', {
score: function(frm, cdt, cdn) {
frm.events.calculate_total_score(frm, cdt, cdn);
}
});

View File

@@ -0,0 +1,172 @@
{
"actions": [],
"autoname": "naming_series:",
"creation": "2020-04-19 22:45:12.356209",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"naming_series",
"therapy_session",
"patient",
"assessment_template",
"column_break_4",
"healthcare_practitioner",
"assessment_datetime",
"assessment_description",
"section_break_7",
"assessment_sheet",
"section_break_9",
"total_score_obtained",
"column_break_11",
"total_score",
"scale_min",
"scale_max",
"amended_from"
],
"fields": [
{
"fetch_from": "therapy_session.patient",
"fieldname": "patient",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Patient",
"options": "Patient",
"reqd": 1
},
{
"fieldname": "assessment_template",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Assessment Template",
"options": "Patient Assessment Template",
"reqd": 1
},
{
"fieldname": "therapy_session",
"fieldtype": "Link",
"label": "Therapy Session",
"options": "Therapy Session"
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"fetch_from": "therapy_session.practitioner",
"fieldname": "healthcare_practitioner",
"fieldtype": "Link",
"label": "Healthcare Practitioner",
"options": "Healthcare Practitioner"
},
{
"fieldname": "assessment_datetime",
"fieldtype": "Datetime",
"label": "Assessment Datetime"
},
{
"fieldname": "section_break_7",
"fieldtype": "Section Break"
},
{
"fieldname": "assessment_sheet",
"fieldtype": "Table",
"label": "Assessment Sheet",
"options": "Patient Assessment Sheet"
},
{
"fieldname": "section_break_9",
"fieldtype": "Section Break"
},
{
"fieldname": "total_score",
"fieldtype": "Int",
"label": "Total Score",
"read_only": 1
},
{
"fieldname": "column_break_11",
"fieldtype": "Column Break"
},
{
"fieldname": "total_score_obtained",
"fieldtype": "Int",
"label": "Total Score Obtained",
"read_only": 1
},
{
"fieldname": "scale_min",
"fieldtype": "Int",
"hidden": 1,
"label": "Scale Min",
"read_only": 1
},
{
"fieldname": "scale_max",
"fieldtype": "Int",
"hidden": 1,
"label": "Scale Max",
"read_only": 1
},
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Naming Series",
"options": "HLC-PA-.YYYY.-"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Patient Assessment",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "assessment_description",
"fieldtype": "Small Text",
"label": "Assessment Description"
}
],
"is_submittable": 1,
"links": [],
"modified": "2020-04-21 13:23:09.815007",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Assessment",
"owner": "Administrator",
"permissions": [
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"cancel": 1,
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Physician",
"share": 1,
"submit": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "patient",
"track_changes": 1
}

View File

@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
class PatientAssessment(Document):
def validate(self):
self.set_total_score()
def set_total_score(self):
total_score = 0
for entry in self.assessment_sheet:
total_score += int(entry.score)
self.total_score_obtained = total_score
@frappe.whitelist()
def create_patient_assessment(source_name, target_doc=None):
doc = get_mapped_doc('Therapy Session', source_name, {
'Therapy Session': {
'doctype': 'Patient Assessment',
'field_map': [
['therapy_session', 'name'],
['patient', 'patient'],
['practitioner', 'practitioner']
]
}
}, target_doc)
return doc

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestPatientAssessment(unittest.TestCase):
pass

View File

@@ -0,0 +1,32 @@
{
"actions": [],
"creation": "2020-04-19 19:33:00.115395",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"assessment_parameter"
],
"fields": [
{
"fieldname": "assessment_parameter",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Assessment Parameter",
"options": "Patient Assessment Parameter",
"reqd": 1
}
],
"istable": 1,
"links": [],
"modified": "2020-04-19 19:33:00.115395",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Assessment Detail",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class PatientAssessmentDetail(Document):
pass

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Patient Assessment Parameter', {
// refresh: function(frm) {
// }
});

View File

@@ -0,0 +1,45 @@
{
"actions": [],
"autoname": "field:assessment_parameter",
"creation": "2020-04-15 14:34:46.551042",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"assessment_parameter"
],
"fields": [
{
"fieldname": "assessment_parameter",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Assessment Parameter",
"reqd": 1,
"unique": 1
}
],
"links": [],
"modified": "2020-04-20 09:22:19.135196",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Assessment Parameter",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class PatientAssessmentParameter(Document):
pass

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestPatientAssessmentParameter(unittest.TestCase):
pass

View File

@@ -0,0 +1,57 @@
{
"actions": [],
"creation": "2020-04-19 23:07:02.220244",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"parameter",
"score",
"time",
"column_break_4",
"comments"
],
"fields": [
{
"fieldname": "parameter",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Parameter",
"options": "Patient Assessment Parameter",
"reqd": 1
},
{
"fieldname": "score",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Score",
"reqd": 1
},
{
"fieldname": "time",
"fieldtype": "Time",
"label": "Time"
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"fieldname": "comments",
"fieldtype": "Small Text",
"label": "Comments"
}
],
"istable": 1,
"links": [],
"modified": "2020-04-20 09:56:28.746619",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Assessment Sheet",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class PatientAssessmentSheet(Document):
pass

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Patient Assessment Template', {
// refresh: function(frm) {
// }
});

View File

@@ -0,0 +1,109 @@
{
"actions": [],
"autoname": "field:assessment_name",
"creation": "2020-04-19 19:33:13.204707",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"assessment_name",
"section_break_2",
"parameters",
"assessment_scale_details_section",
"scale_min",
"scale_max",
"column_break_8",
"assessment_description"
],
"fields": [
{
"fieldname": "parameters",
"fieldtype": "Table",
"label": "Parameters",
"options": "Patient Assessment Detail"
},
{
"fieldname": "assessment_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Assessment Name",
"reqd": 1,
"unique": 1
},
{
"fieldname": "section_break_2",
"fieldtype": "Section Break",
"label": "Assessment Parameters"
},
{
"fieldname": "assessment_scale_details_section",
"fieldtype": "Section Break",
"label": "Assessment Scale"
},
{
"fieldname": "scale_min",
"fieldtype": "Int",
"label": "Scale Minimum"
},
{
"fieldname": "scale_max",
"fieldtype": "Int",
"label": "Scale Maximum"
},
{
"fieldname": "column_break_8",
"fieldtype": "Column Break"
},
{
"fieldname": "assessment_description",
"fieldtype": "Small Text",
"label": "Assessment Description"
}
],
"links": [],
"modified": "2020-04-21 13:14:19.075167",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Assessment Template",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Healthcare Administrator",
"share": 1,
"write": 1
},
{
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Physician",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

Some files were not shown because too many files have changed in this diff Show More