Merge branch 'develop' into pay-received-amt-fix
This commit is contained in:
commit
74c8e7bb97
58
.mergify.yml
Normal file
58
.mergify.yml
Normal file
@ -0,0 +1,58 @@
|
||||
pull_request_rules:
|
||||
- name: Auto-close PRs on stable branch
|
||||
conditions:
|
||||
- and:
|
||||
- and:
|
||||
- author!=surajshetty3416
|
||||
- author!=gavindsouza
|
||||
- author!=rohitwaghchaure
|
||||
- author!=nabinhait
|
||||
- or:
|
||||
- base=version-13
|
||||
- base=version-12
|
||||
actions:
|
||||
close:
|
||||
comment:
|
||||
message: |
|
||||
@{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on an appropriate hotfix branch.
|
||||
https://github.com/frappe/erpnext/wiki/Pull-Request-Checklist#which-branch
|
||||
|
||||
- name: backport to version-13-hotfix
|
||||
conditions:
|
||||
- label="backport version-13-hotfix"
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- version-13-hotfix
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
|
||||
- name: backport to version-13-pre-release
|
||||
conditions:
|
||||
- label="backport version-13-pre-release"
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- version-13-pre-release
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
|
||||
- name: backport to version-12-hotfix
|
||||
conditions:
|
||||
- label="backport version-12-hotfix"
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- version-12-hotfix
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
|
||||
- name: backport to version-12-pre-release
|
||||
conditions:
|
||||
- label="backport version-12-pre-release"
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- version-12-pre-release
|
||||
assignees:
|
||||
- "{{ author }}"
|
@ -11,6 +11,7 @@ coverage:
|
||||
comment:
|
||||
layout: "diff, files"
|
||||
require_changes: true
|
||||
|
||||
after_n_builds: 3
|
||||
|
||||
ignore:
|
||||
- "erpnext/demo"
|
||||
|
@ -6,7 +6,7 @@ context('Organizational Chart', () => {
|
||||
|
||||
it('navigates to org chart', () => {
|
||||
cy.visit('/app');
|
||||
cy.awesomebar('Organizational Chart');
|
||||
cy.visit('/app/organizational-chart');
|
||||
cy.url().should('include', '/organizational-chart');
|
||||
|
||||
cy.window().its('frappe.csrf_token').then(csrf_token => {
|
||||
|
@ -7,7 +7,7 @@ context('Organizational Chart Mobile', () => {
|
||||
it('navigates to org chart', () => {
|
||||
cy.viewport(375, 667);
|
||||
cy.visit('/app');
|
||||
cy.awesomebar('Organizational Chart');
|
||||
cy.visit('/app/organizational-chart');
|
||||
cy.url().should('include', '/organizational-chart');
|
||||
|
||||
cy.window().its('frappe.csrf_token').then(csrf_token => {
|
||||
|
@ -374,12 +374,15 @@ def make_gl_entries(doc, credit_account, debit_account, against,
|
||||
try:
|
||||
make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
|
||||
frappe.db.commit()
|
||||
except Exception:
|
||||
frappe.db.rollback()
|
||||
traceback = frappe.get_traceback()
|
||||
frappe.log_error(message=traceback)
|
||||
except Exception as e:
|
||||
if frappe.flags.in_test:
|
||||
raise e
|
||||
else:
|
||||
frappe.db.rollback()
|
||||
traceback = frappe.get_traceback()
|
||||
frappe.log_error(message=traceback)
|
||||
|
||||
frappe.flags.deferred_accounting_error = True
|
||||
frappe.flags.deferred_accounting_error = True
|
||||
|
||||
def send_mail(deferred_process):
|
||||
title = _("Error while processing deferred accounting for {0}").format(deferred_process)
|
||||
|
@ -45,6 +45,49 @@ frappe.treeview_settings["Account"] = {
|
||||
],
|
||||
root_label: "Accounts",
|
||||
get_tree_nodes: 'erpnext.accounts.utils.get_children',
|
||||
on_get_node: function(nodes, deep=false) {
|
||||
if (frappe.boot.user.can_read.indexOf("GL Entry") == -1) return;
|
||||
|
||||
let accounts = [];
|
||||
if (deep) {
|
||||
// in case of `get_all_nodes`
|
||||
accounts = nodes.reduce((acc, node) => [...acc, ...node.data], []);
|
||||
} else {
|
||||
accounts = nodes;
|
||||
}
|
||||
|
||||
const get_balances = frappe.call({
|
||||
method: 'erpnext.accounts.utils.get_account_balances',
|
||||
args: {
|
||||
accounts: accounts,
|
||||
company: cur_tree.args.company
|
||||
},
|
||||
});
|
||||
|
||||
get_balances.then(r => {
|
||||
if (!r.message || r.message.length == 0) return;
|
||||
|
||||
for (let account of r.message) {
|
||||
|
||||
const node = cur_tree.nodes && cur_tree.nodes[account.value];
|
||||
if (!node || node.is_root) continue;
|
||||
|
||||
// show Dr if positive since balance is calculated as debit - credit else show Cr
|
||||
const balance = account.balance_in_account_currency || account.balance;
|
||||
const dr_or_cr = balance > 0 ? "Dr": "Cr";
|
||||
const format = (value, currency) => format_currency(Math.abs(value), currency);
|
||||
|
||||
if (account.balance!==undefined) {
|
||||
$('<span class="balance-area pull-right">'
|
||||
+ (account.balance_in_account_currency ?
|
||||
(format(account.balance_in_account_currency, account.account_currency) + " / ") : "")
|
||||
+ format(account.balance, account.company_currency)
|
||||
+ " " + dr_or_cr
|
||||
+ '</span>').insertBefore(node.$ul);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
add_tree_node: 'erpnext.accounts.utils.add_ac',
|
||||
menu_items:[
|
||||
{
|
||||
@ -122,24 +165,6 @@ frappe.treeview_settings["Account"] = {
|
||||
}
|
||||
}, "add");
|
||||
},
|
||||
onrender: function(node) {
|
||||
if (frappe.boot.user.can_read.indexOf("GL Entry") !== -1) {
|
||||
|
||||
// show Dr if positive since balance is calculated as debit - credit else show Cr
|
||||
let balance = node.data.balance_in_account_currency || node.data.balance;
|
||||
let dr_or_cr = balance > 0 ? "Dr": "Cr";
|
||||
|
||||
if (node.data && node.data.balance!==undefined) {
|
||||
$('<span class="balance-area pull-right">'
|
||||
+ (node.data.balance_in_account_currency ?
|
||||
(format_currency(Math.abs(node.data.balance_in_account_currency),
|
||||
node.data.account_currency) + " / ") : "")
|
||||
+ format_currency(Math.abs(node.data.balance), node.data.company_currency)
|
||||
+ " " + dr_or_cr
|
||||
+ '</span>').insertBefore(node.$ul);
|
||||
}
|
||||
}
|
||||
},
|
||||
toolbar: [
|
||||
{
|
||||
label:__("Add Child"),
|
||||
|
@ -12,11 +12,6 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
||||
frm.set_df_property('import_file_section', 'hidden', frm.doc.company ? 0 : 1);
|
||||
frm.set_df_property('chart_preview', 'hidden',
|
||||
$(frm.fields_dict['chart_tree'].wrapper).html()!="" ? 0 : 1);
|
||||
|
||||
// Show import button when file is successfully attached
|
||||
if (frm.page && frm.page.show_import_button) {
|
||||
create_import_button(frm);
|
||||
}
|
||||
},
|
||||
|
||||
download_template: function(frm) {
|
||||
@ -78,8 +73,12 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
||||
frm.page.set_indicator("");
|
||||
$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper on removing file
|
||||
} else {
|
||||
generate_tree_preview(frm);
|
||||
validate_csv_data(frm);
|
||||
frappe.run_serially([
|
||||
() => validate_coa(frm),
|
||||
() => generate_tree_preview(frm),
|
||||
() => create_import_button(frm),
|
||||
() => frm.set_df_property('chart_preview', 'hidden', 0),
|
||||
]);
|
||||
}
|
||||
},
|
||||
|
||||
@ -104,42 +103,27 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
||||
}
|
||||
});
|
||||
|
||||
var validate_csv_data = function(frm) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_accounts",
|
||||
args: {file_name: frm.doc.import_file},
|
||||
callback: function(r) {
|
||||
if(r.message && r.message[0]===true) {
|
||||
frm.page["show_import_button"] = true;
|
||||
frm.page["total_accounts"] = r.message[1];
|
||||
frm.trigger("refresh");
|
||||
} else {
|
||||
frm.page.set_indicator(__('Resolve error and upload again.'), 'orange');
|
||||
frappe.throw(__(r.message));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var create_import_button = function(frm) {
|
||||
frm.page.set_primary_action(__("Import"), function () {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
||||
args: {
|
||||
file_name: frm.doc.import_file,
|
||||
company: frm.doc.company
|
||||
},
|
||||
freeze: true,
|
||||
freeze_message: __("Creating Accounts..."),
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
clearInterval(frm.page["interval"]);
|
||||
frm.page.set_indicator(__('Import Successful'), 'blue');
|
||||
create_reset_button(frm);
|
||||
if (frm.page.show_import_button) {
|
||||
frm.page.set_primary_action(__("Import"), function () {
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
||||
args: {
|
||||
file_name: frm.doc.import_file,
|
||||
company: frm.doc.company
|
||||
},
|
||||
freeze: true,
|
||||
freeze_message: __("Creating Accounts..."),
|
||||
callback: function(r) {
|
||||
if (!r.exc) {
|
||||
clearInterval(frm.page["interval"]);
|
||||
frm.page.set_indicator(__('Import Successful'), 'blue');
|
||||
create_reset_button(frm);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}).addClass('btn btn-primary');
|
||||
});
|
||||
}).addClass('btn btn-primary');
|
||||
}
|
||||
};
|
||||
|
||||
var create_reset_button = function(frm) {
|
||||
@ -150,24 +134,48 @@ var create_reset_button = function(frm) {
|
||||
}).addClass('btn btn-primary');
|
||||
};
|
||||
|
||||
var generate_tree_preview = function(frm) {
|
||||
let parent = __('All Accounts');
|
||||
$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper to load new data
|
||||
var validate_coa = function(frm) {
|
||||
if (frm.doc.import_file) {
|
||||
let parent = __('All Accounts');
|
||||
|
||||
// generate tree structure based on the csv data
|
||||
new frappe.ui.Tree({
|
||||
parent: $(frm.fields_dict['chart_tree'].wrapper),
|
||||
label: parent,
|
||||
expandable: true,
|
||||
method: 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
|
||||
args: {
|
||||
file_name: frm.doc.import_file,
|
||||
parent: parent,
|
||||
doctype: 'Chart of Accounts Importer',
|
||||
file_type: frm.doc.file_type
|
||||
},
|
||||
onclick: function(node) {
|
||||
parent = node.value;
|
||||
}
|
||||
});
|
||||
return frappe.call({
|
||||
'method': 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
|
||||
'args': {
|
||||
file_name: frm.doc.import_file,
|
||||
parent: parent,
|
||||
doctype: 'Chart of Accounts Importer',
|
||||
file_type: frm.doc.file_type,
|
||||
for_validate: 1
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message['show_import_button']) {
|
||||
frm.page['show_import_button'] = Boolean(r.message['show_import_button']);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var generate_tree_preview = function(frm) {
|
||||
if (frm.doc.import_file) {
|
||||
let parent = __('All Accounts');
|
||||
$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper to load new data
|
||||
|
||||
// generate tree structure based on the csv data
|
||||
return new frappe.ui.Tree({
|
||||
parent: $(frm.fields_dict['chart_tree'].wrapper),
|
||||
label: parent,
|
||||
expandable: true,
|
||||
method: 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
|
||||
args: {
|
||||
file_name: frm.doc.import_file,
|
||||
parent: parent,
|
||||
doctype: 'Chart of Accounts Importer',
|
||||
file_type: frm.doc.file_type
|
||||
},
|
||||
onclick: function(node) {
|
||||
parent = node.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -25,8 +25,16 @@ from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import
|
||||
|
||||
|
||||
class ChartofAccountsImporter(Document):
|
||||
def validate(self):
|
||||
validate_accounts(self.import_file)
|
||||
pass
|
||||
|
||||
def validate_columns(data):
|
||||
if not data:
|
||||
frappe.throw(_('No data found. Seems like you uploaded a blank file'))
|
||||
|
||||
no_of_columns = max([len(d) for d in data])
|
||||
|
||||
if no_of_columns > 7:
|
||||
frappe.throw(_('More columns found than expected. Please compare the uploaded file with standard template'))
|
||||
|
||||
@frappe.whitelist()
|
||||
def validate_company(company):
|
||||
@ -56,6 +64,7 @@ def import_coa(file_name, company):
|
||||
else:
|
||||
data = generate_data_from_excel(file_doc, extension)
|
||||
|
||||
frappe.local.flags.ignore_root_company_validation = True
|
||||
forest = build_forest(data)
|
||||
create_charts(company, custom_chart=forest)
|
||||
|
||||
@ -120,7 +129,7 @@ def generate_data_from_excel(file_doc, extension, as_dict=False):
|
||||
return data
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_coa(doctype, parent, is_root=False, file_name=None):
|
||||
def get_coa(doctype, parent, is_root=False, file_name=None, for_validate=0):
|
||||
''' called by tree view (to fetch node's children) '''
|
||||
|
||||
file_doc, extension = get_file(file_name)
|
||||
@ -131,13 +140,21 @@ def get_coa(doctype, parent, is_root=False, file_name=None):
|
||||
else:
|
||||
data = generate_data_from_excel(file_doc, extension)
|
||||
|
||||
forest = build_forest(data)
|
||||
accounts = build_tree_from_json("", chart_data=forest) # returns alist of dict in a tree render-able form
|
||||
validate_columns(data)
|
||||
validate_accounts(file_doc, extension)
|
||||
|
||||
# filter out to show data for the selected node only
|
||||
accounts = [d for d in accounts if d['parent_account']==parent]
|
||||
if not for_validate:
|
||||
forest = build_forest(data)
|
||||
accounts = build_tree_from_json("", chart_data=forest) # returns a list of dict in a tree render-able form
|
||||
|
||||
return accounts
|
||||
# filter out to show data for the selected node only
|
||||
accounts = [d for d in accounts if d['parent_account']==parent]
|
||||
|
||||
return accounts
|
||||
else:
|
||||
return {
|
||||
'show_import_button': 1
|
||||
}
|
||||
|
||||
def build_forest(data):
|
||||
'''
|
||||
@ -294,10 +311,7 @@ def get_sample_template(writer):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def validate_accounts(file_name):
|
||||
|
||||
file_doc, extension = get_file(file_name)
|
||||
|
||||
def validate_accounts(file_doc, extension):
|
||||
if extension == 'csv':
|
||||
accounts = generate_data_from_csv(file_doc, as_dict=True)
|
||||
else:
|
||||
@ -316,15 +330,10 @@ def validate_accounts(file_name):
|
||||
|
||||
validate_root(accounts_dict)
|
||||
|
||||
validate_account_types(accounts_dict)
|
||||
|
||||
return [True, len(accounts)]
|
||||
|
||||
def validate_root(accounts):
|
||||
roots = [accounts[d] for d in accounts if not accounts[d].get('parent_account')]
|
||||
if len(roots) < 4:
|
||||
frappe.throw(_("Number of root accounts cannot be less than 4"))
|
||||
|
||||
error_messages = []
|
||||
|
||||
for account in roots:
|
||||
@ -333,9 +342,19 @@ def validate_root(accounts):
|
||||
elif account.get("root_type") not in get_root_types() and account.get("account_name"):
|
||||
error_messages.append(_("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity").format(account.get("account_name")))
|
||||
|
||||
validate_missing_roots(roots)
|
||||
|
||||
if error_messages:
|
||||
frappe.throw("<br>".join(error_messages))
|
||||
|
||||
def validate_missing_roots(roots):
|
||||
root_types_added = set(d.get('root_type') for d in roots)
|
||||
|
||||
missing = list(set(get_root_types()) - root_types_added)
|
||||
|
||||
if missing:
|
||||
frappe.throw(_("Please add Root Account for - {0}").format(' , '.join(missing)))
|
||||
|
||||
def get_root_types():
|
||||
return ('Asset', 'Liability', 'Expense', 'Income', 'Equity')
|
||||
|
||||
@ -361,23 +380,6 @@ def get_mandatory_account_types():
|
||||
{'account_type': 'Stock', 'root_type': 'Asset'}
|
||||
]
|
||||
|
||||
|
||||
def validate_account_types(accounts):
|
||||
account_types_for_ledger = ["Cost of Goods Sold", "Depreciation", "Fixed Asset", "Payable", "Receivable", "Stock Adjustment"]
|
||||
account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group'] == 1]
|
||||
|
||||
missing = list(set(account_types_for_ledger) - set(account_types))
|
||||
if missing:
|
||||
frappe.throw(_("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing)))
|
||||
|
||||
account_types_for_group = ["Bank", "Cash", "Stock"]
|
||||
# fix logic bug
|
||||
account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group'] == 1]
|
||||
|
||||
missing = list(set(account_types_for_group) - set(account_groups))
|
||||
if missing:
|
||||
frappe.throw(_("Please identify/create Account (Group) for type - {0}").format(' , '.join(missing)))
|
||||
|
||||
def unset_existing_data(company):
|
||||
linked = frappe.db.sql('''select fieldname from tabDocField
|
||||
where fieldtype="Link" and options="Account" and parent="Company"''', as_dict=True)
|
||||
|
@ -1,63 +1,33 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2014-10-02 13:35:44.155278",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 1,
|
||||
"actions": [],
|
||||
"creation": "2014-10-02 13:35:44.155278",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"company"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"options": "Company"
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2016-07-11 03:28:00.505946",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Fiscal Year Company",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_seen": 0
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-09-28 18:01:53.495929",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Fiscal Year Company",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -13,10 +13,12 @@
|
||||
"voucher_type",
|
||||
"naming_series",
|
||||
"finance_book",
|
||||
"tax_withholding_category",
|
||||
"column_break1",
|
||||
"from_template",
|
||||
"company",
|
||||
"posting_date",
|
||||
"apply_tds",
|
||||
"2_add_edit_gl_entries",
|
||||
"accounts",
|
||||
"section_break99",
|
||||
@ -498,16 +500,32 @@
|
||||
"options": "Journal Entry Template",
|
||||
"print_hide": 1,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.apply_tds",
|
||||
"fieldname": "tax_withholding_category",
|
||||
"fieldtype": "Link",
|
||||
"label": "Tax Withholding Category",
|
||||
"mandatory_depends_on": "eval:doc.apply_tds",
|
||||
"options": "Tax Withholding Category"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:['Credit Note', 'Debit Note'].includes(doc.voucher_type)",
|
||||
"fieldname": "apply_tds",
|
||||
"fieldtype": "Check",
|
||||
"label": "Apply Tax Withholding Amount "
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 176,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-30 13:56:01.121995",
|
||||
"modified": "2021-09-09 15:31:14.484029",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Journal Entry",
|
||||
"naming_rule": "By \"Naming Series\" field",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
|
@ -15,6 +15,9 @@ from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts
|
||||
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import (
|
||||
get_party_account_based_on_invoice_discounting,
|
||||
)
|
||||
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
|
||||
get_party_tax_withholding_details,
|
||||
)
|
||||
from erpnext.accounts.party import get_party_account
|
||||
from erpnext.accounts.utils import (
|
||||
check_if_stock_and_account_balance_synced,
|
||||
@ -57,7 +60,8 @@ class JournalEntry(AccountsController):
|
||||
|
||||
self.validate_against_jv()
|
||||
self.validate_reference_doc()
|
||||
self.set_against_account()
|
||||
if self.docstatus == 0:
|
||||
self.set_against_account()
|
||||
self.create_remarks()
|
||||
self.set_print_format_fields()
|
||||
self.validate_expense_claim()
|
||||
@ -66,6 +70,10 @@ class JournalEntry(AccountsController):
|
||||
self.set_account_and_party_balance()
|
||||
self.validate_inter_company_accounts()
|
||||
self.validate_stock_accounts()
|
||||
|
||||
if self.docstatus == 0:
|
||||
self.apply_tax_withholding()
|
||||
|
||||
if not self.title:
|
||||
self.title = self.get_title()
|
||||
|
||||
@ -139,6 +147,72 @@ class JournalEntry(AccountsController):
|
||||
frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
|
||||
.format(account), StockAccountInvalidTransaction)
|
||||
|
||||
def apply_tax_withholding(self):
|
||||
from erpnext.accounts.report.general_ledger.general_ledger import get_account_type_map
|
||||
|
||||
if not self.apply_tds or self.voucher_type not in ('Debit Note', 'Credit Note'):
|
||||
return
|
||||
|
||||
parties = [d.party for d in self.get('accounts') if d.party]
|
||||
parties = list(set(parties))
|
||||
|
||||
if len(parties) > 1:
|
||||
frappe.throw(_("Cannot apply TDS against multiple parties in one entry"))
|
||||
|
||||
account_type_map = get_account_type_map(self.company)
|
||||
party_type = 'supplier' if self.voucher_type == 'Credit Note' else 'customer'
|
||||
doctype = 'Purchase Invoice' if self.voucher_type == 'Credit Note' else 'Sales Invoice'
|
||||
debit_or_credit = 'debit_in_account_currency' if self.voucher_type == 'Credit Note' else 'credit_in_account_currency'
|
||||
rev_debit_or_credit = 'credit_in_account_currency' if debit_or_credit == 'debit_in_account_currency' else 'debit_in_account_currency'
|
||||
|
||||
party_account = get_party_account(party_type.title(), parties[0], self.company)
|
||||
|
||||
net_total = sum(d.get(debit_or_credit) for d in self.get('accounts') if account_type_map.get(d.account)
|
||||
not in ('Tax', 'Chargeable'))
|
||||
|
||||
party_amount = sum(d.get(rev_debit_or_credit) for d in self.get('accounts') if d.account == party_account)
|
||||
|
||||
inv = frappe._dict({
|
||||
party_type: parties[0],
|
||||
'doctype': doctype,
|
||||
'company': self.company,
|
||||
'posting_date': self.posting_date,
|
||||
'net_total': net_total
|
||||
})
|
||||
|
||||
tax_withholding_details = get_party_tax_withholding_details(inv, self.tax_withholding_category)
|
||||
|
||||
if not tax_withholding_details:
|
||||
return
|
||||
|
||||
accounts = []
|
||||
for d in self.get('accounts'):
|
||||
if d.get('account') == tax_withholding_details.get("account_head"):
|
||||
d.update({
|
||||
'account': tax_withholding_details.get("account_head"),
|
||||
debit_or_credit: tax_withholding_details.get('tax_amount')
|
||||
})
|
||||
|
||||
accounts.append(d.get('account'))
|
||||
|
||||
if d.get('account') == party_account:
|
||||
d.update({
|
||||
rev_debit_or_credit: party_amount - tax_withholding_details.get('tax_amount')
|
||||
})
|
||||
|
||||
if not accounts or tax_withholding_details.get("account_head") not in accounts:
|
||||
self.append("accounts", {
|
||||
'account': tax_withholding_details.get("account_head"),
|
||||
rev_debit_or_credit: tax_withholding_details.get('tax_amount'),
|
||||
'against_account': parties[0]
|
||||
})
|
||||
|
||||
to_remove = [d for d in self.get('accounts')
|
||||
if not d.get(rev_debit_or_credit) and d.account == tax_withholding_details.get("account_head")]
|
||||
|
||||
for d in to_remove:
|
||||
self.remove(d)
|
||||
|
||||
def update_inter_company_jv(self):
|
||||
if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
|
||||
frappe.db.set_value("Journal Entry", self.inter_company_journal_entry_reference,\
|
||||
|
@ -390,6 +390,9 @@ class PaymentEntry(AccountsController):
|
||||
invoice_paid_amount_map[invoice_key]['discounted_amt'] = ref.total_amount * (term.discount / 100)
|
||||
|
||||
for key, allocated_amount in iteritems(invoice_payment_amount_map):
|
||||
if not invoice_paid_amount_map.get(key):
|
||||
frappe.throw(_('Payment term {0} not used in {1}').format(key[0], key[1]))
|
||||
|
||||
outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding'))
|
||||
discounted_amt = flt(invoice_paid_amount_map.get(key, {}).get('discounted_amt'))
|
||||
|
||||
|
@ -93,6 +93,7 @@
|
||||
"options": "Payment Term"
|
||||
},
|
||||
{
|
||||
"depends_on": "exchange_gain_loss",
|
||||
"fieldname": "exchange_gain_loss",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Exchange Gain/Loss",
|
||||
@ -103,7 +104,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-04-21 13:30:11.605388",
|
||||
"modified": "2021-09-26 17:06:55.597389",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry Reference",
|
||||
|
@ -7,6 +7,7 @@
|
||||
"field_order": [
|
||||
"reference_type",
|
||||
"reference_name",
|
||||
"reference_row",
|
||||
"column_break_3",
|
||||
"invoice_type",
|
||||
"invoice_number",
|
||||
@ -121,11 +122,17 @@
|
||||
"label": "Amount",
|
||||
"options": "Currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "reference_row",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Reference Row"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-08-30 10:58:42.665107",
|
||||
"modified": "2021-09-20 17:23:09.455803",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Reconciliation Allocation",
|
||||
|
@ -40,6 +40,7 @@ class POSInvoice(SalesInvoice):
|
||||
self.validate_change_amount()
|
||||
self.validate_change_account()
|
||||
self.validate_item_cost_centers()
|
||||
self.validate_warehouse()
|
||||
self.validate_serialised_or_batched_item()
|
||||
self.validate_stock_availablility()
|
||||
self.validate_return_items_qty()
|
||||
|
@ -4,7 +4,7 @@
|
||||
frappe.ui.form.on('POS Invoice Merge Log', {
|
||||
setup: function(frm) {
|
||||
frm.set_query("pos_invoice", "pos_invoices", doc => {
|
||||
return{
|
||||
return {
|
||||
filters: {
|
||||
'docstatus': 1,
|
||||
'customer': doc.customer,
|
||||
@ -12,5 +12,10 @@ frappe.ui.form.on('POS Invoice Merge Log', {
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
merge_invoices_based_on: function(frm) {
|
||||
frm.set_value('customer', '');
|
||||
frm.set_value('customer_group', '');
|
||||
}
|
||||
});
|
||||
|
@ -6,9 +6,11 @@
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"posting_date",
|
||||
"customer",
|
||||
"merge_invoices_based_on",
|
||||
"column_break_3",
|
||||
"pos_closing_entry",
|
||||
"customer",
|
||||
"customer_group",
|
||||
"section_break_3",
|
||||
"pos_invoices",
|
||||
"references_section",
|
||||
@ -88,12 +90,27 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "POS Closing Entry",
|
||||
"options": "POS Closing Entry"
|
||||
},
|
||||
{
|
||||
"fieldname": "merge_invoices_based_on",
|
||||
"fieldtype": "Select",
|
||||
"label": "Merge Invoices Based On",
|
||||
"options": "Customer\nCustomer Group",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.merge_invoices_based_on == 'Customer Group'",
|
||||
"fieldname": "customer_group",
|
||||
"fieldtype": "Link",
|
||||
"label": "Customer Group",
|
||||
"mandatory_depends_on": "eval:doc.merge_invoices_based_on == 'Customer Group'",
|
||||
"options": "Customer Group"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-12-01 11:53:57.267579",
|
||||
"modified": "2021-09-14 11:17:19.001142",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Invoice Merge Log",
|
||||
|
@ -23,6 +23,9 @@ class POSInvoiceMergeLog(Document):
|
||||
self.validate_pos_invoice_status()
|
||||
|
||||
def validate_customer(self):
|
||||
if self.merge_invoices_based_on == 'Customer Group':
|
||||
return
|
||||
|
||||
for d in self.pos_invoices:
|
||||
if d.customer != self.customer:
|
||||
frappe.throw(_("Row #{}: POS Invoice {} is not against customer {}").format(d.idx, d.pos_invoice, self.customer))
|
||||
@ -124,7 +127,7 @@ class POSInvoiceMergeLog(Document):
|
||||
found = False
|
||||
for i in items:
|
||||
if (i.item_code == item.item_code and not i.serial_no and not i.batch_no and
|
||||
i.uom == item.uom and i.net_rate == item.net_rate):
|
||||
i.uom == item.uom and i.net_rate == item.net_rate and i.warehouse == item.warehouse):
|
||||
found = True
|
||||
i.qty = i.qty + item.qty
|
||||
|
||||
@ -172,6 +175,11 @@ class POSInvoiceMergeLog(Document):
|
||||
invoice.discount_amount = 0.0
|
||||
invoice.taxes_and_charges = None
|
||||
invoice.ignore_pricing_rule = 1
|
||||
invoice.customer = self.customer
|
||||
|
||||
if self.merge_invoices_based_on == 'Customer Group':
|
||||
invoice.flags.ignore_pos_profile = True
|
||||
invoice.pos_profile = ''
|
||||
|
||||
return invoice
|
||||
|
||||
@ -228,7 +236,7 @@ def get_all_unconsolidated_invoices():
|
||||
return pos_invoices
|
||||
|
||||
def get_invoice_customer_map(pos_invoices):
|
||||
# pos_invoice_customer_map = { 'Customer 1': [{}, {}, {}], 'Custoemr 2' : [{}] }
|
||||
# pos_invoice_customer_map = { 'Customer 1': [{}, {}, {}], 'Customer 2' : [{}] }
|
||||
pos_invoice_customer_map = {}
|
||||
for invoice in pos_invoices:
|
||||
customer = invoice.get('customer')
|
||||
|
@ -7,10 +7,8 @@ from __future__ import unicode_literals
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe import MandatoryError
|
||||
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.healthcare.doctype.lab_test_template.lab_test_template import make_item_price
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.stock.get_item_details import get_item_details
|
||||
@ -623,3 +621,12 @@ def delete_existing_pricing_rules():
|
||||
"Pricing Rule Item Group", "Pricing Rule Brand"]:
|
||||
|
||||
frappe.db.sql("delete from `tab{0}`".format(doctype))
|
||||
|
||||
|
||||
def make_item_price(item, price_list_name, item_price):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Item Price',
|
||||
'price_list': price_list_name,
|
||||
'item_code': item,
|
||||
'price_list_rate': item_price
|
||||
}).insert(ignore_permissions=True, ignore_mandatory=True)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,7 @@ from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
||||
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
|
||||
check_if_return_invoice_linked_with_payment_entry,
|
||||
is_overdue,
|
||||
unlink_inter_company_doc,
|
||||
update_linked_doc,
|
||||
validate_inter_company_party,
|
||||
@ -1145,6 +1146,12 @@ class PurchaseInvoice(BuyingController):
|
||||
if not self.apply_tds:
|
||||
return
|
||||
|
||||
if self.apply_tds and not self.get('tax_withholding_category'):
|
||||
self.tax_withholding_category = frappe.db.get_value('Supplier', self.supplier, 'tax_withholding_category')
|
||||
|
||||
if not self.tax_withholding_category:
|
||||
return
|
||||
|
||||
tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category)
|
||||
|
||||
if not tax_withholding_details:
|
||||
@ -1175,10 +1182,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.status = 'Draft'
|
||||
return
|
||||
|
||||
precision = self.precision("outstanding_amount")
|
||||
outstanding_amount = flt(self.outstanding_amount, precision)
|
||||
due_date = getdate(self.due_date)
|
||||
nowdate = getdate()
|
||||
outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
|
||||
|
||||
if not status:
|
||||
if self.docstatus == 2:
|
||||
@ -1186,9 +1190,11 @@ class PurchaseInvoice(BuyingController):
|
||||
elif self.docstatus == 1:
|
||||
if self.is_internal_transfer():
|
||||
self.status = 'Internal Transfer'
|
||||
elif outstanding_amount > 0 and due_date < nowdate:
|
||||
elif is_overdue(self):
|
||||
self.status = "Overdue"
|
||||
elif outstanding_amount > 0 and due_date >= nowdate:
|
||||
elif 0 < outstanding_amount < flt(self.grand_total, self.precision("grand_total")):
|
||||
self.status = "Partly Paid"
|
||||
elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
|
||||
self.status = "Unpaid"
|
||||
#Check if outstanding amount is 0 due to debit note issued against invoice
|
||||
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
||||
|
@ -2,28 +2,58 @@
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
// render
|
||||
frappe.listview_settings['Purchase Invoice'] = {
|
||||
add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
||||
"currency", "is_return", "release_date", "on_hold", "represents_company", "is_internal_supplier"],
|
||||
get_indicator: function(doc) {
|
||||
if ((flt(doc.outstanding_amount) <= 0) && doc.docstatus == 1 && doc.status == 'Debit Note Issued') {
|
||||
return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<=,0"];
|
||||
} else if (flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
|
||||
if(cint(doc.on_hold) && !doc.release_date) {
|
||||
return [__("On Hold"), "darkgrey"];
|
||||
} else if (cint(doc.on_hold) && doc.release_date && frappe.datetime.get_diff(doc.release_date, frappe.datetime.nowdate()) > 0) {
|
||||
return [__("Temporarily on Hold"), "darkgrey"];
|
||||
} else if (frappe.datetime.get_diff(doc.due_date) < 0) {
|
||||
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
|
||||
} else {
|
||||
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"];
|
||||
}
|
||||
} else if (cint(doc.is_return)) {
|
||||
return [__("Return"), "gray", "is_return,=,Yes"];
|
||||
} else if (doc.company == doc.represents_company && doc.is_internal_supplier) {
|
||||
return [__("Internal Transfer"), "darkgrey", "outstanding_amount,=,0"];
|
||||
} else if (flt(doc.outstanding_amount)==0 && doc.docstatus==1) {
|
||||
return [__("Paid"), "green", "outstanding_amount,=,0"];
|
||||
frappe.listview_settings["Purchase Invoice"] = {
|
||||
add_fields: [
|
||||
"supplier",
|
||||
"supplier_name",
|
||||
"base_grand_total",
|
||||
"outstanding_amount",
|
||||
"due_date",
|
||||
"company",
|
||||
"currency",
|
||||
"is_return",
|
||||
"release_date",
|
||||
"on_hold",
|
||||
"represents_company",
|
||||
"is_internal_supplier",
|
||||
],
|
||||
get_indicator(doc) {
|
||||
if (doc.status == "Debit Note Issued") {
|
||||
return [__(doc.status), "darkgrey", "status,=," + doc.status];
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
flt(doc.outstanding_amount) > 0 &&
|
||||
doc.docstatus == 1 &&
|
||||
cint(doc.on_hold)
|
||||
) {
|
||||
if (!doc.release_date) {
|
||||
return [__("On Hold"), "darkgrey"];
|
||||
} else if (
|
||||
frappe.datetime.get_diff(
|
||||
doc.release_date,
|
||||
frappe.datetime.nowdate()
|
||||
) > 0
|
||||
) {
|
||||
return [__("Temporarily on Hold"), "darkgrey"];
|
||||
}
|
||||
}
|
||||
|
||||
const status_colors = {
|
||||
"Unpaid": "orange",
|
||||
"Paid": "green",
|
||||
"Return": "gray",
|
||||
"Overdue": "red",
|
||||
"Partly Paid": "yellow",
|
||||
"Internal Transfer": "darkgrey",
|
||||
};
|
||||
|
||||
if (status_colors[doc.status]) {
|
||||
return [
|
||||
__(doc.status),
|
||||
status_colors[doc.status],
|
||||
"status,=," + doc.status,
|
||||
];
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -97,6 +97,7 @@
|
||||
"width": "100px"
|
||||
},
|
||||
{
|
||||
"depends_on": "exchange_gain_loss",
|
||||
"fieldname": "exchange_gain_loss",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Exchange Gain/Loss",
|
||||
@ -104,6 +105,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "exchange_gain_loss",
|
||||
"fieldname": "ref_exchange_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": "Reference Exchange Rate",
|
||||
@ -115,7 +117,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-04-20 16:26:53.820530",
|
||||
"modified": "2021-09-26 15:47:28.167371",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice Advance",
|
||||
|
@ -453,7 +453,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
|
||||
let row = frappe.get_doc(d.doctype, d.name)
|
||||
set_timesheet_detail_rate(row.doctype, row.name, me.frm.doc.currency, row.timesheet_detail)
|
||||
});
|
||||
calculate_total_billing_amount(this.frm);
|
||||
frm.trigger("calculate_timesheet_totals");
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -725,19 +725,6 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
}
|
||||
},
|
||||
|
||||
project: function(frm){
|
||||
if (!frm.doc.is_return) {
|
||||
frm.call({
|
||||
method: "add_timesheet_data",
|
||||
doc: frm.doc,
|
||||
callback: function(r, rt) {
|
||||
refresh_field(['timesheets'])
|
||||
}
|
||||
})
|
||||
frm.refresh();
|
||||
}
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
frm.redemption_conversion_factor = null;
|
||||
},
|
||||
@ -848,25 +835,92 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
}
|
||||
},
|
||||
|
||||
add_timesheet_row: function(frm, row, exchange_rate) {
|
||||
frm.add_child('timesheets', {
|
||||
'activity_type': row.activity_type,
|
||||
'description': row.description,
|
||||
'time_sheet': row.parent,
|
||||
'billing_hours': row.billing_hours,
|
||||
'billing_amount': flt(row.billing_amount) * flt(exchange_rate),
|
||||
'timesheet_detail': row.name,
|
||||
'project_name': row.project_name
|
||||
project: function(frm) {
|
||||
if (frm.doc.project) {
|
||||
frm.events.add_timesheet_data(frm, {
|
||||
project: frm.doc.project
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
async add_timesheet_data(frm, kwargs) {
|
||||
if (kwargs === "Sales Invoice") {
|
||||
// called via frm.trigger()
|
||||
kwargs = Object();
|
||||
}
|
||||
|
||||
if (!kwargs.hasOwnProperty("project") && frm.doc.project) {
|
||||
kwargs.project = frm.doc.project;
|
||||
}
|
||||
|
||||
const timesheets = await frm.events.get_timesheet_data(frm, kwargs);
|
||||
return frm.events.set_timesheet_data(frm, timesheets);
|
||||
},
|
||||
|
||||
async get_timesheet_data(frm, kwargs) {
|
||||
return frappe.call({
|
||||
method: "erpnext.projects.doctype.timesheet.timesheet.get_projectwise_timesheet_data",
|
||||
args: kwargs
|
||||
}).then(r => {
|
||||
if (!r.exc && r.message.length > 0) {
|
||||
return r.message
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
});
|
||||
frm.refresh_field('timesheets');
|
||||
calculate_total_billing_amount(frm);
|
||||
},
|
||||
|
||||
set_timesheet_data: function(frm, timesheets) {
|
||||
frm.clear_table("timesheets")
|
||||
timesheets.forEach(timesheet => {
|
||||
if (frm.doc.currency != timesheet.currency) {
|
||||
frappe.call({
|
||||
method: "erpnext.setup.utils.get_exchange_rate",
|
||||
args: {
|
||||
from_currency: timesheet.currency,
|
||||
to_currency: frm.doc.currency
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
exchange_rate = r.message;
|
||||
frm.events.append_time_log(frm, timesheet, exchange_rate);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
frm.events.append_time_log(frm, timesheet, 1.0);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
append_time_log: function(frm, time_log, exchange_rate) {
|
||||
const row = frm.add_child("timesheets");
|
||||
row.activity_type = time_log.activity_type;
|
||||
row.description = time_log.description;
|
||||
row.time_sheet = time_log.time_sheet;
|
||||
row.from_time = time_log.from_time;
|
||||
row.to_time = time_log.to_time;
|
||||
row.billing_hours = time_log.billing_hours;
|
||||
row.billing_amount = flt(time_log.billing_amount) * flt(exchange_rate);
|
||||
row.timesheet_detail = time_log.name;
|
||||
row.project_name = time_log.project_name;
|
||||
|
||||
frm.refresh_field("timesheets");
|
||||
frm.trigger("calculate_timesheet_totals");
|
||||
},
|
||||
|
||||
calculate_timesheet_totals: function(frm) {
|
||||
frm.set_value("total_billing_amount",
|
||||
frm.doc.timesheets.reduce((a, b) => a + (b["billing_amount"] || 0.0), 0.0));
|
||||
frm.set_value("total_billing_hours",
|
||||
frm.doc.timesheets.reduce((a, b) => a + (b["billing_hours"] || 0.0), 0.0));
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
if (frm.doc.docstatus===0 && !frm.doc.is_return) {
|
||||
frm.add_custom_button(__('Fetch Timesheet'), function() {
|
||||
frm.add_custom_button(__("Fetch Timesheet"), function() {
|
||||
let d = new frappe.ui.Dialog({
|
||||
title: __('Fetch Timesheet'),
|
||||
title: __("Fetch Timesheet"),
|
||||
fields: [
|
||||
{
|
||||
"label" : __("From"),
|
||||
@ -875,8 +929,8 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
"reqd": 1,
|
||||
},
|
||||
{
|
||||
fieldtype: 'Column Break',
|
||||
fieldname: 'col_break_1',
|
||||
fieldtype: "Column Break",
|
||||
fieldname: "col_break_1",
|
||||
},
|
||||
{
|
||||
"label" : __("To"),
|
||||
@ -893,48 +947,18 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
},
|
||||
],
|
||||
primary_action: function() {
|
||||
let data = d.get_values();
|
||||
frappe.call({
|
||||
method: "erpnext.projects.doctype.timesheet.timesheet.get_projectwise_timesheet_data",
|
||||
args: {
|
||||
from_time: data.from_time,
|
||||
to_time: data.to_time,
|
||||
project: data.project
|
||||
},
|
||||
callback: function(r) {
|
||||
if (!r.exc && r.message.length > 0) {
|
||||
frm.clear_table('timesheets')
|
||||
r.message.forEach((d) => {
|
||||
let exchange_rate = 1.0;
|
||||
if (frm.doc.currency != d.currency) {
|
||||
frappe.call({
|
||||
method: 'erpnext.setup.utils.get_exchange_rate',
|
||||
args: {
|
||||
from_currency: d.currency,
|
||||
to_currency: frm.doc.currency
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
exchange_rate = r.message;
|
||||
frm.events.add_timesheet_row(frm, d, exchange_rate);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
frm.events.add_timesheet_row(frm, d, exchange_rate);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
frappe.msgprint(__('No Timesheets found with the selected filters.'))
|
||||
}
|
||||
d.hide();
|
||||
}
|
||||
const data = d.get_values();
|
||||
frm.events.add_timesheet_data(frm, {
|
||||
from_time: data.from_time,
|
||||
to_time: data.to_time,
|
||||
project: data.project
|
||||
});
|
||||
d.hide();
|
||||
},
|
||||
primary_action_label: __('Get Timesheets')
|
||||
primary_action_label: __("Get Timesheets")
|
||||
});
|
||||
d.show();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (frm.doc.is_debit_note) {
|
||||
@ -967,26 +991,20 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
frm: frm
|
||||
});
|
||||
},
|
||||
|
||||
create_dunning: function(frm) {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_dunning",
|
||||
frm: frm
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
var calculate_total_billing_amount = function(frm) {
|
||||
var doc = frm.doc;
|
||||
|
||||
doc.total_billing_amount = 0.0
|
||||
if (doc.timesheets) {
|
||||
doc.timesheets.forEach((d) => {
|
||||
doc.total_billing_amount += flt(d.billing_amount)
|
||||
});
|
||||
frappe.ui.form.on("Sales Invoice Timesheet", {
|
||||
timesheets_remove(frm) {
|
||||
frm.trigger("calculate_timesheet_totals");
|
||||
}
|
||||
|
||||
refresh_field('total_billing_amount')
|
||||
}
|
||||
});
|
||||
|
||||
var set_timesheet_detail_rate = function(cdt, cdn, currency, timelog) {
|
||||
frappe.call({
|
||||
@ -1033,276 +1051,3 @@ var select_loyalty_program = function(frm, loyalty_programs) {
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
// Healthcare
|
||||
var get_healthcare_services_to_invoice = function(frm) {
|
||||
var me = this;
|
||||
let selected_patient = '';
|
||||
var dialog = new frappe.ui.Dialog({
|
||||
title: __("Get Items from Healthcare Services"),
|
||||
fields:[
|
||||
{
|
||||
fieldtype: 'Link',
|
||||
options: 'Patient',
|
||||
label: 'Patient',
|
||||
fieldname: "patient",
|
||||
reqd: true
|
||||
},
|
||||
{ fieldtype: 'Section Break' },
|
||||
{ fieldtype: 'HTML', fieldname: 'results_area' }
|
||||
]
|
||||
});
|
||||
var $wrapper;
|
||||
var $results;
|
||||
var $placeholder;
|
||||
dialog.set_values({
|
||||
'patient': frm.doc.patient
|
||||
});
|
||||
dialog.fields_dict["patient"].df.onchange = () => {
|
||||
var patient = dialog.fields_dict.patient.input.value;
|
||||
if(patient && patient!=selected_patient){
|
||||
selected_patient = patient;
|
||||
var method = "erpnext.healthcare.utils.get_healthcare_services_to_invoice";
|
||||
var args = {patient: patient, company: frm.doc.company};
|
||||
var columns = (["service", "reference_name", "reference_type"]);
|
||||
get_healthcare_items(frm, true, $results, $placeholder, method, args, columns);
|
||||
}
|
||||
else if(!patient){
|
||||
selected_patient = '';
|
||||
$results.empty();
|
||||
$results.append($placeholder);
|
||||
}
|
||||
}
|
||||
$wrapper = dialog.fields_dict.results_area.$wrapper.append(`<div class="results"
|
||||
style="border: 1px solid #d1d8dd; border-radius: 3px; height: 300px; overflow: auto;"></div>`);
|
||||
$results = $wrapper.find('.results');
|
||||
$placeholder = $(`<div class="multiselect-empty-state">
|
||||
<span class="text-center" style="margin-top: -40px;">
|
||||
<i class="fa fa-2x fa-heartbeat text-extra-muted"></i>
|
||||
<p class="text-extra-muted">No billable Healthcare Services found</p>
|
||||
</span>
|
||||
</div>`);
|
||||
$results.on('click', '.list-item--head :checkbox', (e) => {
|
||||
$results.find('.list-item-container .list-row-check')
|
||||
.prop("checked", ($(e.target).is(':checked')));
|
||||
});
|
||||
set_primary_action(frm, dialog, $results, true);
|
||||
dialog.show();
|
||||
};
|
||||
|
||||
var get_healthcare_items = function(frm, invoice_healthcare_services, $results, $placeholder, method, args, columns) {
|
||||
var me = this;
|
||||
$results.empty();
|
||||
frappe.call({
|
||||
method: method,
|
||||
args: args,
|
||||
callback: function(data) {
|
||||
if(data.message){
|
||||
$results.append(make_list_row(columns, invoice_healthcare_services));
|
||||
for(let i=0; i<data.message.length; i++){
|
||||
$results.append(make_list_row(columns, invoice_healthcare_services, data.message[i]));
|
||||
}
|
||||
}else {
|
||||
$results.append($placeholder);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var make_list_row= function(columns, invoice_healthcare_services, result={}) {
|
||||
var me = this;
|
||||
// Make a head row by default (if result not passed)
|
||||
let head = Object.keys(result).length === 0;
|
||||
let contents = ``;
|
||||
columns.forEach(function(column) {
|
||||
contents += `<div class="list-item__content ellipsis">
|
||||
${
|
||||
head ? `<span class="ellipsis">${__(frappe.model.unscrub(column))}</span>`
|
||||
|
||||
:(column !== "name" ? `<span class="ellipsis">${__(result[column])}</span>`
|
||||
: `<a class="list-id ellipsis">
|
||||
${__(result[column])}</a>`)
|
||||
}
|
||||
</div>`;
|
||||
})
|
||||
|
||||
let $row = $(`<div class="list-item">
|
||||
<div class="list-item__content" style="flex: 0 0 10px;">
|
||||
<input type="checkbox" class="list-row-check" ${result.checked ? 'checked' : ''}>
|
||||
</div>
|
||||
${contents}
|
||||
</div>`);
|
||||
|
||||
$row = list_row_data_items(head, $row, result, invoice_healthcare_services);
|
||||
return $row;
|
||||
};
|
||||
|
||||
var set_primary_action= function(frm, dialog, $results, invoice_healthcare_services) {
|
||||
var me = this;
|
||||
dialog.set_primary_action(__('Add'), function() {
|
||||
let checked_values = get_checked_values($results);
|
||||
if(checked_values.length > 0){
|
||||
if(invoice_healthcare_services) {
|
||||
frm.set_value("patient", dialog.fields_dict.patient.input.value);
|
||||
}
|
||||
frm.set_value("items", []);
|
||||
add_to_item_line(frm, checked_values, invoice_healthcare_services);
|
||||
dialog.hide();
|
||||
}
|
||||
else{
|
||||
if(invoice_healthcare_services){
|
||||
frappe.msgprint(__("Please select Healthcare Service"));
|
||||
}
|
||||
else{
|
||||
frappe.msgprint(__("Please select Drug"));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var get_checked_values= function($results) {
|
||||
return $results.find('.list-item-container').map(function() {
|
||||
let checked_values = {};
|
||||
if ($(this).find('.list-row-check:checkbox:checked').length > 0 ) {
|
||||
checked_values['dn'] = $(this).attr('data-dn');
|
||||
checked_values['dt'] = $(this).attr('data-dt');
|
||||
checked_values['item'] = $(this).attr('data-item');
|
||||
if($(this).attr('data-rate') != 'undefined'){
|
||||
checked_values['rate'] = $(this).attr('data-rate');
|
||||
}
|
||||
else{
|
||||
checked_values['rate'] = false;
|
||||
}
|
||||
if($(this).attr('data-income-account') != 'undefined'){
|
||||
checked_values['income_account'] = $(this).attr('data-income-account');
|
||||
}
|
||||
else{
|
||||
checked_values['income_account'] = false;
|
||||
}
|
||||
if($(this).attr('data-qty') != 'undefined'){
|
||||
checked_values['qty'] = $(this).attr('data-qty');
|
||||
}
|
||||
else{
|
||||
checked_values['qty'] = false;
|
||||
}
|
||||
if($(this).attr('data-description') != 'undefined'){
|
||||
checked_values['description'] = $(this).attr('data-description');
|
||||
}
|
||||
else{
|
||||
checked_values['description'] = false;
|
||||
}
|
||||
return checked_values;
|
||||
}
|
||||
}).get();
|
||||
};
|
||||
|
||||
var get_drugs_to_invoice = function(frm) {
|
||||
var me = this;
|
||||
let selected_encounter = '';
|
||||
var dialog = new frappe.ui.Dialog({
|
||||
title: __("Get Items from Prescriptions"),
|
||||
fields:[
|
||||
{ fieldtype: 'Link', options: 'Patient', label: 'Patient', fieldname: "patient", reqd: true },
|
||||
{ fieldtype: 'Link', options: 'Patient Encounter', label: 'Patient Encounter', fieldname: "encounter", reqd: true,
|
||||
description:'Quantity will be calculated only for items which has "Nos" as UoM. You may change as required for each invoice item.',
|
||||
get_query: function(doc) {
|
||||
return {
|
||||
filters: {
|
||||
patient: dialog.get_value("patient"),
|
||||
company: frm.doc.company,
|
||||
docstatus: 1
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
{ fieldtype: 'Section Break' },
|
||||
{ fieldtype: 'HTML', fieldname: 'results_area' }
|
||||
]
|
||||
});
|
||||
var $wrapper;
|
||||
var $results;
|
||||
var $placeholder;
|
||||
dialog.set_values({
|
||||
'patient': frm.doc.patient,
|
||||
'encounter': ""
|
||||
});
|
||||
dialog.fields_dict["encounter"].df.onchange = () => {
|
||||
var encounter = dialog.fields_dict.encounter.input.value;
|
||||
if(encounter && encounter!=selected_encounter){
|
||||
selected_encounter = encounter;
|
||||
var method = "erpnext.healthcare.utils.get_drugs_to_invoice";
|
||||
var args = {encounter: encounter};
|
||||
var columns = (["drug_code", "quantity", "description"]);
|
||||
get_healthcare_items(frm, false, $results, $placeholder, method, args, columns);
|
||||
}
|
||||
else if(!encounter){
|
||||
selected_encounter = '';
|
||||
$results.empty();
|
||||
$results.append($placeholder);
|
||||
}
|
||||
}
|
||||
$wrapper = dialog.fields_dict.results_area.$wrapper.append(`<div class="results"
|
||||
style="border: 1px solid #d1d8dd; border-radius: 3px; height: 300px; overflow: auto;"></div>`);
|
||||
$results = $wrapper.find('.results');
|
||||
$placeholder = $(`<div class="multiselect-empty-state">
|
||||
<span class="text-center" style="margin-top: -40px;">
|
||||
<i class="fa fa-2x fa-heartbeat text-extra-muted"></i>
|
||||
<p class="text-extra-muted">No Drug Prescription found</p>
|
||||
</span>
|
||||
</div>`);
|
||||
$results.on('click', '.list-item--head :checkbox', (e) => {
|
||||
$results.find('.list-item-container .list-row-check')
|
||||
.prop("checked", ($(e.target).is(':checked')));
|
||||
});
|
||||
set_primary_action(frm, dialog, $results, false);
|
||||
dialog.show();
|
||||
};
|
||||
|
||||
var list_row_data_items = function(head, $row, result, invoice_healthcare_services) {
|
||||
if(invoice_healthcare_services){
|
||||
head ? $row.addClass('list-item--head')
|
||||
: $row = $(`<div class="list-item-container"
|
||||
data-dn= "${result.reference_name}" data-dt= "${result.reference_type}" data-item= "${result.service}"
|
||||
data-rate = ${result.rate}
|
||||
data-income-account = "${result.income_account}"
|
||||
data-qty = ${result.qty}
|
||||
data-description = "${result.description}">
|
||||
</div>`).append($row);
|
||||
}
|
||||
else{
|
||||
head ? $row.addClass('list-item--head')
|
||||
: $row = $(`<div class="list-item-container"
|
||||
data-item= "${result.drug_code}"
|
||||
data-qty = ${result.quantity}
|
||||
data-description = "${result.description}">
|
||||
</div>`).append($row);
|
||||
}
|
||||
return $row
|
||||
};
|
||||
|
||||
var add_to_item_line = function(frm, checked_values, invoice_healthcare_services){
|
||||
if(invoice_healthcare_services){
|
||||
frappe.call({
|
||||
doc: frm.doc,
|
||||
method: "set_healthcare_services",
|
||||
args:{
|
||||
checked_values: checked_values
|
||||
},
|
||||
callback: function() {
|
||||
frm.trigger("validate");
|
||||
frm.refresh_fields();
|
||||
}
|
||||
});
|
||||
}
|
||||
else{
|
||||
for(let i=0; i<checked_values.length; i++){
|
||||
var si_item = frappe.model.add_child(frm.doc, 'Sales Invoice Item', 'items');
|
||||
frappe.model.set_value(si_item.doctype, si_item.name, 'item_code', checked_values[i]['item']);
|
||||
frappe.model.set_value(si_item.doctype, si_item.name, 'qty', 1);
|
||||
if(checked_values[i]['qty'] > 1){
|
||||
frappe.model.set_value(si_item.doctype, si_item.name, 'qty', parseFloat(checked_values[i]['qty']));
|
||||
}
|
||||
}
|
||||
frm.refresh_fields();
|
||||
}
|
||||
};
|
||||
|
@ -74,6 +74,7 @@
|
||||
"time_sheet_list",
|
||||
"timesheets",
|
||||
"total_billing_amount",
|
||||
"total_billing_hours",
|
||||
"section_break_30",
|
||||
"total_qty",
|
||||
"base_total",
|
||||
@ -1651,7 +1652,7 @@
|
||||
"label": "Status",
|
||||
"length": 30,
|
||||
"no_copy": 1,
|
||||
"options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nCancelled\nInternal Transfer",
|
||||
"options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nPartly Paid\nUnpaid\nUnpaid and Discounted\nPartly Paid and Discounted\nOverdue and Discounted\nOverdue\nCancelled\nInternal Transfer",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
@ -1953,6 +1954,7 @@
|
||||
"fetch_from": "customer.represents_company",
|
||||
"fieldname": "represents_company",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Represents Company",
|
||||
"options": "Company",
|
||||
"read_only": 1
|
||||
@ -2010,6 +2012,13 @@
|
||||
"hidden": 1,
|
||||
"label": "Ignore Default Payment Terms Template",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "total_billing_hours",
|
||||
"fieldtype": "Float",
|
||||
"label": "Total Billing Hours",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
@ -2022,11 +2031,12 @@
|
||||
"link_fieldname": "consolidated_invoice"
|
||||
}
|
||||
],
|
||||
"modified": "2021-09-08 15:24:25.486499",
|
||||
"modified": "2021-10-02 03:36:10.251715",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
"name_case": "Title Case",
|
||||
"naming_rule": "By \"Naming Series\" field",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
|
@ -40,7 +40,6 @@ from erpnext.assets.doctype.asset.depreciation import (
|
||||
post_depreciation_entries,
|
||||
)
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
from erpnext.healthcare.utils import manage_invoice_submit_cancel
|
||||
from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data
|
||||
from erpnext.setup.doctype.company.company import update_company_current_month_sales
|
||||
from erpnext.stock.doctype.batch.batch import set_batch_nos
|
||||
@ -263,13 +262,6 @@ class SalesInvoice(SellingController):
|
||||
if self.redeem_loyalty_points and not self.is_consolidated and self.loyalty_points:
|
||||
self.apply_loyalty_points()
|
||||
|
||||
# Healthcare Service Invoice.
|
||||
domain_settings = frappe.get_doc('Domain Settings')
|
||||
active_domains = [d.domain for d in domain_settings.active_domains]
|
||||
|
||||
if "Healthcare" in active_domains:
|
||||
manage_invoice_submit_cancel(self, "on_submit")
|
||||
|
||||
self.process_common_party_accounting()
|
||||
|
||||
def validate_pos_return(self):
|
||||
@ -352,12 +344,6 @@ class SalesInvoice(SellingController):
|
||||
|
||||
unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference)
|
||||
|
||||
# Healthcare Service Invoice.
|
||||
domain_settings = frappe.get_doc('Domain Settings')
|
||||
active_domains = [d.domain for d in domain_settings.active_domains]
|
||||
|
||||
if "Healthcare" in active_domains:
|
||||
manage_invoice_submit_cancel(self, "on_cancel")
|
||||
self.unlink_sales_invoice_from_timesheets()
|
||||
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
|
||||
|
||||
@ -499,7 +485,7 @@ class SalesInvoice(SellingController):
|
||||
self.account_for_change_amount = frappe.get_cached_value('Company', self.company, 'default_cash_account')
|
||||
|
||||
from erpnext.stock.get_item_details import get_pos_profile, get_pos_profile_item_details
|
||||
if not self.pos_profile:
|
||||
if not self.pos_profile and not self.flags.ignore_pos_profile:
|
||||
pos_profile = get_pos_profile(self.company) or {}
|
||||
if not pos_profile:
|
||||
return
|
||||
@ -772,7 +758,7 @@ class SalesInvoice(SellingController):
|
||||
if self.project:
|
||||
for data in get_projectwise_timesheet_data(self.project):
|
||||
self.append('timesheets', {
|
||||
'time_sheet': data.parent,
|
||||
'time_sheet': data.time_sheet,
|
||||
'billing_hours': data.billing_hours,
|
||||
'billing_amount': data.billing_amount,
|
||||
'timesheet_detail': data.name,
|
||||
@ -783,12 +769,11 @@ class SalesInvoice(SellingController):
|
||||
self.calculate_billing_amount_for_timesheet()
|
||||
|
||||
def calculate_billing_amount_for_timesheet(self):
|
||||
total_billing_amount = 0.0
|
||||
for data in self.timesheets:
|
||||
if data.billing_amount:
|
||||
total_billing_amount += data.billing_amount
|
||||
def timesheet_sum(field):
|
||||
return sum((ts.get(field) or 0.0) for ts in self.timesheets)
|
||||
|
||||
self.total_billing_amount = total_billing_amount
|
||||
self.total_billing_amount = timesheet_sum("billing_amount")
|
||||
self.total_billing_hours = timesheet_sum("billing_hours")
|
||||
|
||||
def get_warehouse(self):
|
||||
user_pos_profile = frappe.db.sql("""select name, warehouse from `tabPOS Profile`
|
||||
@ -1430,59 +1415,13 @@ class SalesInvoice(SellingController):
|
||||
if points_to_redeem < 1: # since points_to_redeem is integer
|
||||
break
|
||||
|
||||
# Healthcare
|
||||
@frappe.whitelist()
|
||||
def set_healthcare_services(self, checked_values):
|
||||
self.set("items", [])
|
||||
from erpnext.stock.get_item_details import get_item_details
|
||||
for checked_item in checked_values:
|
||||
item_line = self.append("items", {})
|
||||
price_list, price_list_currency = frappe.db.get_values("Price List", {"selling": 1}, ['name', 'currency'])[0]
|
||||
args = {
|
||||
'doctype': "Sales Invoice",
|
||||
'item_code': checked_item['item'],
|
||||
'company': self.company,
|
||||
'customer': frappe.db.get_value("Patient", self.patient, "customer"),
|
||||
'selling_price_list': price_list,
|
||||
'price_list_currency': price_list_currency,
|
||||
'plc_conversion_rate': 1.0,
|
||||
'conversion_rate': 1.0
|
||||
}
|
||||
item_details = get_item_details(args)
|
||||
item_line.item_code = checked_item['item']
|
||||
item_line.qty = 1
|
||||
if checked_item['qty']:
|
||||
item_line.qty = checked_item['qty']
|
||||
if checked_item['rate']:
|
||||
item_line.rate = checked_item['rate']
|
||||
else:
|
||||
item_line.rate = item_details.price_list_rate
|
||||
item_line.amount = float(item_line.rate) * float(item_line.qty)
|
||||
if checked_item['income_account']:
|
||||
item_line.income_account = checked_item['income_account']
|
||||
if checked_item['dt']:
|
||||
item_line.reference_dt = checked_item['dt']
|
||||
if checked_item['dn']:
|
||||
item_line.reference_dn = checked_item['dn']
|
||||
if checked_item['description']:
|
||||
item_line.description = checked_item['description']
|
||||
|
||||
self.set_missing_values(for_validate = True)
|
||||
|
||||
def set_status(self, update=False, status=None, update_modified=True):
|
||||
if self.is_new():
|
||||
if self.get('amended_from'):
|
||||
self.status = 'Draft'
|
||||
return
|
||||
|
||||
precision = self.precision("outstanding_amount")
|
||||
outstanding_amount = flt(self.outstanding_amount, precision)
|
||||
due_date = getdate(self.due_date)
|
||||
nowdate = getdate()
|
||||
|
||||
discounting_status = None
|
||||
if self.is_discounted:
|
||||
discounting_status = get_discounting_status(self.name)
|
||||
outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
|
||||
|
||||
if not status:
|
||||
if self.docstatus == 2:
|
||||
@ -1490,15 +1429,13 @@ class SalesInvoice(SellingController):
|
||||
elif self.docstatus == 1:
|
||||
if self.is_internal_transfer():
|
||||
self.status = 'Internal Transfer'
|
||||
elif outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discounting_status=='Disbursed':
|
||||
self.status = "Overdue and Discounted"
|
||||
elif outstanding_amount > 0 and due_date < nowdate:
|
||||
elif is_overdue(self):
|
||||
self.status = "Overdue"
|
||||
elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discounting_status=='Disbursed':
|
||||
self.status = "Unpaid and Discounted"
|
||||
elif outstanding_amount > 0 and due_date >= nowdate:
|
||||
elif 0 < outstanding_amount < flt(self.grand_total, self.precision("grand_total")):
|
||||
self.status = "Partly Paid"
|
||||
elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
|
||||
self.status = "Unpaid"
|
||||
#Check if outstanding amount is 0 due to credit note issued against invoice
|
||||
# Check if outstanding amount is 0 due to credit note issued against invoice
|
||||
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
||||
self.status = "Credit Note Issued"
|
||||
elif self.is_return == 1:
|
||||
@ -1507,12 +1444,42 @@ class SalesInvoice(SellingController):
|
||||
self.status = "Paid"
|
||||
else:
|
||||
self.status = "Submitted"
|
||||
|
||||
if (
|
||||
self.status in ("Unpaid", "Partly Paid", "Overdue")
|
||||
and self.is_discounted
|
||||
and get_discounting_status(self.name) == "Disbursed"
|
||||
):
|
||||
self.status += " and Discounted"
|
||||
|
||||
else:
|
||||
self.status = "Draft"
|
||||
|
||||
if update:
|
||||
self.db_set('status', self.status, update_modified = update_modified)
|
||||
|
||||
def is_overdue(doc):
|
||||
outstanding_amount = flt(doc.outstanding_amount, doc.precision("outstanding_amount"))
|
||||
|
||||
if outstanding_amount <= 0:
|
||||
return
|
||||
|
||||
grand_total = flt(doc.grand_total, doc.precision("grand_total"))
|
||||
nowdate = getdate()
|
||||
if doc.payment_schedule:
|
||||
# calculate payable amount till date
|
||||
payable_amount = sum(
|
||||
payment.payment_amount
|
||||
for payment in doc.payment_schedule
|
||||
if getdate(payment.due_date) < nowdate
|
||||
)
|
||||
|
||||
if (grand_total - outstanding_amount) < payable_amount:
|
||||
return True
|
||||
|
||||
elif getdate(doc.due_date) < nowdate:
|
||||
return True
|
||||
|
||||
def get_discounting_status(sales_invoice):
|
||||
status = None
|
||||
|
||||
|
@ -6,18 +6,20 @@ frappe.listview_settings['Sales Invoice'] = {
|
||||
add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
||||
"currency", "is_return"],
|
||||
get_indicator: function(doc) {
|
||||
var status_color = {
|
||||
const status_colors = {
|
||||
"Draft": "grey",
|
||||
"Unpaid": "orange",
|
||||
"Paid": "green",
|
||||
"Return": "gray",
|
||||
"Credit Note Issued": "gray",
|
||||
"Unpaid and Discounted": "orange",
|
||||
"Partly Paid and Discounted": "yellow",
|
||||
"Overdue and Discounted": "red",
|
||||
"Overdue": "red",
|
||||
"Partly Paid": "yellow",
|
||||
"Internal Transfer": "darkgrey"
|
||||
};
|
||||
return [__(doc.status), status_color[doc.status], "status,=,"+doc.status];
|
||||
return [__(doc.status), status_colors[doc.status], "status,=,"+doc.status];
|
||||
},
|
||||
right_column: "grand_total"
|
||||
};
|
||||
|
@ -133,6 +133,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
def test_payment_entry_unlink_against_invoice(self):
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||
|
||||
si = frappe.copy_doc(test_records[0])
|
||||
si.is_pos = 0
|
||||
si.insert()
|
||||
@ -156,6 +157,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
def test_payment_entry_unlink_against_standalone_credit_note(self):
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||
|
||||
si1 = create_sales_invoice(rate=1000)
|
||||
si2 = create_sales_invoice(rate=300)
|
||||
si3 = create_sales_invoice(qty=-1, rate=300, is_return=1)
|
||||
@ -1420,15 +1422,22 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(si)
|
||||
|
||||
expected_itemised_tax = {
|
||||
"999800": {
|
||||
"_Test Item": {
|
||||
"Service Tax": {
|
||||
"tax_rate": 10.0,
|
||||
"tax_amount": 1500.0
|
||||
"tax_amount": 1000.0
|
||||
}
|
||||
},
|
||||
"_Test Item 2": {
|
||||
"Service Tax": {
|
||||
"tax_rate": 10.0,
|
||||
"tax_amount": 500.0
|
||||
}
|
||||
}
|
||||
}
|
||||
expected_itemised_taxable_amount = {
|
||||
"999800": 15000.0
|
||||
"_Test Item": 10000.0,
|
||||
"_Test Item 2": 5000.0
|
||||
}
|
||||
|
||||
self.assertEqual(itemised_tax, expected_itemised_tax)
|
||||
@ -1639,6 +1648,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
def test_credit_note(self):
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||
|
||||
si = create_sales_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
|
||||
|
||||
outstanding_amount = get_outstanding_amount(si.doctype,
|
||||
@ -1790,6 +1800,47 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
check_gl_entries(self, si.name, expected_gle, "2019-01-30")
|
||||
|
||||
def test_deferred_revenue_post_account_freeze_upto_by_admin(self):
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
|
||||
frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', None)
|
||||
|
||||
deferred_account = create_account(account_name="Deferred Revenue",
|
||||
parent_account="Current Liabilities - _TC", company="_Test Company")
|
||||
|
||||
item = create_item("_Test Item for Deferred Accounting")
|
||||
item.enable_deferred_revenue = 1
|
||||
item.deferred_revenue_account = deferred_account
|
||||
item.no_of_months = 12
|
||||
item.save()
|
||||
|
||||
si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_save=True)
|
||||
si.items[0].enable_deferred_revenue = 1
|
||||
si.items[0].service_start_date = "2019-01-10"
|
||||
si.items[0].service_end_date = "2019-03-15"
|
||||
si.items[0].deferred_revenue_account = deferred_account
|
||||
si.save()
|
||||
si.submit()
|
||||
|
||||
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', getdate('2019-01-31'))
|
||||
frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', 'System Manager')
|
||||
|
||||
pda1 = frappe.get_doc(dict(
|
||||
doctype='Process Deferred Accounting',
|
||||
posting_date=nowdate(),
|
||||
start_date="2019-01-01",
|
||||
end_date="2019-03-31",
|
||||
type="Income",
|
||||
company="_Test Company"
|
||||
))
|
||||
|
||||
pda1.insert()
|
||||
self.assertRaises(frappe.ValidationError, pda1.submit)
|
||||
|
||||
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
|
||||
frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', None)
|
||||
|
||||
def test_fixed_deferred_revenue(self):
|
||||
deferred_account = create_account(account_name="Deferred Revenue",
|
||||
parent_account="Current Liabilities - _TC", company="_Test Company")
|
||||
@ -2262,6 +2313,54 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
party_link.delete()
|
||||
frappe.db.set_value('Accounts Settings', None, 'enable_common_party_accounting', 0)
|
||||
|
||||
def test_payment_statuses(self):
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||
|
||||
today = nowdate()
|
||||
|
||||
# Test Overdue
|
||||
si = create_sales_invoice(do_not_submit=True)
|
||||
si.payment_schedule = []
|
||||
si.append("payment_schedule", {
|
||||
"due_date": add_days(today, -5),
|
||||
"invoice_portion": 50,
|
||||
"payment_amount": si.grand_total / 2
|
||||
})
|
||||
si.append("payment_schedule", {
|
||||
"due_date": add_days(today, 5),
|
||||
"invoice_portion": 50,
|
||||
"payment_amount": si.grand_total / 2
|
||||
})
|
||||
si.submit()
|
||||
self.assertEqual(si.status, "Overdue")
|
||||
|
||||
# Test payment less than due amount
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
|
||||
pe.reference_no = "1"
|
||||
pe.reference_date = nowdate()
|
||||
pe.paid_amount = 1
|
||||
pe.references[0].allocated_amount = pe.paid_amount
|
||||
pe.submit()
|
||||
si.reload()
|
||||
self.assertEqual(si.status, "Overdue")
|
||||
|
||||
# Test Partly Paid
|
||||
pe = frappe.copy_doc(pe)
|
||||
pe.paid_amount = si.grand_total / 2
|
||||
pe.references[0].allocated_amount = pe.paid_amount
|
||||
pe.submit()
|
||||
si.reload()
|
||||
self.assertEqual(si.status, "Partly Paid")
|
||||
|
||||
# Test Paid
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
|
||||
pe.reference_no = "1"
|
||||
pe.reference_date = nowdate()
|
||||
pe.paid_amount = si.outstanding_amount
|
||||
pe.submit()
|
||||
si.reload()
|
||||
self.assertEqual(si.status, "Paid")
|
||||
|
||||
def get_sales_invoice_for_e_invoice():
|
||||
si = make_sales_invoice_for_ewaybill()
|
||||
si.naming_series = 'INV-2020-.#####'
|
||||
@ -2294,6 +2393,7 @@ def make_test_address_for_ewaybill():
|
||||
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
|
||||
address = frappe.get_doc({
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_line2": "_Test Address Line 2",
|
||||
"address_title": "_Test Address for Eway bill",
|
||||
"address_type": "Billing",
|
||||
"city": "_Test City",
|
||||
@ -2315,11 +2415,12 @@ def make_test_address_for_ewaybill():
|
||||
|
||||
address.save()
|
||||
|
||||
if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Shipping'):
|
||||
if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Billing'):
|
||||
address = frappe.get_doc({
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_line2": "_Test Address Line 2",
|
||||
"address_title": "_Test Customer-Address for Eway bill",
|
||||
"address_type": "Shipping",
|
||||
"address_type": "Billing",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
@ -2339,9 +2440,34 @@ def make_test_address_for_ewaybill():
|
||||
|
||||
address.save()
|
||||
|
||||
if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Shipping'):
|
||||
address = frappe.get_doc({
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_line2": "_Test Address Line 2",
|
||||
"address_title": "_Test Customer-Address for Eway bill",
|
||||
"address_type": "Shipping",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+910000000000",
|
||||
"gst_state": "Maharashtra",
|
||||
"gst_state_number": "27",
|
||||
"pincode": "410098"
|
||||
}).insert()
|
||||
|
||||
address.append("links", {
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer"
|
||||
})
|
||||
|
||||
address.save()
|
||||
|
||||
if not frappe.db.exists('Address', '_Test Dispatch-Address for Eway bill-Shipping'):
|
||||
address = frappe.get_doc({
|
||||
"address_line1": "_Test Dispatch Address Line 1",
|
||||
"address_line2": "_Test Dispatch Address Line 2",
|
||||
"address_title": "_Test Dispatch-Address for Eway bill",
|
||||
"address_type": "Shipping",
|
||||
"city": "_Test City",
|
||||
@ -2356,11 +2482,6 @@ def make_test_address_for_ewaybill():
|
||||
"pincode": "1100101"
|
||||
}).insert()
|
||||
|
||||
address.append("links", {
|
||||
"link_doctype": "Company",
|
||||
"link_name": "_Test Company"
|
||||
})
|
||||
|
||||
address.save()
|
||||
|
||||
def make_test_transporter_for_ewaybill():
|
||||
@ -2400,7 +2521,8 @@ def make_sales_invoice_for_ewaybill():
|
||||
|
||||
si.distance = 2000
|
||||
si.company_address = "_Test Address for Eway bill-Billing"
|
||||
si.customer_address = "_Test Customer-Address for Eway bill-Shipping"
|
||||
si.customer_address = "_Test Customer-Address for Eway bill-Billing"
|
||||
si.shipping_address_name = "_Test Customer-Address for Eway bill-Shipping"
|
||||
si.dispatch_address_name = "_Test Dispatch-Address for Eway bill-Shipping"
|
||||
si.vehicle_no = "KA12KA1234"
|
||||
si.gst_category = "Registered Regular"
|
||||
|
@ -98,6 +98,7 @@
|
||||
"width": "120px"
|
||||
},
|
||||
{
|
||||
"depends_on": "exchange_gain_loss",
|
||||
"fieldname": "exchange_gain_loss",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Exchange Gain/Loss",
|
||||
@ -105,6 +106,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "exchange_gain_loss",
|
||||
"fieldname": "ref_exchange_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": "Reference Exchange Rate",
|
||||
@ -116,7 +118,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-06-04 20:25:49.832052",
|
||||
"modified": "2021-09-26 15:47:46.911595",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Advance",
|
||||
|
@ -7,12 +7,19 @@
|
||||
"field_order": [
|
||||
"activity_type",
|
||||
"description",
|
||||
"billing_hours",
|
||||
"billing_amount",
|
||||
"section_break_3",
|
||||
"from_time",
|
||||
"column_break_5",
|
||||
"to_time",
|
||||
"section_break_7",
|
||||
"billing_hours",
|
||||
"column_break_9",
|
||||
"billing_amount",
|
||||
"section_break_11",
|
||||
"time_sheet",
|
||||
"project_name",
|
||||
"timesheet_detail"
|
||||
"timesheet_detail",
|
||||
"column_break_13",
|
||||
"project_name"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@ -64,20 +71,53 @@
|
||||
"label": "Description",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "from_time",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "From Time"
|
||||
},
|
||||
{
|
||||
"fieldname": "to_time",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "To Time"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_3",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Time"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_7",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Totals"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_11",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Reference"
|
||||
},
|
||||
{
|
||||
"fieldname": "project_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Project Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_13",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-06-08 14:43:02.748981",
|
||||
"modified": "2021-10-02 03:48:44.979777",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Timesheet",
|
||||
|
@ -100,6 +100,7 @@ def get_tax_withholding_details(tax_withholding_category, posting_date, company)
|
||||
for account_detail in tax_withholding.accounts:
|
||||
if company == account_detail.company:
|
||||
return frappe._dict({
|
||||
"tax_withholding_category": tax_withholding_category,
|
||||
"account_head": account_detail.account,
|
||||
"rate": tax_rate_detail.tax_withholding_rate,
|
||||
"from_date": tax_rate_detail.from_date,
|
||||
@ -206,18 +207,39 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
|
||||
|
||||
def get_invoice_vouchers(parties, tax_details, company, party_type='Supplier'):
|
||||
dr_or_cr = 'credit' if party_type == 'Supplier' else 'debit'
|
||||
doctype = 'Purchase Invoice' if party_type == 'Supplier' else 'Sales Invoice'
|
||||
|
||||
filters = {
|
||||
dr_or_cr: ['>', 0],
|
||||
'company': company,
|
||||
'party_type': party_type,
|
||||
'party': ['in', parties],
|
||||
frappe.scrub(party_type): ['in', parties],
|
||||
'posting_date': ['between', (tax_details.from_date, tax_details.to_date)],
|
||||
'is_opening': 'No',
|
||||
'is_cancelled': 0
|
||||
'docstatus': 1
|
||||
}
|
||||
|
||||
return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck="voucher_no") or [""]
|
||||
if not tax_details.get('consider_party_ledger_amount') and doctype != "Sales Invoice":
|
||||
filters.update({
|
||||
'apply_tds': 1,
|
||||
'tax_withholding_category': tax_details.get('tax_withholding_category')
|
||||
})
|
||||
|
||||
invoices = frappe.get_all(doctype, filters=filters, pluck="name") or [""]
|
||||
|
||||
journal_entries = frappe.db.sql("""
|
||||
SELECT j.name
|
||||
FROM `tabJournal Entry` j, `tabJournal Entry Account` ja
|
||||
WHERE
|
||||
j.docstatus = 1
|
||||
AND j.is_opening = 'No'
|
||||
AND j.posting_date between %s and %s
|
||||
AND ja.{dr_or_cr} > 0
|
||||
AND ja.party in %s
|
||||
""".format(dr_or_cr=dr_or_cr), (tax_details.from_date, tax_details.to_date, tuple(parties)), as_list=1)
|
||||
|
||||
if journal_entries:
|
||||
journal_entries = journal_entries[0]
|
||||
|
||||
return invoices + journal_entries
|
||||
|
||||
def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type='Supplier'):
|
||||
# for advance vouchers, debit and credit is reversed
|
||||
|
@ -176,6 +176,29 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
||||
for d in invoices:
|
||||
d.cancel()
|
||||
|
||||
def test_multi_category_single_supplier(self):
|
||||
frappe.db.set_value("Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category")
|
||||
invoices = []
|
||||
|
||||
pi = create_purchase_invoice(supplier = "Test TDS Supplier5", rate = 500, do_not_save=True)
|
||||
pi.tax_withholding_category = "Test Service Category"
|
||||
pi.save()
|
||||
pi.submit()
|
||||
invoices.append(pi)
|
||||
|
||||
# Second Invoice will apply TDS checked
|
||||
pi1 = create_purchase_invoice(supplier = "Test TDS Supplier5", rate = 2500, do_not_save=True)
|
||||
pi1.tax_withholding_category = "Test Goods Category"
|
||||
pi1.save()
|
||||
pi1.submit()
|
||||
invoices.append(pi1)
|
||||
|
||||
self.assertEqual(pi1.taxes[0].tax_amount, 250)
|
||||
|
||||
#delete invoices to avoid clashing
|
||||
for d in invoices:
|
||||
d.cancel()
|
||||
|
||||
def cancel_invoices():
|
||||
purchase_invoices = frappe.get_all("Purchase Invoice", {
|
||||
'supplier': ['in', ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']],
|
||||
@ -251,7 +274,8 @@ def create_sales_invoice(**args):
|
||||
|
||||
def create_records():
|
||||
# create a new suppliers
|
||||
for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3', 'Test TDS Supplier4']:
|
||||
for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3',
|
||||
'Test TDS Supplier4', 'Test TDS Supplier5']:
|
||||
if frappe.db.exists('Supplier', name):
|
||||
continue
|
||||
|
||||
@ -390,3 +414,39 @@ def create_tax_with_holding_category():
|
||||
'account': 'TDS - _TC'
|
||||
}]
|
||||
}).insert()
|
||||
|
||||
if not frappe.db.exists("Tax Withholding Category", "Test Service Category"):
|
||||
frappe.get_doc({
|
||||
"doctype": "Tax Withholding Category",
|
||||
"name": "Test Service Category",
|
||||
"category_name": "Test Service Category",
|
||||
"rates": [{
|
||||
'from_date': fiscal_year[1],
|
||||
'to_date': fiscal_year[2],
|
||||
'tax_withholding_rate': 10,
|
||||
'single_threshold': 2000,
|
||||
'cumulative_threshold': 2000
|
||||
}],
|
||||
"accounts": [{
|
||||
'company': '_Test Company',
|
||||
'account': 'TDS - _TC'
|
||||
}]
|
||||
}).insert()
|
||||
|
||||
if not frappe.db.exists("Tax Withholding Category", "Test Goods Category"):
|
||||
frappe.get_doc({
|
||||
"doctype": "Tax Withholding Category",
|
||||
"name": "Test Goods Category",
|
||||
"category_name": "Test Goods Category",
|
||||
"rates": [{
|
||||
'from_date': fiscal_year[1],
|
||||
'to_date': fiscal_year[2],
|
||||
'tax_withholding_rate': 10,
|
||||
'single_threshold': 2000,
|
||||
'cumulative_threshold': 2000
|
||||
}],
|
||||
"accounts": [{
|
||||
'company': '_Test Company',
|
||||
'account': 'TDS - _TC'
|
||||
}]
|
||||
}).insert()
|
||||
|
@ -284,13 +284,16 @@ def check_freezing_date(posting_date, adv_adj=False):
|
||||
"""
|
||||
Nobody can do GL Entries where posting date is before freezing date
|
||||
except authorized person
|
||||
|
||||
Administrator has all the roles so this check will be bypassed if any role is allowed to post
|
||||
Hence stop admin to bypass if accounts are freezed
|
||||
"""
|
||||
if not adv_adj:
|
||||
acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
|
||||
if acc_frozen_upto:
|
||||
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
|
||||
if getdate(posting_date) <= getdate(acc_frozen_upto) \
|
||||
and not frozen_accounts_modifier in frappe.get_roles():
|
||||
and not frozen_accounts_modifier in frappe.get_roles() or frappe.session.user == 'Administrator':
|
||||
frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto)))
|
||||
|
||||
def set_as_cancel(voucher_type, voucher_no):
|
||||
|
@ -1,12 +1,15 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"add_total_row": 1,
|
||||
"columns": [],
|
||||
"creation": "2018-08-21 11:25:00.551823",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2018-09-21 11:25:00.551823",
|
||||
"modified": "2021-09-20 17:43:39.518851",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "TDS Computation Summary",
|
||||
|
@ -2,11 +2,10 @@ from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
|
||||
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
|
||||
get_advance_vouchers,
|
||||
get_debit_note_amount,
|
||||
from erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly import (
|
||||
get_result,
|
||||
get_tds_docs,
|
||||
)
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
|
||||
@ -17,9 +16,12 @@ def execute(filters=None):
|
||||
filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name')
|
||||
|
||||
columns = get_columns(filters)
|
||||
res = get_result(filters)
|
||||
tds_docs, tds_accounts, tax_category_map = get_tds_docs(filters)
|
||||
|
||||
return columns, res
|
||||
res = get_result(filters, tds_docs, tds_accounts, tax_category_map)
|
||||
final_result = group_by_supplier_and_category(res)
|
||||
|
||||
return columns, final_result
|
||||
|
||||
def validate_filters(filters):
|
||||
''' Validate if dates are properly set and lie in the same fiscal year'''
|
||||
@ -33,81 +35,39 @@ def validate_filters(filters):
|
||||
|
||||
filters["fiscal_year"] = from_year
|
||||
|
||||
def get_result(filters):
|
||||
# if no supplier selected, fetch data for all tds applicable supplier
|
||||
# else fetch relevant data for selected supplier
|
||||
pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
|
||||
fields = ["name", pan+" as pan", "tax_withholding_category", "supplier_type", "supplier_name"]
|
||||
def group_by_supplier_and_category(data):
|
||||
supplier_category_wise_map = {}
|
||||
|
||||
if filters.supplier:
|
||||
filters.supplier = frappe.db.get_list('Supplier',
|
||||
{"name": filters.supplier}, fields)
|
||||
else:
|
||||
filters.supplier = frappe.db.get_list('Supplier',
|
||||
{"tax_withholding_category": ["!=", ""]}, fields)
|
||||
for row in data:
|
||||
supplier_category_wise_map.setdefault((row.get('supplier'), row.get('section_code')), {
|
||||
'pan': row.get('pan'),
|
||||
'supplier': row.get('supplier'),
|
||||
'supplier_name': row.get('supplier_name'),
|
||||
'section_code': row.get('section_code'),
|
||||
'entity_type': row.get('entity_type'),
|
||||
'tds_rate': row.get('tds_rate'),
|
||||
'total_amount_credited': 0.0,
|
||||
'tds_deducted': 0.0
|
||||
})
|
||||
|
||||
supplier_category_wise_map.get((row.get('supplier'), row.get('section_code')))['total_amount_credited'] += \
|
||||
row.get('total_amount_credited', 0.0)
|
||||
|
||||
supplier_category_wise_map.get((row.get('supplier'), row.get('section_code')))['tds_deducted'] += \
|
||||
row.get('tds_deducted', 0.0)
|
||||
|
||||
final_result = get_final_result(supplier_category_wise_map)
|
||||
|
||||
return final_result
|
||||
|
||||
|
||||
def get_final_result(supplier_category_wise_map):
|
||||
out = []
|
||||
for supplier in filters.supplier:
|
||||
tds = frappe.get_doc("Tax Withholding Category", supplier.tax_withholding_category)
|
||||
rate = [d.tax_withholding_rate for d in tds.rates if d.fiscal_year == filters.fiscal_year]
|
||||
|
||||
if rate:
|
||||
rate = rate[0]
|
||||
|
||||
try:
|
||||
account = [d.account for d in tds.accounts if d.company == filters.company][0]
|
||||
|
||||
except IndexError:
|
||||
account = []
|
||||
total_invoiced_amount, tds_deducted = get_invoice_and_tds_amount(supplier.name, account,
|
||||
filters.company, filters.from_date, filters.to_date, filters.fiscal_year)
|
||||
|
||||
if total_invoiced_amount or tds_deducted:
|
||||
row = [supplier.pan, supplier.name]
|
||||
|
||||
if filters.naming_series == 'Naming Series':
|
||||
row.append(supplier.supplier_name)
|
||||
|
||||
row.extend([tds.name, supplier.supplier_type, rate, total_invoiced_amount, tds_deducted])
|
||||
out.append(row)
|
||||
for key, value in supplier_category_wise_map.items():
|
||||
out.append(value)
|
||||
|
||||
return out
|
||||
|
||||
def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date, fiscal_year):
|
||||
''' calculate total invoice amount and total tds deducted for given supplier '''
|
||||
|
||||
entries = frappe.db.sql("""
|
||||
select voucher_no, credit
|
||||
from `tabGL Entry`
|
||||
where party in (%s) and credit > 0
|
||||
and company=%s and is_cancelled = 0
|
||||
and posting_date between %s and %s
|
||||
""", (supplier, company, from_date, to_date), as_dict=1)
|
||||
|
||||
supplier_credit_amount = flt(sum(d.credit for d in entries))
|
||||
|
||||
vouchers = [d.voucher_no for d in entries]
|
||||
vouchers += get_advance_vouchers([supplier], company=company,
|
||||
from_date=from_date, to_date=to_date)
|
||||
|
||||
tds_deducted = 0
|
||||
if vouchers:
|
||||
tds_deducted = flt(frappe.db.sql("""
|
||||
select sum(credit)
|
||||
from `tabGL Entry`
|
||||
where account=%s and posting_date between %s and %s
|
||||
and company=%s and credit > 0 and voucher_no in ({0})
|
||||
""".format(', '.join("'%s'" % d for d in vouchers)),
|
||||
(account, from_date, to_date, company))[0][0])
|
||||
|
||||
date_range_filter = [fiscal_year, from_date, to_date]
|
||||
|
||||
debit_note_amount = get_debit_note_amount([supplier], date_range_filter, company=company)
|
||||
|
||||
total_invoiced_amount = supplier_credit_amount + tds_deducted - debit_note_amount
|
||||
|
||||
return total_invoiced_amount, tds_deducted
|
||||
|
||||
def get_columns(filters):
|
||||
columns = [
|
||||
{
|
||||
@ -149,7 +109,7 @@ def get_columns(filters):
|
||||
{
|
||||
"label": _("TDS Rate %"),
|
||||
"fieldname": "tds_rate",
|
||||
"fieldtype": "Float",
|
||||
"fieldtype": "Percent",
|
||||
"width": 90
|
||||
},
|
||||
{
|
||||
|
@ -16,69 +16,6 @@ frappe.query_reports["TDS Payable Monthly"] = {
|
||||
"label": __("Supplier"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier",
|
||||
"get_query": function() {
|
||||
return {
|
||||
"filters": {
|
||||
"tax_withholding_category": ["!=", ""],
|
||||
}
|
||||
}
|
||||
},
|
||||
on_change: function() {
|
||||
frappe.query_report.set_filter_value("purchase_invoice", "");
|
||||
frappe.query_report.refresh();
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"purchase_invoice",
|
||||
"label": __("Purchase Invoice"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Purchase Invoice",
|
||||
"get_query": function() {
|
||||
return {
|
||||
"filters": {
|
||||
"name": ["in", frappe.query_report.invoices]
|
||||
}
|
||||
}
|
||||
},
|
||||
on_change: function() {
|
||||
let supplier = frappe.query_report.get_filter_value('supplier');
|
||||
if(!supplier) return; // return if no supplier selected
|
||||
|
||||
// filter invoices based on selected supplier
|
||||
let invoices = [];
|
||||
frappe.query_report.invoice_data.map(d => {
|
||||
if(d.supplier==supplier)
|
||||
invoices.push(d.name)
|
||||
});
|
||||
frappe.query_report.invoices = invoices;
|
||||
frappe.query_report.refresh();
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"purchase_order",
|
||||
"label": __("Purchase Order"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Purchase Order",
|
||||
"get_query": function() {
|
||||
return {
|
||||
"filters": {
|
||||
"name": ["in", frappe.query_report.invoices]
|
||||
}
|
||||
}
|
||||
},
|
||||
on_change: function() {
|
||||
let supplier = frappe.query_report.get_filter_value('supplier');
|
||||
if(!supplier) return; // return if no supplier selected
|
||||
|
||||
// filter invoices based on selected supplier
|
||||
let invoices = [];
|
||||
frappe.query_report.invoice_data.map(d => {
|
||||
if(d.supplier==supplier)
|
||||
invoices.push(d.name)
|
||||
});
|
||||
frappe.query_report.invoices = invoices;
|
||||
frappe.query_report.refresh();
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"from_date",
|
||||
@ -96,23 +33,5 @@ frappe.query_reports["TDS Payable Monthly"] = {
|
||||
"reqd": 1,
|
||||
"width": "60px"
|
||||
}
|
||||
],
|
||||
|
||||
onload: function(report) {
|
||||
// fetch all tds applied invoices
|
||||
frappe.call({
|
||||
"method": "erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly.get_tds_invoices_and_orders",
|
||||
callback: function(r) {
|
||||
let invoices = [];
|
||||
|
||||
r.message.map(d => {
|
||||
invoices.push(d.name);
|
||||
});
|
||||
|
||||
report["invoice_data"] = r.message.invoices;
|
||||
report["invoices"] = invoices;
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,13 +1,15 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"columns": [],
|
||||
"creation": "2018-08-21 11:32:30.874923",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2019-09-24 13:46:16.473711",
|
||||
"modified": "2021-09-20 12:05:50.387572",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "TDS Payable Monthly",
|
||||
|
@ -8,19 +8,12 @@ from frappe import _
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
filters["invoices"] = frappe.cache().hget("invoices", frappe.session.user)
|
||||
validate_filters(filters)
|
||||
set_filters(filters)
|
||||
|
||||
# TDS payment entries
|
||||
payment_entries = get_payment_entires(filters)
|
||||
tds_docs, tds_accounts, tax_category_map = get_tds_docs(filters)
|
||||
|
||||
columns = get_columns(filters)
|
||||
if not filters.get("invoices"):
|
||||
return columns, []
|
||||
|
||||
res = get_result(filters, payment_entries)
|
||||
|
||||
res = get_result(filters, tds_docs, tds_accounts, tax_category_map)
|
||||
return columns, res
|
||||
|
||||
def validate_filters(filters):
|
||||
@ -28,109 +21,59 @@ def validate_filters(filters):
|
||||
if filters.from_date > filters.to_date:
|
||||
frappe.throw(_("From Date must be before To Date"))
|
||||
|
||||
def set_filters(filters):
|
||||
invoices = []
|
||||
|
||||
if not filters.get("invoices"):
|
||||
filters["invoices"] = get_tds_invoices_and_orders()
|
||||
|
||||
if filters.supplier and filters.purchase_invoice:
|
||||
for d in filters["invoices"]:
|
||||
if d.name == filters.purchase_invoice and d.supplier == filters.supplier:
|
||||
invoices.append(d)
|
||||
elif filters.supplier and not filters.purchase_invoice:
|
||||
for d in filters["invoices"]:
|
||||
if d.supplier == filters.supplier:
|
||||
invoices.append(d)
|
||||
elif filters.purchase_invoice and not filters.supplier:
|
||||
for d in filters["invoices"]:
|
||||
if d.name == filters.purchase_invoice:
|
||||
invoices.append(d)
|
||||
elif filters.supplier and filters.purchase_order:
|
||||
for d in filters.get("invoices"):
|
||||
if d.name == filters.purchase_order and d.supplier == filters.supplier:
|
||||
invoices.append(d)
|
||||
elif filters.supplier and not filters.purchase_order:
|
||||
for d in filters.get("invoices"):
|
||||
if d.supplier == filters.supplier:
|
||||
invoices.append(d)
|
||||
elif filters.purchase_order and not filters.supplier:
|
||||
for d in filters.get("invoices"):
|
||||
if d.name == filters.purchase_order:
|
||||
invoices.append(d)
|
||||
|
||||
filters["invoices"] = invoices if invoices else filters["invoices"]
|
||||
filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name')
|
||||
|
||||
#print(filters.get('invoices'))
|
||||
|
||||
def get_result(filters, payment_entries):
|
||||
supplier_map, tds_docs = get_supplier_map(filters, payment_entries)
|
||||
documents = [d.get('name') for d in filters.get('invoices')] + [d.get('name') for d in payment_entries]
|
||||
|
||||
gle_map = get_gle_map(filters, documents)
|
||||
def get_result(filters, tds_docs, tds_accounts, tax_category_map):
|
||||
supplier_map = get_supplier_pan_map()
|
||||
tax_rate_map = get_tax_rate_map(filters)
|
||||
gle_map = get_gle_map(filters, tds_docs)
|
||||
|
||||
out = []
|
||||
for d in gle_map:
|
||||
for name, details in gle_map.items():
|
||||
tds_deducted, total_amount_credited = 0, 0
|
||||
supplier = supplier_map[d]
|
||||
tax_withholding_category = tax_category_map.get(name)
|
||||
rate = tax_rate_map.get(tax_withholding_category)
|
||||
|
||||
tds_doc = tds_docs[supplier.tax_withholding_category]
|
||||
account_list = [i.account for i in tds_doc.accounts if i.company == filters.company]
|
||||
for entry in details:
|
||||
supplier = entry.party or entry.against
|
||||
posting_date = entry.posting_date
|
||||
voucher_type = entry.voucher_type
|
||||
|
||||
if account_list:
|
||||
account = account_list[0]
|
||||
if entry.account in tds_accounts:
|
||||
tds_deducted += (entry.credit - entry.debit)
|
||||
|
||||
for k in gle_map[d]:
|
||||
if k.party == supplier_map[d] and k.credit > 0:
|
||||
total_amount_credited += (k.credit - k.debit)
|
||||
elif account_list and k.account == account and (k.credit - k.debit) > 0:
|
||||
tds_deducted = (k.credit - k.debit)
|
||||
total_amount_credited += (k.credit - k.debit)
|
||||
voucher_type = k.voucher_type
|
||||
total_amount_credited += (entry.credit - entry.debit)
|
||||
|
||||
rate = [i.tax_withholding_rate for i in tds_doc.rates
|
||||
if i.fiscal_year == gle_map[d][0].fiscal_year]
|
||||
|
||||
if rate and len(rate) > 0 and tds_deducted:
|
||||
rate = rate[0]
|
||||
|
||||
row = [supplier.pan, supplier.name]
|
||||
if rate and tds_deducted:
|
||||
row = {
|
||||
'pan' if frappe.db.has_column('Supplier', 'pan') else 'tax_id': supplier_map.get(supplier).pan,
|
||||
'supplier': supplier_map.get(supplier).name
|
||||
}
|
||||
|
||||
if filters.naming_series == 'Naming Series':
|
||||
row.append(supplier.supplier_name)
|
||||
row.update({'supplier_name': supplier_map.get(supplier).supplier_name})
|
||||
|
||||
row.update({
|
||||
'section_code': tax_withholding_category,
|
||||
'entity_type': supplier_map.get(supplier).supplier_type,
|
||||
'tds_rate': rate,
|
||||
'total_amount_credited': total_amount_credited,
|
||||
'tds_deducted': tds_deducted,
|
||||
'transaction_date': posting_date,
|
||||
'transaction_type': voucher_type,
|
||||
'ref_no': name
|
||||
})
|
||||
|
||||
row.extend([tds_doc.name, supplier.supplier_type, rate, total_amount_credited,
|
||||
tds_deducted, gle_map[d][0].posting_date, voucher_type, d])
|
||||
out.append(row)
|
||||
|
||||
return out
|
||||
|
||||
def get_supplier_map(filters, payment_entries):
|
||||
# create a supplier_map of the form {"purchase_invoice": {supplier_name, pan, tds_name}}
|
||||
# pre-fetch all distinct applicable tds docs
|
||||
supplier_map, tds_docs = {}, {}
|
||||
pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
|
||||
supplier_list = [d.supplier for d in filters["invoices"]]
|
||||
def get_supplier_pan_map():
|
||||
supplier_map = frappe._dict()
|
||||
suppliers = frappe.db.get_all('Supplier', fields=['name', 'pan', 'supplier_type', 'supplier_name'])
|
||||
|
||||
supplier_detail = frappe.db.get_all('Supplier',
|
||||
{"name": ["in", supplier_list]},
|
||||
["tax_withholding_category", "name", pan+" as pan", "supplier_type", "supplier_name"])
|
||||
for d in suppliers:
|
||||
supplier_map[d.name] = d
|
||||
|
||||
for d in filters["invoices"]:
|
||||
supplier_map[d.get("name")] = [k for k in supplier_detail
|
||||
if k.name == d.get("supplier")][0]
|
||||
|
||||
for d in payment_entries:
|
||||
supplier_map[d.get("name")] = [k for k in supplier_detail
|
||||
if k.name == d.get("supplier")][0]
|
||||
|
||||
for d in supplier_detail:
|
||||
if d.get("tax_withholding_category") not in tds_docs:
|
||||
tds_docs[d.get("tax_withholding_category")] = \
|
||||
frappe.get_doc("Tax Withholding Category", d.get("tax_withholding_category"))
|
||||
|
||||
return supplier_map, tds_docs
|
||||
return supplier_map
|
||||
|
||||
def get_gle_map(filters, documents):
|
||||
# create gle_map of the form
|
||||
@ -140,10 +83,9 @@ def get_gle_map(filters, documents):
|
||||
gle = frappe.db.get_all('GL Entry',
|
||||
{
|
||||
"voucher_no": ["in", documents],
|
||||
'is_cancelled': 0,
|
||||
'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]),
|
||||
"credit": (">", 0)
|
||||
},
|
||||
["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date", "voucher_type"],
|
||||
["credit", "debit", "account", "voucher_no", "posting_date", "voucher_type", "against", "party"],
|
||||
)
|
||||
|
||||
for d in gle:
|
||||
@ -233,39 +175,57 @@ def get_columns(filters):
|
||||
|
||||
return columns
|
||||
|
||||
def get_payment_entires(filters):
|
||||
filter_dict = {
|
||||
'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]),
|
||||
'party_type': 'Supplier',
|
||||
'apply_tax_withholding_amount': 1
|
||||
def get_tds_docs(filters):
|
||||
tds_documents = []
|
||||
purchase_invoices = []
|
||||
payment_entries = []
|
||||
journal_entries = []
|
||||
tax_category_map = {}
|
||||
|
||||
tds_accounts = frappe.get_all("Tax Withholding Account", {'company': filters.get('company')},
|
||||
pluck="account")
|
||||
|
||||
query_filters = {
|
||||
"credit": ('>', 0),
|
||||
"account": ("in", tds_accounts),
|
||||
"posting_date": ("between", [filters.get("from_date"), filters.get("to_date")]),
|
||||
"is_cancelled": 0
|
||||
}
|
||||
|
||||
if filters.get('purchase_invoice') or filters.get('purchase_order'):
|
||||
parent = frappe.db.get_all('Payment Entry Reference',
|
||||
{'reference_name': ('in', [d.get('name') for d in filters.get('invoices')])}, ['parent'])
|
||||
if filters.get('supplier'):
|
||||
query_filters.update({'against': filters.get('supplier')})
|
||||
|
||||
filter_dict.update({'name': ('in', [d.get('parent') for d in parent])})
|
||||
tds_docs = frappe.get_all("GL Entry", query_filters, ["voucher_no", "voucher_type", "against", "party"])
|
||||
|
||||
payment_entries = frappe.get_all('Payment Entry', fields=['name', 'party_name as supplier'],
|
||||
filters=filter_dict)
|
||||
for d in tds_docs:
|
||||
if d.voucher_type == "Purchase Invoice":
|
||||
purchase_invoices.append(d.voucher_no)
|
||||
elif d.voucher_type == "Payment Entry":
|
||||
payment_entries.append(d.voucher_no)
|
||||
elif d.voucher_type == "Journal Entry":
|
||||
journal_entries.append(d.voucher_no)
|
||||
|
||||
return payment_entries
|
||||
tds_documents.append(d.voucher_no)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_tds_invoices_and_orders():
|
||||
# fetch tds applicable supplier and fetch invoices for these suppliers
|
||||
suppliers = [d.name for d in frappe.db.get_list("Supplier",
|
||||
{"tax_withholding_category": ["!=", ""]}, ["name"])]
|
||||
if purchase_invoices:
|
||||
get_tax_category_map(purchase_invoices, 'Purchase Invoice', tax_category_map)
|
||||
|
||||
invoices = frappe.db.get_list("Purchase Invoice",
|
||||
{"supplier": ["in", suppliers]}, ["name", "supplier"])
|
||||
if payment_entries:
|
||||
get_tax_category_map(payment_entries, 'Payment Entry', tax_category_map)
|
||||
|
||||
orders = frappe.db.get_list("Purchase Order",
|
||||
{"supplier": ["in", suppliers]}, ["name", "supplier"])
|
||||
if journal_entries:
|
||||
get_tax_category_map(journal_entries, 'Journal Entry', tax_category_map)
|
||||
|
||||
invoices = invoices + orders
|
||||
invoices = [d for d in invoices if d.supplier]
|
||||
return tds_documents, tds_accounts, tax_category_map
|
||||
|
||||
frappe.cache().hset("invoices", frappe.session.user, invoices)
|
||||
def get_tax_category_map(vouchers, doctype, tax_category_map):
|
||||
tax_category_map.update(frappe._dict(frappe.get_all(doctype,
|
||||
filters = {'name': ('in', vouchers)}, fields=['name', 'tax_withholding_category'], as_list=1)))
|
||||
|
||||
return invoices
|
||||
def get_tax_rate_map(filters):
|
||||
rate_map = frappe.get_all('Tax Withholding Rate', filters={
|
||||
'from_date': ('<=', filters.get('from_date')),
|
||||
'to_date': ('>=', filters.get('to_date'))
|
||||
}, fields=['parent', 'tax_withholding_rate'], as_list=1)
|
||||
|
||||
return frappe._dict(rate_map)
|
@ -4,11 +4,14 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from json import loads
|
||||
|
||||
import frappe
|
||||
import frappe.defaults
|
||||
from frappe import _, throw
|
||||
from frappe.model.meta import get_field_precision
|
||||
from frappe.utils import cint, cstr, flt, formatdate, get_number_format_info, getdate, now, nowdate
|
||||
from six import string_types
|
||||
|
||||
import erpnext
|
||||
|
||||
@ -787,16 +790,28 @@ def get_children(doctype, parent, company, is_root=False):
|
||||
|
||||
if doctype == 'Account':
|
||||
sort_accounts(acc, is_root, key="value")
|
||||
company_currency = frappe.get_cached_value('Company', company, "default_currency")
|
||||
for each in acc:
|
||||
each["company_currency"] = company_currency
|
||||
each["balance"] = flt(get_balance_on(each.get("value"), in_account_currency=False, company=company))
|
||||
|
||||
if each.account_currency != company_currency:
|
||||
each["balance_in_account_currency"] = flt(get_balance_on(each.get("value"), company=company))
|
||||
|
||||
return acc
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_account_balances(accounts, company):
|
||||
|
||||
if isinstance(accounts, string_types):
|
||||
accounts = loads(accounts)
|
||||
|
||||
if not accounts:
|
||||
return []
|
||||
|
||||
company_currency = frappe.get_cached_value("Company", company, "default_currency")
|
||||
|
||||
for account in accounts:
|
||||
account["company_currency"] = company_currency
|
||||
account["balance"] = flt(get_balance_on(account["value"], in_account_currency=False, company=company))
|
||||
if account["account_currency"] and account["account_currency"] != company_currency:
|
||||
account["balance_in_account_currency"] = flt(get_balance_on(account["value"], company=company))
|
||||
|
||||
return accounts
|
||||
|
||||
def create_payment_gateway_account(gateway, payment_channel="Email"):
|
||||
from erpnext.setup.setup_wizard.operations.install_fixtures import create_bank_account
|
||||
|
||||
|
@ -140,11 +140,6 @@ class Asset(AccountsController):
|
||||
if self.is_existing_asset:
|
||||
return
|
||||
|
||||
docname = self.purchase_receipt or self.purchase_invoice
|
||||
if docname:
|
||||
doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
|
||||
date = frappe.db.get_value(doctype, docname, 'posting_date')
|
||||
|
||||
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
|
||||
frappe.throw(_("Available-for-use Date should be after purchase date"))
|
||||
|
||||
@ -394,10 +389,6 @@ class Asset(AccountsController):
|
||||
if cint(self.number_of_depreciations_booked) > cint(row.total_number_of_depreciations):
|
||||
frappe.throw(_("Number of Depreciations Booked cannot be greater than Total Number of Depreciations"))
|
||||
|
||||
if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(nowdate()):
|
||||
frappe.msgprint(_("Depreciation Row {0}: Depreciation Start Date is entered as past date")
|
||||
.format(row.idx), title=_('Warning'), indicator='red')
|
||||
|
||||
if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(self.purchase_date):
|
||||
frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date")
|
||||
.format(row.idx))
|
||||
@ -444,9 +435,10 @@ class Asset(AccountsController):
|
||||
if accumulated_depreciation_after_full_schedule:
|
||||
accumulated_depreciation_after_full_schedule = max(accumulated_depreciation_after_full_schedule)
|
||||
|
||||
asset_value_after_full_schedule = flt(flt(self.gross_purchase_amount) -
|
||||
flt(accumulated_depreciation_after_full_schedule),
|
||||
self.precision('gross_purchase_amount'))
|
||||
asset_value_after_full_schedule = flt(
|
||||
flt(self.gross_purchase_amount) -
|
||||
flt(self.opening_accumulated_depreciation) -
|
||||
flt(accumulated_depreciation_after_full_schedule), self.precision('gross_purchase_amount'))
|
||||
|
||||
if (row.expected_value_after_useful_life and
|
||||
row.expected_value_after_useful_life < asset_value_after_full_schedule):
|
||||
|
@ -645,12 +645,18 @@ class TestAsset(unittest.TestCase):
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||
qty=1, rate=8000.0, location="Test Location")
|
||||
|
||||
finance_book = frappe.new_doc('Finance Book')
|
||||
finance_book.finance_book_name = 'Income Tax'
|
||||
finance_book.for_income_tax = 1
|
||||
finance_book.insert(ignore_if_duplicate=1)
|
||||
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||
asset = frappe.get_doc('Asset', asset_name)
|
||||
asset.calculate_depreciation = 1
|
||||
asset.available_for_use_date = '2030-07-12'
|
||||
asset.purchase_date = '2030-01-01'
|
||||
asset.append("finance_books", {
|
||||
"finance_book": finance_book.name,
|
||||
"expected_value_after_useful_life": 1000,
|
||||
"depreciation_method": "Written Down Value",
|
||||
"total_number_of_depreciations": 3,
|
||||
|
@ -1121,6 +1121,7 @@
|
||||
"fetch_from": "supplier.represents_company",
|
||||
"fieldname": "represents_company",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Represents Company",
|
||||
"options": "Company",
|
||||
"read_only": 1
|
||||
@ -1143,7 +1144,7 @@
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-08-30 20:03:14.008804",
|
||||
"modified": "2021-09-28 13:10:47.955401",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
@ -30,7 +30,14 @@ frappe.query_reports["Purchase Order Analysis"] = {
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname": "purchase_order",
|
||||
"fieldname":"project",
|
||||
"label": __("Project"),
|
||||
"fieldtype": "Link",
|
||||
"width": "80",
|
||||
"options": "Project"
|
||||
},
|
||||
{
|
||||
"fieldname": "name",
|
||||
"label": __("Purchase Order"),
|
||||
"fieldtype": "Link",
|
||||
"width": "80",
|
||||
|
@ -41,14 +41,12 @@ def get_conditions(filters):
|
||||
if filters.get("from_date") and filters.get("to_date"):
|
||||
conditions += " and po.transaction_date between %(from_date)s and %(to_date)s"
|
||||
|
||||
if filters.get("company"):
|
||||
conditions += " and po.company = %(company)s"
|
||||
for field in ['company', 'name', 'status']:
|
||||
if filters.get(field):
|
||||
conditions += f" and po.{field} = %({field})s"
|
||||
|
||||
if filters.get("purchase_order"):
|
||||
conditions += " and po.name = %(purchase_order)s"
|
||||
|
||||
if filters.get("status"):
|
||||
conditions += " and po.status in %(status)s"
|
||||
if filters.get('project'):
|
||||
conditions += " and poi.project = %(project)s"
|
||||
|
||||
return conditions
|
||||
|
||||
@ -57,6 +55,7 @@ def get_data(conditions, filters):
|
||||
SELECT
|
||||
po.transaction_date as date,
|
||||
poi.schedule_date as required_date,
|
||||
poi.project,
|
||||
po.name as purchase_order,
|
||||
po.status, po.supplier, poi.item_code,
|
||||
poi.qty, poi.received_qty,
|
||||
@ -175,6 +174,12 @@ def get_columns(filters):
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier",
|
||||
"width": 130
|
||||
},{
|
||||
"label": _("Project"),
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"options": "Project",
|
||||
"width": 130
|
||||
}]
|
||||
|
||||
if not filters.get("group_by_po"):
|
||||
|
@ -685,13 +685,17 @@ class AccountsController(TransactionBase):
|
||||
.format(d.reference_name, d.against_order))
|
||||
|
||||
def set_advance_gain_or_loss(self):
|
||||
if not self.get("advances"):
|
||||
if self.get('conversion_rate') == 1 or not self.get("advances"):
|
||||
return
|
||||
|
||||
is_purchase_invoice = self.doctype == 'Purchase Invoice'
|
||||
party_account = self.credit_to if is_purchase_invoice else self.debit_to
|
||||
if get_account_currency(party_account) != self.currency:
|
||||
return
|
||||
|
||||
for d in self.get("advances"):
|
||||
advance_exchange_rate = d.ref_exchange_rate
|
||||
if (d.allocated_amount and self.conversion_rate != 1
|
||||
and self.conversion_rate != advance_exchange_rate):
|
||||
if (d.allocated_amount and self.conversion_rate != advance_exchange_rate):
|
||||
|
||||
base_allocated_amount_in_ref_rate = advance_exchange_rate * d.allocated_amount
|
||||
base_allocated_amount_in_inv_rate = self.conversion_rate * d.allocated_amount
|
||||
@ -710,7 +714,7 @@ class AccountsController(TransactionBase):
|
||||
|
||||
gain_loss_account = frappe.db.get_value('Company', self.company, 'exchange_gain_loss_account')
|
||||
if not gain_loss_account:
|
||||
frappe.throw(_("Please set Default Exchange Gain/Loss Account in Company {}")
|
||||
frappe.throw(_("Please set default Exchange Gain/Loss Account in Company {}")
|
||||
.format(self.get('company')))
|
||||
account_currency = get_account_currency(gain_loss_account)
|
||||
if account_currency != self.company_currency:
|
||||
@ -729,7 +733,7 @@ class AccountsController(TransactionBase):
|
||||
"against": party,
|
||||
dr_or_cr + "_in_account_currency": abs(d.exchange_gain_loss),
|
||||
dr_or_cr: abs(d.exchange_gain_loss),
|
||||
"cost_center": self.cost_center,
|
||||
"cost_center": self.cost_center or erpnext.get_default_cost_center(self.company),
|
||||
"project": self.project
|
||||
}, item=d)
|
||||
)
|
||||
@ -980,42 +984,55 @@ class AccountsController(TransactionBase):
|
||||
item_allowance = {}
|
||||
global_qty_allowance, global_amount_allowance = None, None
|
||||
|
||||
role_allowed_to_over_bill = frappe.db.get_single_value('Accounts Settings', 'role_allowed_to_over_bill')
|
||||
user_roles = frappe.get_roles()
|
||||
|
||||
total_overbilled_amt = 0.0
|
||||
|
||||
for item in self.get("items"):
|
||||
if item.get(item_ref_dn):
|
||||
ref_amt = flt(frappe.db.get_value(ref_dt + " Item",
|
||||
item.get(item_ref_dn), based_on), self.precision(based_on, item))
|
||||
if not ref_amt:
|
||||
frappe.msgprint(
|
||||
_("Warning: System will not check overbilling since amount for Item {0} in {1} is zero")
|
||||
.format(item.item_code, ref_dt))
|
||||
else:
|
||||
already_billed = frappe.db.sql("""
|
||||
select sum(%s)
|
||||
from `tab%s`
|
||||
where %s=%s and docstatus=1 and parent != %s
|
||||
""" % (based_on, self.doctype + " Item", item_ref_dn, '%s', '%s'),
|
||||
(item.get(item_ref_dn), self.name))[0][0]
|
||||
if not item.get(item_ref_dn):
|
||||
continue
|
||||
|
||||
total_billed_amt = flt(flt(already_billed) + flt(item.get(based_on)),
|
||||
self.precision(based_on, item))
|
||||
ref_amt = flt(frappe.db.get_value(ref_dt + " Item",
|
||||
item.get(item_ref_dn), based_on), self.precision(based_on, item))
|
||||
if not ref_amt:
|
||||
frappe.msgprint(
|
||||
_("System will not check overbilling since amount for Item {0} in {1} is zero")
|
||||
.format(item.item_code, ref_dt), title=_("Warning"), indicator="orange")
|
||||
continue
|
||||
|
||||
allowance, item_allowance, global_qty_allowance, global_amount_allowance = \
|
||||
get_allowance_for(item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount")
|
||||
already_billed = frappe.db.sql("""
|
||||
select sum(%s)
|
||||
from `tab%s`
|
||||
where %s=%s and docstatus=1 and parent != %s
|
||||
""" % (based_on, self.doctype + " Item", item_ref_dn, '%s', '%s'),
|
||||
(item.get(item_ref_dn), self.name))[0][0]
|
||||
|
||||
max_allowed_amt = flt(ref_amt * (100 + allowance) / 100)
|
||||
total_billed_amt = flt(flt(already_billed) + flt(item.get(based_on)),
|
||||
self.precision(based_on, item))
|
||||
|
||||
if total_billed_amt < 0 and max_allowed_amt < 0:
|
||||
# while making debit note against purchase return entry(purchase receipt) getting overbill error
|
||||
total_billed_amt = abs(total_billed_amt)
|
||||
max_allowed_amt = abs(max_allowed_amt)
|
||||
allowance, item_allowance, global_qty_allowance, global_amount_allowance = \
|
||||
get_allowance_for(item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount")
|
||||
|
||||
role_allowed_to_over_bill = frappe.db.get_single_value('Accounts Settings', 'role_allowed_to_over_bill')
|
||||
max_allowed_amt = flt(ref_amt * (100 + allowance) / 100)
|
||||
|
||||
if total_billed_amt - max_allowed_amt > 0.01 and role_allowed_to_over_bill not in frappe.get_roles():
|
||||
if self.doctype != "Purchase Invoice":
|
||||
self.throw_overbill_exception(item, max_allowed_amt)
|
||||
elif not cint(frappe.db.get_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice")):
|
||||
self.throw_overbill_exception(item, max_allowed_amt)
|
||||
if total_billed_amt < 0 and max_allowed_amt < 0:
|
||||
# while making debit note against purchase return entry(purchase receipt) getting overbill error
|
||||
total_billed_amt = abs(total_billed_amt)
|
||||
max_allowed_amt = abs(max_allowed_amt)
|
||||
|
||||
overbill_amt = total_billed_amt - max_allowed_amt
|
||||
total_overbilled_amt += overbill_amt
|
||||
|
||||
if overbill_amt > 0.01 and role_allowed_to_over_bill not in user_roles:
|
||||
if self.doctype != "Purchase Invoice":
|
||||
self.throw_overbill_exception(item, max_allowed_amt)
|
||||
elif not cint(frappe.db.get_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice")):
|
||||
self.throw_overbill_exception(item, max_allowed_amt)
|
||||
|
||||
if role_allowed_to_over_bill in user_roles and total_overbilled_amt > 0.1:
|
||||
frappe.msgprint(_("Overbilling of {} ignored because you have {} role.")
|
||||
.format(total_overbilled_amt, role_allowed_to_over_bill), title=_("Warning"), indicator="orange")
|
||||
|
||||
def throw_overbill_exception(self, item, max_allowed_amt):
|
||||
frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings")
|
||||
@ -1668,14 +1685,18 @@ def get_advance_payment_entries(party_type, party, party_account, order_doctype,
|
||||
return list(payment_entries_against_order) + list(unallocated_payment_entries)
|
||||
|
||||
def update_invoice_status():
|
||||
# Daily update the status of the invoices
|
||||
|
||||
frappe.db.sql(""" update `tabSales Invoice` set status = 'Overdue'
|
||||
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
|
||||
|
||||
frappe.db.sql(""" update `tabPurchase Invoice` set status = 'Overdue'
|
||||
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
|
||||
"""Updates status as Overdue for applicable invoices. Runs daily."""
|
||||
|
||||
for doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
frappe.db.sql("""
|
||||
update `tab{}` as dt set dt.status = 'Overdue'
|
||||
where dt.docstatus = 1
|
||||
and dt.status != 'Overdue'
|
||||
and dt.outstanding_amount > 0
|
||||
and (dt.grand_total - dt.outstanding_amount) <
|
||||
(select sum(payment_amount) from `tabPayment Schedule` as ps
|
||||
where ps.parent = dt.name and ps.due_date < %s)
|
||||
""".format(doctype), getdate())
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_terms(terms_template, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None):
|
||||
|
@ -696,36 +696,6 @@ def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.sql(query, filters)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_healthcare_service_units(doctype, txt, searchfield, start, page_len, filters):
|
||||
query = """
|
||||
select name
|
||||
from `tabHealthcare Service Unit`
|
||||
where
|
||||
is_group = 0
|
||||
and company = {company}
|
||||
and name like {txt}""".format(
|
||||
company = frappe.db.escape(filters.get('company')), txt = frappe.db.escape('%{0}%'.format(txt)))
|
||||
|
||||
if filters and filters.get('inpatient_record'):
|
||||
from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import (
|
||||
get_current_healthcare_service_unit,
|
||||
)
|
||||
service_unit = get_current_healthcare_service_unit(filters.get('inpatient_record'))
|
||||
|
||||
# if the patient is admitted, then appointments should be allowed against the admission service unit,
|
||||
# inspite of it being an Inpatient Occupancy service unit
|
||||
if service_unit:
|
||||
query += " and (allow_appointments = 1 or name = {service_unit})".format(service_unit = frappe.db.escape(service_unit))
|
||||
else:
|
||||
query += " and allow_appointments = 1"
|
||||
else:
|
||||
query += " and allow_appointments = 1"
|
||||
|
||||
return frappe.db.sql(query, filters)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
|
||||
|
@ -6,7 +6,7 @@ import frappe
|
||||
import frappe.utils
|
||||
|
||||
import erpnext
|
||||
from erpnext.demo.setup import education, healthcare, manufacture, retail, setup_data
|
||||
from erpnext.demo.setup import education, manufacture, retail, setup_data
|
||||
from erpnext.demo.user import accounts
|
||||
from erpnext.demo.user import education as edu
|
||||
from erpnext.demo.user import fixed_asset, hr, manufacturing, projects, purchase, sales, stock
|
||||
@ -38,8 +38,6 @@ def make(domain='Manufacturing', days=100):
|
||||
retail.setup_data()
|
||||
elif domain== 'Education':
|
||||
education.setup_data()
|
||||
elif domain== 'Healthcare':
|
||||
healthcare.setup_data()
|
||||
|
||||
site = frappe.local.site
|
||||
frappe.destroy()
|
||||
|
@ -16,9 +16,6 @@ data = {
|
||||
'Education': {
|
||||
'company_name': 'Whitmore College'
|
||||
},
|
||||
'Healthcare': {
|
||||
'company_name': 'ABC Hospital Ltd.'
|
||||
},
|
||||
'Agriculture': {
|
||||
'company_name': 'Schrute Farms'
|
||||
},
|
||||
|
@ -1,171 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe.utils import getdate
|
||||
from frappe.utils.make_random import get_random
|
||||
|
||||
from erpnext.demo.setup.setup_data import import_json
|
||||
from erpnext.healthcare.doctype.lab_test.lab_test import create_test_from_template
|
||||
|
||||
|
||||
def setup_data():
|
||||
frappe.flags.mute_emails = True
|
||||
make_masters()
|
||||
make_patient()
|
||||
make_lab_test()
|
||||
make_consulation()
|
||||
make_appointment()
|
||||
consulation_on_appointment()
|
||||
lab_test_on_encounter()
|
||||
frappe.db.commit()
|
||||
frappe.clear_cache()
|
||||
|
||||
def make_masters():
|
||||
import_json("Healthcare Practitioner")
|
||||
import_drug()
|
||||
frappe.db.commit()
|
||||
|
||||
def make_patient():
|
||||
file_path = get_json_path("Patient")
|
||||
with open(file_path, "r") as open_file:
|
||||
patient_data = json.loads(open_file.read())
|
||||
count = 1
|
||||
|
||||
for d in enumerate(patient_data):
|
||||
patient = frappe.new_doc("Patient")
|
||||
patient.patient_name = d[1]['patient_name'].title()
|
||||
patient.sex = d[1]['gender']
|
||||
patient.blood_group = "A Positive"
|
||||
patient.date_of_birth = datetime.datetime(1990, 3, 25)
|
||||
patient.email_id = d[1]['patient_name'] + "_" + patient.date_of_birth.strftime('%m/%d/%Y') + "@example.com"
|
||||
if count <5:
|
||||
patient.insert()
|
||||
frappe.db.commit()
|
||||
count+=1
|
||||
|
||||
def make_appointment():
|
||||
i = 1
|
||||
while i <= 4:
|
||||
practitioner = get_random("Healthcare Practitioner")
|
||||
department = frappe.get_value("Healthcare Practitioner", practitioner, "department")
|
||||
patient = get_random("Patient")
|
||||
patient_sex = frappe.get_value("Patient", patient, "sex")
|
||||
appointment = frappe.new_doc("Patient Appointment")
|
||||
startDate = datetime.datetime.now()
|
||||
for x in random_date(startDate,0):
|
||||
appointment_datetime = x
|
||||
appointment.appointment_datetime = appointment_datetime
|
||||
appointment.appointment_time = appointment_datetime
|
||||
appointment.appointment_date = appointment_datetime
|
||||
appointment.patient = patient
|
||||
appointment.patient_sex = patient_sex
|
||||
appointment.practitioner = practitioner
|
||||
appointment.department = department
|
||||
appointment.save(ignore_permissions = True)
|
||||
i += 1
|
||||
|
||||
def make_consulation():
|
||||
for i in range(3):
|
||||
practitioner = get_random("Healthcare Practitioner")
|
||||
department = frappe.get_value("Healthcare Practitioner", practitioner, "department")
|
||||
patient = get_random("Patient")
|
||||
patient_sex = frappe.get_value("Patient", patient, "sex")
|
||||
encounter = set_encounter(patient, patient_sex, practitioner, department, getdate(), i)
|
||||
encounter.save(ignore_permissions=True)
|
||||
|
||||
def consulation_on_appointment():
|
||||
for i in range(3):
|
||||
appointment = get_random("Patient Appointment")
|
||||
appointment = frappe.get_doc("Patient Appointment",appointment)
|
||||
encounter = set_encounter(appointment.patient, appointment.patient_sex, appointment.practitioner, appointment.department, appointment.appointment_date, i)
|
||||
encounter.appointment = appointment.name
|
||||
encounter.save(ignore_permissions=True)
|
||||
|
||||
def set_encounter(patient, patient_sex, practitioner, department, encounter_date, i):
|
||||
encounter = frappe.new_doc("Patient Encounter")
|
||||
encounter.patient = patient
|
||||
encounter.patient_sex = patient_sex
|
||||
encounter.practitioner = practitioner
|
||||
encounter.visit_department = department
|
||||
encounter.encounter_date = encounter_date
|
||||
if i > 2 and patient_sex=='Female':
|
||||
encounter.symptoms = "Having chest pains for the last week."
|
||||
encounter.diagnosis = """This patient's description of dull, aching,
|
||||
exertion related substernal chest pain is suggestive of ischemic
|
||||
cardiac origin. Her findings of a FH of early ASCVD, hypertension,
|
||||
and early surgical menopause are pertinent risk factors for development
|
||||
of coronary artery disease. """
|
||||
else:
|
||||
encounter = append_drug_rx(encounter)
|
||||
encounter = append_test_rx(encounter)
|
||||
return encounter
|
||||
|
||||
def make_lab_test():
|
||||
practitioner = get_random("Healthcare Practitioner")
|
||||
patient = get_random("Patient")
|
||||
patient_sex = frappe.get_value("Patient", patient, "sex")
|
||||
template = get_random("Lab Test Template")
|
||||
set_lab_test(patient, patient_sex, practitioner, template)
|
||||
|
||||
def lab_test_on_encounter():
|
||||
i = 1
|
||||
while i <= 2:
|
||||
test_rx = get_random("Lab Prescription", filters={'test_created': 0})
|
||||
test_rx = frappe.get_doc("Lab Prescription", test_rx)
|
||||
encounter = frappe.get_doc("Patient Encounter", test_rx.parent)
|
||||
set_lab_test(encounter.patient, encounter.patient_sex, encounter.practitioner, test_rx.test_code, test_rx.name)
|
||||
i += 1
|
||||
|
||||
def set_lab_test(patient, patient_sex, practitioner, template, rx=None):
|
||||
lab_test = frappe.new_doc("Lab Test")
|
||||
lab_test.practitioner = practitioner
|
||||
lab_test.patient = patient
|
||||
lab_test.patient_sex = patient_sex
|
||||
lab_test.template = template
|
||||
lab_test.prescription = rx
|
||||
create_test_from_template(lab_test)
|
||||
|
||||
def append_test_rx(encounter):
|
||||
i = 1
|
||||
while i <= 2:
|
||||
test_rx = encounter.append("test_prescription")
|
||||
test_rx.test_code = get_random("Lab Test Template")
|
||||
i += 1
|
||||
return encounter
|
||||
|
||||
def append_drug_rx(encounter):
|
||||
i = 1
|
||||
while i <= 3:
|
||||
drug = get_random("Item", filters={"item_group":"Drug"})
|
||||
drug = frappe.get_doc("Item", drug)
|
||||
drug_rx = encounter.append("drug_prescription")
|
||||
drug_rx.drug_code = drug.item_code
|
||||
drug_rx.drug_name = drug.item_name
|
||||
drug_rx.dosage = get_random("Prescription Dosage")
|
||||
drug_rx.period = get_random("Prescription Duration")
|
||||
i += 1
|
||||
return encounter
|
||||
|
||||
def random_date(start,l):
|
||||
current = start
|
||||
while l >= 0:
|
||||
curr = current + datetime.timedelta(minutes=60)
|
||||
yield curr
|
||||
l-=1
|
||||
|
||||
def import_drug():
|
||||
frappe.flags.in_import = True
|
||||
data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'drug_list.json')).read())
|
||||
for d in data:
|
||||
doc = frappe.new_doc("Item")
|
||||
doc.update(d)
|
||||
doc.insert()
|
||||
frappe.flags.in_import = False
|
||||
|
||||
def get_json_path(doctype):
|
||||
return frappe.get_app_path('erpnext', 'demo', 'data', frappe.scrub(doctype) + '.json')
|
@ -196,10 +196,6 @@ def setup_user_roles(domain):
|
||||
'Purchase Manager', 'Projects User', 'Manufacturing User', 'Manufacturing Manager',
|
||||
'Support Team')
|
||||
|
||||
if domain == "Healthcare":
|
||||
user.add_roles('Physician', 'Healthcare Administrator', 'Laboratory User',
|
||||
'Nursing User', 'Patient')
|
||||
|
||||
if domain == "Education":
|
||||
user.add_roles('Academics User')
|
||||
|
||||
|
@ -1,71 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
data = {
|
||||
'desktop_icons': [
|
||||
'Patient',
|
||||
'Patient Appointment',
|
||||
'Patient Encounter',
|
||||
'Lab Test',
|
||||
'Healthcare',
|
||||
'Vital Signs',
|
||||
'Clinical Procedure',
|
||||
'Inpatient Record',
|
||||
'Accounts',
|
||||
'Buying',
|
||||
'Stock',
|
||||
'HR',
|
||||
'ToDo'
|
||||
],
|
||||
'default_portal_role': 'Patient',
|
||||
'restricted_roles': [
|
||||
'Healthcare Administrator',
|
||||
'LabTest Approver',
|
||||
'Laboratory User',
|
||||
'Nursing User',
|
||||
'Physician',
|
||||
'Patient'
|
||||
],
|
||||
'custom_fields': {
|
||||
'Sales Invoice': [
|
||||
{
|
||||
'fieldname': 'patient', 'label': 'Patient', 'fieldtype': 'Link', 'options': 'Patient',
|
||||
'insert_after': 'naming_series'
|
||||
},
|
||||
{
|
||||
'fieldname': 'patient_name', 'label': 'Patient Name', 'fieldtype': 'Data', 'fetch_from': 'patient.patient_name',
|
||||
'insert_after': 'patient', 'read_only': True
|
||||
},
|
||||
{
|
||||
'fieldname': 'ref_practitioner', 'label': 'Referring Practitioner', 'fieldtype': 'Link', 'options': 'Healthcare Practitioner',
|
||||
'insert_after': 'customer'
|
||||
}
|
||||
],
|
||||
'Sales Invoice Item': [
|
||||
{
|
||||
'fieldname': 'reference_dt', 'label': 'Reference DocType', 'fieldtype': 'Link', 'options': 'DocType',
|
||||
'insert_after': 'edit_references'
|
||||
},
|
||||
{
|
||||
'fieldname': 'reference_dn', 'label': 'Reference Name', 'fieldtype': 'Dynamic Link', 'options': 'reference_dt',
|
||||
'insert_after': 'reference_dt'
|
||||
}
|
||||
],
|
||||
'Stock Entry': [
|
||||
{
|
||||
'fieldname': 'inpatient_medication_entry', 'label': 'Inpatient Medication Entry', 'fieldtype': 'Link', 'options': 'Inpatient Medication Entry',
|
||||
'insert_after': 'credit_note', 'read_only': True
|
||||
}
|
||||
],
|
||||
'Stock Entry Detail': [
|
||||
{
|
||||
'fieldname': 'patient', 'label': 'Patient', 'fieldtype': 'Link', 'options': 'Patient',
|
||||
'insert_after': 'po_detail', 'read_only': True
|
||||
},
|
||||
{
|
||||
'fieldname': 'inpatient_medication_entry_child', 'label': 'Inpatient Medication Entry Child', 'fieldtype': 'Data',
|
||||
'insert_after': 'patient', 'read_only': True
|
||||
}
|
||||
]
|
||||
},
|
||||
'on_setup': 'erpnext.healthcare.setup.setup_healthcare'
|
||||
}
|
@ -85,10 +85,8 @@ def add_bank_accounts(response, bank, company):
|
||||
if not acc_subtype:
|
||||
add_account_subtype(account["subtype"])
|
||||
|
||||
existing_bank_account = frappe.db.exists("Bank Account", {
|
||||
'account_name': account["name"],
|
||||
'bank': bank["bank_name"]
|
||||
})
|
||||
bank_account_name = "{} - {}".format(account["name"], bank["bank_name"])
|
||||
existing_bank_account = frappe.db.exists("Bank Account", bank_account_name)
|
||||
|
||||
if not existing_bank_account:
|
||||
try:
|
||||
@ -197,6 +195,7 @@ def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
|
||||
|
||||
plaid = PlaidConnector(access_token)
|
||||
|
||||
transactions = []
|
||||
try:
|
||||
transactions = plaid.get_transactions(start_date=start_date, end_date=end_date, account_id=account_id)
|
||||
except ItemError as e:
|
||||
@ -205,7 +204,7 @@ def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
|
||||
msg += _("Please refresh or reset the Plaid linking of the Bank {}.").format(bank) + " "
|
||||
frappe.log_error(msg, title=_("Plaid Link Refresh Required"))
|
||||
|
||||
return transactions or []
|
||||
return transactions
|
||||
|
||||
|
||||
def new_bank_transaction(transaction):
|
||||
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"chart_name": "Clinical Procedures",
|
||||
"chart_type": "Group By",
|
||||
"creation": "2020-07-14 18:17:54.601236",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Clinical Procedure",
|
||||
"dynamic_filters_json": "[[\"Clinical Procedure\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Clinical Procedure\",\"docstatus\",\"=\",\"1\",false]]",
|
||||
"group_by_based_on": "procedure_template",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2021-01-30 21:03:30.086891",
|
||||
"modified": "2021-02-01 13:36:04.469863",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Clinical Procedures",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"timeseries": 0,
|
||||
"type": "Bar",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"chart_name": "Clinical Procedure Status",
|
||||
"chart_type": "Group By",
|
||||
"creation": "2020-07-14 18:17:54.654325",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Clinical Procedure",
|
||||
"dynamic_filters_json": "[[\"Clinical Procedure\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Clinical Procedure\",\"docstatus\",\"=\",\"1\",false]]",
|
||||
"group_by_based_on": "status",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2021-02-01 13:36:38.787783",
|
||||
"modified": "2021-02-01 13:37:18.718275",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Clinical Procedures Status",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"timeseries": 0,
|
||||
"type": "Bar",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
{
|
||||
"chart_name": "Department wise Patient Appointments",
|
||||
"chart_type": "Custom",
|
||||
"creation": "2020-07-17 11:25:37.190130",
|
||||
"custom_options": "{\"colors\": [\"#7CD5FA\", \"#5F62F6\", \"#7544E2\", \"#EE5555\"], \"barOptions\": {\"stacked\": 1}, \"height\": 300}",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\"}",
|
||||
"filters_json": "{}",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2020-07-22 15:32:05.827566",
|
||||
"modified": "2020-07-22 15:35:12.798035",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Department wise Patient Appointments",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"source": "Department wise Patient Appointments",
|
||||
"timeseries": 0,
|
||||
"type": "Bar",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"chart_name": "Diagnoses",
|
||||
"chart_type": "Group By",
|
||||
"creation": "2020-07-14 18:17:54.705698",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Patient Encounter Diagnosis",
|
||||
"dynamic_filters_json": "",
|
||||
"filters_json": "[]",
|
||||
"group_by_based_on": "diagnosis",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2021-01-30 21:03:33.729487",
|
||||
"modified": "2021-02-01 13:34:57.385335",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Diagnoses",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"timeseries": 0,
|
||||
"type": "Bar",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"chart_name": "In-Patient Status",
|
||||
"chart_type": "Group By",
|
||||
"creation": "2020-07-14 18:17:54.629199",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Inpatient Record",
|
||||
"dynamic_filters_json": "[[\"Inpatient Record\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[]",
|
||||
"group_by_based_on": "status",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2020-07-22 13:22:46.792131",
|
||||
"modified": "2020-07-22 13:33:16.008150",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "In-Patient Status",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"timeseries": 0,
|
||||
"type": "Bar",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"chart_name": "Lab Tests",
|
||||
"chart_type": "Group By",
|
||||
"creation": "2020-07-14 18:17:54.574903",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Lab Test",
|
||||
"dynamic_filters_json": "[[\"Lab Test\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Lab Test\",\"docstatus\",\"=\",\"1\",false]]",
|
||||
"group_by_based_on": "template",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2021-01-30 21:03:28.272914",
|
||||
"modified": "2021-02-01 13:36:08.391433",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Lab Tests",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"timeseries": 0,
|
||||
"type": "Bar",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"based_on": "appointment_datetime",
|
||||
"chart_name": "Patient Appointments",
|
||||
"chart_type": "Count",
|
||||
"creation": "2020-07-14 18:17:54.525082",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Patient Appointment",
|
||||
"dynamic_filters_json": "[[\"Patient Appointment\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Patient Appointment\",\"status\",\"!=\",\"Cancelled\",false]]",
|
||||
"idx": 0,
|
||||
"is_public": 0,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2020-07-22 13:22:46.830491",
|
||||
"modified": "2020-07-22 13:38:02.254190",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Patient Appointments",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"time_interval": "Daily",
|
||||
"timeseries": 1,
|
||||
"timespan": "Last Month",
|
||||
"type": "Line",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"chart_name": "Symptoms",
|
||||
"chart_type": "Group By",
|
||||
"creation": "2020-07-14 18:17:54.680852",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Patient Encounter Symptom",
|
||||
"dynamic_filters_json": "",
|
||||
"filters_json": "[]",
|
||||
"group_by_based_on": "complaint",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2021-01-30 21:03:32.067473",
|
||||
"modified": "2021-02-01 13:35:30.953718",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Symptoms",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"timeseries": 0,
|
||||
"type": "Bar",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
frappe.provide('frappe.dashboards.chart_sources');
|
||||
|
||||
frappe.dashboards.chart_sources["Department wise Patient Appointments"] = {
|
||||
method: "erpnext.healthcare.dashboard_chart_source.department_wise_patient_appointments.department_wise_patient_appointments.get",
|
||||
filters: [
|
||||
{
|
||||
fieldname: "company",
|
||||
label: __("Company"),
|
||||
fieldtype: "Link",
|
||||
options: "Company",
|
||||
default: frappe.defaults.get_user_default("Company")
|
||||
}
|
||||
]
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"creation": "2020-05-18 19:18:42.571045",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart Source",
|
||||
"idx": 0,
|
||||
"modified": "2020-05-18 19:18:42.571045",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Department wise Patient Appointments",
|
||||
"owner": "Administrator",
|
||||
"source_name": "Department wise Patient Appointments",
|
||||
"timeseries": 0
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from frappe.utils.dashboard import cache_source
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@cache_source
|
||||
def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
|
||||
to_date = None, timespan = None, time_interval = None, heatmap_year = None):
|
||||
if chart_name:
|
||||
chart = frappe.get_doc('Dashboard Chart', chart_name)
|
||||
else:
|
||||
chart = frappe._dict(frappe.parse_json(chart))
|
||||
|
||||
filters = frappe.parse_json(filters)
|
||||
|
||||
data = frappe.db.get_list('Medical Department', fields=['name'])
|
||||
if not filters:
|
||||
filters = {}
|
||||
|
||||
status = ['Open', 'Scheduled', 'Closed', 'Cancelled']
|
||||
for department in data:
|
||||
filters['department'] = department.name
|
||||
department['total_appointments'] = frappe.db.count('Patient Appointment', filters=filters)
|
||||
|
||||
for entry in status:
|
||||
filters['status'] = entry
|
||||
department[frappe.scrub(entry)] = frappe.db.count('Patient Appointment', filters=filters)
|
||||
filters.pop('status')
|
||||
|
||||
sorted_department_map = sorted(data, key = lambda i: i['total_appointments'], reverse=True)
|
||||
|
||||
if len(sorted_department_map) > 10:
|
||||
sorted_department_map = sorted_department_map[:10]
|
||||
|
||||
labels = []
|
||||
open_appointments = []
|
||||
scheduled = []
|
||||
closed = []
|
||||
cancelled = []
|
||||
|
||||
for department in sorted_department_map:
|
||||
labels.append(department.name)
|
||||
open_appointments.append(department.open)
|
||||
scheduled.append(department.scheduled)
|
||||
closed.append(department.closed)
|
||||
cancelled.append(department.cancelled)
|
||||
|
||||
return {
|
||||
'labels': labels,
|
||||
'datasets': [
|
||||
{
|
||||
'name': 'Open',
|
||||
'values': open_appointments
|
||||
},
|
||||
{
|
||||
'name': 'Scheduled',
|
||||
'values': scheduled
|
||||
},
|
||||
{
|
||||
'name': 'Closed',
|
||||
'values': closed
|
||||
},
|
||||
{
|
||||
'name': 'Cancelled',
|
||||
'values': cancelled
|
||||
}
|
||||
],
|
||||
'type': 'bar'
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
{
|
||||
"cards": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "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]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "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]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "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]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"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]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"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]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"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]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Inpatient",
|
||||
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Medication Order\",\n\t\t\"label\": \"Inpatient Medication Order\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Medication Entry\",\n\t\t\"label\": \"Inpatient Medication Entry\"\n\t}\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"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]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"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\": \"page\",\n\t\t\"name\": \"patient-progress\",\n\t\t\"label\": \"Patient Progress\"\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]"
|
||||
},
|
||||
{
|
||||
"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\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Inpatient Medication Orders\",\n\t\t\"doctype\": \"Inpatient Medication Order\",\n\t\t\"label\": \"Inpatient Medication Orders\"\n\t}\n]"
|
||||
}
|
||||
],
|
||||
"category": "Domains",
|
||||
"charts": [
|
||||
{
|
||||
"chart_name": "Patient Appointments",
|
||||
"label": "Patient Appointments"
|
||||
}
|
||||
],
|
||||
"charts_label": "",
|
||||
"creation": "2020-03-02 17:23:17.919682",
|
||||
"developer_mode_only": 0,
|
||||
"disable_user_customization": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Desk Page",
|
||||
"extends_another_page": 0,
|
||||
"hide_custom": 0,
|
||||
"idx": 0,
|
||||
"is_standard": 1,
|
||||
"label": "Healthcare",
|
||||
"modified": "2020-11-26 22:09:09.164584",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Healthcare",
|
||||
"onboarding": "Healthcare",
|
||||
"owner": "Administrator",
|
||||
"pin_to_bottom": 0,
|
||||
"pin_to_top": 0,
|
||||
"restrict_to_domain": "Healthcare",
|
||||
"shortcuts": [
|
||||
{
|
||||
"color": "#ffe8cd",
|
||||
"format": "{} Open",
|
||||
"label": "Patient Appointment",
|
||||
"link_to": "Patient Appointment",
|
||||
"stats_filter": "{\n \"status\": \"Open\",\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%']\n}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "#ffe8cd",
|
||||
"format": "{} Active",
|
||||
"label": "Patient",
|
||||
"link_to": "Patient",
|
||||
"stats_filter": "{\n \"status\": \"Active\"\n}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "#cef6d1",
|
||||
"format": "{} Vacant",
|
||||
"label": "Healthcare Service Unit",
|
||||
"link_to": "Healthcare Service Unit",
|
||||
"stats_filter": "{\n \"occupancy_status\": \"Vacant\",\n \"is_group\": 0,\n \"company\": [\"like\", \"%\" + frappe.defaults.get_global_default(\"company\") + \"%\"]\n}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Healthcare Practitioner",
|
||||
"link_to": "Healthcare Practitioner",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Patient History",
|
||||
"link_to": "patient_history",
|
||||
"type": "Page"
|
||||
},
|
||||
{
|
||||
"label": "Dashboard",
|
||||
"link_to": "Healthcare",
|
||||
"type": "Dashboard"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Antibiotic', {
|
||||
});
|
@ -1,151 +0,0 @@
|
||||
{
|
||||
"allow_copy": 1,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:antibiotic_name",
|
||||
"beta": 1,
|
||||
"creation": "2016-02-23 11:11:30.749731",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 0,
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "antibiotic_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Antibiotic Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "abbr",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Abbr",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_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": "2019-10-01 17:58:23.136498",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Antibiotic",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Healthcare Administrator",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Laboratory User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"restrict_to_domain": "Healthcare",
|
||||
"search_fields": "antibiotic_name",
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "antibiotic_name",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class Antibiotic(Document):
|
||||
pass
|
@ -1,10 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestAntibiotic(unittest.TestCase):
|
||||
pass
|
@ -1,83 +0,0 @@
|
||||
// Copyright (c) 2016, ESS LLP and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Appointment Type', {
|
||||
refresh: function(frm) {
|
||||
frm.set_query('price_list', function() {
|
||||
return {
|
||||
filters: {'selling': 1}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query('medical_department', 'items', function(doc) {
|
||||
let item_list = doc.items.map(({medical_department}) => medical_department);
|
||||
return {
|
||||
filters: [
|
||||
['Medical Department', 'name', 'not in', item_list]
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query('op_consulting_charge_item', 'items', function() {
|
||||
return {
|
||||
filters: {
|
||||
is_stock_item: 0
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query('inpatient_visit_charge_item', 'items', function() {
|
||||
return {
|
||||
filters: {
|
||||
is_stock_item: 0
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on('Appointment Type Service Item', {
|
||||
op_consulting_charge_item: function(frm, cdt, cdn) {
|
||||
let d = locals[cdt][cdn];
|
||||
if (frm.doc.price_list && d.op_consulting_charge_item) {
|
||||
frappe.call({
|
||||
'method': 'frappe.client.get_value',
|
||||
args: {
|
||||
'doctype': 'Item Price',
|
||||
'filters': {
|
||||
'item_code': d.op_consulting_charge_item,
|
||||
'price_list': frm.doc.price_list
|
||||
},
|
||||
'fieldname': ['price_list_rate']
|
||||
},
|
||||
callback: function(data) {
|
||||
if (data.message.price_list_rate) {
|
||||
frappe.model.set_value(cdt, cdn, 'op_consulting_charge', data.message.price_list_rate);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
inpatient_visit_charge_item: function(frm, cdt, cdn) {
|
||||
let d = locals[cdt][cdn];
|
||||
if (frm.doc.price_list && d.inpatient_visit_charge_item) {
|
||||
frappe.call({
|
||||
'method': 'frappe.client.get_value',
|
||||
args: {
|
||||
'doctype': 'Item Price',
|
||||
'filters': {
|
||||
'item_code': d.inpatient_visit_charge_item,
|
||||
'price_list': frm.doc.price_list
|
||||
},
|
||||
'fieldname': ['price_list_rate']
|
||||
},
|
||||
callback: function (data) {
|
||||
if (data.message.price_list_rate) {
|
||||
frappe.model.set_value(cdt, cdn, 'inpatient_visit_charge', data.message.price_list_rate);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
@ -1,114 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:appointment_type",
|
||||
"beta": 1,
|
||||
"creation": "2016-07-22 11:52:34.953019",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"appointment_type",
|
||||
"ip",
|
||||
"default_duration",
|
||||
"color",
|
||||
"billing_section",
|
||||
"price_list",
|
||||
"items"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_in_quick_entry": 1,
|
||||
"fieldname": "appointment_type",
|
||||
"fieldtype": "Data",
|
||||
"ignore_xss_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Type",
|
||||
"reqd": 1,
|
||||
"translatable": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
"default": "0",
|
||||
"fieldname": "ip",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Inpatient",
|
||||
"print_hide": 1,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_in_quick_entry": 1,
|
||||
"bold": 1,
|
||||
"fieldname": "default_duration",
|
||||
"fieldtype": "Int",
|
||||
"in_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Default Duration (In Minutes)"
|
||||
},
|
||||
{
|
||||
"allow_in_quick_entry": 1,
|
||||
"fieldname": "color",
|
||||
"fieldtype": "Color",
|
||||
"in_list_view": 1,
|
||||
"label": "Color",
|
||||
"no_copy": 1,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "billing_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Billing"
|
||||
},
|
||||
{
|
||||
"fieldname": "price_list",
|
||||
"fieldtype": "Link",
|
||||
"label": "Price List",
|
||||
"options": "Price List"
|
||||
},
|
||||
{
|
||||
"fieldname": "items",
|
||||
"fieldtype": "Table",
|
||||
"label": "Appointment Type Service Items",
|
||||
"options": "Appointment Type Service Item"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2021-01-22 09:41:05.010524",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Appointment Type",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Healthcare Administrator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Physician",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"restrict_to_domain": "Healthcare",
|
||||
"search_fields": "appointment_type",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, ESS LLP and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class AppointmentType(Document):
|
||||
def validate(self):
|
||||
if self.items and self.price_list:
|
||||
for item in self.items:
|
||||
existing_op_item_price = frappe.db.exists('Item Price', {
|
||||
'item_code': item.op_consulting_charge_item,
|
||||
'price_list': self.price_list
|
||||
})
|
||||
|
||||
if not existing_op_item_price and item.op_consulting_charge_item and item.op_consulting_charge:
|
||||
make_item_price(self.price_list, item.op_consulting_charge_item, item.op_consulting_charge)
|
||||
|
||||
existing_ip_item_price = frappe.db.exists('Item Price', {
|
||||
'item_code': item.inpatient_visit_charge_item,
|
||||
'price_list': self.price_list
|
||||
})
|
||||
|
||||
if not existing_ip_item_price and item.inpatient_visit_charge_item and item.inpatient_visit_charge:
|
||||
make_item_price(self.price_list, item.inpatient_visit_charge_item, item.inpatient_visit_charge)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_service_item_based_on_department(appointment_type, department):
|
||||
item_list = frappe.db.get_value('Appointment Type Service Item',
|
||||
filters = {'medical_department': department, 'parent': appointment_type},
|
||||
fieldname = ['op_consulting_charge_item',
|
||||
'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'],
|
||||
as_dict = 1
|
||||
)
|
||||
|
||||
# if department wise items are not set up
|
||||
# use the generic items
|
||||
if not item_list:
|
||||
item_list = frappe.db.get_value('Appointment Type Service Item',
|
||||
filters = {'parent': appointment_type},
|
||||
fieldname = ['op_consulting_charge_item',
|
||||
'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'],
|
||||
as_dict = 1
|
||||
)
|
||||
|
||||
return item_list
|
||||
|
||||
def make_item_price(price_list, item, item_price):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Item Price',
|
||||
'price_list': price_list,
|
||||
'item_code': item,
|
||||
'price_list_rate': item_price
|
||||
}).insert(ignore_permissions=True, ignore_mandatory=True)
|
@ -1,15 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from frappe import _
|
||||
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'appointment_type',
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Patient Appointments'),
|
||||
'items': ['Patient Appointment']
|
||||
},
|
||||
]
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, ESS LLP and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Appointment Type')
|
||||
|
||||
class TestAppointmentType(unittest.TestCase):
|
||||
pass
|
@ -1,67 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2021-01-22 09:34:53.373105",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"medical_department",
|
||||
"op_consulting_charge_item",
|
||||
"op_consulting_charge",
|
||||
"column_break_4",
|
||||
"inpatient_visit_charge_item",
|
||||
"inpatient_visit_charge"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "medical_department",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Medical Department",
|
||||
"options": "Medical Department"
|
||||
},
|
||||
{
|
||||
"fieldname": "op_consulting_charge_item",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Out Patient Consulting Charge Item",
|
||||
"options": "Item"
|
||||
},
|
||||
{
|
||||
"fieldname": "op_consulting_charge",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Out Patient Consulting Charge"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "inpatient_visit_charge_item",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Inpatient Visit Charge Item",
|
||||
"options": "Item"
|
||||
},
|
||||
{
|
||||
"fieldname": "inpatient_visit_charge",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Inpatient Visit Charge"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-08-17 06:05:02.240812",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Appointment Type Service Item",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
// 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) {
|
||||
|
||||
// }
|
||||
});
|
@ -1,45 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
@ -1,377 +0,0 @@
|
||||
// Copyright (c) 2017, ESS LLP and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Clinical Procedure', {
|
||||
setup: function(frm) {
|
||||
frm.set_query('batch_no', 'items', function(doc, cdt, cdn) {
|
||||
let item = locals[cdt][cdn];
|
||||
if (!item.item_code) {
|
||||
frappe.throw(__('Please enter Item Code to get Batch Number'));
|
||||
} else {
|
||||
let filters = {'item_code': item.item_code};
|
||||
|
||||
if (frm.doc.status == 'In Progress') {
|
||||
filters['posting_date'] = frm.doc.start_date || frappe.datetime.nowdate();
|
||||
if (frm.doc.warehouse) filters['warehouse'] = frm.doc.warehouse;
|
||||
}
|
||||
|
||||
return {
|
||||
query : 'erpnext.controllers.queries.get_batch_no',
|
||||
filters: filters
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
frm.set_query('patient', function () {
|
||||
return {
|
||||
filters: {'status': ['!=', 'Disabled']}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query('appointment', function () {
|
||||
return {
|
||||
filters: {
|
||||
'procedure_template': ['not in', null],
|
||||
'status': ['in', 'Open, Scheduled']
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query('service_unit', function() {
|
||||
return {
|
||||
filters: {
|
||||
'is_group': false,
|
||||
'allow_appointments': true,
|
||||
'company': frm.doc.company
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query('practitioner', function() {
|
||||
return {
|
||||
filters: {
|
||||
'department': frm.doc.medical_department
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
if (frm.doc.consume_stock) {
|
||||
frm.set_indicator_formatter('item_code',
|
||||
function(doc) { return (doc.qty<=doc.actual_qty) ? 'green' : 'orange' ; });
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus == 1) {
|
||||
if (frm.doc.status == 'In Progress') {
|
||||
let btn_label = '';
|
||||
let msg = '';
|
||||
if (frm.doc.consume_stock) {
|
||||
btn_label = __('Complete and Consume');
|
||||
msg = __('Complete {0} and Consume Stock?', [frm.doc.name]);
|
||||
} else {
|
||||
btn_label = 'Complete';
|
||||
msg = __('Complete {0}?', [frm.doc.name]);
|
||||
}
|
||||
|
||||
frm.add_custom_button(__(btn_label), function () {
|
||||
frappe.confirm(
|
||||
msg,
|
||||
function() {
|
||||
frappe.call({
|
||||
method: 'complete_procedure',
|
||||
doc: frm.doc,
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
frappe.show_alert({
|
||||
message: __('Stock Entry {0} created', ['<a class="bold" href="/app/stock-entry/'+ r.message + '">' + r.message + '</a>']),
|
||||
indicator: 'green'
|
||||
});
|
||||
}
|
||||
frm.reload_doc();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}).addClass("btn-primary");
|
||||
|
||||
} else if (frm.doc.status == 'Pending') {
|
||||
frm.add_custom_button(__('Start'), function() {
|
||||
frappe.call({
|
||||
doc: frm.doc,
|
||||
method: 'start_procedure',
|
||||
callback: function(r) {
|
||||
if (!r.exc) {
|
||||
if (r.message == 'insufficient stock') {
|
||||
let msg = __('Stock quantity to start the Procedure is not available in the Warehouse {0}. Do you want to record a Stock Entry?', [frm.doc.warehouse.bold()]);
|
||||
frappe.confirm(
|
||||
msg,
|
||||
function() {
|
||||
frappe.call({
|
||||
doc: frm.doc,
|
||||
method: 'make_material_receipt',
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
if (!r.exc) {
|
||||
frm.reload_doc();
|
||||
let doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
frm.reload_doc();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}).addClass("btn-primary");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
if (frm.is_new()) {
|
||||
frm.add_fetch('procedure_template', 'medical_department', 'medical_department');
|
||||
frm.set_value('start_time', null);
|
||||
}
|
||||
},
|
||||
|
||||
patient: function(frm) {
|
||||
if (frm.doc.patient) {
|
||||
frappe.call({
|
||||
'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
|
||||
args: {
|
||||
patient: frm.doc.patient
|
||||
},
|
||||
callback: function (data) {
|
||||
let age = '';
|
||||
if (data.message.dob) {
|
||||
age = calculate_age(data.message.dob);
|
||||
} else if (data.message.age) {
|
||||
age = data.message.age;
|
||||
if (data.message.age_as_on) {
|
||||
age = __('{0} as on {1}', [age, data.message.age_as_on]);
|
||||
}
|
||||
}
|
||||
frm.set_value('patient_name', data.message.patient_name);
|
||||
frm.set_value('patient_age', age);
|
||||
frm.set_value('patient_sex', data.message.sex);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
frm.set_value('patient_name', '');
|
||||
frm.set_value('patient_age', '');
|
||||
frm.set_value('patient_sex', '');
|
||||
}
|
||||
},
|
||||
|
||||
appointment: function(frm) {
|
||||
if (frm.doc.appointment) {
|
||||
frappe.call({
|
||||
'method': 'frappe.client.get',
|
||||
args: {
|
||||
doctype: 'Patient Appointment',
|
||||
name: frm.doc.appointment
|
||||
},
|
||||
callback: function(data) {
|
||||
let values = {
|
||||
'patient':data.message.patient,
|
||||
'procedure_template': data.message.procedure_template,
|
||||
'medical_department': data.message.department,
|
||||
'practitioner': data.message.practitioner,
|
||||
'start_date': data.message.appointment_date,
|
||||
'start_time': data.message.appointment_time,
|
||||
'notes': data.message.notes,
|
||||
'service_unit': data.message.service_unit,
|
||||
'company': data.message.company
|
||||
};
|
||||
frm.set_value(values);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let values = {
|
||||
'patient': '',
|
||||
'patient_name': '',
|
||||
'patient_sex': '',
|
||||
'patient_age': '',
|
||||
'medical_department': '',
|
||||
'procedure_template': '',
|
||||
'start_date': '',
|
||||
'start_time': '',
|
||||
'notes': '',
|
||||
'service_unit': '',
|
||||
'inpatient_record': ''
|
||||
};
|
||||
frm.set_value(values);
|
||||
}
|
||||
},
|
||||
|
||||
procedure_template: function(frm) {
|
||||
if (frm.doc.procedure_template) {
|
||||
frappe.call({
|
||||
'method': 'frappe.client.get',
|
||||
args: {
|
||||
doctype: 'Clinical Procedure Template',
|
||||
name: frm.doc.procedure_template
|
||||
},
|
||||
callback: function (data) {
|
||||
frm.set_value('medical_department', data.message.medical_department);
|
||||
frm.set_value('consume_stock', data.message.consume_stock);
|
||||
frm.events.set_warehouse(frm);
|
||||
frm.events.set_procedure_consumables(frm);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
service_unit: function(frm) {
|
||||
if (frm.doc.service_unit) {
|
||||
frappe.call({
|
||||
method: 'frappe.client.get_value',
|
||||
args:{
|
||||
fieldname: 'warehouse',
|
||||
doctype: 'Healthcare Service Unit',
|
||||
filters:{name: frm.doc.service_unit},
|
||||
},
|
||||
callback: function(data) {
|
||||
if (data.message) {
|
||||
frm.set_value('warehouse', data.message.warehouse);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
practitioner: function(frm) {
|
||||
if (frm.doc.practitioner) {
|
||||
frappe.call({
|
||||
'method': 'frappe.client.get',
|
||||
args: {
|
||||
doctype: 'Healthcare Practitioner',
|
||||
name: frm.doc.practitioner
|
||||
},
|
||||
callback: function (data) {
|
||||
frappe.model.set_value(frm.doctype,frm.docname, 'practitioner_name', data.message.practitioner_name);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
frappe.model.set_value(frm.doctype,frm.docname, 'practitioner_name', '');
|
||||
}
|
||||
},
|
||||
|
||||
set_warehouse: function(frm) {
|
||||
if (!frm.doc.warehouse) {
|
||||
frappe.call({
|
||||
method: 'frappe.client.get_value',
|
||||
args: {
|
||||
doctype: 'Stock Settings',
|
||||
fieldname: 'default_warehouse'
|
||||
},
|
||||
callback: function (data) {
|
||||
frm.set_value('warehouse', data.message.default_warehouse);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
set_procedure_consumables: function(frm) {
|
||||
frappe.call({
|
||||
method: 'erpnext.healthcare.doctype.clinical_procedure.clinical_procedure.get_procedure_consumables',
|
||||
args: {
|
||||
procedure_template: frm.doc.procedure_template
|
||||
},
|
||||
callback: function(data) {
|
||||
if (data.message) {
|
||||
frm.doc.items = [];
|
||||
$.each(data.message, function(i, v) {
|
||||
let item = frm.add_child('items');
|
||||
item.item_code = v.item_code;
|
||||
item.item_name = v.item_name;
|
||||
item.uom = v.uom;
|
||||
item.stock_uom = v.stock_uom;
|
||||
item.qty = flt(v.qty);
|
||||
item.transfer_qty = v.transfer_qty;
|
||||
item.conversion_factor = v.conversion_factor;
|
||||
item.invoice_separately_as_consumables = v.invoice_separately_as_consumables;
|
||||
item.batch_no = v.batch_no;
|
||||
});
|
||||
refresh_field('items');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
frappe.ui.form.on('Clinical Procedure Item', {
|
||||
qty: function(frm, cdt, cdn) {
|
||||
let d = locals[cdt][cdn];
|
||||
frappe.model.set_value(cdt, cdn, 'transfer_qty', d.qty*d.conversion_factor);
|
||||
},
|
||||
|
||||
uom: function(doc, cdt, cdn) {
|
||||
let d = locals[cdt][cdn];
|
||||
if (d.uom && d.item_code) {
|
||||
return frappe.call({
|
||||
method: 'erpnext.stock.doctype.stock_entry.stock_entry.get_uom_details',
|
||||
args: {
|
||||
item_code: d.item_code,
|
||||
uom: d.uom,
|
||||
qty: d.qty
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
frappe.model.set_value(cdt, cdn, r.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
item_code: function(frm, cdt, cdn) {
|
||||
let d = locals[cdt][cdn];
|
||||
let args = null;
|
||||
if (d.item_code) {
|
||||
args = {
|
||||
'doctype' : 'Clinical Procedure',
|
||||
'item_code' : d.item_code,
|
||||
'company' : frm.doc.company,
|
||||
'warehouse': frm.doc.warehouse
|
||||
};
|
||||
return frappe.call({
|
||||
method: 'erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.get_item_details',
|
||||
args: {args: args},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
let d = locals[cdt][cdn];
|
||||
$.each(r.message, function(k, v) {
|
||||
d[k] = v;
|
||||
});
|
||||
refresh_field('items');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let calculate_age = function(birth) {
|
||||
let ageMS = Date.parse(Date()) - Date.parse(birth);
|
||||
let age = new Date();
|
||||
age.setTime(ageMS);
|
||||
let years = age.getFullYear() - 1970;
|
||||
return `${years} ${__('Years(s)')} ${age.getMonth()} ${__('Month(s)')} ${age.getDate()} ${__('Day(s)')}`;
|
||||
};
|
||||
|
||||
// List Stock items
|
||||
cur_frm.set_query('item_code', 'items', function() {
|
||||
return {
|
||||
filters: {
|
||||
is_stock_item:1
|
||||
}
|
||||
};
|
||||
});
|
@ -1,345 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "naming_series:",
|
||||
"beta": 1,
|
||||
"creation": "2017-04-07 12:52:43.542429",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"naming_series",
|
||||
"title",
|
||||
"appointment",
|
||||
"procedure_template",
|
||||
"medical_code",
|
||||
"column_break_30",
|
||||
"company",
|
||||
"invoiced",
|
||||
"section_break_6",
|
||||
"patient",
|
||||
"patient_name",
|
||||
"patient_sex",
|
||||
"patient_age",
|
||||
"inpatient_record",
|
||||
"notes",
|
||||
"column_break_7",
|
||||
"status",
|
||||
"practitioner",
|
||||
"practitioner_name",
|
||||
"medical_department",
|
||||
"service_unit",
|
||||
"start_date",
|
||||
"start_time",
|
||||
"sample",
|
||||
"consumables_section",
|
||||
"consume_stock",
|
||||
"warehouse",
|
||||
"items",
|
||||
"section_break_24",
|
||||
"invoice_separately_as_consumables",
|
||||
"consumption_invoiced",
|
||||
"consumable_total_amount",
|
||||
"column_break_27",
|
||||
"consumption_details",
|
||||
"sb_refs",
|
||||
"column_break_34",
|
||||
"prescription",
|
||||
"amended_from"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fetch_from": "patient.inpatient_record",
|
||||
"fieldname": "inpatient_record",
|
||||
"fieldtype": "Link",
|
||||
"label": "Inpatient Record",
|
||||
"options": "Inpatient Record",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"label": "Series",
|
||||
"options": "HLC-CPR-.YYYY.-"
|
||||
},
|
||||
{
|
||||
"fieldname": "appointment",
|
||||
"fieldtype": "Link",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Appointment",
|
||||
"options": "Patient Appointment",
|
||||
"set_only_once": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "patient",
|
||||
"fieldtype": "Link",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Patient",
|
||||
"options": "Patient",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "patient_age",
|
||||
"fieldtype": "Data",
|
||||
"label": "Age",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "patient_sex",
|
||||
"fieldtype": "Link",
|
||||
"label": "Gender",
|
||||
"options": "Gender",
|
||||
"read_only": 1,
|
||||
"set_only_once": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "prescription",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"label": "Procedure Prescription",
|
||||
"options": "Procedure Prescription",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "medical_department",
|
||||
"fieldtype": "Link",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Medical Department",
|
||||
"options": "Medical Department"
|
||||
},
|
||||
{
|
||||
"fieldname": "practitioner",
|
||||
"fieldtype": "Link",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Healthcare Practitioner",
|
||||
"options": "Healthcare Practitioner"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_7",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "procedure_template",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Procedure Template",
|
||||
"options": "Clinical Procedure Template",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "service_unit",
|
||||
"fieldtype": "Link",
|
||||
"label": "Service Unit",
|
||||
"options": "Healthcare Service Unit",
|
||||
"set_only_once": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Warehouse",
|
||||
"mandatory_depends_on": "eval: doc.consume_stock == 1",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"default": "Today",
|
||||
"fieldname": "start_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Start Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "start_time",
|
||||
"fieldtype": "Time",
|
||||
"label": "Start Time"
|
||||
},
|
||||
{
|
||||
"fieldname": "sample",
|
||||
"fieldtype": "Link",
|
||||
"label": "Sample",
|
||||
"options": "Sample Collection"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "invoiced",
|
||||
"fieldtype": "Check",
|
||||
"label": "Invoiced",
|
||||
"no_copy": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "notes",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Notes",
|
||||
"set_only_once": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "consume_stock",
|
||||
"fieldtype": "Check",
|
||||
"label": "Consume Stock"
|
||||
},
|
||||
{
|
||||
"fieldname": "items",
|
||||
"fieldtype": "Table",
|
||||
"label": "Consumables",
|
||||
"options": "Clinical Procedure Item"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "invoice_separately_as_consumables",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Invoice Consumables Separately",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "invoice_separately_as_consumables",
|
||||
"fieldname": "consumable_total_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Consumable Total Amount",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "invoice_separately_as_consumables",
|
||||
"fieldname": "consumption_details",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Consumption Details"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "invoice_separately_as_consumables",
|
||||
"fieldname": "consumption_invoiced",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Consumption Invoiced",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"options": "Draft\nSubmitted\nCancelled\nIn Progress\nCompleted\nPending",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"label": "Amended From",
|
||||
"no_copy": 1,
|
||||
"options": "Clinical Procedure",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "consume_stock",
|
||||
"fieldname": "consumables_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Consumables"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_27",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_24",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_30",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "sb_refs",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "patient_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Patient Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "practitioner_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Practitioner Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_34",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Title",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "procedure_template.medical_code",
|
||||
"fieldname": "medical_code",
|
||||
"fieldtype": "Link",
|
||||
"label": "Medical Code",
|
||||
"options": "Medical Code",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-06-29 14:28:11.779815",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Clinical Procedure",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Nursing User",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Physician",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"restrict_to_domain": "Healthcare",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "title",
|
||||
"track_changes": 1
|
||||
}
|
@ -1,255 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, ESS LLP and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.utils import flt, nowdate, nowtime
|
||||
|
||||
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_account
|
||||
from erpnext.healthcare.doctype.lab_test.lab_test import create_sample_doc
|
||||
from erpnext.stock.get_item_details import get_item_details
|
||||
from erpnext.stock.stock_ledger import get_previous_sle
|
||||
|
||||
|
||||
class ClinicalProcedure(Document):
|
||||
def validate(self):
|
||||
self.set_status()
|
||||
self.set_title()
|
||||
if self.consume_stock:
|
||||
self.set_actual_qty()
|
||||
|
||||
if self.items:
|
||||
self.invoice_separately_as_consumables = False
|
||||
for item in self.items:
|
||||
if item.invoice_separately_as_consumables:
|
||||
self.invoice_separately_as_consumables = True
|
||||
|
||||
def before_insert(self):
|
||||
if self.consume_stock:
|
||||
self.set_actual_qty()
|
||||
|
||||
def after_insert(self):
|
||||
if self.prescription:
|
||||
frappe.db.set_value('Procedure Prescription', self.prescription, 'procedure_created', 1)
|
||||
if self.appointment:
|
||||
frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed')
|
||||
template = frappe.get_doc('Clinical Procedure Template', self.procedure_template)
|
||||
if template.sample:
|
||||
patient = frappe.get_doc('Patient', self.patient)
|
||||
sample_collection = create_sample_doc(template, patient, None, self.company)
|
||||
frappe.db.set_value('Clinical Procedure', self.name, 'sample', sample_collection.name)
|
||||
self.reload()
|
||||
|
||||
def set_status(self):
|
||||
if self.docstatus == 0:
|
||||
self.status = 'Draft'
|
||||
elif self.docstatus == 1:
|
||||
if self.status not in ['In Progress', 'Completed']:
|
||||
self.status = 'Pending'
|
||||
elif self.docstatus == 2:
|
||||
self.status = 'Cancelled'
|
||||
|
||||
def set_title(self):
|
||||
self.title = _('{0} - {1}').format(self.patient_name or self.patient, self.procedure_template)[:100]
|
||||
|
||||
@frappe.whitelist()
|
||||
def complete_procedure(self):
|
||||
if self.consume_stock and self.items:
|
||||
stock_entry = make_stock_entry(self)
|
||||
|
||||
if self.items:
|
||||
consumable_total_amount = 0
|
||||
consumption_details = False
|
||||
customer = frappe.db.get_value('Patient', self.patient, 'customer')
|
||||
if customer:
|
||||
for item in self.items:
|
||||
if item.invoice_separately_as_consumables:
|
||||
price_list, price_list_currency = frappe.db.get_values('Price List', {'selling': 1}, ['name', 'currency'])[0]
|
||||
args = {
|
||||
'doctype': 'Sales Invoice',
|
||||
'item_code': item.item_code,
|
||||
'company': self.company,
|
||||
'warehouse': self.warehouse,
|
||||
'customer': customer,
|
||||
'selling_price_list': price_list,
|
||||
'price_list_currency': price_list_currency,
|
||||
'plc_conversion_rate': 1.0,
|
||||
'conversion_rate': 1.0
|
||||
}
|
||||
item_details = get_item_details(args)
|
||||
item_price = item_details.price_list_rate * item.qty
|
||||
item_consumption_details = item_details.item_name + ' ' + str(item.qty) + ' ' + item.uom + ' ' + str(item_price)
|
||||
consumable_total_amount += item_price
|
||||
if not consumption_details:
|
||||
consumption_details = _('Clinical Procedure ({0}):').format(self.name)
|
||||
consumption_details += '\n\t' + item_consumption_details
|
||||
|
||||
if consumable_total_amount > 0:
|
||||
frappe.db.set_value('Clinical Procedure', self.name, 'consumable_total_amount', consumable_total_amount)
|
||||
frappe.db.set_value('Clinical Procedure', self.name, 'consumption_details', consumption_details)
|
||||
else:
|
||||
frappe.throw(_('Please set Customer in Patient {0}').format(frappe.bold(self.patient)), title=_('Customer Not Found'))
|
||||
|
||||
self.db_set('status', 'Completed')
|
||||
|
||||
if self.consume_stock and self.items:
|
||||
return stock_entry
|
||||
|
||||
@frappe.whitelist()
|
||||
def start_procedure(self):
|
||||
allow_start = self.set_actual_qty()
|
||||
if allow_start:
|
||||
self.db_set('status', 'In Progress')
|
||||
return 'success'
|
||||
return 'insufficient stock'
|
||||
|
||||
def set_actual_qty(self):
|
||||
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
|
||||
|
||||
allow_start = True
|
||||
for d in self.get('items'):
|
||||
d.actual_qty = get_stock_qty(d.item_code, self.warehouse)
|
||||
# validate qty
|
||||
if not allow_negative_stock and d.actual_qty < d.qty:
|
||||
allow_start = False
|
||||
break
|
||||
|
||||
return allow_start
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_material_receipt(self, submit=False):
|
||||
stock_entry = frappe.new_doc('Stock Entry')
|
||||
|
||||
stock_entry.stock_entry_type = 'Material Receipt'
|
||||
stock_entry.to_warehouse = self.warehouse
|
||||
stock_entry.company = self.company
|
||||
expense_account = get_account(None, 'expense_account', 'Healthcare Settings', self.company)
|
||||
for item in self.items:
|
||||
if item.qty > item.actual_qty:
|
||||
se_child = stock_entry.append('items')
|
||||
se_child.item_code = item.item_code
|
||||
se_child.item_name = item.item_name
|
||||
se_child.uom = item.uom
|
||||
se_child.stock_uom = item.stock_uom
|
||||
se_child.qty = flt(item.qty - item.actual_qty)
|
||||
se_child.t_warehouse = self.warehouse
|
||||
# in stock uom
|
||||
se_child.transfer_qty = flt(item.transfer_qty)
|
||||
se_child.conversion_factor = flt(item.conversion_factor)
|
||||
cost_center = frappe.get_cached_value('Company', self.company, 'cost_center')
|
||||
se_child.cost_center = cost_center
|
||||
se_child.expense_account = expense_account
|
||||
if submit:
|
||||
stock_entry.submit()
|
||||
return stock_entry
|
||||
return stock_entry.as_dict()
|
||||
|
||||
|
||||
def get_stock_qty(item_code, warehouse):
|
||||
return get_previous_sle({
|
||||
'item_code': item_code,
|
||||
'warehouse': warehouse,
|
||||
'posting_date': nowdate(),
|
||||
'posting_time': nowtime()
|
||||
}).get('qty_after_transaction') or 0
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_procedure_consumables(procedure_template):
|
||||
return get_items('Clinical Procedure Item', procedure_template, 'Clinical Procedure Template')
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def set_stock_items(doc, stock_detail_parent, parenttype):
|
||||
items = get_items('Clinical Procedure Item', stock_detail_parent, parenttype)
|
||||
|
||||
for item in items:
|
||||
se_child = doc.append('items')
|
||||
se_child.item_code = item.item_code
|
||||
se_child.item_name = item.item_name
|
||||
se_child.uom = item.uom
|
||||
se_child.stock_uom = item.stock_uom
|
||||
se_child.qty = flt(item.qty)
|
||||
# in stock uom
|
||||
se_child.transfer_qty = flt(item.transfer_qty)
|
||||
se_child.conversion_factor = flt(item.conversion_factor)
|
||||
if item.batch_no:
|
||||
se_child.batch_no = item.batch_no
|
||||
if parenttype == 'Clinical Procedure Template':
|
||||
se_child.invoice_separately_as_consumables = item.invoice_separately_as_consumables
|
||||
|
||||
return doc
|
||||
|
||||
|
||||
def get_items(table, parent, parenttype):
|
||||
items = frappe.db.get_all(table, filters={
|
||||
'parent': parent,
|
||||
'parenttype': parenttype
|
||||
}, fields=['*'])
|
||||
|
||||
return items
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_stock_entry(doc):
|
||||
stock_entry = frappe.new_doc('Stock Entry')
|
||||
stock_entry = set_stock_items(stock_entry, doc.name, 'Clinical Procedure')
|
||||
stock_entry.stock_entry_type = 'Material Issue'
|
||||
stock_entry.from_warehouse = doc.warehouse
|
||||
stock_entry.company = doc.company
|
||||
expense_account = get_account(None, 'expense_account', 'Healthcare Settings', doc.company)
|
||||
|
||||
for item_line in stock_entry.items:
|
||||
cost_center = frappe.get_cached_value('Company', doc.company, 'cost_center')
|
||||
item_line.cost_center = cost_center
|
||||
item_line.expense_account = expense_account
|
||||
|
||||
stock_entry.save(ignore_permissions=True)
|
||||
stock_entry.submit()
|
||||
return stock_entry.name
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_procedure(source_name, target_doc=None):
|
||||
def set_missing_values(source, target):
|
||||
consume_stock = frappe.db.get_value('Clinical Procedure Template', source.procedure_template, 'consume_stock')
|
||||
if consume_stock:
|
||||
target.consume_stock = 1
|
||||
warehouse = None
|
||||
if source.service_unit:
|
||||
warehouse = frappe.db.get_value('Healthcare Service Unit', source.service_unit, 'warehouse')
|
||||
if not warehouse:
|
||||
warehouse = frappe.db.get_value('Stock Settings', None, 'default_warehouse')
|
||||
if warehouse:
|
||||
target.warehouse = warehouse
|
||||
|
||||
set_stock_items(target, source.procedure_template, 'Clinical Procedure Template')
|
||||
|
||||
doc = get_mapped_doc('Patient Appointment', source_name, {
|
||||
'Patient Appointment': {
|
||||
'doctype': 'Clinical Procedure',
|
||||
'field_map': [
|
||||
['appointment', 'name'],
|
||||
['patient', 'patient'],
|
||||
['patient_age', 'patient_age'],
|
||||
['patient_sex', 'patient_sex'],
|
||||
['procedure_template', 'procedure_template'],
|
||||
['prescription', 'procedure_prescription'],
|
||||
['practitioner', 'practitioner'],
|
||||
['medical_department', 'department'],
|
||||
['start_date', 'appointment_date'],
|
||||
['start_time', 'appointment_time'],
|
||||
['notes', 'notes'],
|
||||
['service_unit', 'service_unit'],
|
||||
['company', 'company'],
|
||||
['invoiced', 'invoiced']
|
||||
]
|
||||
}
|
||||
}, target_doc, set_missing_values)
|
||||
|
||||
return doc
|
@ -1,11 +0,0 @@
|
||||
frappe.listview_settings['Clinical Procedure'] = {
|
||||
get_indicator: function(doc) {
|
||||
var colors = {
|
||||
'Completed': 'green',
|
||||
'In Progress': 'orange',
|
||||
'Pending': 'orange',
|
||||
'Cancelled': 'grey'
|
||||
};
|
||||
return [__(doc.status), colors[doc.status], 'status,=,' + doc.status];
|
||||
}
|
||||
};
|
@ -1,71 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, ESS LLP and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import (
|
||||
create_clinical_procedure_template,
|
||||
create_healthcare_docs,
|
||||
)
|
||||
|
||||
test_dependencies = ['Item']
|
||||
|
||||
class TestClinicalProcedure(unittest.TestCase):
|
||||
def test_procedure_template_item(self):
|
||||
patient, practitioner = create_healthcare_docs()
|
||||
procedure_template = create_clinical_procedure_template()
|
||||
self.assertTrue(frappe.db.exists('Item', procedure_template.item))
|
||||
|
||||
procedure_template.disabled = 1
|
||||
procedure_template.save()
|
||||
self.assertEqual(frappe.db.get_value('Item', procedure_template.item, 'disabled'), 1)
|
||||
|
||||
def test_consumables(self):
|
||||
patient, practitioner = create_healthcare_docs()
|
||||
procedure_template = create_clinical_procedure_template()
|
||||
procedure_template.allow_stock_consumption = 1
|
||||
consumable = create_consumable()
|
||||
procedure_template.append('items', {
|
||||
'item_code': consumable.item_code,
|
||||
'qty': 1,
|
||||
'uom': consumable.stock_uom,
|
||||
'stock_uom': consumable.stock_uom
|
||||
})
|
||||
procedure_template.save()
|
||||
procedure = create_procedure(procedure_template, patient, practitioner)
|
||||
result = procedure.start_procedure()
|
||||
if result == 'insufficient stock':
|
||||
procedure.make_material_receipt(submit=True)
|
||||
result = procedure.start_procedure()
|
||||
self.assertEqual(procedure.status, 'In Progress')
|
||||
result = procedure.complete_procedure()
|
||||
# check consumption
|
||||
self.assertTrue(frappe.db.exists('Stock Entry', result))
|
||||
|
||||
|
||||
def create_consumable():
|
||||
if frappe.db.exists('Item', 'Syringe'):
|
||||
return frappe.get_doc('Item', 'Syringe')
|
||||
consumable = frappe.new_doc('Item')
|
||||
consumable.item_code = 'Syringe'
|
||||
consumable.item_group = '_Test Item Group'
|
||||
consumable.stock_uom = 'Nos'
|
||||
consumable.valuation_rate = 5.00
|
||||
consumable.save()
|
||||
return consumable
|
||||
|
||||
def create_procedure(procedure_template, patient, practitioner):
|
||||
procedure = frappe.new_doc('Clinical Procedure')
|
||||
procedure.procedure_template = procedure_template.name
|
||||
procedure.patient = patient
|
||||
procedure.practitioner = practitioner
|
||||
procedure.consume_stock = procedure_template.allow_stock_consumption
|
||||
procedure.items = procedure_template.items
|
||||
procedure.company = "_Test Company"
|
||||
procedure.warehouse = "_Test Warehouse - _TC"
|
||||
procedure.submit()
|
||||
return procedure
|
@ -1,123 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"beta": 1,
|
||||
"creation": "2017-10-05 16:15:10.876952",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"item_code",
|
||||
"item_name",
|
||||
"qty",
|
||||
"barcode",
|
||||
"uom",
|
||||
"invoice_separately_as_consumables",
|
||||
"column_break_5",
|
||||
"batch_no",
|
||||
"conversion_factor",
|
||||
"stock_uom",
|
||||
"transfer_qty",
|
||||
"actual_qty"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"bold": 1,
|
||||
"columns": 3,
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Item",
|
||||
"options": "Item",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "barcode",
|
||||
"fieldtype": "Data",
|
||||
"label": "Barcode"
|
||||
},
|
||||
{
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Item Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Quantity",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "uom",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "UOM",
|
||||
"options": "UOM",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "invoice_separately_as_consumables",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Invoice Separately as Consumables"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "batch_no",
|
||||
"fieldtype": "Link",
|
||||
"label": "Batch",
|
||||
"options": "Batch"
|
||||
},
|
||||
{
|
||||
"fieldname": "conversion_factor",
|
||||
"fieldtype": "Float",
|
||||
"label": "Conversion Factor",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "stock_uom",
|
||||
"fieldtype": "Link",
|
||||
"label": "Stock UOM",
|
||||
"options": "UOM",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "transfer_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Transfer Qty",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "actual_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Actual Qty (at source/target)",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-03-01 15:34:54.226722",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Clinical Procedure Item",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"restrict_to_domain": "Healthcare",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, earthians and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class ClinicalProcedureItem(Document):
|
||||
pass
|
@ -1,190 +0,0 @@
|
||||
// Copyright (c) 2017, earthians and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Clinical Procedure Template', {
|
||||
template: function(frm) {
|
||||
if (!frm.doc.item_code)
|
||||
frm.set_value('item_code', frm.doc.template);
|
||||
if (!frm.doc.description)
|
||||
frm.set_value('description', frm.doc.template);
|
||||
mark_change_in_item(frm);
|
||||
},
|
||||
|
||||
rate: function(frm) {
|
||||
mark_change_in_item(frm);
|
||||
},
|
||||
|
||||
is_billable: function (frm) {
|
||||
mark_change_in_item(frm);
|
||||
},
|
||||
|
||||
item_group: function(frm) {
|
||||
mark_change_in_item(frm);
|
||||
},
|
||||
|
||||
description: function(frm) {
|
||||
mark_change_in_item(frm);
|
||||
},
|
||||
|
||||
medical_department: function(frm) {
|
||||
mark_change_in_item(frm);
|
||||
},
|
||||
|
||||
medical_code: function(frm) {
|
||||
frm.set_query("medical_code", function() {
|
||||
return {
|
||||
filters: {
|
||||
medical_code_standard: frm.doc.medical_code_standard
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
frm.fields_dict['items'].grid.set_column_disp('barcode', false);
|
||||
frm.fields_dict['items'].grid.set_column_disp('batch_no', false);
|
||||
|
||||
if (!frm.doc.__islocal) {
|
||||
cur_frm.add_custom_button(__('Change Item Code'), function() {
|
||||
change_template_code(frm.doc);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mark_change_in_item = function(frm) {
|
||||
if (!frm.doc.__islocal) {
|
||||
frm.doc.change_in_item = 1;
|
||||
}
|
||||
};
|
||||
|
||||
let change_template_code = function(doc) {
|
||||
let d = new frappe.ui.Dialog({
|
||||
title:__('Change Item Code'),
|
||||
fields:[
|
||||
{
|
||||
'fieldtype': 'Data',
|
||||
'label': 'Item Code',
|
||||
'fieldname': 'item_code',
|
||||
reqd: 1
|
||||
}
|
||||
],
|
||||
primary_action: function() {
|
||||
let values = d.get_values();
|
||||
|
||||
if (values) {
|
||||
frappe.call({
|
||||
'method': 'erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.change_item_code_from_template',
|
||||
'args': {item_code: values.item_code, doc: doc},
|
||||
callback: function () {
|
||||
cur_frm.reload_doc();
|
||||
frappe.show_alert({
|
||||
message: 'Item Code renamed successfully',
|
||||
indicator: 'green'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
d.hide();
|
||||
},
|
||||
primary_action_label: __('Change Item Code')
|
||||
});
|
||||
d.show();
|
||||
|
||||
d.set_values({
|
||||
'item_code': doc.item_code
|
||||
});
|
||||
};
|
||||
|
||||
frappe.ui.form.on('Clinical Procedure Item', {
|
||||
qty: function(frm, cdt, cdn) {
|
||||
let d = locals[cdt][cdn];
|
||||
frappe.model.set_value(cdt, cdn, 'transfer_qty', d.qty * d.conversion_factor);
|
||||
},
|
||||
|
||||
uom: function(doc, cdt, cdn){
|
||||
let d = locals[cdt][cdn];
|
||||
if (d.uom && d.item_code) {
|
||||
return frappe.call({
|
||||
method: 'erpnext.stock.doctype.stock_entry.stock_entry.get_uom_details',
|
||||
args: {
|
||||
item_code: d.item_code,
|
||||
uom: d.uom,
|
||||
qty: d.qty
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
frappe.model.set_value(cdt, cdn, r.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
item_code: function(frm, cdt, cdn) {
|
||||
let d = locals[cdt][cdn];
|
||||
if (d.item_code) {
|
||||
let args = {
|
||||
'item_code' : d.item_code,
|
||||
'transfer_qty' : d.transfer_qty,
|
||||
'quantity' : d.qty
|
||||
};
|
||||
return frappe.call({
|
||||
method: 'erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.get_item_details',
|
||||
args: {args: args},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
let d = locals[cdt][cdn];
|
||||
$.each(r.message, function(k, v) {
|
||||
d[k] = v;
|
||||
});
|
||||
refresh_field('items');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// List Stock items
|
||||
cur_frm.set_query('item_code', 'items', function() {
|
||||
return {
|
||||
filters: {
|
||||
is_stock_item:1
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frappe.tour['Clinical Procedure Template'] = [
|
||||
{
|
||||
fieldname: 'template',
|
||||
title: __('Template Name'),
|
||||
description: __('Enter a name for the Clinical Procedure Template')
|
||||
},
|
||||
{
|
||||
fieldname: 'item_code',
|
||||
title: __('Item Code'),
|
||||
description: __('Set the Item Code which will be used for billing the Clinical Procedure.')
|
||||
},
|
||||
{
|
||||
fieldname: 'item_group',
|
||||
title: __('Item Group'),
|
||||
description: __('Select an Item Group for the Clinical Procedure Item.')
|
||||
},
|
||||
{
|
||||
fieldname: 'is_billable',
|
||||
title: __('Clinical Procedure Rate'),
|
||||
description: __('Check this if the Clinical Procedure is billable and also set the rate.')
|
||||
},
|
||||
{
|
||||
fieldname: 'consume_stock',
|
||||
title: __('Allow Stock Consumption'),
|
||||
description: __('Check this if the Clinical Procedure utilises consumables. Click ') + "<a href='https://docs.erpnext.com/docs/user/manual/en/healthcare/clinical_procedure_template#22-manage-procedure-consumables' target='_blank'>here</a>" + __(' to know more')
|
||||
|
||||
},
|
||||
{
|
||||
fieldname: 'medical_department',
|
||||
title: __('Medical Department'),
|
||||
description: __('You can also set the Medical Department for the template. After saving the document, an Item will automatically be created for billing this Clinical Procedure. You can then use this template while creating Clinical Procedures for Patients. Templates save you from filling up redundant data every single time. You can also create templates for other operations like Lab Tests, Therapy Sessions, etc.')
|
||||
}
|
||||
];
|
@ -1,257 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:template",
|
||||
"beta": 1,
|
||||
"creation": "2017-10-05 14:59:55.438359",
|
||||
"description": "Procedure Template",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"template",
|
||||
"item",
|
||||
"item_code",
|
||||
"item_group",
|
||||
"description",
|
||||
"column_break_5",
|
||||
"disabled",
|
||||
"is_billable",
|
||||
"rate",
|
||||
"medical_department",
|
||||
"medical_coding_section",
|
||||
"medical_code_standard",
|
||||
"medical_code",
|
||||
"consumables",
|
||||
"consume_stock",
|
||||
"items",
|
||||
"sample_collection",
|
||||
"sample",
|
||||
"sample_uom",
|
||||
"sample_qty",
|
||||
"column_break_21",
|
||||
"sample_details",
|
||||
"change_in_item"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "template",
|
||||
"fieldtype": "Data",
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Template Name",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Data",
|
||||
"label": "Item Code",
|
||||
"read_only_depends_on": "eval: !doc.__islocal ",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "item_group",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Item Group",
|
||||
"options": "Item Group",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "medical_department",
|
||||
"fieldtype": "Link",
|
||||
"label": "Medical Department",
|
||||
"options": "Medical Department"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_billable",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Billable"
|
||||
},
|
||||
{
|
||||
"depends_on": "is_billable",
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Rate",
|
||||
"mandatory_depends_on": "is_billable"
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"ignore_xss_filter": 1,
|
||||
"label": "Description",
|
||||
"no_copy": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "consume_stock",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow Stock Consumption",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "consumables",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Consumables"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.consume_stock == 1",
|
||||
"fieldname": "items",
|
||||
"fieldtype": "Table",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Items",
|
||||
"options": "Clinical Procedure Item"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "sample_collection",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Sample Collection"
|
||||
},
|
||||
{
|
||||
"fieldname": "sample",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Sample",
|
||||
"options": "Lab Test Sample"
|
||||
},
|
||||
{
|
||||
"fetch_from": "sample.sample_uom",
|
||||
"fieldname": "sample_uom",
|
||||
"fieldtype": "Data",
|
||||
"label": "Sample UOM",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "sample_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Quantity"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_21",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "sample_details",
|
||||
"fieldtype": "Small Text",
|
||||
"ignore_xss_filter": 1,
|
||||
"label": "Collection Details"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "change_in_item",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Change In Item",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disabled"
|
||||
},
|
||||
{
|
||||
"fieldname": "item",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Item",
|
||||
"no_copy": 1,
|
||||
"options": "Item",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "medical_coding_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Medical Coding"
|
||||
},
|
||||
{
|
||||
"fieldname": "medical_code_standard",
|
||||
"fieldtype": "Link",
|
||||
"label": "Medical Code Standard",
|
||||
"options": "Medical Code Standard"
|
||||
},
|
||||
{
|
||||
"depends_on": "medical_code_standard",
|
||||
"fieldname": "medical_code",
|
||||
"fieldtype": "Link",
|
||||
"label": "Medical Code",
|
||||
"options": "Medical Code"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-06-29 14:12:27.158130",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Clinical Procedure 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
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Nursing User",
|
||||
"share": 1
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Physician",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"restrict_to_domain": "Healthcare",
|
||||
"search_fields": "template",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "template",
|
||||
"track_changes": 1,
|
||||
"track_seen": 1
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, earthians and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.rename_doc import rename_doc
|
||||
|
||||
|
||||
class ClinicalProcedureTemplate(Document):
|
||||
def validate(self):
|
||||
self.enable_disable_item()
|
||||
|
||||
def after_insert(self):
|
||||
create_item_from_template(self)
|
||||
|
||||
def on_update(self):
|
||||
if self.change_in_item:
|
||||
self.update_item_and_item_price()
|
||||
|
||||
def enable_disable_item(self):
|
||||
if self.is_billable:
|
||||
if self.disabled:
|
||||
frappe.db.set_value('Item', self.item, 'disabled', 1)
|
||||
else:
|
||||
frappe.db.set_value('Item', self.item, 'disabled', 0)
|
||||
|
||||
def update_item_and_item_price(self):
|
||||
if self.is_billable and self.item:
|
||||
item_doc = frappe.get_doc('Item', {'item_code': self.item})
|
||||
item_doc.item_name = self.template
|
||||
item_doc.item_group = self.item_group
|
||||
item_doc.description = self.description
|
||||
item_doc.disabled = 0
|
||||
item_doc.save(ignore_permissions=True)
|
||||
|
||||
if self.rate:
|
||||
item_price = frappe.get_doc('Item Price', {'item_code': self.item})
|
||||
item_price.item_name = self.template
|
||||
item_price.price_list_rate = self.rate
|
||||
item_price.save()
|
||||
|
||||
elif not self.is_billable and self.item:
|
||||
frappe.db.set_value('Item', self.item, 'disabled', 1)
|
||||
|
||||
self.db_set('change_in_item', 0)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_item_details(args=None):
|
||||
if not isinstance(args, dict):
|
||||
args = json.loads(args)
|
||||
|
||||
item = frappe.db.get_all('Item',
|
||||
filters={
|
||||
'disabled': 0,
|
||||
'name': args.get('item_code')
|
||||
},
|
||||
fields=['stock_uom', 'item_name']
|
||||
)
|
||||
|
||||
if not item:
|
||||
frappe.throw(_('Item {0} is not active').format(args.get('item_code')))
|
||||
|
||||
item = item[0]
|
||||
ret = {
|
||||
'uom': item.stock_uom,
|
||||
'stock_uom': item.stock_uom,
|
||||
'item_name': item.item_name,
|
||||
'qty': 1,
|
||||
'transfer_qty': 0,
|
||||
'conversion_factor': 1
|
||||
}
|
||||
return ret
|
||||
|
||||
def create_item_from_template(doc):
|
||||
disabled = doc.disabled
|
||||
if doc.is_billable and not doc.disabled:
|
||||
disabled = 0
|
||||
|
||||
uom = frappe.db.exists('UOM', 'Unit') or frappe.db.get_single_value('Stock Settings', 'stock_uom')
|
||||
item = frappe.get_doc({
|
||||
'doctype': 'Item',
|
||||
'item_code': doc.template,
|
||||
'item_name':doc.template,
|
||||
'item_group': doc.item_group,
|
||||
'description':doc.description,
|
||||
'is_sales_item': 1,
|
||||
'is_service_item': 1,
|
||||
'is_purchase_item': 0,
|
||||
'is_stock_item': 0,
|
||||
'show_in_website': 0,
|
||||
'is_pro_applicable': 0,
|
||||
'disabled': disabled,
|
||||
'stock_uom': uom
|
||||
}).insert(ignore_permissions=True, ignore_mandatory=True)
|
||||
|
||||
make_item_price(item.name, doc.rate)
|
||||
doc.db_set('item', item.name)
|
||||
|
||||
def make_item_price(item, item_price):
|
||||
price_list_name = frappe.db.get_value('Price List', {'selling': 1})
|
||||
frappe.get_doc({
|
||||
'doctype': 'Item Price',
|
||||
'price_list': price_list_name,
|
||||
'item_code': item,
|
||||
'price_list_rate': item_price
|
||||
}).insert(ignore_permissions=True, ignore_mandatory=True)
|
||||
|
||||
@frappe.whitelist()
|
||||
def change_item_code_from_template(item_code, doc):
|
||||
doc = frappe._dict(json.loads(doc))
|
||||
|
||||
if frappe.db.exists('Item', {'item_code': item_code}):
|
||||
frappe.throw(_('Item with Item Code {0} already exists').format(item_code))
|
||||
else:
|
||||
rename_doc('Item', doc.item_code, item_code, ignore_permissions=True)
|
||||
frappe.db.set_value('Clinical Procedure Template', doc.name, 'item_code', item_code)
|
||||
return
|
@ -1,15 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from frappe import _
|
||||
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'procedure_template',
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Consultations'),
|
||||
'items': ['Clinical Procedure']
|
||||
}
|
||||
]
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, earthians and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestClinicalProcedureTemplate(unittest.TestCase):
|
||||
pass
|
@ -1,56 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_copy": 1,
|
||||
"beta": 1,
|
||||
"creation": "2017-06-22 13:09:23.159579",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"medical_code",
|
||||
"code",
|
||||
"description"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "medical_code",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Medical Code",
|
||||
"options": "Medical Code",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "medical_code.code",
|
||||
"fieldname": "code",
|
||||
"fieldtype": "Data",
|
||||
"ignore_xss_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Code",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "medical_code.description",
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"ignore_xss_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Description",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-02-26 13:17:49.016293",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Codification Table",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"restrict_to_domain": "Healthcare",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class CodificationTable(Document):
|
||||
pass
|
@ -1,5 +0,0 @@
|
||||
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Complaint', {
|
||||
});
|
@ -1,116 +0,0 @@
|
||||
{
|
||||
"allow_copy": 1,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:complaints",
|
||||
"beta": 1,
|
||||
"creation": "2017-02-15 12:25:28.045267",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "complaints",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 1,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Complaints",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-10-05 11:18:42.017864",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Complaint",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Healthcare Administrator",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Physician",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"restrict_to_domain": "Healthcare",
|
||||
"search_fields": "complaints",
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "complaints",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class Complaint(Document):
|
||||
pass
|
@ -1,10 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestComplaint(unittest.TestCase):
|
||||
pass
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user