Merge branch 'develop' of https://github.com/frappe/erpnext into inv-disc-improvements
This commit is contained in:
commit
d83e39d0ac
@ -121,7 +121,7 @@ frappe.treeview_settings["Account"] = {
|
|||||||
},
|
},
|
||||||
onrender: function(node) {
|
onrender: function(node) {
|
||||||
if(frappe.boot.user.can_read.indexOf("GL Entry") !== -1){
|
if(frappe.boot.user.can_read.indexOf("GL Entry") !== -1){
|
||||||
var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr";
|
var dr_or_cr = in_list(["Liability", "Income", "Equity"], node.data.root_type) ? "Cr" : "Dr";
|
||||||
if (node.data && node.data.balance!==undefined) {
|
if (node.data && node.data.balance!==undefined) {
|
||||||
$('<span class="balance-area pull-right text-muted small">'
|
$('<span class="balance-area pull-right text-muted small">'
|
||||||
+ (node.data.balance_in_account_currency ?
|
+ (node.data.balance_in_account_currency ?
|
||||||
|
|||||||
@ -9,6 +9,26 @@ frappe.ui.form.on('Accounting Dimension', {
|
|||||||
frappe.set_route("List", frm.doc.document_type);
|
frappe.set_route("List", frm.doc.document_type);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let button = frm.doc.disabled ? "Enable" : "Disable";
|
||||||
|
|
||||||
|
frm.add_custom_button(__(button), function() {
|
||||||
|
|
||||||
|
frm.set_value('disabled', 1 - frm.doc.disabled);
|
||||||
|
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension",
|
||||||
|
args: {
|
||||||
|
doc: frm.doc
|
||||||
|
},
|
||||||
|
freeze: true,
|
||||||
|
callback: function(r) {
|
||||||
|
let message = frm.doc.disabled ? "Dimension Disabled" : "Dimension Enabled";
|
||||||
|
frm.save();
|
||||||
|
frappe.show_alert({message:__(message), indicator:'green'});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
document_type: function(frm) {
|
document_type: function(frm) {
|
||||||
@ -21,13 +41,4 @@ frappe.ui.form.on('Accounting Dimension', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
disabled: function(frm) {
|
|
||||||
frappe.call({
|
|
||||||
method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension",
|
|
||||||
args: {
|
|
||||||
doc: frm.doc
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -38,7 +38,8 @@
|
|||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "disabled",
|
"fieldname": "disabled",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Disable"
|
"label": "Disable",
|
||||||
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@ -53,7 +54,7 @@
|
|||||||
"label": "Mandatory For Profit and Loss Account"
|
"label": "Mandatory For Profit and Loss Account"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-05-27 18:18:17.792726",
|
"modified": "2019-07-07 18:56:19.517450",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounting Dimension",
|
"name": "Accounting Dimension",
|
||||||
|
|||||||
@ -121,11 +121,11 @@ def delete_accounting_dimension(doc):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def disable_dimension(doc):
|
def disable_dimension(doc):
|
||||||
if frappe.flags.in_test:
|
if frappe.flags.in_test:
|
||||||
frappe.enqueue(start_dimension_disabling, doc=doc)
|
toggle_disabling(doc=doc)
|
||||||
else:
|
else:
|
||||||
start_dimension_disabling(doc=doc)
|
frappe.enqueue(toggle_disabling, doc=doc)
|
||||||
|
|
||||||
def start_dimension_disabling(doc):
|
def toggle_disabling(doc):
|
||||||
doc = json.loads(doc)
|
doc = json.loads(doc)
|
||||||
|
|
||||||
if doc.get('disabled'):
|
if doc.get('disabled'):
|
||||||
|
|||||||
@ -103,7 +103,7 @@ class BankReconciliation(Document):
|
|||||||
for d in self.get('payment_entries'):
|
for d in self.get('payment_entries'):
|
||||||
if d.clearance_date:
|
if d.clearance_date:
|
||||||
if not d.payment_document:
|
if not d.payment_document:
|
||||||
frappe.throw(_("Row #{0}: Payment document is required to complete the trasaction"))
|
frappe.throw(_("Row #{0}: Payment document is required to complete the transaction"))
|
||||||
|
|
||||||
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
|
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
|
||||||
frappe.throw(_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}")
|
frappe.throw(_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}")
|
||||||
@ -113,10 +113,8 @@ class BankReconciliation(Document):
|
|||||||
if not d.clearance_date:
|
if not d.clearance_date:
|
||||||
d.clearance_date = None
|
d.clearance_date = None
|
||||||
|
|
||||||
frappe.db.set_value(d.payment_document, d.payment_entry, "clearance_date", d.clearance_date)
|
payment_entry = frappe.get_doc(d.payment_document, d.payment_entry)
|
||||||
frappe.db.sql("""update `tab{0}` set clearance_date = %s, modified = %s
|
payment_entry.db_set('clearance_date', d.clearance_date)
|
||||||
where name=%s""".format(d.payment_document),
|
|
||||||
(d.clearance_date, nowdate(), d.payment_entry))
|
|
||||||
|
|
||||||
clearance_date_updated = True
|
clearance_date_updated = True
|
||||||
|
|
||||||
|
|||||||
@ -78,7 +78,6 @@ var validate_csv_data = function(frm) {
|
|||||||
|
|
||||||
var create_import_button = function(frm) {
|
var create_import_button = function(frm) {
|
||||||
frm.page.set_primary_action(__("Start Import"), function () {
|
frm.page.set_primary_action(__("Start Import"), function () {
|
||||||
setup_progress_bar(frm);
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
||||||
args: {
|
args: {
|
||||||
@ -86,11 +85,11 @@ var create_import_button = function(frm) {
|
|||||||
company: frm.doc.company
|
company: frm.doc.company
|
||||||
},
|
},
|
||||||
freeze: true,
|
freeze: true,
|
||||||
|
freeze_message: __("Creating Accounts..."),
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if(!r.exc) {
|
if(!r.exc) {
|
||||||
clearInterval(frm.page["interval"]);
|
clearInterval(frm.page["interval"]);
|
||||||
frm.page.set_indicator(__('Import Successfull'), 'blue');
|
frm.page.set_indicator(__('Import Successfull'), 'blue');
|
||||||
frappe.hide_progress();
|
|
||||||
create_reset_button(frm);
|
create_reset_button(frm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,13 +125,3 @@ var generate_tree_preview = function(frm) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var setup_progress_bar = function(frm) {
|
|
||||||
frm.page["seconds_elapsed"] = 0;
|
|
||||||
frm.page["execution_time"] = (frm.page["total_accounts"] > 100) ? 100 : frm.page["total_accounts"];
|
|
||||||
|
|
||||||
frm.page["interval"] = setInterval(function() {
|
|
||||||
frm.page["seconds_elapsed"] += 1;
|
|
||||||
frappe.show_progress(__('Creating Accounts'), frm.page["seconds_elapsed"], frm.page["execution_time"]);
|
|
||||||
}, 250);
|
|
||||||
};
|
|
||||||
@ -33,6 +33,9 @@ def import_coa(file_name, company):
|
|||||||
|
|
||||||
def generate_data_from_csv(file_name, as_dict=False):
|
def generate_data_from_csv(file_name, as_dict=False):
|
||||||
''' read csv file and return the generated nested tree '''
|
''' read csv file and return the generated nested tree '''
|
||||||
|
if not file_name.endswith('.csv'):
|
||||||
|
frappe.throw("Only CSV files can be used to for importing data. Please check the file format you are trying to upload")
|
||||||
|
|
||||||
file_doc = frappe.get_doc('File', {"file_url": file_name})
|
file_doc = frappe.get_doc('File', {"file_url": file_name})
|
||||||
file_path = file_doc.get_full_path()
|
file_path = file_doc.get_full_path()
|
||||||
|
|
||||||
@ -96,15 +99,27 @@ def build_forest(data):
|
|||||||
return [child] + return_parent(data, parent_account)
|
return [child] + return_parent(data, parent_account)
|
||||||
|
|
||||||
charts_map, paths = {}, []
|
charts_map, paths = {}, []
|
||||||
|
|
||||||
|
line_no = 3
|
||||||
|
error_messages = []
|
||||||
|
|
||||||
for i in data:
|
for i in data:
|
||||||
account_name, _, account_number, is_group, account_type, root_type = i
|
account_name, _, account_number, is_group, account_type, root_type = i
|
||||||
|
|
||||||
|
if not account_name:
|
||||||
|
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
|
||||||
|
|
||||||
charts_map[account_name] = {}
|
charts_map[account_name] = {}
|
||||||
if is_group: charts_map[account_name]["is_group"] = is_group
|
if is_group == 1: charts_map[account_name]["is_group"] = is_group
|
||||||
if account_type: charts_map[account_name]["account_type"] = account_type
|
if account_type: charts_map[account_name]["account_type"] = account_type
|
||||||
if root_type: charts_map[account_name]["root_type"] = root_type
|
if root_type: charts_map[account_name]["root_type"] = root_type
|
||||||
if account_number: charts_map[account_name]["account_number"] = account_number
|
if account_number: charts_map[account_name]["account_number"] = account_number
|
||||||
path = return_parent(data, account_name)[::-1]
|
path = return_parent(data, account_name)[::-1]
|
||||||
paths.append(path) # List of path is created
|
paths.append(path) # List of path is created
|
||||||
|
line_no += 1
|
||||||
|
|
||||||
|
if error_messages:
|
||||||
|
frappe.throw("<br>".join(error_messages))
|
||||||
|
|
||||||
out = {}
|
out = {}
|
||||||
for path in paths:
|
for path in paths:
|
||||||
@ -150,22 +165,27 @@ def validate_root(accounts):
|
|||||||
if len(roots) < 4:
|
if len(roots) < 4:
|
||||||
return _("Number of root accounts cannot be less than 4")
|
return _("Number of root accounts cannot be less than 4")
|
||||||
|
|
||||||
|
error_messages = []
|
||||||
|
|
||||||
for account in roots:
|
for account in roots:
|
||||||
if not account.get("root_type"):
|
if not account.get("root_type") and account.get("account_name"):
|
||||||
return _("Please enter Root Type for - {0}").format(account.get("account_name"))
|
error_messages.append("Please enter Root Type for account- {0}".format(account.get("account_name")))
|
||||||
elif account.get("root_type") not in ("Asset", "Liability", "Expense", "Income", "Equity"):
|
elif account.get("root_type") not in ("Asset", "Liability", "Expense", "Income", "Equity") and account.get("account_name"):
|
||||||
return _('Root Type for "{0}" must be one of the Asset, Liability, Income, Expense and Equity').format(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")))
|
||||||
|
|
||||||
|
if error_messages:
|
||||||
|
return "<br>".join(error_messages)
|
||||||
|
|
||||||
def validate_account_types(accounts):
|
def validate_account_types(accounts):
|
||||||
account_types_for_ledger = ["Cost of Goods Sold", "Depreciation", "Fixed Asset", "Payable", "Receivable", "Stock Adjustment"]
|
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']]
|
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))
|
missing = list(set(account_types_for_ledger) - set(account_types))
|
||||||
if missing:
|
if missing:
|
||||||
return _("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing))
|
return _("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing))
|
||||||
|
|
||||||
account_types_for_group = ["Bank", "Cash", "Stock"]
|
account_types_for_group = ["Bank", "Cash", "Stock"]
|
||||||
account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group']]
|
account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group'] not in ('', 1)]
|
||||||
|
|
||||||
missing = list(set(account_types_for_group) - set(account_groups))
|
missing = list(set(account_types_for_group) - set(account_groups))
|
||||||
if missing:
|
if missing:
|
||||||
|
|||||||
@ -21,10 +21,30 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
|
|||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
if(frm.doc.docstatus==1) {
|
if(frm.doc.docstatus==1) {
|
||||||
|
frappe.db.get_value("Journal Entry Account", {
|
||||||
|
'reference_type': 'Exchange Rate Revaluation',
|
||||||
|
'reference_name': frm.doc.name,
|
||||||
|
'docstatus': 1
|
||||||
|
}, "sum(debit) as sum", (r) =>{
|
||||||
|
let total_amt = 0;
|
||||||
|
frm.doc.accounts.forEach(d=> {
|
||||||
|
total_amt = total_amt + d['new_balance_in_base_currency'];
|
||||||
|
});
|
||||||
|
if(total_amt === r.sum) {
|
||||||
|
frm.add_custom_button(__("Journal Entry"), function(){
|
||||||
|
frappe.route_options = {
|
||||||
|
'reference_type': 'Exchange Rate Revaluation',
|
||||||
|
'reference_name': frm.doc.name
|
||||||
|
};
|
||||||
|
frappe.set_route("List", "Journal Entry");
|
||||||
|
}, __("View"));
|
||||||
|
} else {
|
||||||
frm.add_custom_button(__('Create Journal Entry'), function() {
|
frm.add_custom_button(__('Create Journal Entry'), function() {
|
||||||
return frm.events.make_jv(frm);
|
return frm.events.make_jv(frm);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}, 'Journal Entry');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
get_entries: function(frm) {
|
get_entries: function(frm) {
|
||||||
|
|||||||
@ -848,6 +848,39 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
|
"fieldname": "due_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Due Date",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"has_web_view": 0,
|
||||||
@ -861,7 +894,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2019-01-07 07:05:00.366399",
|
"modified": "2019-05-01 07:05:00.366399",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "GL Entry",
|
"name": "GL Entry",
|
||||||
|
|||||||
@ -502,6 +502,7 @@ class JournalEntry(AccountsController):
|
|||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
"account": d.account,
|
"account": d.account,
|
||||||
"party_type": d.party_type,
|
"party_type": d.party_type,
|
||||||
|
"due_date": self.due_date,
|
||||||
"party": d.party,
|
"party": d.party,
|
||||||
"against": d.against_account,
|
"against": d.against_account,
|
||||||
"debit": flt(d.debit, d.precision("debit")),
|
"debit": flt(d.debit, d.precision("debit")),
|
||||||
|
|||||||
@ -302,7 +302,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
},
|
},
|
||||||
() => frm.set_value("party_balance", r.message.party_balance),
|
() => frm.set_value("party_balance", r.message.party_balance),
|
||||||
() => frm.set_value("party_name", r.message.party_name),
|
() => frm.set_value("party_name", r.message.party_name),
|
||||||
() => frm.events.get_outstanding_documents(frm),
|
() => frm.clear_table("references"),
|
||||||
() => frm.events.hide_unhide_fields(frm),
|
() => frm.events.hide_unhide_fields(frm),
|
||||||
() => frm.events.set_dynamic_labels(frm),
|
() => frm.events.set_dynamic_labels(frm),
|
||||||
() => {
|
() => {
|
||||||
@ -323,9 +323,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
frm.events.set_account_currency_and_balance(frm, frm.doc.paid_from,
|
frm.events.set_account_currency_and_balance(frm, frm.doc.paid_from,
|
||||||
"paid_from_account_currency", "paid_from_account_balance", function(frm) {
|
"paid_from_account_currency", "paid_from_account_balance", function(frm) {
|
||||||
if (frm.doc.payment_type == "Receive") {
|
if (frm.doc.payment_type == "Pay") {
|
||||||
frm.events.get_outstanding_documents(frm);
|
|
||||||
} else if (frm.doc.payment_type == "Pay") {
|
|
||||||
frm.events.paid_amount(frm);
|
frm.events.paid_amount(frm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,9 +335,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
frm.events.set_account_currency_and_balance(frm, frm.doc.paid_to,
|
frm.events.set_account_currency_and_balance(frm, frm.doc.paid_to,
|
||||||
"paid_to_account_currency", "paid_to_account_balance", function(frm) {
|
"paid_to_account_currency", "paid_to_account_balance", function(frm) {
|
||||||
if(frm.doc.payment_type == "Pay") {
|
if (frm.doc.payment_type == "Receive") {
|
||||||
frm.events.get_outstanding_documents(frm);
|
|
||||||
} else if (frm.doc.payment_type == "Receive") {
|
|
||||||
if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
|
if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
|
||||||
if(frm.doc.source_exchange_rate) {
|
if(frm.doc.source_exchange_rate) {
|
||||||
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
|
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
|
||||||
@ -533,18 +529,68 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.events.set_unallocated_amount(frm);
|
frm.events.set_unallocated_amount(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
get_outstanding_documents: function(frm) {
|
get_outstanding_invoice: function(frm) {
|
||||||
|
const today = frappe.datetime.get_today();
|
||||||
|
const fields = [
|
||||||
|
{fieldtype:"Section Break", label: __("Posting Date")},
|
||||||
|
{fieldtype:"Date", label: __("From Date"),
|
||||||
|
fieldname:"from_posting_date", default:frappe.datetime.add_days(today, -30)},
|
||||||
|
{fieldtype:"Column Break"},
|
||||||
|
{fieldtype:"Date", label: __("To Date"), fieldname:"to_posting_date", default:today},
|
||||||
|
{fieldtype:"Section Break", label: __("Due Date")},
|
||||||
|
{fieldtype:"Date", label: __("From Date"), fieldname:"from_due_date"},
|
||||||
|
{fieldtype:"Column Break"},
|
||||||
|
{fieldtype:"Date", label: __("To Date"), fieldname:"to_due_date"},
|
||||||
|
{fieldtype:"Section Break", label: __("Outstanding Amount")},
|
||||||
|
{fieldtype:"Float", label: __("Greater Than Amount"),
|
||||||
|
fieldname:"outstanding_amt_greater_than", default: 0},
|
||||||
|
{fieldtype:"Column Break"},
|
||||||
|
{fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"},
|
||||||
|
{fieldtype:"Section Break"},
|
||||||
|
{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
|
||||||
|
];
|
||||||
|
|
||||||
|
frappe.prompt(fields, function(filters){
|
||||||
|
frappe.flags.allocate_payment_amount = true;
|
||||||
|
frm.events.validate_filters_data(frm, filters);
|
||||||
|
frm.events.get_outstanding_documents(frm, filters);
|
||||||
|
}, __("Filters"), __("Get Outstanding Invoices"));
|
||||||
|
},
|
||||||
|
|
||||||
|
validate_filters_data: function(frm, filters) {
|
||||||
|
const fields = {
|
||||||
|
'Posting Date': ['from_posting_date', 'to_posting_date'],
|
||||||
|
'Due Date': ['from_posting_date', 'to_posting_date'],
|
||||||
|
'Advance Amount': ['from_posting_date', 'to_posting_date'],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let key in fields) {
|
||||||
|
let from_field = fields[key][0];
|
||||||
|
let to_field = fields[key][1];
|
||||||
|
|
||||||
|
if (filters[from_field] && !filters[to_field]) {
|
||||||
|
frappe.throw(__("Error: {0} is mandatory field",
|
||||||
|
[to_field.replace(/_/g, " ")]
|
||||||
|
));
|
||||||
|
} else if (filters[from_field] && filters[from_field] > filters[to_field]) {
|
||||||
|
frappe.throw(__("{0}: {1} must be less than {2}",
|
||||||
|
[key, from_field.replace(/_/g, " "), to_field.replace(/_/g, " ")]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get_outstanding_documents: function(frm, filters) {
|
||||||
frm.clear_table("references");
|
frm.clear_table("references");
|
||||||
|
|
||||||
if(!frm.doc.party) return;
|
if(!frm.doc.party) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
frm.events.check_mandatory_to_fetch(frm);
|
frm.events.check_mandatory_to_fetch(frm);
|
||||||
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
||||||
|
|
||||||
return frappe.call({
|
var args = {
|
||||||
method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_outstanding_reference_documents',
|
|
||||||
args: {
|
|
||||||
args: {
|
|
||||||
"posting_date": frm.doc.posting_date,
|
"posting_date": frm.doc.posting_date,
|
||||||
"company": frm.doc.company,
|
"company": frm.doc.company,
|
||||||
"party_type": frm.doc.party_type,
|
"party_type": frm.doc.party_type,
|
||||||
@ -553,6 +599,17 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
"party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to,
|
"party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to,
|
||||||
"cost_center": frm.doc.cost_center
|
"cost_center": frm.doc.cost_center
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let key in filters) {
|
||||||
|
args[key] = filters[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
frappe.flags.allocate_payment_amount = filters['allocate_payment_amount'];
|
||||||
|
|
||||||
|
return frappe.call({
|
||||||
|
method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_outstanding_reference_documents',
|
||||||
|
args: {
|
||||||
|
args:args
|
||||||
},
|
},
|
||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
if(r.message) {
|
if(r.message) {
|
||||||
@ -608,25 +665,11 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
frm.events.allocate_party_amount_against_ref_docs(frm,
|
frm.events.allocate_party_amount_against_ref_docs(frm,
|
||||||
(frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount));
|
(frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount));
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
allocate_payment_amount: function(frm) {
|
|
||||||
if(frm.doc.payment_type == 'Internal Transfer'){
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if(frm.doc.references.length == 0){
|
|
||||||
frm.events.get_outstanding_documents(frm);
|
|
||||||
}
|
|
||||||
if(frm.doc.payment_type == 'Internal Transfer') {
|
|
||||||
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount);
|
|
||||||
} else {
|
|
||||||
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
allocate_party_amount_against_ref_docs: function(frm, paid_amount) {
|
allocate_party_amount_against_ref_docs: function(frm, paid_amount) {
|
||||||
var total_positive_outstanding_including_order = 0;
|
var total_positive_outstanding_including_order = 0;
|
||||||
var total_negative_outstanding = 0;
|
var total_negative_outstanding = 0;
|
||||||
@ -677,7 +720,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
$.each(frm.doc.references || [], function(i, row) {
|
$.each(frm.doc.references || [], function(i, row) {
|
||||||
row.allocated_amount = 0 //If allocate payment amount checkbox is unchecked, set zero to allocate amount
|
row.allocated_amount = 0 //If allocate payment amount checkbox is unchecked, set zero to allocate amount
|
||||||
if(frm.doc.allocate_payment_amount){
|
if(frappe.flags.allocate_payment_amount){
|
||||||
if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
|
if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
|
||||||
if(row.outstanding_amount >= allocated_positive_outstanding) {
|
if(row.outstanding_amount >= allocated_positive_outstanding) {
|
||||||
row.allocated_amount = allocated_positive_outstanding;
|
row.allocated_amount = allocated_positive_outstanding;
|
||||||
@ -958,7 +1001,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
if(frm.doc.payment_type != "Internal") {
|
if(frm.doc.payment_type != "Internal") {
|
||||||
frm.events.get_outstanding_documents(frm);
|
frm.clear_table("references");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|||||||
@ -40,7 +40,7 @@
|
|||||||
"target_exchange_rate",
|
"target_exchange_rate",
|
||||||
"base_received_amount",
|
"base_received_amount",
|
||||||
"section_break_14",
|
"section_break_14",
|
||||||
"allocate_payment_amount",
|
"get_outstanding_invoice",
|
||||||
"references",
|
"references",
|
||||||
"section_break_34",
|
"section_break_34",
|
||||||
"total_allocated_amount",
|
"total_allocated_amount",
|
||||||
@ -325,19 +325,15 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
|
||||||
"collapsible_depends_on": "references",
|
|
||||||
"depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)",
|
"depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)",
|
||||||
"fieldname": "section_break_14",
|
"fieldname": "section_break_14",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Reference"
|
"label": "Reference"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "1",
|
"fieldname": "get_outstanding_invoice",
|
||||||
"depends_on": "eval:in_list(['Pay', 'Receive'], doc.payment_type)",
|
"fieldtype": "Button",
|
||||||
"fieldname": "allocate_payment_amount",
|
"label": "Get Outstanding Invoice"
|
||||||
"fieldtype": "Check",
|
|
||||||
"label": "Allocate Payment Amount"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "references",
|
"fieldname": "references",
|
||||||
@ -570,7 +566,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-05-25 22:02:40.575822",
|
"modified": "2019-05-27 15:53:21.108857",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry",
|
"name": "Payment Entry",
|
||||||
|
|||||||
@ -574,8 +574,8 @@ def get_outstanding_reference_documents(args):
|
|||||||
# Get negative outstanding sales /purchase invoices
|
# Get negative outstanding sales /purchase invoices
|
||||||
negative_outstanding_invoices = []
|
negative_outstanding_invoices = []
|
||||||
if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
|
if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
|
||||||
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"),
|
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"),
|
||||||
args.get("party"), args.get("party_account"), party_account_currency, company_currency)
|
args.get("party_account"), args.get("company"), party_account_currency, company_currency)
|
||||||
|
|
||||||
# Get positive outstanding sales /purchase invoices/ Fees
|
# Get positive outstanding sales /purchase invoices/ Fees
|
||||||
condition = ""
|
condition = ""
|
||||||
@ -587,8 +587,21 @@ def get_outstanding_reference_documents(args):
|
|||||||
if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account():
|
if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account():
|
||||||
condition += " and cost_center='%s'" % args.get("cost_center")
|
condition += " and cost_center='%s'" % args.get("cost_center")
|
||||||
|
|
||||||
|
date_fields_dict = {
|
||||||
|
'posting_date': ['from_posting_date', 'to_posting_date'],
|
||||||
|
'due_date': ['from_due_date', 'to_due_date']
|
||||||
|
}
|
||||||
|
|
||||||
|
for fieldname, date_fields in date_fields_dict.items():
|
||||||
|
if args.get(date_fields[0]) and args.get(date_fields[1]):
|
||||||
|
condition += " and {0} between '{1}' and '{2}'".format(fieldname,
|
||||||
|
args.get(date_fields[0]), args.get(date_fields[1]))
|
||||||
|
|
||||||
|
if args.get("company"):
|
||||||
|
condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
|
||||||
|
|
||||||
outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
|
outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
|
||||||
args.get("party_account"), condition=condition)
|
args.get("party_account"), filters=args, condition=condition, limit=100)
|
||||||
|
|
||||||
for d in outstanding_invoices:
|
for d in outstanding_invoices:
|
||||||
d["exchange_rate"] = 1
|
d["exchange_rate"] = 1
|
||||||
@ -606,12 +619,19 @@ def get_outstanding_reference_documents(args):
|
|||||||
orders_to_be_billed = []
|
orders_to_be_billed = []
|
||||||
if (args.get("party_type") != "Student"):
|
if (args.get("party_type") != "Student"):
|
||||||
orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"),
|
orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"),
|
||||||
args.get("party"), party_account_currency, company_currency)
|
args.get("party"), args.get("company"), party_account_currency, company_currency, filters=args)
|
||||||
|
|
||||||
return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
|
data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
frappe.msgprint(_("No outstanding invoices found for the {0} <b>{1}</b>.")
|
||||||
|
.format(args.get("party_type").lower(), args.get("party")))
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_orders_to_be_billed(posting_date, party_type, party, party_account_currency, company_currency, cost_center=None):
|
def get_orders_to_be_billed(posting_date, party_type, party,
|
||||||
|
company, party_account_currency, company_currency, cost_center=None, filters=None):
|
||||||
if party_type == "Customer":
|
if party_type == "Customer":
|
||||||
voucher_type = 'Sales Order'
|
voucher_type = 'Sales Order'
|
||||||
elif party_type == "Supplier":
|
elif party_type == "Supplier":
|
||||||
@ -641,6 +661,7 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
|||||||
where
|
where
|
||||||
{party_type} = %s
|
{party_type} = %s
|
||||||
and docstatus = 1
|
and docstatus = 1
|
||||||
|
and company = %s
|
||||||
and ifnull(status, "") != "Closed"
|
and ifnull(status, "") != "Closed"
|
||||||
and {ref_field} > advance_paid
|
and {ref_field} > advance_paid
|
||||||
and abs(100 - per_billed) > 0.01
|
and abs(100 - per_billed) > 0.01
|
||||||
@ -652,10 +673,14 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
|||||||
"voucher_type": voucher_type,
|
"voucher_type": voucher_type,
|
||||||
"party_type": scrub(party_type),
|
"party_type": scrub(party_type),
|
||||||
"condition": condition
|
"condition": condition
|
||||||
}), party, as_dict=True)
|
}), (party, company), as_dict=True)
|
||||||
|
|
||||||
order_list = []
|
order_list = []
|
||||||
for d in orders:
|
for d in orders:
|
||||||
|
if not (d.outstanding_amount >= filters.get("outstanding_amt_greater_than")
|
||||||
|
and d.outstanding_amount <= filters.get("outstanding_amt_less_than")):
|
||||||
|
continue
|
||||||
|
|
||||||
d["voucher_type"] = voucher_type
|
d["voucher_type"] = voucher_type
|
||||||
# This assumes that the exchange rate required is the one in the SO
|
# This assumes that the exchange rate required is the one in the SO
|
||||||
d["exchange_rate"] = get_exchange_rate(party_account_currency, company_currency, posting_date)
|
d["exchange_rate"] = get_exchange_rate(party_account_currency, company_currency, posting_date)
|
||||||
@ -663,7 +688,8 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
|||||||
|
|
||||||
return order_list
|
return order_list
|
||||||
|
|
||||||
def get_negative_outstanding_invoices(party_type, party, party_account, party_account_currency, company_currency, cost_center=None):
|
def get_negative_outstanding_invoices(party_type, party, party_account,
|
||||||
|
company, party_account_currency, company_currency, cost_center=None):
|
||||||
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
|
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
|
||||||
supplier_condition = ""
|
supplier_condition = ""
|
||||||
if voucher_type == "Purchase Invoice":
|
if voucher_type == "Purchase Invoice":
|
||||||
@ -684,7 +710,8 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac
|
|||||||
from
|
from
|
||||||
`tab{voucher_type}`
|
`tab{voucher_type}`
|
||||||
where
|
where
|
||||||
{party_type} = %s and {party_account} = %s and docstatus = 1 and outstanding_amount < 0
|
{party_type} = %s and {party_account} = %s and docstatus = 1 and
|
||||||
|
company = %s and outstanding_amount < 0
|
||||||
{supplier_condition}
|
{supplier_condition}
|
||||||
order by
|
order by
|
||||||
posting_date, name
|
posting_date, name
|
||||||
@ -696,7 +723,7 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac
|
|||||||
"party_type": scrub(party_type),
|
"party_type": scrub(party_type),
|
||||||
"party_account": "debit_to" if party_type == "Customer" else "credit_to",
|
"party_account": "debit_to" if party_type == "Customer" else "credit_to",
|
||||||
"cost_center": cost_center
|
"cost_center": cost_center
|
||||||
}), (party, party_account), as_dict=True)
|
}), (party, party_account, company), as_dict=True)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@ -924,7 +951,6 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
pe.paid_to_account_currency = party_account_currency if payment_type=="Pay" else bank.account_currency
|
pe.paid_to_account_currency = party_account_currency if payment_type=="Pay" else bank.account_currency
|
||||||
pe.paid_amount = paid_amount
|
pe.paid_amount = paid_amount
|
||||||
pe.received_amount = received_amount
|
pe.received_amount = received_amount
|
||||||
pe.allocate_payment_amount = 1
|
|
||||||
pe.letter_head = doc.get("letter_head")
|
pe.letter_head = doc.get("letter_head")
|
||||||
|
|
||||||
if pe.party_type in ["Customer", "Supplier"]:
|
if pe.party_type in ["Customer", "Supplier"]:
|
||||||
|
|||||||
@ -8,6 +8,10 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) {
|
|||||||
return { filters: { selling: 1 } };
|
return { filters: { selling: 1 } };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query("tc_name", function() {
|
||||||
|
return { filters: { selling: 1 } };
|
||||||
|
});
|
||||||
|
|
||||||
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||||
return erpnext.queries.warehouse(frm.doc);
|
return erpnext.queries.warehouse(frm.doc);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -337,6 +337,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
self.update_against_document_in_jv()
|
self.update_against_document_in_jv()
|
||||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||||
|
|
||||||
self.update_billing_status_in_pr()
|
self.update_billing_status_in_pr()
|
||||||
|
|
||||||
# Updating stock ledger should always be called after updating prevdoc status,
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
@ -416,6 +417,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"account": self.credit_to,
|
"account": self.credit_to,
|
||||||
"party_type": "Supplier",
|
"party_type": "Supplier",
|
||||||
"party": self.supplier,
|
"party": self.supplier,
|
||||||
|
"due_date": self.due_date,
|
||||||
"against": self.against_expense_account,
|
"against": self.against_expense_account,
|
||||||
"credit": grand_total_in_company_currency,
|
"credit": grand_total_in_company_currency,
|
||||||
"credit_in_account_currency": grand_total_in_company_currency \
|
"credit_in_account_currency": grand_total_in_company_currency \
|
||||||
@ -484,9 +486,13 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"credit": flt(item.rm_supp_cost)
|
"credit": flt(item.rm_supp_cost)
|
||||||
}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item))
|
}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item))
|
||||||
elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()):
|
elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()):
|
||||||
|
|
||||||
|
expense_account = (item.expense_account
|
||||||
|
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
"account": item.expense_account if not item.enable_deferred_expense else item.deferred_expense_account,
|
"account": expense_account,
|
||||||
"against": self.supplier,
|
"against": self.supplier,
|
||||||
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||||
"debit_in_account_currency": (flt(item.base_net_amount,
|
"debit_in_account_currency": (flt(item.base_net_amount,
|
||||||
@ -769,6 +775,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||||
|
|
||||||
self.update_billing_status_in_pr()
|
self.update_billing_status_in_pr()
|
||||||
|
|
||||||
# Updating stock ledger should always be called after updating prevdoc status,
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
|
|||||||
@ -191,9 +191,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
|||||||
method: "erpnext.selling.doctype.quotation.quotation.make_sales_invoice",
|
method: "erpnext.selling.doctype.quotation.quotation.make_sales_invoice",
|
||||||
source_doctype: "Quotation",
|
source_doctype: "Quotation",
|
||||||
target: me.frm,
|
target: me.frm,
|
||||||
setters: {
|
setters: [{
|
||||||
customer: me.frm.doc.customer || undefined,
|
fieldtype: 'Link',
|
||||||
},
|
label: __('Customer'),
|
||||||
|
options: 'Customer',
|
||||||
|
fieldname: 'party_name',
|
||||||
|
default: me.frm.doc.customer,
|
||||||
|
}],
|
||||||
get_query_filters: {
|
get_query_filters: {
|
||||||
docstatus: 1,
|
docstatus: 1,
|
||||||
status: ["!=", "Lost"],
|
status: ["!=", "Lost"],
|
||||||
|
|||||||
@ -734,6 +734,7 @@ class SalesInvoice(SellingController):
|
|||||||
"account": self.debit_to,
|
"account": self.debit_to,
|
||||||
"party_type": "Customer",
|
"party_type": "Customer",
|
||||||
"party": self.customer,
|
"party": self.customer,
|
||||||
|
"due_date": self.due_date,
|
||||||
"against": self.against_income_account,
|
"against": self.against_income_account,
|
||||||
"debit": grand_total_in_company_currency,
|
"debit": grand_total_in_company_currency,
|
||||||
"debit_in_account_currency": grand_total_in_company_currency \
|
"debit_in_account_currency": grand_total_in_company_currency \
|
||||||
@ -783,10 +784,13 @@ class SalesInvoice(SellingController):
|
|||||||
asset.db_set("disposal_date", self.posting_date)
|
asset.db_set("disposal_date", self.posting_date)
|
||||||
asset.set_status("Sold" if self.docstatus==1 else None)
|
asset.set_status("Sold" if self.docstatus==1 else None)
|
||||||
else:
|
else:
|
||||||
account_currency = get_account_currency(item.income_account)
|
income_account = (item.income_account
|
||||||
|
if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account)
|
||||||
|
|
||||||
|
account_currency = get_account_currency(income_account)
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
"account": item.income_account if not item.enable_deferred_revenue else item.deferred_revenue_account,
|
"account": income_account,
|
||||||
"against": self.customer,
|
"against": self.customer,
|
||||||
"credit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
"credit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||||
"credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount"))
|
"credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount"))
|
||||||
|
|||||||
@ -108,3 +108,14 @@ frappe.query_reports["Accounts Payable"] = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
dimensions.forEach((dimension) => {
|
||||||
|
frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{
|
||||||
|
"fieldname": dimension["fieldname"],
|
||||||
|
"label": __(dimension["label"]),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": dimension["document_type"]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -92,3 +92,14 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
dimensions.forEach((dimension) => {
|
||||||
|
frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{
|
||||||
|
"fieldname": dimension["fieldname"],
|
||||||
|
"label": __(dimension["label"]),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": dimension["document_type"]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -172,3 +172,14 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
dimensions.forEach((dimension) => {
|
||||||
|
frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{
|
||||||
|
"fieldname": dimension["fieldname"],
|
||||||
|
"label": __(dimension["label"]),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": dimension["document_type"]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
from frappe import _, scrub
|
from frappe import _, scrub
|
||||||
from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr
|
from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||||
|
|
||||||
class ReceivablePayableReport(object):
|
class ReceivablePayableReport(object):
|
||||||
def __init__(self, filters=None):
|
def __init__(self, filters=None):
|
||||||
@ -553,6 +554,14 @@ class ReceivablePayableReport(object):
|
|||||||
conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts)))
|
conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts)))
|
||||||
values += accounts
|
values += accounts
|
||||||
|
|
||||||
|
accounting_dimensions = get_accounting_dimensions()
|
||||||
|
|
||||||
|
if accounting_dimensions:
|
||||||
|
for dimension in accounting_dimensions:
|
||||||
|
if self.filters.get(dimension):
|
||||||
|
conditions.append("{0} = %s".format(dimension))
|
||||||
|
values.append(self.filters.get(dimension))
|
||||||
|
|
||||||
return " and ".join(conditions), values
|
return " and ".join(conditions), values
|
||||||
|
|
||||||
def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher):
|
def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher):
|
||||||
|
|||||||
@ -116,3 +116,14 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
dimensions.forEach((dimension) => {
|
||||||
|
frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{
|
||||||
|
"fieldname": dimension["fieldname"],
|
||||||
|
"label": __(dimension["label"]),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": dimension["document_type"]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -63,9 +63,7 @@ frappe.query_reports["Budget Variance Report"] = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
let dimension_filters = erpnext.get_dimension_filters();
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
|
||||||
dimension_filters.then((dimensions) => {
|
|
||||||
dimensions.forEach((dimension) => {
|
dimensions.forEach((dimension) => {
|
||||||
frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]);
|
frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -8,17 +8,19 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
// The last item in the array is the definition for Presentation Currency
|
// The last item in the array is the definition for Presentation Currency
|
||||||
// filter. It won't be used in cash flow for now so we pop it. Please take
|
// filter. It won't be used in cash flow for now so we pop it. Please take
|
||||||
// of this if you are working here.
|
// of this if you are working here.
|
||||||
frappe.query_reports["Cash Flow"]["filters"].pop();
|
|
||||||
|
|
||||||
frappe.query_reports["Cash Flow"]["filters"].push({
|
frappe.query_reports["Cash Flow"]["filters"].splice(5, 1);
|
||||||
|
|
||||||
|
frappe.query_reports["Cash Flow"]["filters"].push(
|
||||||
|
{
|
||||||
"fieldname": "accumulated_values",
|
"fieldname": "accumulated_values",
|
||||||
"label": __("Accumulated Values"),
|
"label": __("Accumulated Values"),
|
||||||
"fieldtype": "Check"
|
"fieldtype": "Check"
|
||||||
});
|
},
|
||||||
|
{
|
||||||
frappe.query_reports["Cash Flow"]["filters"].push({
|
|
||||||
"fieldname": "include_default_book_entries",
|
"fieldname": "include_default_book_entries",
|
||||||
"label": __("Include Default Book Entries"),
|
"label": __("Include Default Book Entries"),
|
||||||
"fieldtype": "Check"
|
"fieldtype": "Check"
|
||||||
});
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
@ -69,7 +69,9 @@ def execute(filters=None):
|
|||||||
add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency)
|
add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency)
|
||||||
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
|
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
|
||||||
|
|
||||||
return columns, data
|
chart = get_chart_data(columns, data)
|
||||||
|
|
||||||
|
return columns, data, None, chart
|
||||||
|
|
||||||
def get_cash_flow_accounts():
|
def get_cash_flow_accounts():
|
||||||
operation_accounts = {
|
operation_accounts = {
|
||||||
@ -172,3 +174,20 @@ def add_total_row_account(out, data, label, period_list, currency, consolidated
|
|||||||
|
|
||||||
out.append(total_row)
|
out.append(total_row)
|
||||||
out.append({})
|
out.append({})
|
||||||
|
|
||||||
|
def get_chart_data(columns, data):
|
||||||
|
labels = [d.get("label") for d in columns[2:]]
|
||||||
|
datasets = [{'name':account.get('account').replace("'", ""), 'values': [account.get('total')]} for account in data if account.get('parent_account') == None and account.get('currency')]
|
||||||
|
datasets = datasets[:-1]
|
||||||
|
|
||||||
|
chart = {
|
||||||
|
"data": {
|
||||||
|
'labels': labels,
|
||||||
|
'datasets': datasets
|
||||||
|
},
|
||||||
|
"type": "bar"
|
||||||
|
}
|
||||||
|
|
||||||
|
chart["fieldtype"] = "Currency"
|
||||||
|
|
||||||
|
return chart
|
||||||
|
|||||||
@ -159,9 +159,7 @@ frappe.query_reports["General Ledger"] = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
let dimension_filters = erpnext.get_dimension_filters();
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
|
||||||
dimension_filters.then((dimensions) => {
|
|
||||||
dimensions.forEach((dimension) => {
|
dimensions.forEach((dimension) => {
|
||||||
frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{
|
frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{
|
||||||
"fieldname": dimension["fieldname"],
|
"fieldname": dimension["fieldname"],
|
||||||
|
|||||||
@ -11,6 +11,7 @@ from erpnext.accounts.utils import get_account_currency
|
|||||||
from erpnext.accounts.report.financial_statements import get_cost_centers_with_children
|
from erpnext.accounts.report.financial_statements import get_cost_centers_with_children
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
if not filters:
|
if not filters:
|
||||||
@ -274,7 +275,7 @@ def group_by_field(group_by):
|
|||||||
return 'voucher_no'
|
return 'voucher_no'
|
||||||
|
|
||||||
def initialize_gle_map(gl_entries, filters):
|
def initialize_gle_map(gl_entries, filters):
|
||||||
gle_map = frappe._dict()
|
gle_map = OrderedDict()
|
||||||
group_by = group_by_field(filters.get('group_by'))
|
group_by = group_by_field(filters.get('group_by'))
|
||||||
|
|
||||||
for gle in gl_entries:
|
for gle in gl_entries:
|
||||||
|
|||||||
@ -93,4 +93,6 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss):
|
|||||||
else:
|
else:
|
||||||
chart["type"] = "line"
|
chart["type"] = "line"
|
||||||
|
|
||||||
|
chart["fieldtype"] = "Currency"
|
||||||
|
|
||||||
return chart
|
return chart
|
||||||
@ -16,7 +16,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
"fieldname": "based_on",
|
"fieldname": "based_on",
|
||||||
"label": __("Based On"),
|
"label": __("Based On"),
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"options": "Cost Center\nProject",
|
"options": ["Cost Center", "Project"],
|
||||||
"default": "Cost Center",
|
"default": "Cost Center",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
@ -104,5 +104,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
"parent_field": "parent_account",
|
"parent_field": "parent_account",
|
||||||
"initial_depth": 3
|
"initial_depth": 3
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
dimensions.forEach((dimension) => {
|
||||||
|
frappe.query_reports["Profitability Analysis"].filters[1].options.push(dimension["document_type"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -24,8 +24,17 @@ def get_accounts_data(based_on, company):
|
|||||||
if based_on == 'cost_center':
|
if based_on == 'cost_center':
|
||||||
return frappe.db.sql("""select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt
|
return frappe.db.sql("""select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt
|
||||||
from `tabCost Center` where company=%s order by name""", company, as_dict=True)
|
from `tabCost Center` where company=%s order by name""", company, as_dict=True)
|
||||||
else:
|
elif based_on == 'project':
|
||||||
return frappe.get_all('Project', fields = ["name"], filters = {'company': company}, order_by = 'name')
|
return frappe.get_all('Project', fields = ["name"], filters = {'company': company}, order_by = 'name')
|
||||||
|
else:
|
||||||
|
filters = {}
|
||||||
|
doctype = frappe.unscrub(based_on)
|
||||||
|
has_company = frappe.db.has_column(doctype, 'company')
|
||||||
|
|
||||||
|
if has_company:
|
||||||
|
filters.update({'company': company})
|
||||||
|
|
||||||
|
return frappe.get_all(doctype, fields = ["name"], filters = filters, order_by = 'name')
|
||||||
|
|
||||||
def get_data(accounts, filters, based_on):
|
def get_data(accounts, filters, based_on):
|
||||||
if not accounts:
|
if not accounts:
|
||||||
|
|||||||
@ -67,3 +67,14 @@ frappe.query_reports["Sales Register"] = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
dimensions.forEach((dimension) => {
|
||||||
|
frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{
|
||||||
|
"fieldname": dimension["fieldname"],
|
||||||
|
"label": __(dimension["label"]),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": dimension["document_type"]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
return _execute(filters)
|
return _execute(filters)
|
||||||
@ -163,6 +164,16 @@ def get_conditions(filters):
|
|||||||
where parent=`tabSales Invoice`.name
|
where parent=`tabSales Invoice`.name
|
||||||
and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)"""
|
and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)"""
|
||||||
|
|
||||||
|
accounting_dimensions = get_accounting_dimensions()
|
||||||
|
|
||||||
|
if accounting_dimensions:
|
||||||
|
for dimension in accounting_dimensions:
|
||||||
|
if filters.get(dimension):
|
||||||
|
conditions += """ and exists(select name from `tabSales Invoice Item`
|
||||||
|
where parent=`tabSales Invoice`.name
|
||||||
|
and ifnull(`tabSales Invoice Item`.{0}, '') = %({0})s)""".format(dimension)
|
||||||
|
|
||||||
|
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
def get_invoices(filters, additional_query_columns):
|
def get_invoices(filters, additional_query_columns):
|
||||||
|
|||||||
@ -96,9 +96,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let dimension_filters = erpnext.get_dimension_filters();
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
|
||||||
dimension_filters.then((dimensions) => {
|
|
||||||
dimensions.forEach((dimension) => {
|
dimensions.forEach((dimension) => {
|
||||||
frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{
|
frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{
|
||||||
"fieldname": dimension["fieldname"],
|
"fieldname": dimension["fieldname"],
|
||||||
|
|||||||
@ -628,7 +628,7 @@ def get_held_invoices(party_type, party):
|
|||||||
return held_invoices
|
return held_invoices
|
||||||
|
|
||||||
|
|
||||||
def get_outstanding_invoices(party_type, party, account, condition=None):
|
def get_outstanding_invoices(party_type, party, account, condition=None, filters=None):
|
||||||
outstanding_invoices = []
|
outstanding_invoices = []
|
||||||
precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2
|
precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2
|
||||||
|
|
||||||
@ -644,7 +644,8 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
|||||||
|
|
||||||
invoice_list = frappe.db.sql("""
|
invoice_list = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount
|
voucher_no, voucher_type, posting_date, due_date,
|
||||||
|
ifnull(sum({dr_or_cr}), 0) as invoice_amount
|
||||||
from
|
from
|
||||||
`tabGL Entry`
|
`tabGL Entry`
|
||||||
where
|
where
|
||||||
@ -677,7 +678,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
|||||||
""".format(payment_dr_or_cr=payment_dr_or_cr), {
|
""".format(payment_dr_or_cr=payment_dr_or_cr), {
|
||||||
"party_type": party_type,
|
"party_type": party_type,
|
||||||
"party": party,
|
"party": party,
|
||||||
"account": account,
|
"account": account
|
||||||
}, as_dict=True)
|
}, as_dict=True)
|
||||||
|
|
||||||
pe_map = frappe._dict()
|
pe_map = frappe._dict()
|
||||||
@ -688,10 +689,12 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
|||||||
payment_amount = pe_map.get((d.voucher_type, d.voucher_no), 0)
|
payment_amount = pe_map.get((d.voucher_type, d.voucher_no), 0)
|
||||||
outstanding_amount = flt(d.invoice_amount - payment_amount, precision)
|
outstanding_amount = flt(d.invoice_amount - payment_amount, precision)
|
||||||
if outstanding_amount > 0.5 / (10**precision):
|
if outstanding_amount > 0.5 / (10**precision):
|
||||||
if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices:
|
if (filters.get("outstanding_amt_greater_than") and
|
||||||
due_date = frappe.db.get_value(
|
not (outstanding_amount >= filters.get("outstanding_amt_greater_than") and
|
||||||
d.voucher_type, d.voucher_no, "posting_date" if party_type == "Employee" else "due_date")
|
outstanding_amount <= filters.get("outstanding_amt_less_than"))):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices:
|
||||||
outstanding_invoices.append(
|
outstanding_invoices.append(
|
||||||
frappe._dict({
|
frappe._dict({
|
||||||
'voucher_no': d.voucher_no,
|
'voucher_no': d.voucher_no,
|
||||||
@ -700,7 +703,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
|||||||
'invoice_amount': flt(d.invoice_amount),
|
'invoice_amount': flt(d.invoice_amount),
|
||||||
'payment_amount': payment_amount,
|
'payment_amount': payment_amount,
|
||||||
'outstanding_amount': outstanding_amount,
|
'outstanding_amount': outstanding_amount,
|
||||||
'due_date': due_date
|
'due_date': d.due_date
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -731,6 +734,7 @@ def get_children(doctype, parent, company, is_root=False):
|
|||||||
parent_fieldname = 'parent_' + doctype.lower().replace(' ', '_')
|
parent_fieldname = 'parent_' + doctype.lower().replace(' ', '_')
|
||||||
fields = [
|
fields = [
|
||||||
'name as value',
|
'name as value',
|
||||||
|
'root_type',
|
||||||
'is_group as expandable'
|
'is_group as expandable'
|
||||||
]
|
]
|
||||||
filters = [['docstatus', '<', 2]]
|
filters = [['docstatus', '<', 2]]
|
||||||
@ -738,7 +742,7 @@ def get_children(doctype, parent, company, is_root=False):
|
|||||||
filters.append(['ifnull(`{0}`,"")'.format(parent_fieldname), '=', '' if is_root else parent])
|
filters.append(['ifnull(`{0}`,"")'.format(parent_fieldname), '=', '' if is_root else parent])
|
||||||
|
|
||||||
if is_root:
|
if is_root:
|
||||||
fields += ['root_type', 'report_type', 'account_currency'] if doctype == 'Account' else []
|
fields += ['report_type', 'account_currency'] if doctype == 'Account' else []
|
||||||
filters.append(['company', '=', company])
|
filters.append(['company', '=', company])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -66,10 +66,11 @@
|
|||||||
"net_total",
|
"net_total",
|
||||||
"total_net_weight",
|
"total_net_weight",
|
||||||
"taxes_section",
|
"taxes_section",
|
||||||
"taxes_and_charges",
|
"tax_category",
|
||||||
"column_break_50",
|
"column_break_50",
|
||||||
"shipping_rule",
|
"shipping_rule",
|
||||||
"section_break_52",
|
"section_break_52",
|
||||||
|
"taxes_and_charges",
|
||||||
"taxes",
|
"taxes",
|
||||||
"sec_tax_breakup",
|
"sec_tax_breakup",
|
||||||
"other_charges_calculation",
|
"other_charges_calculation",
|
||||||
@ -569,7 +570,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "taxes_and_charges",
|
"fieldname": "taxes_and_charges",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Taxes and Charges",
|
"label": "Purchase Taxes and Charges Template",
|
||||||
"oldfieldname": "purchase_other_charges",
|
"oldfieldname": "purchase_other_charges",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Purchase Taxes and Charges Template",
|
"options": "Purchase Taxes and Charges Template",
|
||||||
@ -1032,12 +1033,18 @@
|
|||||||
"fieldname": "update_auto_repeat_reference",
|
"fieldname": "update_auto_repeat_reference",
|
||||||
"fieldtype": "Button",
|
"fieldtype": "Button",
|
||||||
"label": "Update Auto Repeat Reference"
|
"label": "Update Auto Repeat Reference"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tax_category",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Tax Category",
|
||||||
|
"options": "Tax Category"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 105,
|
"idx": 105,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-06-24 21:22:05.483429",
|
"modified": "2019-07-11 18:25:49.509343",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order",
|
"name": "Purchase Order",
|
||||||
|
|||||||
@ -26,8 +26,8 @@ def get_data():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "page",
|
"type": "page",
|
||||||
"name": "medical_record",
|
"name": "patient_history",
|
||||||
"label": _("Patient Medical Record"),
|
"label": _("Patient History"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "page",
|
"type": "page",
|
||||||
|
|||||||
@ -3,43 +3,6 @@ from frappe import _
|
|||||||
|
|
||||||
def get_data():
|
def get_data():
|
||||||
return [
|
return [
|
||||||
{
|
|
||||||
"label": _("Production"),
|
|
||||||
"icon": "fa fa-star",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"type": "doctype",
|
|
||||||
"name": "Work Order",
|
|
||||||
"description": _("Orders released for production."),
|
|
||||||
"onboard": 1,
|
|
||||||
"dependencies": ["Item", "BOM"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "doctype",
|
|
||||||
"name": "Production Plan",
|
|
||||||
"description": _("Generate Material Requests (MRP) and Work Orders."),
|
|
||||||
"onboard": 1,
|
|
||||||
"dependencies": ["Item", "BOM"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "doctype",
|
|
||||||
"name": "Stock Entry",
|
|
||||||
"onboard": 1,
|
|
||||||
"dependencies": ["Item"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "doctype",
|
|
||||||
"name": "Timesheet",
|
|
||||||
"description": _("Time Sheet for manufacturing."),
|
|
||||||
"onboard": 1,
|
|
||||||
"dependencies": ["Activity Type"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "doctype",
|
|
||||||
"name": "Job Card"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": _("Bill of Materials"),
|
"label": _("Bill of Materials"),
|
||||||
"items": [
|
"items": [
|
||||||
@ -85,6 +48,43 @@ def get_data():
|
|||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": _("Production"),
|
||||||
|
"icon": "fa fa-star",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Work Order",
|
||||||
|
"description": _("Orders released for production."),
|
||||||
|
"onboard": 1,
|
||||||
|
"dependencies": ["Item", "BOM"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Production Plan",
|
||||||
|
"description": _("Generate Material Requests (MRP) and Work Orders."),
|
||||||
|
"onboard": 1,
|
||||||
|
"dependencies": ["Item", "BOM"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Stock Entry",
|
||||||
|
"onboard": 1,
|
||||||
|
"dependencies": ["Item"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Timesheet",
|
||||||
|
"description": _("Time Sheet for manufacturing."),
|
||||||
|
"onboard": 1,
|
||||||
|
"dependencies": ["Activity Type"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Job Card"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": _("Tools"),
|
"label": _("Tools"),
|
||||||
"icon": "fa fa-wrench",
|
"icon": "fa fa-wrench",
|
||||||
|
|||||||
@ -281,9 +281,9 @@ def get_data():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "report",
|
"type": "report",
|
||||||
|
"is_query_report": True,
|
||||||
"name": "Item Shortage Report",
|
"name": "Item Shortage Report",
|
||||||
"route": "#Report/Bin/Item Shortage Report",
|
"doctype": "Bin"
|
||||||
"doctype": "Purchase Receipt"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "report",
|
"type": "report",
|
||||||
|
|||||||
@ -21,13 +21,7 @@ def get_data():
|
|||||||
"type": "doctype",
|
"type": "doctype",
|
||||||
"name": "Issue Priority",
|
"name": "Issue Priority",
|
||||||
"description": _("Issue Priority."),
|
"description": _("Issue Priority."),
|
||||||
},
|
}
|
||||||
{
|
|
||||||
"type": "doctype",
|
|
||||||
"name": "Communication",
|
|
||||||
"description": _("Communication log."),
|
|
||||||
"onboard": 1,
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -97,4 +91,15 @@ def get_data():
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": _("Settings"),
|
||||||
|
"icon": "fa fa-list",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Support Settings",
|
||||||
|
"label": _("Support Settings"),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
]
|
]
|
||||||
@ -206,10 +206,11 @@ def bom(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
|
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
|
||||||
idx desc, name
|
idx desc, name
|
||||||
limit %(start)s, %(page_len)s """.format(
|
limit %(start)s, %(page_len)s """.format(
|
||||||
fcond=get_filters_cond(doctype, filters, conditions),
|
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
|
||||||
mcond=get_match_cond(doctype),
|
mcond=get_match_cond(doctype),
|
||||||
key=searchfield), {
|
key=frappe.db.escape(searchfield)),
|
||||||
'txt': '%' + txt + '%',
|
{
|
||||||
|
'txt': "%"+frappe.db.escape(txt)+"%",
|
||||||
'_txt': txt.replace("%", ""),
|
'_txt': txt.replace("%", ""),
|
||||||
'start': start or 0,
|
'start': start or 0,
|
||||||
'page_len': page_len or 20
|
'page_len': page_len or 20
|
||||||
|
|||||||
@ -75,7 +75,7 @@ def validate_returned_items(doc):
|
|||||||
|
|
||||||
items_returned = False
|
items_returned = False
|
||||||
for d in doc.get("items"):
|
for d in doc.get("items"):
|
||||||
if flt(d.qty) < 0 or d.get('received_qty') < 0:
|
if d.item_code and (flt(d.qty) < 0 or d.get('received_qty') < 0):
|
||||||
if d.item_code not in valid_items:
|
if d.item_code not in valid_items:
|
||||||
frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
|
frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
|
||||||
.format(d.idx, d.item_code, doc.doctype, doc.return_against))
|
.format(d.idx, d.item_code, doc.doctype, doc.return_against))
|
||||||
@ -107,6 +107,9 @@ def validate_returned_items(doc):
|
|||||||
|
|
||||||
items_returned = True
|
items_returned = True
|
||||||
|
|
||||||
|
elif d.item_name:
|
||||||
|
items_returned = True
|
||||||
|
|
||||||
if not items_returned:
|
if not items_returned:
|
||||||
frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
|
frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
|
||||||
|
|
||||||
|
|||||||
@ -284,7 +284,7 @@ class StatusUpdater(Document):
|
|||||||
frappe.db.sql("""update `tab%(target_parent_dt)s`
|
frappe.db.sql("""update `tab%(target_parent_dt)s`
|
||||||
set %(target_parent_field)s = round(
|
set %(target_parent_field)s = round(
|
||||||
ifnull((select
|
ifnull((select
|
||||||
ifnull(sum(if(%(target_ref_field)s > %(target_field)s, abs(%(target_field)s), abs(%(target_ref_field)s))), 0)
|
ifnull(sum(if(abs(%(target_ref_field)s) > abs(%(target_field)s), abs(%(target_field)s), abs(%(target_ref_field)s))), 0)
|
||||||
/ sum(abs(%(target_ref_field)s)) * 100
|
/ sum(abs(%(target_ref_field)s)) * 100
|
||||||
from `tab%(target_dt)s` where parent="%(name)s" having sum(abs(%(target_ref_field)s)) > 0), 0), 6)
|
from `tab%(target_dt)s` where parent="%(name)s" having sum(abs(%(target_ref_field)s)) > 0), 0), 6)
|
||||||
%(update_modified)s
|
%(update_modified)s
|
||||||
|
|||||||
@ -15,6 +15,9 @@ class calculate_taxes_and_totals(object):
|
|||||||
self.calculate()
|
self.calculate()
|
||||||
|
|
||||||
def calculate(self):
|
def calculate(self):
|
||||||
|
if not len(self.doc.get("items")):
|
||||||
|
return
|
||||||
|
|
||||||
self.discount_amount_applied = False
|
self.discount_amount_applied = False
|
||||||
self._calculate()
|
self._calculate()
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,9 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from requests_oauthlib import OAuth2Session
|
from requests_oauthlib import OAuth2Session
|
||||||
import json, requests
|
import json
|
||||||
|
import requests
|
||||||
|
import traceback
|
||||||
from erpnext import encode_company_abbr
|
from erpnext import encode_company_abbr
|
||||||
|
|
||||||
# QuickBooks requires a redirect URL, User will be redirect to this URL
|
# QuickBooks requires a redirect URL, User will be redirect to this URL
|
||||||
@ -32,7 +34,6 @@ def callback(*args, **kwargs):
|
|||||||
class QuickBooksMigrator(Document):
|
class QuickBooksMigrator(Document):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(QuickBooksMigrator, self).__init__(*args, **kwargs)
|
super(QuickBooksMigrator, self).__init__(*args, **kwargs)
|
||||||
from pprint import pprint
|
|
||||||
self.oauth = OAuth2Session(
|
self.oauth = OAuth2Session(
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
redirect_uri=self.redirect_url,
|
redirect_uri=self.redirect_url,
|
||||||
@ -46,7 +47,9 @@ class QuickBooksMigrator(Document):
|
|||||||
if self.company:
|
if self.company:
|
||||||
# We need a Cost Center corresponding to the selected erpnext Company
|
# We need a Cost Center corresponding to the selected erpnext Company
|
||||||
self.default_cost_center = frappe.db.get_value('Company', self.company, 'cost_center')
|
self.default_cost_center = frappe.db.get_value('Company', self.company, 'cost_center')
|
||||||
self.default_warehouse = frappe.get_all('Warehouse', filters={"company": self.company, "is_group": 0})[0]["name"]
|
company_warehouses = frappe.get_all('Warehouse', filters={"company": self.company, "is_group": 0})
|
||||||
|
if company_warehouses:
|
||||||
|
self.default_warehouse = company_warehouses[0].name
|
||||||
if self.authorization_endpoint:
|
if self.authorization_endpoint:
|
||||||
self.authorization_url = self.oauth.authorization_url(self.authorization_endpoint)[0]
|
self.authorization_url = self.oauth.authorization_url(self.authorization_endpoint)[0]
|
||||||
|
|
||||||
@ -218,7 +221,7 @@ class QuickBooksMigrator(Document):
|
|||||||
|
|
||||||
def _fetch_general_ledger(self):
|
def _fetch_general_ledger(self):
|
||||||
try:
|
try:
|
||||||
query_uri = "{}/company/{}/reports/GeneralLedger".format(self.api_endpoint ,self.quickbooks_company_id)
|
query_uri = "{}/company/{}/reports/GeneralLedger".format(self.api_endpoint, self.quickbooks_company_id)
|
||||||
response = self._get(query_uri,
|
response = self._get(query_uri,
|
||||||
params={
|
params={
|
||||||
"columns": ",".join(["tx_date", "txn_type", "credit_amt", "debt_amt"]),
|
"columns": ",".join(["tx_date", "txn_type", "credit_amt", "debt_amt"]),
|
||||||
@ -493,17 +496,17 @@ class QuickBooksMigrator(Document):
|
|||||||
"account_currency": customer["CurrencyRef"]["value"],
|
"account_currency": customer["CurrencyRef"]["value"],
|
||||||
"company": self.company,
|
"company": self.company,
|
||||||
})[0]["name"]
|
})[0]["name"]
|
||||||
except Exception as e:
|
except Exception:
|
||||||
receivable_account = None
|
receivable_account = None
|
||||||
erpcustomer = frappe.get_doc({
|
erpcustomer = frappe.get_doc({
|
||||||
"doctype": "Customer",
|
"doctype": "Customer",
|
||||||
"quickbooks_id": customer["Id"],
|
"quickbooks_id": customer["Id"],
|
||||||
"customer_name" : encode_company_abbr(customer["DisplayName"], self.company),
|
"customer_name": encode_company_abbr(customer["DisplayName"], self.company),
|
||||||
"customer_type" : "Individual",
|
"customer_type": "Individual",
|
||||||
"customer_group" : "Commercial",
|
"customer_group": "Commercial",
|
||||||
"default_currency": customer["CurrencyRef"]["value"],
|
"default_currency": customer["CurrencyRef"]["value"],
|
||||||
"accounts": [{"company": self.company, "account": receivable_account}],
|
"accounts": [{"company": self.company, "account": receivable_account}],
|
||||||
"territory" : "All Territories",
|
"territory": "All Territories",
|
||||||
"company": self.company,
|
"company": self.company,
|
||||||
}).insert()
|
}).insert()
|
||||||
if "BillAddr" in customer:
|
if "BillAddr" in customer:
|
||||||
@ -521,7 +524,7 @@ class QuickBooksMigrator(Document):
|
|||||||
item_dict = {
|
item_dict = {
|
||||||
"doctype": "Item",
|
"doctype": "Item",
|
||||||
"quickbooks_id": item["Id"],
|
"quickbooks_id": item["Id"],
|
||||||
"item_code" : encode_company_abbr(item["Name"], self.company),
|
"item_code": encode_company_abbr(item["Name"], self.company),
|
||||||
"stock_uom": "Unit",
|
"stock_uom": "Unit",
|
||||||
"is_stock_item": 0,
|
"is_stock_item": 0,
|
||||||
"item_group": "All Item Groups",
|
"item_group": "All Item Groups",
|
||||||
@ -549,14 +552,14 @@ class QuickBooksMigrator(Document):
|
|||||||
erpsupplier = frappe.get_doc({
|
erpsupplier = frappe.get_doc({
|
||||||
"doctype": "Supplier",
|
"doctype": "Supplier",
|
||||||
"quickbooks_id": vendor["Id"],
|
"quickbooks_id": vendor["Id"],
|
||||||
"supplier_name" : encode_company_abbr(vendor["DisplayName"], self.company),
|
"supplier_name": encode_company_abbr(vendor["DisplayName"], self.company),
|
||||||
"supplier_group" : "All Supplier Groups",
|
"supplier_group": "All Supplier Groups",
|
||||||
"company": self.company,
|
"company": self.company,
|
||||||
}).insert()
|
}).insert()
|
||||||
if "BillAddr" in vendor:
|
if "BillAddr" in vendor:
|
||||||
self._create_address(erpsupplier, "Supplier", vendor["BillAddr"], "Billing")
|
self._create_address(erpsupplier, "Supplier", vendor["BillAddr"], "Billing")
|
||||||
if "ShipAddr" in vendor:
|
if "ShipAddr" in vendor:
|
||||||
self._create_address(erpsupplier, "Supplier",vendor["ShipAddr"], "Shipping")
|
self._create_address(erpsupplier, "Supplier", vendor["ShipAddr"], "Shipping")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._log_error(e)
|
self._log_error(e)
|
||||||
|
|
||||||
@ -1200,7 +1203,7 @@ class QuickBooksMigrator(Document):
|
|||||||
|
|
||||||
|
|
||||||
def _create_address(self, entity, doctype, address, address_type):
|
def _create_address(self, entity, doctype, address, address_type):
|
||||||
try :
|
try:
|
||||||
if not frappe.db.exists({"doctype": "Address", "quickbooks_id": address["Id"]}):
|
if not frappe.db.exists({"doctype": "Address", "quickbooks_id": address["Id"]}):
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Address",
|
"doctype": "Address",
|
||||||
@ -1252,8 +1255,6 @@ class QuickBooksMigrator(Document):
|
|||||||
|
|
||||||
|
|
||||||
def _log_error(self, execption, data=""):
|
def _log_error(self, execption, data=""):
|
||||||
import json, traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
frappe.log_error(title="QuickBooks Migration Error",
|
frappe.log_error(title="QuickBooks Migration Error",
|
||||||
message="\n".join([
|
message="\n".join([
|
||||||
"Data",
|
"Data",
|
||||||
|
|||||||
@ -21,9 +21,9 @@ frappe.ui.form.on('Patient', {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (frm.doc.patient_name && frappe.user.has_role("Physician")) {
|
if (frm.doc.patient_name && frappe.user.has_role("Physician")) {
|
||||||
frm.add_custom_button(__('Medical Record'), function () {
|
frm.add_custom_button(__('Patient History'), function () {
|
||||||
frappe.route_options = { "patient": frm.doc.name };
|
frappe.route_options = { "patient": frm.doc.name };
|
||||||
frappe.set_route("medical_record");
|
frappe.set_route("patient_history");
|
||||||
},"View");
|
},"View");
|
||||||
}
|
}
|
||||||
if (!frm.doc.__islocal && (frappe.user.has_role("Nursing User") || frappe.user.has_role("Physician"))) {
|
if (!frm.doc.__islocal && (frappe.user.has_role("Nursing User") || frappe.user.has_role("Physician"))) {
|
||||||
|
|||||||
@ -30,9 +30,9 @@ frappe.ui.form.on('Patient Appointment', {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
if(frm.doc.patient){
|
if(frm.doc.patient){
|
||||||
frm.add_custom_button(__('Medical Record'), function() {
|
frm.add_custom_button(__('Patient History'), function() {
|
||||||
frappe.route_options = {"patient": frm.doc.patient};
|
frappe.route_options = {"patient": frm.doc.patient};
|
||||||
frappe.set_route("medical_record");
|
frappe.set_route("patient_history");
|
||||||
},__("View"));
|
},__("View"));
|
||||||
}
|
}
|
||||||
if(frm.doc.status == "Open"){
|
if(frm.doc.status == "Open"){
|
||||||
|
|||||||
@ -41,10 +41,10 @@ frappe.ui.form.on('Patient Encounter', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
frm.add_custom_button(__('Medical Record'), function() {
|
frm.add_custom_button(__('Patient History'), function() {
|
||||||
if (frm.doc.patient) {
|
if (frm.doc.patient) {
|
||||||
frappe.route_options = {"patient": frm.doc.patient};
|
frappe.route_options = {"patient": frm.doc.patient};
|
||||||
frappe.set_route("medical_record");
|
frappe.set_route("patient_history");
|
||||||
} else {
|
} else {
|
||||||
frappe.msgprint(__("Please select Patient"));
|
frappe.msgprint(__("Please select Patient"));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
@ -1,182 +0,0 @@
|
|||||||
frappe.provide("frappe.medical_record");
|
|
||||||
frappe.pages['medical_record'].on_page_load = function(wrapper) {
|
|
||||||
var me = this;
|
|
||||||
var page = frappe.ui.make_app_page({
|
|
||||||
parent: wrapper,
|
|
||||||
title: 'Medical Record',
|
|
||||||
});
|
|
||||||
|
|
||||||
frappe.breadcrumbs.add("Medical");
|
|
||||||
|
|
||||||
page.main.html(frappe.render_template("patient_select", {}));
|
|
||||||
var patient = frappe.ui.form.make_control({
|
|
||||||
parent: page.main.find(".patient"),
|
|
||||||
df: {
|
|
||||||
fieldtype: "Link",
|
|
||||||
options: "Patient",
|
|
||||||
fieldname: "patient",
|
|
||||||
change: function(){
|
|
||||||
page.main.find(".frappe-list").html("");
|
|
||||||
draw_page(patient.get_value(), me);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
only_input: true,
|
|
||||||
});
|
|
||||||
patient.refresh();
|
|
||||||
|
|
||||||
|
|
||||||
this.page.main.on("click", ".medical_record-message", function() {
|
|
||||||
var doctype = $(this).attr("data-doctype"),
|
|
||||||
docname = $(this).attr("data-docname");
|
|
||||||
|
|
||||||
if (doctype && docname) {
|
|
||||||
frappe.route_options = {
|
|
||||||
scroll_to: { "doctype": doctype, "name": docname }
|
|
||||||
};
|
|
||||||
frappe.set_route(["Form", doctype, docname]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.page.sidebar.on("click", ".edit-details", function() {
|
|
||||||
patient = patient.get_value();
|
|
||||||
if (patient) {
|
|
||||||
frappe.set_route(["Form", "Patient", patient]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
frappe.pages['medical_record'].refresh = function() {
|
|
||||||
var me = this;
|
|
||||||
|
|
||||||
if(frappe.route_options) {
|
|
||||||
if(frappe.route_options.patient){
|
|
||||||
me.page.main.find(".frappe-list").html("");
|
|
||||||
var patient = frappe.route_options.patient;
|
|
||||||
draw_page(patient,me);
|
|
||||||
me.page.main.find("[data-fieldname='patient']").val(patient);
|
|
||||||
frappe.route_options = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var show_patient_info = function(patient, me){
|
|
||||||
frappe.call({
|
|
||||||
"method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail",
|
|
||||||
args: {
|
|
||||||
patient: patient
|
|
||||||
},
|
|
||||||
callback: function (r) {
|
|
||||||
var data = r.message;
|
|
||||||
var details = "";
|
|
||||||
if(data.email) details += "<br><b>Email :</b> " + data.email;
|
|
||||||
if(data.mobile) details += "<br><b>Mobile :</b> " + data.mobile;
|
|
||||||
if(data.occupation) details += "<br><b>Occupation :</b> " + data.occupation;
|
|
||||||
if(data.blood_group) details += "<br><b>Blood group : </b> " + data.blood_group;
|
|
||||||
if(data.allergies) details += "<br><br><b>Allergies : </b> "+ data.allergies;
|
|
||||||
if(data.medication) details += "<br><b>Medication : </b> "+ data.medication;
|
|
||||||
if(data.alcohol_current_use) details += "<br><br><b>Alcohol use : </b> "+ data.alcohol_current_use;
|
|
||||||
if(data.alcohol_past_use) details += "<br><b>Alcohol past use : </b> "+ data.alcohol_past_use;
|
|
||||||
if(data.tobacco_current_use) details += "<br><b>Tobacco use : </b> "+ data.tobacco_current_use;
|
|
||||||
if(data.tobacco_past_use) details += "<br><b>Tobacco past use : </b> "+ data.tobacco_past_use;
|
|
||||||
if(data.medical_history) details += "<br><br><b>Medical history : </b> "+ data.medical_history;
|
|
||||||
if(data.surgical_history) details += "<br><b>Surgical history : </b> "+ data.surgical_history;
|
|
||||||
if(data.surrounding_factors) details += "<br><br><b>Occupational hazards : </b> "+ data.surrounding_factors;
|
|
||||||
if(data.other_risk_factors) details += "<br><b>Other risk factors : </b> " + data.other_risk_factors;
|
|
||||||
if(data.patient_details) details += "<br><br><b>More info : </b> " + data.patient_details;
|
|
||||||
|
|
||||||
if(details){
|
|
||||||
details = "<div style='padding-left:10px; font-size:13px;' align='center'></br><b class='text-muted'>Patient Details</b>" + details + "</div>";
|
|
||||||
}
|
|
||||||
|
|
||||||
var vitals = "";
|
|
||||||
if(data.temperature) vitals += "<br><b>Temperature :</b> " + data.temperature;
|
|
||||||
if(data.pulse) vitals += "<br><b>Pulse :</b> " + data.pulse;
|
|
||||||
if(data.respiratory_rate) vitals += "<br><b>Respiratory Rate :</b> " + data.respiratory_rate;
|
|
||||||
if(data.bp) vitals += "<br><b>BP :</b> " + data.bp;
|
|
||||||
if(data.bmi) vitals += "<br><b>BMI :</b> " + data.bmi;
|
|
||||||
if(data.height) vitals += "<br><b>Height :</b> " + data.height;
|
|
||||||
if(data.weight) vitals += "<br><b>Weight :</b> " + data.weight;
|
|
||||||
if(data.signs_date) vitals += "<br><b>Date :</b> " + data.signs_date;
|
|
||||||
|
|
||||||
if(vitals){
|
|
||||||
vitals = "<div style='padding-left:10px; font-size:13px;' align='center'></br><b class='text-muted'>Vital Signs</b>" + vitals + "<br></div>";
|
|
||||||
details = vitals + details;
|
|
||||||
}
|
|
||||||
if(details) details += "<div align='center'><br><a class='btn btn-default btn-sm edit-details'>Edit Details</a></b> </div>";
|
|
||||||
|
|
||||||
me.page.sidebar.addClass("col-sm-3");
|
|
||||||
me.page.sidebar.html(details);
|
|
||||||
me.page.wrapper.find(".layout-main-section-wrapper").addClass("col-sm-9");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
var draw_page = function(patient, me){
|
|
||||||
frappe.model.with_doctype("Patient Medical Record", function() {
|
|
||||||
me.page.list = new frappe.ui.BaseList({
|
|
||||||
hide_refresh: true,
|
|
||||||
page: me.page,
|
|
||||||
method: 'erpnext.healthcare.page.medical_record.medical_record.get_feed',
|
|
||||||
args: {name: patient},
|
|
||||||
parent: $("<div></div>").appendTo(me.page.main),
|
|
||||||
render_view: function(values) {
|
|
||||||
var me = this;
|
|
||||||
var wrapper = me.page.main.find(".result-list").get(0);
|
|
||||||
values.map(function (value) {
|
|
||||||
var row = $('<div class="list-row">').data("data", value).appendTo($(wrapper)).get(0);
|
|
||||||
new frappe.medical_record.Feed(row, value);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
show_filters: true,
|
|
||||||
doctype: "Patient Medical Record",
|
|
||||||
});
|
|
||||||
show_patient_info(patient, me);
|
|
||||||
me.page.list.run();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
frappe.medical_record.last_feed_date = false;
|
|
||||||
frappe.medical_record.Feed = Class.extend({
|
|
||||||
init: function(row, data) {
|
|
||||||
this.scrub_data(data);
|
|
||||||
this.add_date_separator(row, data);
|
|
||||||
if(!data.add_class)
|
|
||||||
data.add_class = "label-default";
|
|
||||||
|
|
||||||
data.link = "";
|
|
||||||
if (data.reference_doctype && data.reference_name) {
|
|
||||||
data.link = frappe.format(data.reference_name, {fieldtype: "Link", options: data.reference_doctype},
|
|
||||||
{label: __(data.reference_doctype)});
|
|
||||||
}
|
|
||||||
|
|
||||||
$(row)
|
|
||||||
.append(frappe.render_template("medical_record_row", data))
|
|
||||||
.find("a").addClass("grey");
|
|
||||||
},
|
|
||||||
scrub_data: function(data) {
|
|
||||||
data.by = frappe.user.full_name(data.owner);
|
|
||||||
data.imgsrc = frappe.utils.get_file_link(frappe.user_info(data.owner).image);
|
|
||||||
|
|
||||||
data.icon = "icon-flag";
|
|
||||||
},
|
|
||||||
add_date_separator: function(row, data) {
|
|
||||||
var date = frappe.datetime.str_to_obj(data.creation);
|
|
||||||
var last = frappe.medical_record.last_feed_date;
|
|
||||||
|
|
||||||
if((last && frappe.datetime.obj_to_str(last) != frappe.datetime.obj_to_str(date)) || (!last)) {
|
|
||||||
var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date));
|
|
||||||
if(diff < 1) {
|
|
||||||
var pdate = 'Today';
|
|
||||||
} else if(diff < 2) {
|
|
||||||
pdate = 'Yesterday';
|
|
||||||
} else {
|
|
||||||
pdate = frappe.datetime.global_date_format(date);
|
|
||||||
}
|
|
||||||
data.date_sep = pdate;
|
|
||||||
data.date_class = pdate=='Today' ? "date-indicator blue" : "date-indicator";
|
|
||||||
} else {
|
|
||||||
data.date_sep = null;
|
|
||||||
data.date_class = "";
|
|
||||||
}
|
|
||||||
frappe.medical_record.last_feed_date = date;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -1,24 +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.utils import cint
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_feed(start, page_length, name):
|
|
||||||
"""get feed"""
|
|
||||||
result = frappe.db.sql("""select name, owner, modified, creation,
|
|
||||||
reference_doctype, reference_name, subject
|
|
||||||
from `tabPatient Medical Record`
|
|
||||||
where patient=%(patient)s
|
|
||||||
order by creation desc
|
|
||||||
limit %(start)s, %(page_length)s""",
|
|
||||||
{
|
|
||||||
"start": cint(start),
|
|
||||||
"page_length": cint(page_length),
|
|
||||||
"patient": name
|
|
||||||
}, as_dict=True)
|
|
||||||
|
|
||||||
return result
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
<div class="row medical_record-row" data-creation="{%= creation.split(" ")[0] + " 00:00:00" %}">
|
|
||||||
<div class="col-xs-3 text-right medical_record-date"><span class="{%= date_class %}">
|
|
||||||
{%= date_sep || "" %}</span>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-9 medical_record-message"
|
|
||||||
data-doctype="{%= reference_doctype %}"
|
|
||||||
data-docname="{%= reference_name %}"
|
|
||||||
title="{%= by %} / {%= frappe.datetime.str_to_user(creation) %}">
|
|
||||||
<span class="avatar avatar-small">
|
|
||||||
<div class="avatar-frame" style="background-image: url({{ imgsrc }});"></div>
|
|
||||||
<!-- <img src="{%= imgsrc %}"> -->
|
|
||||||
</span>
|
|
||||||
<span class="small">
|
|
||||||
{% if (reference_doctype && reference_name) { %}
|
|
||||||
{%= __("{0}: {1}", [link, "<strong>" + subject + "</strong>"]) %}
|
|
||||||
{% } else { %}
|
|
||||||
{%= subject %}
|
|
||||||
{% } %}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
<div class="text-center col-sm-9" style="padding: 40px;">
|
|
||||||
|
|
||||||
<p>{%= __("Select Patient") %}</p>
|
|
||||||
<p class="patient" style="margin: auto; max-width: 300px; margin-bottom: 20px;"></p>
|
|
||||||
</div>
|
|
||||||
@ -14,6 +14,10 @@
|
|||||||
margin-bottom: -4px;
|
margin-bottom: -4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.medical_record-row > * {
|
||||||
|
z-index: -999;
|
||||||
|
}
|
||||||
|
|
||||||
.date-indicator {
|
.date-indicator {
|
||||||
background:none;
|
background:none;
|
||||||
font-size:12px;
|
font-size:12px;
|
||||||
@ -35,6 +39,61 @@
|
|||||||
color: #5e64ff;
|
color: #5e64ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.div-bg-color {
|
||||||
|
background: #fafbfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-color-white {
|
||||||
|
background: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.width-full {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-3 {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-2 {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mr-3 {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Box {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #d1d5da;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-column {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 1;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.py-3 {
|
||||||
|
padding-top: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-bottom {
|
||||||
|
border-bottom: 1px #e1e4e8 solid;
|
||||||
|
}
|
||||||
|
|
||||||
.date-indicator.blue::after {
|
.date-indicator.blue::after {
|
||||||
background: #5e64ff;
|
background: #5e64ff;
|
||||||
}
|
}
|
||||||
@ -65,8 +124,3 @@
|
|||||||
#page-medical_record .list-filters {
|
#page-medical_record .list-filters {
|
||||||
display: none ;
|
display: none ;
|
||||||
}
|
}
|
||||||
|
|
||||||
#page-medical_record .octicon-heart {
|
|
||||||
color: #ff5858;
|
|
||||||
margin: 0px 5px;
|
|
||||||
}
|
|
||||||
20
erpnext/healthcare/page/patient_history/patient_history.html
Normal file
20
erpnext/healthcare/page/patient_history/patient_history.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<p class="text-center">{%= __("Select Patient") %}</p>
|
||||||
|
<p class="patient" style="margin: auto; max-width: 300px; margin-bottom: 20px;"></p>
|
||||||
|
<div class="patient_details" style="z-index=0"></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-9 patient_documents">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="col-sm-12 show_chart_btns" align="center">
|
||||||
|
</div>
|
||||||
|
<div id="chart" class="col-sm-12 patient_vital_charts">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-12 patient_documents_list">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-12 text-center py-3">
|
||||||
|
<a class="btn btn-sm btn-default btn-get-records" style="display:none">More..</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
300
erpnext/healthcare/page/patient_history/patient_history.js
Normal file
300
erpnext/healthcare/page/patient_history/patient_history.js
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
frappe.provide("frappe.patient_history");
|
||||||
|
frappe.pages['patient_history'].on_page_load = function(wrapper) {
|
||||||
|
var me = this;
|
||||||
|
var page = frappe.ui.make_app_page({
|
||||||
|
parent: wrapper,
|
||||||
|
title: 'Patient History',
|
||||||
|
single_column: true
|
||||||
|
});
|
||||||
|
|
||||||
|
frappe.breadcrumbs.add("Healthcare");
|
||||||
|
let pid = '';
|
||||||
|
page.main.html(frappe.render_template("patient_history", {}));
|
||||||
|
var patient = frappe.ui.form.make_control({
|
||||||
|
parent: page.main.find(".patient"),
|
||||||
|
df: {
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Patient",
|
||||||
|
fieldname: "patient",
|
||||||
|
change: function(){
|
||||||
|
if(pid != patient.get_value() && patient.get_value()){
|
||||||
|
me.start = 0;
|
||||||
|
me.page.main.find(".patient_documents_list").html("");
|
||||||
|
get_documents(patient.get_value(), me);
|
||||||
|
show_patient_info(patient.get_value(), me);
|
||||||
|
show_patient_vital_charts(patient.get_value(), me, "bp", "mmHg", "Blood Pressure");
|
||||||
|
}
|
||||||
|
pid = patient.get_value();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
only_input: true,
|
||||||
|
});
|
||||||
|
patient.refresh();
|
||||||
|
|
||||||
|
if (frappe.route_options){
|
||||||
|
patient.set_value(frappe.route_options.patient);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.page.main.on("click", ".btn-show-chart", function() {
|
||||||
|
var btn_show_id = $(this).attr("data-show-chart-id"), pts = $(this).attr("data-pts");
|
||||||
|
var title = $(this).attr("data-title");
|
||||||
|
show_patient_vital_charts(patient.get_value(), me, btn_show_id, pts, title);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.page.main.on("click", ".btn-more", function() {
|
||||||
|
var doctype = $(this).attr("data-doctype"), docname = $(this).attr("data-docname");
|
||||||
|
if(me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched') == "1"){
|
||||||
|
me.page.main.find("."+docname).hide();
|
||||||
|
me.page.main.find("."+docname).parent().find('.document-html').show();
|
||||||
|
}else{
|
||||||
|
if(doctype && docname){
|
||||||
|
let exclude = ["patient", "patient_name", 'patient_sex', "encounter_date"];
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.healthcare.utils.render_doc_as_html",
|
||||||
|
args:{
|
||||||
|
doctype: doctype,
|
||||||
|
docname: docname,
|
||||||
|
exclude_fields: exclude
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message){
|
||||||
|
me.page.main.find("."+docname).hide();
|
||||||
|
me.page.main.find("."+docname).parent().find('.document-html').html(r.message.html+"\
|
||||||
|
<div align='center'><a class='btn octicon octicon-chevron-up btn-default btn-xs\
|
||||||
|
btn-less' data-doctype='"+doctype+"' data-docname='"+docname+"'></a></div>");
|
||||||
|
me.page.main.find("."+docname).parent().find('.document-html').show();
|
||||||
|
me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched', "1");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
freeze: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.page.main.on("click", ".btn-less", function() {
|
||||||
|
var docname = $(this).attr("data-docname");
|
||||||
|
me.page.main.find("."+docname).parent().find('.document-id').show();
|
||||||
|
me.page.main.find("."+docname).parent().find('.document-html').hide();
|
||||||
|
});
|
||||||
|
me.start = 0;
|
||||||
|
me.page.main.on("click", ".btn-get-records", function(){
|
||||||
|
get_documents(patient.get_value(), me);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var get_documents = function(patient, me){
|
||||||
|
frappe.call({
|
||||||
|
"method": "erpnext.healthcare.page.patient_history.patient_history.get_feed",
|
||||||
|
args: {
|
||||||
|
name: patient,
|
||||||
|
start: me.start,
|
||||||
|
page_length: 20
|
||||||
|
},
|
||||||
|
callback: function (r) {
|
||||||
|
var data = r.message;
|
||||||
|
if(data.length){
|
||||||
|
add_to_records(me, data);
|
||||||
|
}else{
|
||||||
|
me.page.main.find(".patient_documents_list").append("<div class='text-muted' align='center'><br><br>No more records..<br><br></div>");
|
||||||
|
me.page.main.find(".btn-get-records").hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var add_to_records = function(me, data){
|
||||||
|
var details = "<ul class='nav nav-pills nav-stacked'>";
|
||||||
|
var i;
|
||||||
|
for(i=0; i<data.length; i++){
|
||||||
|
if(data[i].reference_doctype){
|
||||||
|
let label = '';
|
||||||
|
if(data[i].subject){
|
||||||
|
label += "<br/>"+data[i].subject;
|
||||||
|
}
|
||||||
|
data[i] = add_date_separator(data[i]);
|
||||||
|
if(frappe.user_info(data[i].owner).image){
|
||||||
|
data[i].imgsrc = frappe.utils.get_file_link(frappe.user_info(data[i].owner).image);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
data[i].imgsrc = false;
|
||||||
|
}
|
||||||
|
var time_line_heading = data[i].practitioner ? `${data[i].practitioner} ` : ``;
|
||||||
|
time_line_heading += data[i].reference_doctype + " - "+ data[i].reference_name;
|
||||||
|
details += `<li data-toggle='pill' class='patient_doc_menu'
|
||||||
|
data-doctype='${data[i].reference_doctype}' data-docname='${data[i].reference_name}'>
|
||||||
|
<div class='col-sm-12 d-flex border-bottom py-3'>`;
|
||||||
|
if (data[i].imgsrc){
|
||||||
|
details += `<span class='mr-3'>
|
||||||
|
<img class='avtar' src='${data[i].imgsrc}' width='32' height='32'>
|
||||||
|
</img>
|
||||||
|
</span>`;
|
||||||
|
}else{
|
||||||
|
details += `<span class='mr-3 avatar avatar-small' style='width:32px; height:32px;'><div align='center' class='standard-image'
|
||||||
|
style='background-color: #fafbfc;'>${data[i].practitioner ? data[i].practitioner.charAt(0) : "U"}</div></span>`;
|
||||||
|
}
|
||||||
|
details += `<div class='d-flex flex-column width-full'>
|
||||||
|
<div>
|
||||||
|
`+time_line_heading+` on
|
||||||
|
<span>
|
||||||
|
${data[i].date_sep}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class='Box p-3 mt-2'>
|
||||||
|
<span class='${data[i].reference_name} document-id'>${label}
|
||||||
|
<div align='center'>
|
||||||
|
<a class='btn octicon octicon-chevron-down btn-default btn-xs btn-more'
|
||||||
|
data-doctype='${data[i].reference_doctype}' data-docname='${data[i].reference_name}'>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span class='document-html' hidden data-fetched="0">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
details += "</ul>";
|
||||||
|
me.page.main.find(".patient_documents_list").append(details);
|
||||||
|
me.start += data.length;
|
||||||
|
if(data.length===20){
|
||||||
|
me.page.main.find(".btn-get-records").show();
|
||||||
|
}else{
|
||||||
|
me.page.main.find(".btn-get-records").hide();
|
||||||
|
me.page.main.find(".patient_documents_list").append("<div class='text-muted' align='center'><br><br>No more records..<br><br></div>");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var add_date_separator = function(data) {
|
||||||
|
var date = frappe.datetime.str_to_obj(data.creation);
|
||||||
|
|
||||||
|
var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date));
|
||||||
|
if(diff < 1) {
|
||||||
|
var pdate = 'Today';
|
||||||
|
} else if(diff < 2) {
|
||||||
|
pdate = 'Yesterday';
|
||||||
|
} else {
|
||||||
|
pdate = frappe.datetime.global_date_format(date);
|
||||||
|
}
|
||||||
|
data.date_sep = pdate;
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
var show_patient_info = function(patient, me){
|
||||||
|
frappe.call({
|
||||||
|
"method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail",
|
||||||
|
args: {
|
||||||
|
patient: patient
|
||||||
|
},
|
||||||
|
callback: function (r) {
|
||||||
|
var data = r.message;
|
||||||
|
var details = "";
|
||||||
|
if(data.image){
|
||||||
|
details += "<div><img class='thumbnail' width=75% src='"+data.image+"'></div>";
|
||||||
|
}
|
||||||
|
details += "<b>" + data.patient_name +"</b><br>" + data.sex;
|
||||||
|
if(data.email) details += "<br>" + data.email;
|
||||||
|
if(data.mobile) details += "<br>" + data.mobile;
|
||||||
|
if(data.occupation) details += "<br><br><b>Occupation :</b> " + data.occupation;
|
||||||
|
if(data.blood_group) details += "<br><b>Blood group : </b> " + data.blood_group;
|
||||||
|
if(data.allergies) details += "<br><br><b>Allergies : </b> "+ data.allergies.replace("\n", "<br>");
|
||||||
|
if(data.medication) details += "<br><b>Medication : </b> "+ data.medication.replace("\n", "<br>");
|
||||||
|
if(data.alcohol_current_use) details += "<br><br><b>Alcohol use : </b> "+ data.alcohol_current_use;
|
||||||
|
if(data.alcohol_past_use) details += "<br><b>Alcohol past use : </b> "+ data.alcohol_past_use;
|
||||||
|
if(data.tobacco_current_use) details += "<br><b>Tobacco use : </b> "+ data.tobacco_current_use;
|
||||||
|
if(data.tobacco_past_use) details += "<br><b>Tobacco past use : </b> "+ data.tobacco_past_use;
|
||||||
|
if(data.medical_history) details += "<br><br><b>Medical history : </b> "+ data.medical_history.replace("\n", "<br>");
|
||||||
|
if(data.surgical_history) details += "<br><b>Surgical history : </b> "+ data.surgical_history.replace("\n", "<br>");
|
||||||
|
if(data.surrounding_factors) details += "<br><br><b>Occupational hazards : </b> "+ data.surrounding_factors.replace("\n", "<br>");
|
||||||
|
if(data.other_risk_factors) details += "<br><b>Other risk factors : </b> " + data.other_risk_factors.replace("\n", "<br>");
|
||||||
|
if(data.patient_details) details += "<br><br><b>More info : </b> " + data.patient_details.replace("\n", "<br>");
|
||||||
|
|
||||||
|
if(details){
|
||||||
|
details = "<div style='padding-left:10px; font-size:13px;' align='center'>" + details + "</div>";
|
||||||
|
}
|
||||||
|
me.page.main.find(".patient_details").html(details);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.healthcare.utils.get_patient_vitals",
|
||||||
|
args:{
|
||||||
|
patient: patient
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message){
|
||||||
|
var show_chart_btns_html = "<div style='padding-top:5px;'><a class='btn btn-default btn-xs btn-show-chart' \
|
||||||
|
data-show-chart-id='bp' data-pts='mmHg' data-title='Blood Pressure'>Blood Pressure</a>\
|
||||||
|
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='pulse_rate' \
|
||||||
|
data-pts='per Minutes' data-title='Respiratory/Pulse Rate'>Respiratory/Pulse Rate</a>\
|
||||||
|
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='temperature' \
|
||||||
|
data-pts='°C or °F' data-title='Temperature'>Temperature</a>\
|
||||||
|
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='bmi' \
|
||||||
|
data-pts='bmi' data-title='BMI'>BMI</a></div>";
|
||||||
|
me.page.main.find(".show_chart_btns").html(show_chart_btns_html);
|
||||||
|
var data = r.message;
|
||||||
|
let labels = [], datasets = [];
|
||||||
|
let bp_systolic = [], bp_diastolic = [], temperature = [];
|
||||||
|
let pulse = [], respiratory_rate = [], bmi = [], height = [], weight = [];
|
||||||
|
for(var i=0; i<data.length; i++){
|
||||||
|
labels.push(data[i].signs_date+"||"+data[i].signs_time);
|
||||||
|
if(btn_show_id=="bp"){
|
||||||
|
bp_systolic.push(data[i].bp_systolic);
|
||||||
|
bp_diastolic.push(data[i].bp_diastolic);
|
||||||
|
}
|
||||||
|
if(btn_show_id=="temperature"){
|
||||||
|
temperature.push(data[i].temperature);
|
||||||
|
}
|
||||||
|
if(btn_show_id=="pulse_rate"){
|
||||||
|
pulse.push(data[i].pulse);
|
||||||
|
respiratory_rate.push(data[i].respiratory_rate);
|
||||||
|
}
|
||||||
|
if(btn_show_id=="bmi"){
|
||||||
|
bmi.push(data[i].bmi);
|
||||||
|
height.push(data[i].height);
|
||||||
|
weight.push(data[i].weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(btn_show_id=="temperature"){
|
||||||
|
datasets.push({name: "Temperature", values: temperature, chartType:'line'});
|
||||||
|
}
|
||||||
|
if(btn_show_id=="bmi"){
|
||||||
|
datasets.push({name: "BMI", values: bmi, chartType:'line'});
|
||||||
|
datasets.push({name: "Height", values: height, chartType:'line'});
|
||||||
|
datasets.push({name: "Weight", values: weight, chartType:'line'});
|
||||||
|
}
|
||||||
|
if(btn_show_id=="bp"){
|
||||||
|
datasets.push({name: "BP Systolic", values: bp_systolic, chartType:'line'});
|
||||||
|
datasets.push({name: "BP Diastolic", values: bp_diastolic, chartType:'line'});
|
||||||
|
}
|
||||||
|
if(btn_show_id=="pulse_rate"){
|
||||||
|
datasets.push({name: "Heart Rate / Pulse", values: pulse, chartType:'line'});
|
||||||
|
datasets.push({name: "Respiratory Rate", values: respiratory_rate, chartType:'line'});
|
||||||
|
}
|
||||||
|
new Chart( ".patient_vital_charts", {
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: datasets
|
||||||
|
},
|
||||||
|
|
||||||
|
title: title,
|
||||||
|
type: 'axis-mixed', // 'axis-mixed', 'bar', 'line', 'pie', 'percentage'
|
||||||
|
height: 150,
|
||||||
|
colors: ['purple', '#ffa3ef', 'light-blue'],
|
||||||
|
|
||||||
|
tooltipOptions: {
|
||||||
|
formatTooltipX: d => (d + '').toUpperCase(),
|
||||||
|
formatTooltipY: d => d + ' ' + pts,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
me.page.main.find(".patient_vital_charts").html("");
|
||||||
|
me.page.main.find(".show_chart_btns").html("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -1,18 +1,21 @@
|
|||||||
{
|
{
|
||||||
"content": null,
|
"content": null,
|
||||||
"creation": "2016-06-09 11:33:14.025787",
|
"creation": "2018-08-08 17:09:13.816199",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Page",
|
"doctype": "Page",
|
||||||
"icon": "icon-play",
|
"icon": "",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"modified": "2018-08-06 11:40:39.705660",
|
"modified": "2018-08-08 17:09:55.969424",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Healthcare",
|
"module": "Healthcare",
|
||||||
"name": "medical_record",
|
"name": "patient_history",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"page_name": "medical_record",
|
"page_name": "patient_history",
|
||||||
"restrict_to_domain": "Healthcare",
|
"restrict_to_domain": "Healthcare",
|
||||||
"roles": [
|
"roles": [
|
||||||
|
{
|
||||||
|
"role": "Healthcare Administrator"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"role": "Physician"
|
"role": "Physician"
|
||||||
}
|
}
|
||||||
@ -21,5 +24,5 @@
|
|||||||
"standard": "Yes",
|
"standard": "Yes",
|
||||||
"style": null,
|
"style": null,
|
||||||
"system_page": 0,
|
"system_page": 0,
|
||||||
"title": "Medical Record"
|
"title": "Patient History"
|
||||||
}
|
}
|
||||||
39
erpnext/healthcare/page/patient_history/patient_history.py
Normal file
39
erpnext/healthcare/page/patient_history/patient_history.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2018, ESS LLP and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe.utils import cint
|
||||||
|
from erpnext.healthcare.utils import render_docs_as_html
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_feed(name, start=0, page_length=20):
|
||||||
|
"""get feed"""
|
||||||
|
result = frappe.db.sql("""select name, owner, creation,
|
||||||
|
reference_doctype, reference_name, subject
|
||||||
|
from `tabPatient Medical Record`
|
||||||
|
where patient=%(patient)s
|
||||||
|
order by creation desc
|
||||||
|
limit %(start)s, %(page_length)s""",
|
||||||
|
{
|
||||||
|
"patient": name,
|
||||||
|
"start": cint(start),
|
||||||
|
"page_length": cint(page_length)
|
||||||
|
}, as_dict=True)
|
||||||
|
return result
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_feed_for_dt(doctype, docname):
|
||||||
|
"""get feed"""
|
||||||
|
result = frappe.db.sql("""select name, owner, modified, creation,
|
||||||
|
reference_doctype, reference_name, subject
|
||||||
|
from `tabPatient Medical Record`
|
||||||
|
where reference_name=%(docname)s and reference_doctype=%(doctype)s
|
||||||
|
order by creation desc""",
|
||||||
|
{
|
||||||
|
"docname": docname,
|
||||||
|
"doctype": doctype
|
||||||
|
}, as_dict=True)
|
||||||
|
|
||||||
|
return result
|
||||||
@ -429,3 +429,116 @@ def get_children(doctype, parent, company, is_root=False):
|
|||||||
occupancy_msg = str(occupied) + " Occupied out of " + str(occupancy_total)
|
occupancy_msg = str(occupied) + " Occupied out of " + str(occupancy_total)
|
||||||
each["occupied_out_of_vacant"] = occupancy_msg
|
each["occupied_out_of_vacant"] = occupancy_msg
|
||||||
return hc_service_units
|
return hc_service_units
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_patient_vitals(patient, from_date=None, to_date=None):
|
||||||
|
if not patient: return
|
||||||
|
vitals = frappe.db.sql("""select * from `tabVital Signs` where \
|
||||||
|
docstatus=1 and patient=%s order by signs_date, signs_time""", \
|
||||||
|
(patient), as_dict=1)
|
||||||
|
if vitals and vitals[0]:
|
||||||
|
return vitals
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def render_docs_as_html(docs):
|
||||||
|
# docs key value pair {doctype: docname}
|
||||||
|
docs_html = "<div class='col-md-12 col-sm-12 text-muted'>"
|
||||||
|
for doc in docs:
|
||||||
|
docs_html += render_doc_as_html(doc['doctype'], doc['docname'])['html'] + "<br/>"
|
||||||
|
return {'html': docs_html}
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def render_doc_as_html(doctype, docname, exclude_fields = []):
|
||||||
|
#render document as html, three column layout will break
|
||||||
|
doc = frappe.get_doc(doctype, docname)
|
||||||
|
meta = frappe.get_meta(doctype)
|
||||||
|
doc_html = "<div class='col-md-12 col-sm-12'>"
|
||||||
|
section_html = ""
|
||||||
|
section_label = ""
|
||||||
|
html = ""
|
||||||
|
sec_on = False
|
||||||
|
col_on = 0
|
||||||
|
has_data = False
|
||||||
|
for df in meta.fields:
|
||||||
|
#on section break append append previous section and html to doc html
|
||||||
|
if df.fieldtype == "Section Break":
|
||||||
|
if has_data and col_on and sec_on:
|
||||||
|
doc_html += section_html + html + "</div>"
|
||||||
|
elif has_data and not col_on and sec_on:
|
||||||
|
doc_html += "<div class='col-md-12 col-sm-12'\
|
||||||
|
><div class='col-md-12 col-sm-12'>" \
|
||||||
|
+ section_html + html +"</div></div>"
|
||||||
|
while col_on:
|
||||||
|
doc_html += "</div>"
|
||||||
|
col_on -= 1
|
||||||
|
sec_on = True
|
||||||
|
has_data= False
|
||||||
|
col_on = 0
|
||||||
|
section_html = ""
|
||||||
|
html = ""
|
||||||
|
if df.label:
|
||||||
|
section_label = df.label
|
||||||
|
continue
|
||||||
|
#on column break append html to section html or doc html
|
||||||
|
if df.fieldtype == "Column Break":
|
||||||
|
if sec_on and has_data:
|
||||||
|
section_html += "<div class='col-md-12 col-sm-12'\
|
||||||
|
><div class='col-md-6 col\
|
||||||
|
-sm-6'><b>" + section_label + "</b>" + html + "</div><div \
|
||||||
|
class='col-md-6 col-sm-6'>"
|
||||||
|
elif has_data:
|
||||||
|
doc_html += "<div class='col-md-12 col-sm-12'><div class='col-m\
|
||||||
|
d-6 col-sm-6'>" + html + "</div><div class='col-md-6 col-sm-6'>"
|
||||||
|
elif sec_on and not col_on:
|
||||||
|
section_html += "<div class='col-md-6 col-sm-6'>"
|
||||||
|
html = ""
|
||||||
|
col_on += 1
|
||||||
|
if df.label:
|
||||||
|
html += '<br>' + df.label
|
||||||
|
continue
|
||||||
|
#on table iterate in items and create table based on in_list_view, append to section html or doc html
|
||||||
|
if df.fieldtype == "Table":
|
||||||
|
items = doc.get(df.fieldname)
|
||||||
|
if not items: continue
|
||||||
|
child_meta = frappe.get_meta(df.options)
|
||||||
|
if not has_data : has_data = True
|
||||||
|
table_head = ""
|
||||||
|
table_row = ""
|
||||||
|
create_head = True
|
||||||
|
for item in items:
|
||||||
|
table_row += '<tr>'
|
||||||
|
for cdf in child_meta.fields:
|
||||||
|
if cdf.in_list_view:
|
||||||
|
if create_head:
|
||||||
|
table_head += '<th>' + cdf.label + '</th>'
|
||||||
|
if item.get(cdf.fieldname):
|
||||||
|
table_row += '<td>' + str(item.get(cdf.fieldname)) \
|
||||||
|
+ '</td>'
|
||||||
|
else:
|
||||||
|
table_row += '<td></td>'
|
||||||
|
create_head = False
|
||||||
|
table_row += '</tr>'
|
||||||
|
if sec_on:
|
||||||
|
section_html += '<table class="table table-condensed \
|
||||||
|
bordered">' + table_head + table_row + '</table>'
|
||||||
|
else:
|
||||||
|
html += '<table class="table table-condensed table-bordered">' \
|
||||||
|
+ table_head + table_row + '</table>'
|
||||||
|
continue
|
||||||
|
#on other field types add label and value to html
|
||||||
|
if not df.hidden and not df.print_hide and doc.get(df.fieldname) and df.fieldname not in exclude_fields:
|
||||||
|
html += "<br>{0} : {1}".format(df.label or df.fieldname, \
|
||||||
|
doc.get(df.fieldname))
|
||||||
|
if not has_data : has_data = True
|
||||||
|
if sec_on and col_on and has_data:
|
||||||
|
doc_html += section_html + html + "</div></div>"
|
||||||
|
elif sec_on and not col_on and has_data:
|
||||||
|
doc_html += "<div class='col-md-12 col-sm-12'\
|
||||||
|
><div class='col-md-12 col-sm-12'>" \
|
||||||
|
+ section_html + html +"</div></div>"
|
||||||
|
if doc_html:
|
||||||
|
doc_html = "<div class='small'><div class='col-md-12 text-right'><a class='btn btn-default btn-xs' href='#Form/%s/%s'></a></div>" %(doctype, docname) + doc_html + "</div>"
|
||||||
|
|
||||||
|
return {'html': doc_html}
|
||||||
|
|||||||
@ -1,69 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
|
||||||
# For license information, please see license.txt
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import frappe, math
|
|
||||||
from frappe import _
|
|
||||||
from frappe.utils import flt, rounded
|
|
||||||
from frappe.model.mapper import get_mapped_doc
|
|
||||||
from frappe.model.document import Document
|
|
||||||
|
|
||||||
from erpnext.hr.doctype.employee_loan.employee_loan import get_monthly_repayment_amount, check_repayment_method
|
|
||||||
|
|
||||||
class EmployeeLoanApplication(Document):
|
|
||||||
def validate(self):
|
|
||||||
check_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods)
|
|
||||||
self.validate_loan_amount()
|
|
||||||
self.get_repayment_details()
|
|
||||||
|
|
||||||
def validate_loan_amount(self):
|
|
||||||
maximum_loan_limit = frappe.db.get_value('Loan Type', self.loan_type, 'maximum_loan_amount')
|
|
||||||
if maximum_loan_limit and self.loan_amount > maximum_loan_limit:
|
|
||||||
frappe.throw(_("Loan Amount cannot exceed Maximum Loan Amount of {0}").format(maximum_loan_limit))
|
|
||||||
|
|
||||||
def get_repayment_details(self):
|
|
||||||
if self.repayment_method == "Repay Over Number of Periods":
|
|
||||||
self.repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
|
|
||||||
|
|
||||||
if self.repayment_method == "Repay Fixed Amount per Period":
|
|
||||||
monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
|
|
||||||
if monthly_interest_rate:
|
|
||||||
monthly_interest_amount = self.loan_amount * monthly_interest_rate
|
|
||||||
if monthly_interest_amount >= self.repayment_amount:
|
|
||||||
frappe.throw(_("Repayment amount {} should be greater than monthly interest amount {}").
|
|
||||||
format(self.repayment_amount, monthly_interest_amount))
|
|
||||||
|
|
||||||
self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
|
|
||||||
math.log(self.repayment_amount - (monthly_interest_amount))) /
|
|
||||||
(math.log(1 + monthly_interest_rate)))
|
|
||||||
else:
|
|
||||||
self.repayment_periods = self.loan_amount / self.repayment_amount
|
|
||||||
|
|
||||||
self.calculate_payable_amount()
|
|
||||||
|
|
||||||
def calculate_payable_amount(self):
|
|
||||||
balance_amount = self.loan_amount
|
|
||||||
self.total_payable_amount = 0
|
|
||||||
self.total_payable_interest = 0
|
|
||||||
|
|
||||||
while(balance_amount > 0):
|
|
||||||
interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12*100))
|
|
||||||
balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount)
|
|
||||||
|
|
||||||
self.total_payable_interest += interest_amount
|
|
||||||
|
|
||||||
self.total_payable_amount = self.loan_amount + self.total_payable_interest
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def make_employee_loan(source_name, target_doc = None):
|
|
||||||
doclist = get_mapped_doc("Employee Loan Application", source_name, {
|
|
||||||
"Employee Loan Application": {
|
|
||||||
"doctype": "Employee Loan",
|
|
||||||
"validation": {
|
|
||||||
"docstatus": ["=", 1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, target_doc)
|
|
||||||
|
|
||||||
return doclist
|
|
||||||
@ -12,7 +12,7 @@ from erpnext.hr.doctype.employee_onboarding.employee_onboarding import Incomplet
|
|||||||
class TestEmployeeOnboarding(unittest.TestCase):
|
class TestEmployeeOnboarding(unittest.TestCase):
|
||||||
def test_employee_onboarding_incomplete_task(self):
|
def test_employee_onboarding_incomplete_task(self):
|
||||||
if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}):
|
if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}):
|
||||||
return frappe.get_doc('Employee Onboarding', {'employee_name': 'Test Researcher'})
|
frappe.delete_doc('Employee Onboarding', {'employee_name': 'Test Researcher'})
|
||||||
_set_up()
|
_set_up()
|
||||||
applicant = get_job_applicant()
|
applicant = get_job_applicant()
|
||||||
onboarding = frappe.new_doc('Employee Onboarding')
|
onboarding = frappe.new_doc('Employee Onboarding')
|
||||||
@ -39,9 +39,10 @@ class TestEmployeeOnboarding(unittest.TestCase):
|
|||||||
|
|
||||||
# complete the task
|
# complete the task
|
||||||
project = frappe.get_doc('Project', onboarding.project)
|
project = frappe.get_doc('Project', onboarding.project)
|
||||||
project.load_tasks()
|
for task in frappe.get_all('Task', dict(project=project.name)):
|
||||||
project.tasks[0].status = 'Completed'
|
task = frappe.get_doc('Task', task.name)
|
||||||
project.save()
|
task.status = 'Completed'
|
||||||
|
task.save()
|
||||||
|
|
||||||
# make employee
|
# make employee
|
||||||
onboarding.reload()
|
onboarding.reload()
|
||||||
@ -71,4 +72,3 @@ def _set_up():
|
|||||||
project = "Employee Onboarding : Test Researcher - test@researcher.com"
|
project = "Employee Onboarding : Test Researcher - test@researcher.com"
|
||||||
frappe.db.sql("delete from tabProject where name=%s", project)
|
frappe.db.sql("delete from tabProject where name=%s", project)
|
||||||
frappe.db.sql("delete from tabTask where project=%s", project)
|
frappe.db.sql("delete from tabTask where project=%s", project)
|
||||||
frappe.db.sql("delete from `tabProject Task` where parent=%s", project)
|
|
||||||
|
|||||||
@ -10,33 +10,36 @@ from erpnext.accounts.doctype.account.test_account import create_account
|
|||||||
|
|
||||||
test_records = frappe.get_test_records('Expense Claim')
|
test_records = frappe.get_test_records('Expense Claim')
|
||||||
test_dependencies = ['Employee']
|
test_dependencies = ['Employee']
|
||||||
|
company_name = '_Test Company 4'
|
||||||
|
|
||||||
|
|
||||||
class TestExpenseClaim(unittest.TestCase):
|
class TestExpenseClaim(unittest.TestCase):
|
||||||
def test_total_expense_claim_for_project(self):
|
def test_total_expense_claim_for_project(self):
|
||||||
frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """)
|
frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """)
|
||||||
frappe.db.sql("""delete from `tabProject Task` where parent = "_Test Project 1" """)
|
|
||||||
frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """)
|
frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """)
|
||||||
frappe.db.sql("delete from `tabExpense Claim` where project='_Test Project 1'")
|
frappe.db.sql("update `tabExpense Claim` set project = '', task = ''")
|
||||||
|
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"project_name": "_Test Project 1",
|
"project_name": "_Test Project 1",
|
||||||
"doctype": "Project",
|
"doctype": "Project"
|
||||||
}).save()
|
}).save()
|
||||||
|
|
||||||
task = frappe.get_doc({
|
task = frappe.get_doc(dict(
|
||||||
"doctype": "Task",
|
doctype = 'Task',
|
||||||
"subject": "_Test Project Task 1",
|
subject = '_Test Project Task 1',
|
||||||
"project": "_Test Project 1"
|
status = 'Open',
|
||||||
}).save()
|
project = '_Test Project 1'
|
||||||
|
)).insert()
|
||||||
|
|
||||||
task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"})
|
task_name = task.name
|
||||||
payable_account = get_payable_account("Wind Power LLC")
|
payable_account = get_payable_account(company_name)
|
||||||
make_expense_claim(payable_account, 300, 200, "Wind Power LLC","Travel Expenses - WP", "_Test Project 1", task_name)
|
|
||||||
|
make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", "_Test Project 1", task_name)
|
||||||
|
|
||||||
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
|
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
|
||||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
|
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
|
||||||
|
|
||||||
expense_claim2 = make_expense_claim(payable_account, 600, 500, "Wind Power LLC", "Travel Expenses - WP","_Test Project 1", task_name)
|
expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4","_Test Project 1", task_name)
|
||||||
|
|
||||||
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700)
|
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700)
|
||||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700)
|
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700)
|
||||||
@ -48,8 +51,8 @@ class TestExpenseClaim(unittest.TestCase):
|
|||||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
|
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
|
||||||
|
|
||||||
def test_expense_claim_status(self):
|
def test_expense_claim_status(self):
|
||||||
payable_account = get_payable_account("Wind Power LLC")
|
payable_account = get_payable_account(company_name)
|
||||||
expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP")
|
expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4")
|
||||||
|
|
||||||
je_dict = make_bank_entry("Expense Claim", expense_claim.name)
|
je_dict = make_bank_entry("Expense Claim", expense_claim.name)
|
||||||
je = frappe.get_doc(je_dict)
|
je = frappe.get_doc(je_dict)
|
||||||
@ -66,9 +69,9 @@ class TestExpenseClaim(unittest.TestCase):
|
|||||||
self.assertEqual(expense_claim.status, "Unpaid")
|
self.assertEqual(expense_claim.status, "Unpaid")
|
||||||
|
|
||||||
def test_expense_claim_gl_entry(self):
|
def test_expense_claim_gl_entry(self):
|
||||||
payable_account = get_payable_account("Wind Power LLC")
|
payable_account = get_payable_account(company_name)
|
||||||
taxes = generate_taxes()
|
taxes = generate_taxes()
|
||||||
expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP", do_not_submit=True, taxes=taxes)
|
expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", do_not_submit=True, taxes=taxes)
|
||||||
expense_claim.submit()
|
expense_claim.submit()
|
||||||
|
|
||||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||||
@ -78,9 +81,9 @@ class TestExpenseClaim(unittest.TestCase):
|
|||||||
self.assertTrue(gl_entries)
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
expected_values = dict((d[0], d) for d in [
|
expected_values = dict((d[0], d) for d in [
|
||||||
['CGST - WP',10.0, 0.0],
|
['CGST - _TC4',18.0, 0.0],
|
||||||
[payable_account, 0.0, 210.0],
|
[payable_account, 0.0, 218.0],
|
||||||
["Travel Expenses - WP", 200.0, 0.0]
|
["Travel Expenses - _TC4", 200.0, 0.0]
|
||||||
])
|
])
|
||||||
|
|
||||||
for gle in gl_entries:
|
for gle in gl_entries:
|
||||||
@ -89,14 +92,14 @@ class TestExpenseClaim(unittest.TestCase):
|
|||||||
self.assertEquals(expected_values[gle.account][2], gle.credit)
|
self.assertEquals(expected_values[gle.account][2], gle.credit)
|
||||||
|
|
||||||
def test_rejected_expense_claim(self):
|
def test_rejected_expense_claim(self):
|
||||||
payable_account = get_payable_account("Wind Power LLC")
|
payable_account = get_payable_account(company_name)
|
||||||
expense_claim = frappe.get_doc({
|
expense_claim = frappe.get_doc({
|
||||||
"doctype": "Expense Claim",
|
"doctype": "Expense Claim",
|
||||||
"employee": "_T-Employee-00001",
|
"employee": "_T-Employee-00001",
|
||||||
"payable_account": payable_account,
|
"payable_account": payable_account,
|
||||||
"approval_status": "Rejected",
|
"approval_status": "Rejected",
|
||||||
"expenses":
|
"expenses":
|
||||||
[{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "amount": 300, "sanctioned_amount": 200 }]
|
[{ "expense_type": "Travel", "default_account": "Travel Expenses - _TC4", "amount": 300, "sanctioned_amount": 200 }]
|
||||||
})
|
})
|
||||||
expense_claim.submit()
|
expense_claim.submit()
|
||||||
|
|
||||||
@ -111,9 +114,9 @@ def get_payable_account(company):
|
|||||||
|
|
||||||
def generate_taxes():
|
def generate_taxes():
|
||||||
parent_account = frappe.db.get_value('Account',
|
parent_account = frappe.db.get_value('Account',
|
||||||
{'company': "Wind Power LLC", 'is_group':1, 'account_type': 'Tax'},
|
{'company': company_name, 'is_group':1, 'account_type': 'Tax'},
|
||||||
'name')
|
'name')
|
||||||
account = create_account(company="Wind Power LLC", account_name="CGST", account_type="Tax", parent_account=parent_account)
|
account = create_account(company=company_name, account_name="CGST", account_type="Tax", parent_account=parent_account)
|
||||||
return {'taxes':[{
|
return {'taxes':[{
|
||||||
"account_head": account,
|
"account_head": account,
|
||||||
"rate": 0,
|
"rate": 0,
|
||||||
@ -124,15 +127,18 @@ def generate_taxes():
|
|||||||
|
|
||||||
def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None):
|
def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None):
|
||||||
employee = frappe.db.get_value("Employee", {"status": "Active"})
|
employee = frappe.db.get_value("Employee", {"status": "Active"})
|
||||||
|
currency = frappe.db.get_value('Company', company, 'default_currency')
|
||||||
expense_claim = {
|
expense_claim = {
|
||||||
"doctype": "Expense Claim",
|
"doctype": "Expense Claim",
|
||||||
"employee": employee,
|
"employee": employee,
|
||||||
"payable_account": payable_account,
|
"payable_account": payable_account,
|
||||||
"approval_status": "Approved",
|
"approval_status": "Approved",
|
||||||
"company": company,
|
"company": company,
|
||||||
|
'currency': currency,
|
||||||
"expenses":
|
"expenses":
|
||||||
[{"expense_type": "Travel",
|
[{"expense_type": "Travel",
|
||||||
"default_account": account,
|
"default_account": account,
|
||||||
|
'currency': currency,
|
||||||
"amount": amount,
|
"amount": amount,
|
||||||
"sanctioned_amount": sanctioned_amount}]}
|
"sanctioned_amount": sanctioned_amount}]}
|
||||||
if taxes:
|
if taxes:
|
||||||
|
|||||||
@ -4,6 +4,12 @@
|
|||||||
frappe.provide("erpnext.job_offer");
|
frappe.provide("erpnext.job_offer");
|
||||||
|
|
||||||
frappe.ui.form.on("Job Offer", {
|
frappe.ui.form.on("Job Offer", {
|
||||||
|
onload: function (frm) {
|
||||||
|
frm.set_query("select_terms", function() {
|
||||||
|
return { filters: { hr: 1 } };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
select_terms: function (frm) {
|
select_terms: function (frm) {
|
||||||
erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function (r) {
|
erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function (r) {
|
||||||
if (!r.exc) {
|
if (!r.exc) {
|
||||||
|
|||||||
@ -39,31 +39,19 @@ frappe.ui.form.on('Loan', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
refresh: function (frm) {
|
refresh: function (frm) {
|
||||||
if (frm.doc.docstatus == 1 && frm.doc.status == "Sanctioned") {
|
if (frm.doc.docstatus == 1) {
|
||||||
|
if (frm.doc.status == "Sanctioned") {
|
||||||
frm.add_custom_button(__('Create Disbursement Entry'), function() {
|
frm.add_custom_button(__('Create Disbursement Entry'), function() {
|
||||||
frm.trigger("make_jv");
|
frm.trigger("make_jv");
|
||||||
})
|
}).addClass("btn-primary");
|
||||||
}
|
} else if (frm.doc.status == "Disbursed" && frm.doc.repayment_start_date && (frm.doc.applicant_type == 'Member' || frm.doc.repay_from_salary == 0)) {
|
||||||
if (frm.doc.repayment_schedule) {
|
|
||||||
let total_amount_paid = 0;
|
|
||||||
$.each(frm.doc.repayment_schedule || [], function(i, row) {
|
|
||||||
if (row.paid) {
|
|
||||||
total_amount_paid += row.total_payment;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
frm.set_value("total_amount_paid", total_amount_paid);
|
|
||||||
; }
|
|
||||||
if (frm.doc.docstatus == 1 && frm.doc.repayment_start_date && (frm.doc.applicant_type == 'Member' || frm.doc.repay_from_salary == 0)) {
|
|
||||||
frm.add_custom_button(__('Create Repayment Entry'), function() {
|
frm.add_custom_button(__('Create Repayment Entry'), function() {
|
||||||
frm.trigger("make_repayment_entry");
|
frm.trigger("make_repayment_entry");
|
||||||
})
|
}).addClass("btn-primary");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
frm.trigger("toggle_fields");
|
frm.trigger("toggle_fields");
|
||||||
},
|
},
|
||||||
status: function (frm) {
|
|
||||||
frm.toggle_reqd("disbursement_date", frm.doc.status == 'Disbursed')
|
|
||||||
frm.toggle_reqd("repayment_start_date", frm.doc.status == 'Disbursed')
|
|
||||||
},
|
|
||||||
|
|
||||||
make_jv: function (frm) {
|
make_jv: function (frm) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_copy": 0,
|
||||||
|
"allow_events_in_timeline": 0,
|
||||||
"allow_guest_to_view": 0,
|
"allow_guest_to_view": 0,
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 0,
|
"allow_rename": 0,
|
||||||
@ -20,6 +21,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "applicant_type",
|
"fieldname": "applicant_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -53,6 +55,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "applicant",
|
"fieldname": "applicant",
|
||||||
"fieldtype": "Dynamic Link",
|
"fieldtype": "Dynamic Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -86,6 +89,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "applicant_name",
|
"fieldname": "applicant_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -118,6 +122,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "loan_application",
|
"fieldname": "loan_application",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -151,6 +156,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "loan_type",
|
"fieldname": "loan_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -184,6 +190,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "column_break_3",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -215,7 +222,8 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "",
|
"default": "Today",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "posting_date",
|
"fieldname": "posting_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -248,6 +256,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -282,6 +291,7 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "Sanctioned",
|
"default": "Sanctioned",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "status",
|
"fieldname": "status",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -299,7 +309,7 @@
|
|||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
@ -316,6 +326,7 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"depends_on": "eval:doc.applicant_type==\"Employee\"",
|
"depends_on": "eval:doc.applicant_type==\"Employee\"",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "repay_from_salary",
|
"fieldname": "repay_from_salary",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -348,6 +359,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "section_break_8",
|
"fieldname": "section_break_8",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -380,6 +392,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "loan_amount",
|
"fieldname": "loan_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -415,6 +428,7 @@
|
|||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "",
|
"default": "",
|
||||||
"fetch_from": "loan_type.rate_of_interest",
|
"fetch_from": "loan_type.rate_of_interest",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "rate_of_interest",
|
"fieldname": "rate_of_interest",
|
||||||
"fieldtype": "Percent",
|
"fieldtype": "Percent",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -448,6 +462,8 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"depends_on": "eval:doc.status==\"Disbursed\"",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "disbursement_date",
|
"fieldname": "disbursement_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -480,6 +496,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "repayment_start_date",
|
"fieldname": "repayment_start_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -499,7 +516,7 @@
|
|||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"remember_last_selected_value": 0,
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
@ -512,6 +529,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "column_break_11",
|
"fieldname": "column_break_11",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -544,6 +562,7 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "Repay Over Number of Periods",
|
"default": "Repay Over Number of Periods",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "repayment_method",
|
"fieldname": "repayment_method",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -579,6 +598,7 @@
|
|||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "",
|
"default": "",
|
||||||
"depends_on": "",
|
"depends_on": "",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "repayment_periods",
|
"fieldname": "repayment_periods",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -613,6 +633,7 @@
|
|||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "",
|
"default": "",
|
||||||
"depends_on": "",
|
"depends_on": "",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "monthly_repayment_amount",
|
"fieldname": "monthly_repayment_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -646,6 +667,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "account_info",
|
"fieldname": "account_info",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -678,6 +700,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "mode_of_payment",
|
"fieldname": "mode_of_payment",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -711,6 +734,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "payment_account",
|
"fieldname": "payment_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -744,6 +768,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "column_break_9",
|
"fieldname": "column_break_9",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -775,6 +800,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "loan_account",
|
"fieldname": "loan_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -808,6 +834,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "interest_income_account",
|
"fieldname": "interest_income_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -841,6 +868,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "section_break_15",
|
"fieldname": "section_break_15",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -873,6 +901,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "repayment_schedule",
|
"fieldname": "repayment_schedule",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -906,6 +935,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "section_break_17",
|
"fieldname": "section_break_17",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -939,6 +969,7 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "total_payment",
|
"fieldname": "total_payment",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -972,6 +1003,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "column_break_19",
|
"fieldname": "column_break_19",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -1004,6 +1036,7 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "total_interest_payable",
|
"fieldname": "total_interest_payable",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -1037,6 +1070,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "total_amount_paid",
|
"fieldname": "total_amount_paid",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -1070,6 +1104,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "amended_from",
|
"fieldname": "amended_from",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -1106,7 +1141,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-08-21 16:15:53.267145",
|
"modified": "2019-07-10 13:04:20.953694",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Loan",
|
"name": "Loan",
|
||||||
@ -1149,7 +1184,6 @@
|
|||||||
"set_user_permissions": 0,
|
"set_user_permissions": 0,
|
||||||
"share": 0,
|
"share": 0,
|
||||||
"submit": 0,
|
"submit": 0,
|
||||||
"user_permission_doctypes": "[\"Employee\"]",
|
|
||||||
"write": 0
|
"write": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@ -6,30 +6,34 @@ from __future__ import unicode_literals
|
|||||||
import frappe, math, json
|
import frappe, math, json
|
||||||
import erpnext
|
import erpnext
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt, rounded, add_months, nowdate
|
from frappe.utils import flt, rounded, add_months, nowdate, getdate
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
|
|
||||||
class Loan(AccountsController):
|
class Loan(AccountsController):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
check_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods)
|
validate_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods)
|
||||||
if not self.company:
|
self.set_missing_fields()
|
||||||
self.company = erpnext.get_default_company()
|
|
||||||
if not self.posting_date:
|
|
||||||
self.posting_date = nowdate()
|
|
||||||
if self.loan_type and not self.rate_of_interest:
|
|
||||||
self.rate_of_interest = frappe.db.get_value("Loan Type", self.loan_type, "rate_of_interest")
|
|
||||||
if self.repayment_method == "Repay Over Number of Periods":
|
|
||||||
self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
|
|
||||||
if self.status == "Repaid/Closed":
|
|
||||||
self.total_amount_paid = self.total_payment
|
|
||||||
if self.status == 'Disbursed' and self.repayment_start_date < self.disbursement_date:
|
|
||||||
frappe.throw(_("Repayment Start Date cannot be before Disbursement Date."))
|
|
||||||
|
|
||||||
if self.status == "Disbursed":
|
|
||||||
self.make_repayment_schedule()
|
self.make_repayment_schedule()
|
||||||
self.set_repayment_period()
|
self.set_repayment_period()
|
||||||
self.calculate_totals()
|
self.calculate_totals()
|
||||||
|
|
||||||
|
def set_missing_fields(self):
|
||||||
|
if not self.company:
|
||||||
|
self.company = erpnext.get_default_company()
|
||||||
|
|
||||||
|
if not self.posting_date:
|
||||||
|
self.posting_date = nowdate()
|
||||||
|
|
||||||
|
if self.loan_type and not self.rate_of_interest:
|
||||||
|
self.rate_of_interest = frappe.db.get_value("Loan Type", self.loan_type, "rate_of_interest")
|
||||||
|
|
||||||
|
if self.repayment_method == "Repay Over Number of Periods":
|
||||||
|
self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
|
||||||
|
|
||||||
|
if self.status == "Repaid/Closed":
|
||||||
|
self.total_amount_paid = self.total_payment
|
||||||
|
|
||||||
|
|
||||||
def make_jv_entry(self):
|
def make_jv_entry(self):
|
||||||
self.check_permission('write')
|
self.check_permission('write')
|
||||||
journal_entry = frappe.new_doc('Journal Entry')
|
journal_entry = frappe.new_doc('Journal Entry')
|
||||||
@ -105,20 +109,31 @@ def update_total_amount_paid(doc):
|
|||||||
frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid)
|
frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid)
|
||||||
|
|
||||||
def update_disbursement_status(doc):
|
def update_disbursement_status(doc):
|
||||||
disbursement = frappe.db.sql("""select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount
|
disbursement = frappe.db.sql("""
|
||||||
from `tabGL Entry` where account = %s and against_voucher_type = 'Loan' and against_voucher = %s""",
|
select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount
|
||||||
(doc.payment_account, doc.name), as_dict=1)[0]
|
from `tabGL Entry`
|
||||||
if disbursement.disbursed_amount == doc.loan_amount:
|
where account = %s and against_voucher_type = 'Loan' and against_voucher = %s
|
||||||
frappe.db.set_value("Loan", doc.name , "status", "Disbursed")
|
""", (doc.payment_account, doc.name), as_dict=1)[0]
|
||||||
if disbursement.disbursed_amount == 0:
|
|
||||||
frappe.db.set_value("Loan", doc.name , "status", "Sanctioned")
|
|
||||||
if disbursement.disbursed_amount > doc.loan_amount:
|
|
||||||
frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount))
|
|
||||||
if disbursement.disbursed_amount > 0:
|
|
||||||
frappe.db.set_value("Loan", doc.name , "disbursement_date", disbursement.posting_date)
|
|
||||||
frappe.db.set_value("Loan", doc.name , "repayment_start_date", disbursement.posting_date)
|
|
||||||
|
|
||||||
def check_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods):
|
disbursement_date = None
|
||||||
|
if not disbursement or disbursement.disbursed_amount == 0:
|
||||||
|
status = "Sanctioned"
|
||||||
|
elif disbursement.disbursed_amount == doc.loan_amount:
|
||||||
|
disbursement_date = disbursement.posting_date
|
||||||
|
status = "Disbursed"
|
||||||
|
elif disbursement.disbursed_amount > doc.loan_amount:
|
||||||
|
frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount))
|
||||||
|
|
||||||
|
if status == 'Disbursed' and getdate(disbursement_date) > getdate(frappe.db.get_value("Loan", doc.name, "repayment_start_date")):
|
||||||
|
frappe.throw(_("Disbursement Date cannot be after Loan Repayment Start Date"))
|
||||||
|
|
||||||
|
frappe.db.sql("""
|
||||||
|
update `tabLoan`
|
||||||
|
set status = %s, disbursement_date = %s
|
||||||
|
where name = %s
|
||||||
|
""", (status, disbursement_date, doc.name))
|
||||||
|
|
||||||
|
def validate_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods):
|
||||||
if repayment_method == "Repay Over Number of Periods" and not repayment_periods:
|
if repayment_method == "Repay Over Number of Periods" and not repayment_periods:
|
||||||
frappe.throw(_("Please enter Repayment Periods"))
|
frappe.throw(_("Please enter Repayment Periods"))
|
||||||
|
|
||||||
|
|||||||
@ -23,9 +23,8 @@ frappe.ui.form.on('Loan Application', {
|
|||||||
},
|
},
|
||||||
add_toolbar_buttons: function(frm) {
|
add_toolbar_buttons: function(frm) {
|
||||||
if (frm.doc.status == "Approved") {
|
if (frm.doc.status == "Approved") {
|
||||||
frm.add_custom_button(__('Loan'), function() {
|
frm.add_custom_button(__('Create Loan'), function() {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
type: "GET",
|
|
||||||
method: "erpnext.hr.doctype.loan_application.loan_application.make_loan",
|
method: "erpnext.hr.doctype.loan_application.loan_application.make_loan",
|
||||||
args: {
|
args: {
|
||||||
"source_name": frm.doc.name
|
"source_name": frm.doc.name
|
||||||
@ -37,7 +36,7 @@ frappe.ui.form.on('Loan Application', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
}).addClass("btn-primary");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -9,11 +9,11 @@ from frappe.utils import flt, rounded
|
|||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
from erpnext.hr.doctype.loan.loan import get_monthly_repayment_amount, check_repayment_method
|
from erpnext.hr.doctype.loan.loan import get_monthly_repayment_amount, validate_repayment_method
|
||||||
|
|
||||||
class LoanApplication(Document):
|
class LoanApplication(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
check_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods)
|
validate_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods)
|
||||||
self.validate_loan_amount()
|
self.validate_loan_amount()
|
||||||
self.get_repayment_details()
|
self.get_repayment_details()
|
||||||
|
|
||||||
|
|||||||
@ -618,7 +618,7 @@ class SalarySlip(TransactionBase):
|
|||||||
elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(row.depends_on_payment_days):
|
elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(row.depends_on_payment_days):
|
||||||
amount, additional_amount = 0, 0
|
amount, additional_amount = 0, 0
|
||||||
elif not row.amount:
|
elif not row.amount:
|
||||||
amount = row.default_amount + row.additional_amount
|
amount = flt(row.default_amount) + flt(row.additional_amount)
|
||||||
|
|
||||||
# apply rounding
|
# apply rounding
|
||||||
if frappe.get_cached_value("Salary Component", row.salary_component, "round_to_the_nearest_integer"):
|
if frappe.get_cached_value("Salary Component", row.salary_component, "round_to_the_nearest_integer"):
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class ShiftType(Document):
|
|||||||
'skip_auto_attendance':'0',
|
'skip_auto_attendance':'0',
|
||||||
'attendance':('is', 'not set'),
|
'attendance':('is', 'not set'),
|
||||||
'time':('>=', self.process_attendance_after),
|
'time':('>=', self.process_attendance_after),
|
||||||
'shift_actual_start': ('<', self.last_sync_of_checkin),
|
'shift_actual_end': ('<', self.last_sync_of_checkin),
|
||||||
'shift': self.name
|
'shift': self.name
|
||||||
}
|
}
|
||||||
logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time")
|
logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time")
|
||||||
|
|||||||
@ -2,6 +2,10 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Blanket Order', {
|
frappe.ui.form.on('Blanket Order', {
|
||||||
|
onload: function(frm) {
|
||||||
|
frm.trigger('set_tc_name_filter');
|
||||||
|
},
|
||||||
|
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.add_fetch("customer", "customer_name", "customer_name");
|
frm.add_fetch("customer", "customer_name", "customer_name");
|
||||||
frm.add_fetch("supplier", "supplier_name", "supplier_name");
|
frm.add_fetch("supplier", "supplier_name", "supplier_name");
|
||||||
@ -44,4 +48,23 @@ frappe.ui.form.on('Blanket Order', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
set_tc_name_filter: function(frm) {
|
||||||
|
if (frm.doc.blanket_order_type === 'Selling') {
|
||||||
|
frm.set_query("tc_name", function() {
|
||||||
|
return { filters: { selling: 1 } };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (frm.doc.blanket_order_type === 'Purchasing') {
|
||||||
|
frm.set_query("tc_name", function() {
|
||||||
|
return { filters: { buying: 1 } };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
blanket_order_type: function (frm) {
|
||||||
|
frm.trigger('set_tc_name_filter');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'blanket_order',
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'items': ['Purchase Order', 'Sales Order']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -594,6 +594,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
|
|||||||
sum(bom_item.{qty_field}/ifnull(bom.quantity, 1)) * %(qty)s as qty,
|
sum(bom_item.{qty_field}/ifnull(bom.quantity, 1)) * %(qty)s as qty,
|
||||||
item.description,
|
item.description,
|
||||||
item.image,
|
item.image,
|
||||||
|
bom.project,
|
||||||
item.stock_uom,
|
item.stock_uom,
|
||||||
item.allow_alternative_item,
|
item.allow_alternative_item,
|
||||||
item_default.default_warehouse,
|
item_default.default_warehouse,
|
||||||
|
|||||||
27
erpnext/manufacturing/doctype/bom/bom_dashboard.py
Normal file
27
erpnext/manufacturing/doctype/bom/bom_dashboard.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'bom_no',
|
||||||
|
'non_standard_fieldnames': {
|
||||||
|
'Item': 'default_bom',
|
||||||
|
'Purchase Order': 'bom',
|
||||||
|
'Purchase Receipt': 'bom',
|
||||||
|
'Purchase Invoice': 'bom'
|
||||||
|
},
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'label': _('Stock'),
|
||||||
|
'items': ['Item', 'Stock Entry', 'Quality Inspection']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _('Manufacture'),
|
||||||
|
'items': ['BOM', 'Work Order', 'Job Card', 'Production Plan']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _('Purchase'),
|
||||||
|
'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'operation',
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'label': _('Manufacture'),
|
||||||
|
'items': ['BOM', 'Work Order', 'Job Card', 'Timesheet']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
12
erpnext/manufacturing/doctype/routing/routing_dashboard.py
Normal file
12
erpnext/manufacturing/doctype/routing/routing_dashboard.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'routing',
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'items': ['BOM']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'workstation',
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'label': _('Manufacture'),
|
||||||
|
'items': ['BOM', 'Routing', 'Work Order', 'Job Card', 'Operation', 'Timesheet']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -605,6 +605,7 @@ erpnext.patches.v11_1.delete_scheduling_tool
|
|||||||
erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019
|
erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019
|
||||||
execute:frappe.delete_doc_if_exists("Page", "support-analytics")
|
execute:frappe.delete_doc_if_exists("Page", "support-analytics")
|
||||||
erpnext.patches.v12_0.make_item_manufacturer
|
erpnext.patches.v12_0.make_item_manufacturer
|
||||||
|
erpnext.patches.v12_0.remove_patient_medical_record_page
|
||||||
erpnext.patches.v11_1.move_customer_lead_to_dynamic_column
|
erpnext.patches.v11_1.move_customer_lead_to_dynamic_column
|
||||||
erpnext.patches.v11_1.set_default_action_for_quality_inspection
|
erpnext.patches.v11_1.set_default_action_for_quality_inspection
|
||||||
erpnext.patches.v11_1.delete_bom_browser
|
erpnext.patches.v11_1.delete_bom_browser
|
||||||
@ -615,3 +616,7 @@ erpnext.patches.v11_1.set_missing_opportunity_from
|
|||||||
erpnext.patches.v12_0.set_quotation_status
|
erpnext.patches.v12_0.set_quotation_status
|
||||||
erpnext.patches.v12_0.set_priority_for_support
|
erpnext.patches.v12_0.set_priority_for_support
|
||||||
erpnext.patches.v12_0.delete_priority_property_setter
|
erpnext.patches.v12_0.delete_priority_property_setter
|
||||||
|
execute:frappe.delete_doc("DocType", "Project Task")
|
||||||
|
erpnext.patches.v11_1.update_default_supplier_in_item_defaults
|
||||||
|
erpnext.patches.v12_0.update_due_date_in_gle
|
||||||
|
erpnext.patches.v12_0.add_default_buying_selling_terms_in_company
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
# Copyright (c) 2018, Frappe and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
'''
|
||||||
|
default supplier was not set in the item defaults for multi company instance,
|
||||||
|
this patch will set the default supplier
|
||||||
|
|
||||||
|
'''
|
||||||
|
if not frappe.db.has_column('Item', 'default_supplier'):
|
||||||
|
return
|
||||||
|
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'item_default')
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'item')
|
||||||
|
|
||||||
|
companies = frappe.get_all("Company")
|
||||||
|
if len(companies) > 1:
|
||||||
|
frappe.db.sql(""" UPDATE `tabItem Default`, `tabItem`
|
||||||
|
SET `tabItem Default`.default_supplier = `tabItem`.default_supplier
|
||||||
|
WHERE
|
||||||
|
`tabItem Default`.parent = `tabItem`.name and `tabItem Default`.default_supplier is null
|
||||||
|
and `tabItem`.default_supplier is not null and `tabItem`.default_supplier != '' """)
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
# Copyright (c) 2019, Frappe and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.model.utils.rename_field import rename_field
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc("setup", "doctype", "company")
|
||||||
|
if frappe.db.has_column('Company', 'default_terms'):
|
||||||
|
rename_field('Company', "default_terms", "default_selling_terms")
|
||||||
|
|
||||||
|
for company in frappe.get_all("Company", ["name", "default_selling_terms", "default_buying_terms"]):
|
||||||
|
if company.default_selling_terms and not company.default_buying_terms:
|
||||||
|
frappe.db.set_value("Company", company.name, "default_buying_terms", company.default_selling_terms)
|
||||||
|
|
||||||
|
frappe.reload_doc("setup", "doctype", "terms_and_conditions")
|
||||||
|
frappe.db.sql("update `tabTerms and Conditions` set selling=1, buying=1, hr=1")
|
||||||
@ -3,6 +3,8 @@ import frappe
|
|||||||
from erpnext.regional.india.setup import make_custom_fields
|
from erpnext.regional.india.setup import make_custom_fields
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
|
frappe.reload_doc("accounts", "doctype", "tax_category")
|
||||||
|
frappe.reload_doc("stock", "doctype", "item_manufacturer")
|
||||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||||
if not company:
|
if not company:
|
||||||
return
|
return
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
# Copyright (c) 2019
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.delete_doc("Page", "medical_record")
|
||||||
@ -33,10 +33,13 @@ def set_priorities_service_level():
|
|||||||
service_level_priorities = frappe.get_list("Service Level", fields=["name", "priority", "response_time", "response_time_period", "resolution_time", "resolution_time_period"])
|
service_level_priorities = frappe.get_list("Service Level", fields=["name", "priority", "response_time", "response_time_period", "resolution_time", "resolution_time_period"])
|
||||||
|
|
||||||
frappe.reload_doc("support", "doctype", "service_level")
|
frappe.reload_doc("support", "doctype", "service_level")
|
||||||
|
frappe.reload_doc("support", "doctype", "support_settings")
|
||||||
|
frappe.db.set_value('Support Settings', None, 'track_service_level_agreement', 1)
|
||||||
|
|
||||||
for service_level in service_level_priorities:
|
for service_level in service_level_priorities:
|
||||||
if service_level:
|
if service_level:
|
||||||
doc = frappe.get_doc("Service Level", service_level.name)
|
doc = frappe.get_doc("Service Level", service_level.name)
|
||||||
|
if not doc.priorities:
|
||||||
doc.append("priorities", {
|
doc.append("priorities", {
|
||||||
"priority": service_level.priority,
|
"priority": service_level.priority,
|
||||||
"default_priority": 1,
|
"default_priority": 1,
|
||||||
@ -45,6 +48,7 @@ def set_priorities_service_level():
|
|||||||
"resolution_time": service_level.resolution_time,
|
"resolution_time": service_level.resolution_time,
|
||||||
"resolution_time_period": service_level.resolution_time_period
|
"resolution_time_period": service_level.resolution_time_period
|
||||||
})
|
})
|
||||||
|
doc.flags.ignore_validate = True
|
||||||
doc.save(ignore_permissions=True)
|
doc.save(ignore_permissions=True)
|
||||||
except frappe.db.TableMissingError:
|
except frappe.db.TableMissingError:
|
||||||
frappe.reload_doc("support", "doctype", "service_level")
|
frappe.reload_doc("support", "doctype", "service_level")
|
||||||
@ -73,6 +77,7 @@ def set_priorities_service_level_agreement():
|
|||||||
"resolution_time": service_level_agreement.resolution_time,
|
"resolution_time": service_level_agreement.resolution_time,
|
||||||
"resolution_time_period": service_level_agreement.resolution_time_period
|
"resolution_time_period": service_level_agreement.resolution_time_period
|
||||||
})
|
})
|
||||||
|
doc.flags.ignore_validate = True
|
||||||
doc.save(ignore_permissions=True)
|
doc.save(ignore_permissions=True)
|
||||||
except frappe.db.TableMissingError:
|
except frappe.db.TableMissingError:
|
||||||
frappe.reload_doc("support", "doctype", "service_level_agreement")
|
frappe.reload_doc("support", "doctype", "service_level_agreement")
|
||||||
@ -2,10 +2,9 @@ import frappe
|
|||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
frappe.reload_doctype('Task')
|
frappe.reload_doctype('Task')
|
||||||
frappe.reload_doctype('Project Task')
|
|
||||||
|
|
||||||
# add "Completed" if customized
|
# add "Completed" if customized
|
||||||
for doctype in ('Task', 'Project Task'):
|
for doctype in ('Task'):
|
||||||
property_setter_name = frappe.db.exists('Property Setter', dict(doc_type = doctype, field_name = 'status', property = 'options'))
|
property_setter_name = frappe.db.exists('Property Setter', dict(doc_type = doctype, field_name = 'status', property = 'options'))
|
||||||
if property_setter_name:
|
if property_setter_name:
|
||||||
property_setter = frappe.get_doc('Property Setter', property_setter_name)
|
property_setter = frappe.get_doc('Property Setter', property_setter_name)
|
||||||
|
|||||||
17
erpnext/patches/v12_0/update_due_date_in_gle.py
Normal file
17
erpnext/patches/v12_0/update_due_date_in_gle.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc("accounts", "doctype", "gl_entry")
|
||||||
|
|
||||||
|
for doctype in ["Sales Invoice", "Purchase Invoice", "Journal Entry"]:
|
||||||
|
frappe.reload_doc("accounts", "doctype", frappe.scrub(doctype))
|
||||||
|
|
||||||
|
frappe.db.sql(""" UPDATE `tabGL Entry`, `tab{doctype}`
|
||||||
|
SET
|
||||||
|
`tabGL Entry`.due_date = `tab{doctype}`.due_date
|
||||||
|
WHERE
|
||||||
|
`tabGL Entry`.voucher_no = `tab{doctype}`.name and `tabGL Entry`.party is not null
|
||||||
|
and `tabGL Entry`.voucher_type in ('Sales Invoice', 'Purchase Invoice', 'Journal Entry')
|
||||||
|
and `tabGL Entry`.account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable'))""" #nosec
|
||||||
|
.format(doctype=doctype))
|
||||||
@ -110,4 +110,4 @@ def build_cache(item_code):
|
|||||||
def enqueue_build_cache(item_code):
|
def enqueue_build_cache(item_code):
|
||||||
if frappe.cache().hget('item_cache_build_in_progress', item_code):
|
if frappe.cache().hget('item_cache_build_in_progress', item_code):
|
||||||
return
|
return
|
||||||
frappe.enqueue(build_cache, item_code=item_code, queue='short')
|
frappe.enqueue(build_cache, item_code=item_code, queue='long')
|
||||||
|
|||||||
@ -1,23 +1,6 @@
|
|||||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
frappe.ui.form.on("Project", {
|
frappe.ui.form.on("Project", {
|
||||||
setup: function (frm) {
|
|
||||||
frm.set_indicator_formatter('title',
|
|
||||||
function (doc) {
|
|
||||||
let indicator = 'orange';
|
|
||||||
if (doc.status == 'Overdue') {
|
|
||||||
indicator = 'red';
|
|
||||||
} else if (doc.status == 'Cancelled') {
|
|
||||||
indicator = 'dark grey';
|
|
||||||
} else if (doc.status == 'Completed') {
|
|
||||||
indicator = 'green';
|
|
||||||
}
|
|
||||||
return indicator;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
onload: function (frm) {
|
onload: function (frm) {
|
||||||
var so = frappe.meta.get_docfield("Project", "sales_order");
|
var so = frappe.meta.get_docfield("Project", "sales_order");
|
||||||
so.get_route_options_for_new_doc = function (field) {
|
so.get_route_options_for_new_doc = function (field) {
|
||||||
@ -99,58 +82,4 @@ frappe.ui.form.on("Project", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
tasks_refresh: function (frm) {
|
|
||||||
var grid = frm.get_field('tasks').grid;
|
|
||||||
grid.wrapper.find('select[data-fieldname="status"]').each(function () {
|
|
||||||
if ($(this).val() === 'Open') {
|
|
||||||
$(this).addClass('input-indicator-open');
|
|
||||||
} else {
|
|
||||||
$(this).removeClass('input-indicator-open');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
status: function(frm) {
|
|
||||||
if (frm.doc.status === 'Cancelled') {
|
|
||||||
frappe.confirm(__('Set tasks in this project as cancelled?'), () => {
|
|
||||||
frm.doc.tasks = frm.doc.tasks.map(task => {
|
|
||||||
task.status = 'Cancelled';
|
|
||||||
return task;
|
|
||||||
});
|
|
||||||
frm.refresh_field('tasks');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
frappe.ui.form.on("Project Task", {
|
|
||||||
edit_task: function(frm, doctype, name) {
|
|
||||||
var doc = frappe.get_doc(doctype, name);
|
|
||||||
if(doc.task_id) {
|
|
||||||
frappe.set_route("Form", "Task", doc.task_id);
|
|
||||||
} else {
|
|
||||||
frappe.msgprint(__("Save the document first."));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
edit_timesheet: function(frm, cdt, cdn) {
|
|
||||||
var child = locals[cdt][cdn];
|
|
||||||
frappe.route_options = {"project": frm.doc.project_name, "task": child.task_id};
|
|
||||||
frappe.set_route("List", "Timesheet");
|
|
||||||
},
|
|
||||||
|
|
||||||
make_timesheet: function(frm, cdt, cdn) {
|
|
||||||
var child = locals[cdt][cdn];
|
|
||||||
frappe.model.with_doctype('Timesheet', function() {
|
|
||||||
var doc = frappe.model.get_new_doc('Timesheet');
|
|
||||||
var row = frappe.model.add_child(doc, 'time_logs');
|
|
||||||
row.project = frm.doc.project_name;
|
|
||||||
row.task = child.task_id;
|
|
||||||
frappe.set_route('Form', doc.doctype, doc.name);
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
status: function(frm, doctype, name) {
|
|
||||||
frm.trigger('tasks_refresh');
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -19,10 +19,6 @@ class Project(Document):
|
|||||||
return '{0}: {1}'.format(_(self.status), frappe.safe_decode(self.project_name))
|
return '{0}: {1}'.format(_(self.status), frappe.safe_decode(self.project_name))
|
||||||
|
|
||||||
def onload(self):
|
def onload(self):
|
||||||
"""Load project tasks for quick view"""
|
|
||||||
if not self.get('__unsaved') and not self.get("tasks"):
|
|
||||||
self.load_tasks()
|
|
||||||
|
|
||||||
self.set_onload('activity_summary', frappe.db.sql('''select activity_type,
|
self.set_onload('activity_summary', frappe.db.sql('''select activity_type,
|
||||||
sum(hours) as total_hours
|
sum(hours) as total_hours
|
||||||
from `tabTimesheet Detail` where project=%s and docstatus < 2 group by activity_type
|
from `tabTimesheet Detail` where project=%s and docstatus < 2 group by activity_type
|
||||||
@ -33,57 +29,19 @@ class Project(Document):
|
|||||||
def before_print(self):
|
def before_print(self):
|
||||||
self.onload()
|
self.onload()
|
||||||
|
|
||||||
def load_tasks(self):
|
|
||||||
"""Load `tasks` from the database"""
|
|
||||||
if frappe.flags.in_import:
|
|
||||||
return
|
|
||||||
project_task_custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task"}, "fieldname")
|
|
||||||
|
|
||||||
self.tasks = []
|
|
||||||
for task in self.get_tasks():
|
|
||||||
task_map = {
|
|
||||||
"title": task.subject,
|
|
||||||
"status": task.status,
|
|
||||||
"start_date": task.exp_start_date,
|
|
||||||
"end_date": task.exp_end_date,
|
|
||||||
"description": task.description,
|
|
||||||
"task_id": task.name,
|
|
||||||
"task_weight": task.task_weight
|
|
||||||
}
|
|
||||||
|
|
||||||
self.map_custom_fields(task, task_map, project_task_custom_fields)
|
|
||||||
|
|
||||||
self.append("tasks", task_map)
|
|
||||||
|
|
||||||
def get_tasks(self):
|
|
||||||
if self.name is None:
|
|
||||||
return {}
|
|
||||||
else:
|
|
||||||
filters = {"project": self.name}
|
|
||||||
|
|
||||||
if self.get("deleted_task_list"):
|
|
||||||
filters.update({
|
|
||||||
'name': ("not in", self.deleted_task_list)
|
|
||||||
})
|
|
||||||
|
|
||||||
return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc, status asc")
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_weights()
|
|
||||||
self.sync_tasks()
|
|
||||||
self.tasks = []
|
|
||||||
self.load_tasks()
|
|
||||||
if not self.is_new():
|
if not self.is_new():
|
||||||
self.copy_from_template()
|
self.copy_from_template()
|
||||||
self.validate_dates()
|
|
||||||
self.send_welcome_email()
|
self.send_welcome_email()
|
||||||
self.update_percent_complete(from_validate=True)
|
self.update_costing()
|
||||||
|
self.update_percent_complete()
|
||||||
|
|
||||||
def copy_from_template(self):
|
def copy_from_template(self):
|
||||||
'''
|
'''
|
||||||
Copy tasks from template
|
Copy tasks from template
|
||||||
'''
|
'''
|
||||||
if self.project_template and not len(self.tasks or []):
|
if self.project_template and not frappe.db.get_all('Task', dict(project = self.name), limit=1):
|
||||||
|
|
||||||
# has a template, and no loaded tasks, so lets create
|
# has a template, and no loaded tasks, so lets create
|
||||||
if not self.expected_start_date:
|
if not self.expected_start_date:
|
||||||
@ -108,104 +66,6 @@ class Project(Document):
|
|||||||
task_weight = task.task_weight
|
task_weight = task.task_weight
|
||||||
)).insert()
|
)).insert()
|
||||||
|
|
||||||
# reload tasks after project
|
|
||||||
self.load_tasks()
|
|
||||||
|
|
||||||
def validate_dates(self):
|
|
||||||
if self.tasks:
|
|
||||||
for d in self.tasks:
|
|
||||||
if self.expected_start_date:
|
|
||||||
if d.start_date and getdate(d.start_date) < getdate(self.expected_start_date):
|
|
||||||
frappe.throw(_("Start date of task <b>{0}</b> cannot be less than <b>{1}</b> expected start date <b>{2}</b>")
|
|
||||||
.format(d.title, self.name, self.expected_start_date))
|
|
||||||
if d.end_date and getdate(d.end_date) < getdate(self.expected_start_date):
|
|
||||||
frappe.throw(_("End date of task <b>{0}</b> cannot be less than <b>{1}</b> expected start date <b>{2}</b>")
|
|
||||||
.format(d.title, self.name, self.expected_start_date))
|
|
||||||
|
|
||||||
if self.expected_end_date:
|
|
||||||
if d.start_date and getdate(d.start_date) > getdate(self.expected_end_date):
|
|
||||||
frappe.throw(_("Start date of task <b>{0}</b> cannot be greater than <b>{1}</b> expected end date <b>{2}</b>")
|
|
||||||
.format(d.title, self.name, self.expected_end_date))
|
|
||||||
if d.end_date and getdate(d.end_date) > getdate(self.expected_end_date):
|
|
||||||
frappe.throw(_("End date of task <b>{0}</b> cannot be greater than <b>{1}</b> expected end date <b>{2}</b>")
|
|
||||||
.format(d.title, self.name, self.expected_end_date))
|
|
||||||
|
|
||||||
if self.expected_start_date and self.expected_end_date:
|
|
||||||
if getdate(self.expected_end_date) < getdate(self.expected_start_date):
|
|
||||||
frappe.throw(_("Expected End Date can not be less than Expected Start Date"))
|
|
||||||
|
|
||||||
def validate_weights(self):
|
|
||||||
for task in self.tasks:
|
|
||||||
if task.task_weight is not None:
|
|
||||||
if task.task_weight < 0:
|
|
||||||
frappe.throw(_("Task weight cannot be negative"))
|
|
||||||
|
|
||||||
def sync_tasks(self):
|
|
||||||
"""sync tasks and remove table"""
|
|
||||||
if not hasattr(self, "deleted_task_list"):
|
|
||||||
self.set("deleted_task_list", [])
|
|
||||||
|
|
||||||
if self.flags.dont_sync_tasks: return
|
|
||||||
task_names = []
|
|
||||||
|
|
||||||
existing_task_data = {}
|
|
||||||
|
|
||||||
fields = ["title", "status", "start_date", "end_date", "description", "task_weight", "task_id"]
|
|
||||||
exclude_fieldtype = ["Button", "Column Break",
|
|
||||||
"Section Break", "Table", "Read Only", "Attach", "Attach Image", "Color", "Geolocation", "HTML", "Image"]
|
|
||||||
|
|
||||||
custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task",
|
|
||||||
"fieldtype": ("not in", exclude_fieldtype)}, "fieldname")
|
|
||||||
|
|
||||||
for d in custom_fields:
|
|
||||||
fields.append(d.fieldname)
|
|
||||||
|
|
||||||
for d in frappe.get_all('Project Task',
|
|
||||||
fields = fields,
|
|
||||||
filters = {'parent': self.name}):
|
|
||||||
existing_task_data.setdefault(d.task_id, d)
|
|
||||||
|
|
||||||
for t in self.tasks:
|
|
||||||
if t.task_id:
|
|
||||||
task = frappe.get_doc("Task", t.task_id)
|
|
||||||
else:
|
|
||||||
task = frappe.new_doc("Task")
|
|
||||||
task.project = self.name
|
|
||||||
|
|
||||||
if not t.task_id or self.is_row_updated(t, existing_task_data, fields):
|
|
||||||
task.update({
|
|
||||||
"subject": t.title,
|
|
||||||
"status": t.status,
|
|
||||||
"exp_start_date": t.start_date,
|
|
||||||
"exp_end_date": t.end_date,
|
|
||||||
"description": t.description,
|
|
||||||
"task_weight": t.task_weight
|
|
||||||
})
|
|
||||||
|
|
||||||
self.map_custom_fields(t, task, custom_fields)
|
|
||||||
|
|
||||||
task.flags.ignore_links = True
|
|
||||||
task.flags.from_project = True
|
|
||||||
task.flags.ignore_feed = True
|
|
||||||
|
|
||||||
if t.task_id:
|
|
||||||
task.update({
|
|
||||||
"modified_by": frappe.session.user,
|
|
||||||
"modified": now()
|
|
||||||
})
|
|
||||||
|
|
||||||
task.run_method("validate")
|
|
||||||
task.db_update()
|
|
||||||
else:
|
|
||||||
task.save(ignore_permissions = True)
|
|
||||||
task_names.append(task.name)
|
|
||||||
else:
|
|
||||||
task_names.append(task.name)
|
|
||||||
|
|
||||||
# delete
|
|
||||||
for t in frappe.get_all("Task", ["name"], {"project": self.name, "name": ("not in", task_names)}):
|
|
||||||
self.deleted_task_list.append(t.name)
|
|
||||||
|
|
||||||
def is_row_updated(self, row, existing_task_data, fields):
|
def is_row_updated(self, row, existing_task_data, fields):
|
||||||
if self.get("__islocal") or not existing_task_data: return True
|
if self.get("__islocal") or not existing_task_data: return True
|
||||||
|
|
||||||
@ -215,28 +75,23 @@ class Project(Document):
|
|||||||
if row.get(field) != d.get(field):
|
if row.get(field) != d.get(field):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def map_custom_fields(self, source, target, custom_fields):
|
|
||||||
for field in custom_fields:
|
|
||||||
target.update({
|
|
||||||
field.fieldname: source.get(field.fieldname)
|
|
||||||
})
|
|
||||||
|
|
||||||
def update_project(self):
|
def update_project(self):
|
||||||
|
'''Called externally by Task'''
|
||||||
self.update_percent_complete()
|
self.update_percent_complete()
|
||||||
self.update_costing()
|
self.update_costing()
|
||||||
|
self.db_update()
|
||||||
|
|
||||||
def after_insert(self):
|
def after_insert(self):
|
||||||
self.copy_from_template()
|
self.copy_from_template()
|
||||||
if self.sales_order:
|
if self.sales_order:
|
||||||
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
|
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
|
||||||
|
|
||||||
def update_percent_complete(self, from_validate=False):
|
def update_percent_complete(self):
|
||||||
if not self.tasks: return
|
total = frappe.db.count('Task', dict(project=self.name))
|
||||||
total = frappe.db.sql("""select count(name) from tabTask where project=%s""", self.name)[0][0]
|
|
||||||
|
|
||||||
if not total and self.percent_complete:
|
if not total:
|
||||||
self.percent_complete = 0
|
self.percent_complete = 0
|
||||||
|
else:
|
||||||
if (self.percent_complete_method == "Task Completion" and total > 0) or (
|
if (self.percent_complete_method == "Task Completion" and total > 0) or (
|
||||||
not self.percent_complete_method and total > 0):
|
not self.percent_complete_method and total > 0):
|
||||||
completed = frappe.db.sql("""select count(name) from tabTask where
|
completed = frappe.db.sql("""select count(name) from tabTask where
|
||||||
@ -268,9 +123,6 @@ class Project(Document):
|
|||||||
else:
|
else:
|
||||||
self.status = "Open"
|
self.status = "Open"
|
||||||
|
|
||||||
if not from_validate:
|
|
||||||
self.db_update()
|
|
||||||
|
|
||||||
def update_costing(self):
|
def update_costing(self):
|
||||||
from_time_sheet = frappe.db.sql("""select
|
from_time_sheet = frappe.db.sql("""select
|
||||||
sum(costing_amount) as costing_amount,
|
sum(costing_amount) as costing_amount,
|
||||||
@ -297,7 +149,6 @@ class Project(Document):
|
|||||||
self.update_sales_amount()
|
self.update_sales_amount()
|
||||||
self.update_billed_amount()
|
self.update_billed_amount()
|
||||||
self.calculate_gross_margin()
|
self.calculate_gross_margin()
|
||||||
self.db_update()
|
|
||||||
|
|
||||||
def calculate_gross_margin(self):
|
def calculate_gross_margin(self):
|
||||||
expense_amount = (flt(self.total_costing_amount) + flt(self.total_expense_claim)
|
expense_amount = (flt(self.total_costing_amount) + flt(self.total_expense_claim)
|
||||||
@ -348,57 +199,6 @@ class Project(Document):
|
|||||||
content=content.format(*messages))
|
content=content.format(*messages))
|
||||||
user.welcome_email_sent = 1
|
user.welcome_email_sent = 1
|
||||||
|
|
||||||
def on_update(self):
|
|
||||||
self.delete_task()
|
|
||||||
self.load_tasks()
|
|
||||||
self.update_project()
|
|
||||||
self.update_dependencies_on_duplicated_project()
|
|
||||||
|
|
||||||
def delete_task(self):
|
|
||||||
if not self.get('deleted_task_list'): return
|
|
||||||
|
|
||||||
for d in self.get('deleted_task_list'):
|
|
||||||
# unlink project
|
|
||||||
frappe.db.set_value('Task', d, 'project', '')
|
|
||||||
|
|
||||||
self.deleted_task_list = []
|
|
||||||
|
|
||||||
def update_dependencies_on_duplicated_project(self):
|
|
||||||
if self.flags.dont_sync_tasks: return
|
|
||||||
if not self.copied_from:
|
|
||||||
self.copied_from = self.name
|
|
||||||
|
|
||||||
if self.name != self.copied_from and self.get('__unsaved'):
|
|
||||||
# duplicated project
|
|
||||||
dependency_map = {}
|
|
||||||
for task in self.tasks:
|
|
||||||
_task = frappe.db.get_value(
|
|
||||||
'Task',
|
|
||||||
{"subject": task.title, "project": self.copied_from},
|
|
||||||
['name', 'depends_on_tasks'],
|
|
||||||
as_dict=True
|
|
||||||
)
|
|
||||||
|
|
||||||
if _task is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
name = _task.name
|
|
||||||
|
|
||||||
dependency_map[task.title] = [x['subject'] for x in frappe.get_list(
|
|
||||||
'Task Depends On', {"parent": name}, ['subject'])]
|
|
||||||
|
|
||||||
for key, value in iteritems(dependency_map):
|
|
||||||
task_name = frappe.db.get_value('Task', {"subject": key, "project": self.name })
|
|
||||||
|
|
||||||
task_doc = frappe.get_doc('Task', task_name)
|
|
||||||
|
|
||||||
for dt in value:
|
|
||||||
dt_name = frappe.db.get_value('Task', {"subject": dt, "project": self.name})
|
|
||||||
task_doc.append('depends_on', {"task": dt_name})
|
|
||||||
|
|
||||||
task_doc.db_update()
|
|
||||||
|
|
||||||
|
|
||||||
def get_timeline_data(doctype, name):
|
def get_timeline_data(doctype, name):
|
||||||
'''Return timeline for attendance'''
|
'''Return timeline for attendance'''
|
||||||
return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*)
|
return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*)
|
||||||
|
|||||||
@ -19,18 +19,18 @@ class TestProject(unittest.TestCase):
|
|||||||
|
|
||||||
project = get_project('Test Project with Template')
|
project = get_project('Test Project with Template')
|
||||||
|
|
||||||
project.load_tasks()
|
tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc')
|
||||||
|
|
||||||
task1 = project.tasks[0]
|
task1 = tasks[0]
|
||||||
self.assertEqual(task1.title, 'Task 1')
|
self.assertEqual(task1.subject, 'Task 1')
|
||||||
self.assertEqual(task1.description, 'Task 1 description')
|
self.assertEqual(task1.description, 'Task 1 description')
|
||||||
self.assertEqual(getdate(task1.start_date), getdate('2019-01-01'))
|
self.assertEqual(getdate(task1.exp_start_date), getdate('2019-01-01'))
|
||||||
self.assertEqual(getdate(task1.end_date), getdate('2019-01-04'))
|
self.assertEqual(getdate(task1.exp_end_date), getdate('2019-01-04'))
|
||||||
|
|
||||||
self.assertEqual(len(project.tasks), 4)
|
self.assertEqual(len(tasks), 4)
|
||||||
task4 = project.tasks[3]
|
task4 = tasks[3]
|
||||||
self.assertEqual(task4.title, 'Task 4')
|
self.assertEqual(task4.subject, 'Task 4')
|
||||||
self.assertEqual(getdate(task4.end_date), getdate('2019-01-06'))
|
self.assertEqual(getdate(task4.exp_end_date), getdate('2019-01-06'))
|
||||||
|
|
||||||
def get_project(name):
|
def get_project(name):
|
||||||
template = get_project_template()
|
template = get_project_template()
|
||||||
|
|||||||
@ -1,12 +1,6 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"project_name": "_Test Project",
|
"project_name": "_Test Project",
|
||||||
"status": "Open",
|
|
||||||
"tasks":[
|
|
||||||
{
|
|
||||||
"title": "_Test Task",
|
|
||||||
"status": "Open"
|
"status": "Open"
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,430 +0,0 @@
|
|||||||
{
|
|
||||||
"allow_copy": 0,
|
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2015-02-22 11:15:28.201059",
|
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
|
||||||
"document_type": "Other",
|
|
||||||
"editable_grid": 1,
|
|
||||||
"engine": "InnoDB",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 3,
|
|
||||||
"fieldname": "title",
|
|
||||||
"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": "Title",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 3,
|
|
||||||
"default": "Open",
|
|
||||||
"fieldname": "status",
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Status",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"options": "Open\nWorking\nPending Review\nOverdue\nCompleted\nCancelled",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "task_id",
|
|
||||||
"fieldname": "edit_task",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "View Task",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "edit_timesheet",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "View Timesheet",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "make_timesheet",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Make Timesheet",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_6",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
|
||||||
"fieldname": "start_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Start Date",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
|
||||||
"default": "",
|
|
||||||
"fieldname": "end_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "End Date",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "task_weight",
|
|
||||||
"fieldtype": "Float",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Weight",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_6",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "description",
|
|
||||||
"fieldtype": "Text Editor",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Description",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "task_id",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 1,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Task ID",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"options": "Task",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 1,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2019-02-19 12:30:52.648868",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Projects",
|
|
||||||
"name": "Project Task",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [],
|
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
|
|
||||||
# For license information, please see license.txt
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import frappe
|
|
||||||
from frappe.model.document import Document
|
|
||||||
|
|
||||||
class ProjectTask(Document):
|
|
||||||
pass
|
|
||||||
@ -158,12 +158,6 @@ class Task(NestedSet):
|
|||||||
if check_if_child_exists(self.name):
|
if check_if_child_exists(self.name):
|
||||||
throw(_("Child Task exists for this Task. You can not delete this Task."))
|
throw(_("Child Task exists for this Task. You can not delete this Task."))
|
||||||
|
|
||||||
if self.project:
|
|
||||||
tasks = frappe.get_doc('Project', self.project).tasks
|
|
||||||
for task in tasks:
|
|
||||||
if task.get('task_id') == self.name:
|
|
||||||
frappe.delete_doc('Project Task', task.name)
|
|
||||||
|
|
||||||
self.update_nsm_model()
|
self.update_nsm_model()
|
||||||
|
|
||||||
def update_status(self):
|
def update_status(self):
|
||||||
|
|||||||
@ -109,7 +109,7 @@ class CallPopup {
|
|||||||
});
|
});
|
||||||
wrapper.append(`
|
wrapper.append(`
|
||||||
<div class="caller-info flex">
|
<div class="caller-info flex">
|
||||||
${frappe.avatar(null, 'avatar-xl', contact.name, contact.image)}
|
${frappe.avatar(null, 'avatar-xl', contact.name, contact.image || '')}
|
||||||
<div>
|
<div>
|
||||||
<h5>${contact_name}</h5>
|
<h5>${contact_name}</h5>
|
||||||
<div>${contact.mobile_no || ''}</div>
|
<div>${contact.mobile_no || ''}</div>
|
||||||
|
|||||||
@ -52,3 +52,13 @@ $.extend(frappe.breadcrumbs.preferred, {
|
|||||||
"Sales Partner": "Selling",
|
"Sales Partner": "Selling",
|
||||||
"Brand": "Selling"
|
"Brand": "Selling"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$.extend(frappe.breadcrumbs.module_map, {
|
||||||
|
'ERPNext Integrations': 'Integrations',
|
||||||
|
'Geo': 'Settings',
|
||||||
|
'Accounts': 'Accounting',
|
||||||
|
'Portal': 'Website',
|
||||||
|
'Utilities': 'Settings',
|
||||||
|
'Shopping Cart': 'Website',
|
||||||
|
'Contacts': 'CRM'
|
||||||
|
});
|
||||||
|
|||||||
@ -61,6 +61,14 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(this.frm.fields_dict.tc_name) {
|
||||||
|
this.frm.set_query("tc_name", function() {
|
||||||
|
return{
|
||||||
|
filters: { 'buying': 1 }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
me.frm.set_query('supplier', erpnext.queries.supplier);
|
me.frm.set_query('supplier', erpnext.queries.supplier);
|
||||||
me.frm.set_query('contact_person', erpnext.queries.contact_query);
|
me.frm.set_query('contact_person', erpnext.queries.contact_query);
|
||||||
me.frm.set_query('supplier_address', erpnext.queries.address_query);
|
me.frm.set_query('supplier_address', erpnext.queries.address_query);
|
||||||
@ -133,6 +141,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
|
|
||||||
price_list_rate: function(doc, cdt, cdn) {
|
price_list_rate: function(doc, cdt, cdn) {
|
||||||
var item = frappe.get_doc(cdt, cdn);
|
var item = frappe.get_doc(cdt, cdn);
|
||||||
|
|
||||||
frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]);
|
frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]);
|
||||||
|
|
||||||
let item_rate = item.price_list_rate;
|
let item_rate = item.price_list_rate;
|
||||||
@ -146,6 +155,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
|
|
||||||
if (item.discount_amount) {
|
if (item.discount_amount) {
|
||||||
item.rate = flt((item.price_list_rate) - (item.discount_amount), precision('rate', item));
|
item.rate = flt((item.price_list_rate) - (item.discount_amount), precision('rate', item));
|
||||||
|
} else {
|
||||||
|
item.rate = item_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.calculate_taxes_and_totals();
|
this.calculate_taxes_and_totals();
|
||||||
|
|||||||
@ -383,8 +383,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
|
|
||||||
setup_sms: function() {
|
setup_sms: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
let blacklist = ['Purchase Invoice', 'BOM'];
|
||||||
if(this.frm.doc.docstatus===1 && !in_list(["Lost", "Stopped", "Closed"], this.frm.doc.status)
|
if(this.frm.doc.docstatus===1 && !in_list(["Lost", "Stopped", "Closed"], this.frm.doc.status)
|
||||||
&& this.frm.doctype != "Purchase Invoice") {
|
&& !blacklist.includes(this.frm.doctype)) {
|
||||||
this.frm.page.add_menu_item(__('Send SMS'), function() { me.send_sms(); });
|
this.frm.page.add_menu_item(__('Send SMS'), function() { me.send_sms(); });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -584,8 +585,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
me.frm.set_value("letter_head", company_doc.default_letter_head);
|
me.frm.set_value("letter_head", company_doc.default_letter_head);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (company_doc.default_terms && me.frm.doc.doctype != "Purchase Invoice" && frappe.meta.has_field(me.frm.doc.doctype, "tc_name")) {
|
let selling_doctypes_for_tc = ["Sales Invoice", "Quotation", "Sales Order", "Delivery Note"];
|
||||||
me.frm.set_value("tc_name", company_doc.default_terms);
|
if (company_doc.default_selling_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") &&
|
||||||
|
selling_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) {
|
||||||
|
me.frm.set_value("tc_name", company_doc.default_selling_terms);
|
||||||
|
}
|
||||||
|
let buying_doctypes_for_tc = ["Request for Quotation", "Supplier Quotation", "Purchase Order",
|
||||||
|
"Material Request", "Purchase Receipt"];
|
||||||
|
// Purchase Invoice is excluded as per issue #3345
|
||||||
|
if (company_doc.default_buying_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") &&
|
||||||
|
buying_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) {
|
||||||
|
me.frm.set_value("tc_name", company_doc.default_buying_terms);
|
||||||
}
|
}
|
||||||
|
|
||||||
frappe.run_serially([
|
frappe.run_serially([
|
||||||
|
|||||||
@ -129,9 +129,7 @@ function get_filters(){
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
let dimension_filters = erpnext.get_dimension_filters();
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
|
||||||
dimension_filters.then((dimensions) => {
|
|
||||||
dimensions.forEach((dimension) => {
|
dimensions.forEach((dimension) => {
|
||||||
filters.push({
|
filters.push({
|
||||||
"fieldname": dimension["fieldname"],
|
"fieldname": dimension["fieldname"],
|
||||||
|
|||||||
@ -20,8 +20,10 @@ erpnext.SMSManager = function SMSManager(doc) {
|
|||||||
'Purchase Receipt' : 'Items has been received against purchase receipt: ' + doc.name
|
'Purchase Receipt' : 'Items has been received against purchase receipt: ' + doc.name
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_list(['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice'], doc.doctype))
|
if (in_list(['Sales Order', 'Delivery Note', 'Sales Invoice'], doc.doctype))
|
||||||
this.show(doc.contact_person, 'Customer', doc.customer, '', default_msg[doc.doctype]);
|
this.show(doc.contact_person, 'Customer', doc.customer, '', default_msg[doc.doctype]);
|
||||||
|
else if (doc.doctype === 'Quotation')
|
||||||
|
this.show(doc.contact_person, 'Customer', doc.party_name, '', default_msg[doc.doctype]);
|
||||||
else if (in_list(['Purchase Order', 'Purchase Receipt'], doc.doctype))
|
else if (in_list(['Purchase Order', 'Purchase Receipt'], doc.doctype))
|
||||||
this.show(doc.contact_person, 'Supplier', doc.supplier, '', default_msg[doc.doctype]);
|
this.show(doc.contact_person, 'Supplier', doc.supplier, '', default_msg[doc.doctype]);
|
||||||
else if (doc.doctype == 'Lead')
|
else if (doc.doctype == 'Lead')
|
||||||
|
|||||||
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