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) {
|
||||
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) {
|
||||
$('<span class="balance-area pull-right text-muted small">'
|
||||
+ (node.data.balance_in_account_currency ?
|
||||
|
@ -9,6 +9,26 @@ frappe.ui.form.on('Accounting Dimension', {
|
||||
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) {
|
||||
@ -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",
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable"
|
||||
"label": "Disable",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@ -53,7 +54,7 @@
|
||||
"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",
|
||||
"module": "Accounts",
|
||||
"name": "Accounting Dimension",
|
||||
|
@ -121,11 +121,11 @@ def delete_accounting_dimension(doc):
|
||||
@frappe.whitelist()
|
||||
def disable_dimension(doc):
|
||||
if frappe.flags.in_test:
|
||||
frappe.enqueue(start_dimension_disabling, doc=doc)
|
||||
toggle_disabling(doc=doc)
|
||||
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)
|
||||
|
||||
if doc.get('disabled'):
|
||||
|
@ -103,7 +103,7 @@ class BankReconciliation(Document):
|
||||
for d in self.get('payment_entries'):
|
||||
if d.clearance_date:
|
||||
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):
|
||||
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:
|
||||
d.clearance_date = None
|
||||
|
||||
frappe.db.set_value(d.payment_document, d.payment_entry, "clearance_date", d.clearance_date)
|
||||
frappe.db.sql("""update `tab{0}` set clearance_date = %s, modified = %s
|
||||
where name=%s""".format(d.payment_document),
|
||||
(d.clearance_date, nowdate(), d.payment_entry))
|
||||
payment_entry = frappe.get_doc(d.payment_document, d.payment_entry)
|
||||
payment_entry.db_set('clearance_date', d.clearance_date)
|
||||
|
||||
clearance_date_updated = True
|
||||
|
||||
|
@ -78,7 +78,6 @@ var validate_csv_data = function(frm) {
|
||||
|
||||
var create_import_button = function(frm) {
|
||||
frm.page.set_primary_action(__("Start Import"), function () {
|
||||
setup_progress_bar(frm);
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
||||
args: {
|
||||
@ -86,11 +85,11 @@ var create_import_button = function(frm) {
|
||||
company: frm.doc.company
|
||||
},
|
||||
freeze: true,
|
||||
freeze_message: __("Creating Accounts..."),
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
clearInterval(frm.page["interval"]);
|
||||
frm.page.set_indicator(__('Import Successfull'), 'blue');
|
||||
frappe.hide_progress();
|
||||
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):
|
||||
''' 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_path = file_doc.get_full_path()
|
||||
|
||||
@ -96,15 +99,27 @@ def build_forest(data):
|
||||
return [child] + return_parent(data, parent_account)
|
||||
|
||||
charts_map, paths = {}, []
|
||||
|
||||
line_no = 3
|
||||
error_messages = []
|
||||
|
||||
for i in data:
|
||||
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] = {}
|
||||
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 root_type: charts_map[account_name]["root_type"] = root_type
|
||||
if account_number: charts_map[account_name]["account_number"] = account_number
|
||||
path = return_parent(data, account_name)[::-1]
|
||||
paths.append(path) # List of path is created
|
||||
line_no += 1
|
||||
|
||||
if error_messages:
|
||||
frappe.throw("<br>".join(error_messages))
|
||||
|
||||
out = {}
|
||||
for path in paths:
|
||||
@ -150,22 +165,27 @@ def validate_root(accounts):
|
||||
if len(roots) < 4:
|
||||
return _("Number of root accounts cannot be less than 4")
|
||||
|
||||
error_messages = []
|
||||
|
||||
for account in roots:
|
||||
if not account.get("root_type"):
|
||||
return _("Please enter Root Type for - {0}").format(account.get("account_name"))
|
||||
elif account.get("root_type") not in ("Asset", "Liability", "Expense", "Income", "Equity"):
|
||||
return _('Root Type for "{0}" must be one of the Asset, Liability, Income, Expense and Equity').format(account.get("account_name"))
|
||||
if not account.get("root_type") and 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") and account.get("account_name"):
|
||||
error_messages.append("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity".format(account.get("account_name")))
|
||||
|
||||
if error_messages:
|
||||
return "<br>".join(error_messages)
|
||||
|
||||
def validate_account_types(accounts):
|
||||
account_types_for_ledger = ["Cost of Goods Sold", "Depreciation", "Fixed Asset", "Payable", "Receivable", "Stock Adjustment"]
|
||||
account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group']]
|
||||
account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group'] == 1]
|
||||
|
||||
missing = list(set(account_types_for_ledger) - set(account_types))
|
||||
if missing:
|
||||
return _("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing))
|
||||
|
||||
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))
|
||||
if missing:
|
||||
|
@ -21,9 +21,29 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
|
||||
|
||||
refresh: function(frm) {
|
||||
if(frm.doc.docstatus==1) {
|
||||
frm.add_custom_button(__('Create Journal Entry'), function() {
|
||||
return frm.events.make_jv(frm);
|
||||
});
|
||||
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() {
|
||||
return frm.events.make_jv(frm);
|
||||
});
|
||||
}
|
||||
}, 'Journal Entry');
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -848,6 +848,39 @@
|
||||
"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,
|
||||
"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,
|
||||
@ -861,7 +894,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-01-07 07:05:00.366399",
|
||||
"modified": "2019-05-01 07:05:00.366399",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "GL Entry",
|
||||
|
@ -502,6 +502,7 @@ class JournalEntry(AccountsController):
|
||||
self.get_gl_dict({
|
||||
"account": d.account,
|
||||
"party_type": d.party_type,
|
||||
"due_date": self.due_date,
|
||||
"party": d.party,
|
||||
"against": d.against_account,
|
||||
"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_name", r.message.party_name),
|
||||
() => frm.events.get_outstanding_documents(frm),
|
||||
() => frm.clear_table("references"),
|
||||
() => frm.events.hide_unhide_fields(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,
|
||||
"paid_from_account_currency", "paid_from_account_balance", function(frm) {
|
||||
if (frm.doc.payment_type == "Receive") {
|
||||
frm.events.get_outstanding_documents(frm);
|
||||
} else if (frm.doc.payment_type == "Pay") {
|
||||
if (frm.doc.payment_type == "Pay") {
|
||||
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,
|
||||
"paid_to_account_currency", "paid_to_account_balance", function(frm) {
|
||||
if(frm.doc.payment_type == "Pay") {
|
||||
frm.events.get_outstanding_documents(frm);
|
||||
} else if (frm.doc.payment_type == "Receive") {
|
||||
if (frm.doc.payment_type == "Receive") {
|
||||
if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
|
||||
if(frm.doc.source_exchange_rate) {
|
||||
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
|
||||
@ -533,26 +529,87 @@ frappe.ui.form.on('Payment Entry', {
|
||||
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");
|
||||
|
||||
if(!frm.doc.party) return;
|
||||
if(!frm.doc.party) {
|
||||
return;
|
||||
}
|
||||
|
||||
frm.events.check_mandatory_to_fetch(frm);
|
||||
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
||||
|
||||
var args = {
|
||||
"posting_date": frm.doc.posting_date,
|
||||
"company": frm.doc.company,
|
||||
"party_type": frm.doc.party_type,
|
||||
"payment_type": frm.doc.payment_type,
|
||||
"party": frm.doc.party,
|
||||
"party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to,
|
||||
"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: {
|
||||
"posting_date": frm.doc.posting_date,
|
||||
"company": frm.doc.company,
|
||||
"party_type": frm.doc.party_type,
|
||||
"payment_type": frm.doc.payment_type,
|
||||
"party": frm.doc.party,
|
||||
"party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to,
|
||||
"cost_center": frm.doc.cost_center
|
||||
}
|
||||
args:args
|
||||
},
|
||||
callback: function(r, rt) {
|
||||
if(r.message) {
|
||||
@ -608,25 +665,11 @@ frappe.ui.form.on('Payment Entry', {
|
||||
|
||||
frm.events.allocate_party_amount_against_ref_docs(frm,
|
||||
(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) {
|
||||
var total_positive_outstanding_including_order = 0;
|
||||
var total_negative_outstanding = 0;
|
||||
@ -677,7 +720,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
|
||||
$.each(frm.doc.references || [], function(i, row) {
|
||||
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 >= 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") {
|
||||
frm.events.get_outstanding_documents(frm);
|
||||
frm.clear_table("references");
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
@ -40,7 +40,7 @@
|
||||
"target_exchange_rate",
|
||||
"base_received_amount",
|
||||
"section_break_14",
|
||||
"allocate_payment_amount",
|
||||
"get_outstanding_invoice",
|
||||
"references",
|
||||
"section_break_34",
|
||||
"total_allocated_amount",
|
||||
@ -325,19 +325,15 @@
|
||||
"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)",
|
||||
"fieldname": "section_break_14",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Reference"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "eval:in_list(['Pay', 'Receive'], doc.payment_type)",
|
||||
"fieldname": "allocate_payment_amount",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allocate Payment Amount"
|
||||
"fieldname": "get_outstanding_invoice",
|
||||
"fieldtype": "Button",
|
||||
"label": "Get Outstanding Invoice"
|
||||
},
|
||||
{
|
||||
"fieldname": "references",
|
||||
@ -570,7 +566,7 @@
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-05-25 22:02:40.575822",
|
||||
"modified": "2019-05-27 15:53:21.108857",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry",
|
||||
|
@ -574,8 +574,8 @@ def get_outstanding_reference_documents(args):
|
||||
# Get negative outstanding sales /purchase invoices
|
||||
negative_outstanding_invoices = []
|
||||
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"),
|
||||
args.get("party"), args.get("party_account"), party_account_currency, company_currency)
|
||||
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"),
|
||||
args.get("party_account"), args.get("company"), party_account_currency, company_currency)
|
||||
|
||||
# Get positive outstanding sales /purchase invoices/ Fees
|
||||
condition = ""
|
||||
@ -585,10 +585,23 @@ def get_outstanding_reference_documents(args):
|
||||
|
||||
# Add cost center condition
|
||||
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"),
|
||||
args.get("party_account"), condition=condition)
|
||||
args.get("party_account"), filters=args, condition=condition, limit=100)
|
||||
|
||||
for d in outstanding_invoices:
|
||||
d["exchange_rate"] = 1
|
||||
@ -606,12 +619,19 @@ def get_outstanding_reference_documents(args):
|
||||
orders_to_be_billed = []
|
||||
if (args.get("party_type") != "Student"):
|
||||
orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"),
|
||||
args.get("party"), party_account_currency, company_currency)
|
||||
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":
|
||||
voucher_type = 'Sales Order'
|
||||
elif party_type == "Supplier":
|
||||
@ -641,6 +661,7 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
||||
where
|
||||
{party_type} = %s
|
||||
and docstatus = 1
|
||||
and company = %s
|
||||
and ifnull(status, "") != "Closed"
|
||||
and {ref_field} > advance_paid
|
||||
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,
|
||||
"party_type": scrub(party_type),
|
||||
"condition": condition
|
||||
}), party, as_dict=True)
|
||||
}), (party, company), as_dict=True)
|
||||
|
||||
order_list = []
|
||||
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
|
||||
# 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)
|
||||
@ -663,7 +688,8 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
||||
|
||||
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"
|
||||
supplier_condition = ""
|
||||
if voucher_type == "Purchase Invoice":
|
||||
@ -684,7 +710,8 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac
|
||||
from
|
||||
`tab{voucher_type}`
|
||||
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}
|
||||
order by
|
||||
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_account": "debit_to" if party_type == "Customer" else "credit_to",
|
||||
"cost_center": cost_center
|
||||
}), (party, party_account), as_dict=True)
|
||||
}), (party, party_account, company), as_dict=True)
|
||||
|
||||
|
||||
@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_amount = paid_amount
|
||||
pe.received_amount = received_amount
|
||||
pe.allocate_payment_amount = 1
|
||||
pe.letter_head = doc.get("letter_head")
|
||||
|
||||
if pe.party_type in ["Customer", "Supplier"]:
|
||||
|
@ -8,6 +8,10 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) {
|
||||
return { filters: { selling: 1 } };
|
||||
});
|
||||
|
||||
frm.set_query("tc_name", function() {
|
||||
return { filters: { selling: 1 } };
|
||||
});
|
||||
|
||||
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||
return erpnext.queries.warehouse(frm.doc);
|
||||
});
|
||||
|
@ -337,7 +337,8 @@ class PurchaseInvoice(BuyingController):
|
||||
if not self.is_return:
|
||||
self.update_against_document_in_jv()
|
||||
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,
|
||||
# because updating ordered qty in bin depends upon updated ordered qty in PO
|
||||
@ -416,6 +417,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"account": self.credit_to,
|
||||
"party_type": "Supplier",
|
||||
"party": self.supplier,
|
||||
"due_date": self.due_date,
|
||||
"against": self.against_expense_account,
|
||||
"credit": 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)
|
||||
}, 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()):
|
||||
|
||||
expense_account = (item.expense_account
|
||||
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
|
||||
|
||||
gl_entries.append(
|
||||
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,
|
||||
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||
"debit_in_account_currency": (flt(item.base_net_amount,
|
||||
@ -769,7 +775,8 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
if not self.is_return:
|
||||
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,
|
||||
# because updating ordered qty in bin depends upon updated ordered qty in PO
|
||||
|
@ -191,9 +191,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
method: "erpnext.selling.doctype.quotation.quotation.make_sales_invoice",
|
||||
source_doctype: "Quotation",
|
||||
target: me.frm,
|
||||
setters: {
|
||||
customer: me.frm.doc.customer || undefined,
|
||||
},
|
||||
setters: [{
|
||||
fieldtype: 'Link',
|
||||
label: __('Customer'),
|
||||
options: 'Customer',
|
||||
fieldname: 'party_name',
|
||||
default: me.frm.doc.customer,
|
||||
}],
|
||||
get_query_filters: {
|
||||
docstatus: 1,
|
||||
status: ["!=", "Lost"],
|
||||
|
@ -734,6 +734,7 @@ class SalesInvoice(SellingController):
|
||||
"account": self.debit_to,
|
||||
"party_type": "Customer",
|
||||
"party": self.customer,
|
||||
"due_date": self.due_date,
|
||||
"against": self.against_income_account,
|
||||
"debit": 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.set_status("Sold" if self.docstatus==1 else None)
|
||||
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(
|
||||
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,
|
||||
"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"))
|
||||
|
@ -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
|
||||
from frappe import _, scrub
|
||||
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):
|
||||
def __init__(self, filters=None):
|
||||
@ -553,6 +554,14 @@ class ReceivablePayableReport(object):
|
||||
conditions.append("account in (%s)" % ','.join(['%s'] *len(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
|
||||
|
||||
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();
|
||||
|
||||
dimension_filters.then((dimensions) => {
|
||||
erpnext.dimension_filters.then((dimensions) => {
|
||||
dimensions.forEach((dimension) => {
|
||||
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
|
||||
// filter. It won't be used in cash flow for now so we pop it. Please take
|
||||
// of this if you are working here.
|
||||
frappe.query_reports["Cash Flow"]["filters"].pop();
|
||||
|
||||
frappe.query_reports["Cash Flow"]["filters"].push({
|
||||
"fieldname": "accumulated_values",
|
||||
"label": __("Accumulated Values"),
|
||||
"fieldtype": "Check"
|
||||
});
|
||||
frappe.query_reports["Cash Flow"]["filters"].splice(5, 1);
|
||||
|
||||
frappe.query_reports["Cash Flow"]["filters"].push({
|
||||
"fieldname": "include_default_book_entries",
|
||||
"label": __("Include Default Book Entries"),
|
||||
"fieldtype": "Check"
|
||||
});
|
||||
frappe.query_reports["Cash Flow"]["filters"].push(
|
||||
{
|
||||
"fieldname": "accumulated_values",
|
||||
"label": __("Accumulated Values"),
|
||||
"fieldtype": "Check"
|
||||
},
|
||||
{
|
||||
"fieldname": "include_default_book_entries",
|
||||
"label": __("Include Default Book Entries"),
|
||||
"fieldtype": "Check"
|
||||
}
|
||||
);
|
||||
});
|
@ -69,7 +69,9 @@ def execute(filters=None):
|
||||
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)
|
||||
|
||||
return columns, data
|
||||
chart = get_chart_data(columns, data)
|
||||
|
||||
return columns, data, None, chart
|
||||
|
||||
def get_cash_flow_accounts():
|
||||
operation_accounts = {
|
||||
@ -171,4 +173,21 @@ def add_total_row_account(out, data, label, period_list, currency, consolidated
|
||||
total_row["total"] += row["total"]
|
||||
|
||||
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();
|
||||
|
||||
dimension_filters.then((dimensions) => {
|
||||
erpnext.dimension_filters.then((dimensions) => {
|
||||
dimensions.forEach((dimension) => {
|
||||
frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{
|
||||
"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 six import iteritems
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||
from collections import OrderedDict
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters:
|
||||
@ -274,7 +275,7 @@ def group_by_field(group_by):
|
||||
return 'voucher_no'
|
||||
|
||||
def initialize_gle_map(gl_entries, filters):
|
||||
gle_map = frappe._dict()
|
||||
gle_map = OrderedDict()
|
||||
group_by = group_by_field(filters.get('group_by'))
|
||||
|
||||
for gle in gl_entries:
|
||||
|
@ -93,4 +93,6 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss):
|
||||
else:
|
||||
chart["type"] = "line"
|
||||
|
||||
chart["fieldtype"] = "Currency"
|
||||
|
||||
return chart
|
@ -16,7 +16,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
"fieldname": "based_on",
|
||||
"label": __("Based On"),
|
||||
"fieldtype": "Select",
|
||||
"options": "Cost Center\nProject",
|
||||
"options": ["Cost Center", "Project"],
|
||||
"default": "Cost Center",
|
||||
"reqd": 1
|
||||
},
|
||||
@ -104,5 +104,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
"parent_field": "parent_account",
|
||||
"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':
|
||||
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)
|
||||
else:
|
||||
elif based_on == 'project':
|
||||
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):
|
||||
if not accounts:
|
||||
@ -42,7 +51,7 @@ def get_data(accounts, filters, based_on):
|
||||
accumulate_values_into_parents(accounts, accounts_by_name)
|
||||
|
||||
data = prepare_data(accounts, filters, total_row, parent_children_map, based_on)
|
||||
data = filter_out_zero_value_rows(data, parent_children_map,
|
||||
data = filter_out_zero_value_rows(data, parent_children_map,
|
||||
show_zero_values=filters.get("show_zero_values"))
|
||||
|
||||
return data
|
||||
@ -112,14 +121,14 @@ def prepare_data(accounts, filters, total_row, parent_children_map, based_on):
|
||||
|
||||
for key in value_fields:
|
||||
row[key] = flt(d.get(key, 0.0), 3)
|
||||
|
||||
|
||||
if abs(row[key]) >= 0.005:
|
||||
# ignore zero values
|
||||
has_value = True
|
||||
|
||||
row["has_value"] = has_value
|
||||
data.append(row)
|
||||
|
||||
|
||||
data.extend([{},total_row])
|
||||
|
||||
return data
|
||||
@ -174,7 +183,7 @@ def set_gl_entries_by_account(company, from_date, to_date, based_on, gl_entries_
|
||||
if from_date:
|
||||
additional_conditions.append("and posting_date >= %(from_date)s")
|
||||
|
||||
gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit,
|
||||
gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit,
|
||||
is_opening, (select root_type from `tabAccount` where name = account) as type
|
||||
from `tabGL Entry` where company=%(company)s
|
||||
{additional_conditions}
|
||||
|
@ -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
|
||||
from frappe.utils import flt
|
||||
from frappe import msgprint, _
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(filters)
|
||||
@ -163,6 +164,16 @@ def get_conditions(filters):
|
||||
where parent=`tabSales Invoice`.name
|
||||
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
|
||||
|
||||
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();
|
||||
|
||||
dimension_filters.then((dimensions) => {
|
||||
erpnext.dimension_filters.then((dimensions) => {
|
||||
dimensions.forEach((dimension) => {
|
||||
frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{
|
||||
"fieldname": dimension["fieldname"],
|
||||
|
@ -628,7 +628,7 @@ def get_held_invoices(party_type, party):
|
||||
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 = []
|
||||
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("""
|
||||
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
|
||||
`tabGL Entry`
|
||||
where
|
||||
@ -677,7 +678,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
||||
""".format(payment_dr_or_cr=payment_dr_or_cr), {
|
||||
"party_type": party_type,
|
||||
"party": party,
|
||||
"account": account,
|
||||
"account": account
|
||||
}, as_dict=True)
|
||||
|
||||
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)
|
||||
outstanding_amount = flt(d.invoice_amount - payment_amount, precision)
|
||||
if outstanding_amount > 0.5 / (10**precision):
|
||||
if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices:
|
||||
due_date = frappe.db.get_value(
|
||||
d.voucher_type, d.voucher_no, "posting_date" if party_type == "Employee" else "due_date")
|
||||
if (filters.get("outstanding_amt_greater_than") and
|
||||
not (outstanding_amount >= filters.get("outstanding_amt_greater_than") and
|
||||
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(
|
||||
frappe._dict({
|
||||
'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),
|
||||
'payment_amount': payment_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(' ', '_')
|
||||
fields = [
|
||||
'name as value',
|
||||
'root_type',
|
||||
'is_group as expandable'
|
||||
]
|
||||
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])
|
||||
|
||||
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])
|
||||
|
||||
else:
|
||||
|
@ -66,10 +66,11 @@
|
||||
"net_total",
|
||||
"total_net_weight",
|
||||
"taxes_section",
|
||||
"taxes_and_charges",
|
||||
"tax_category",
|
||||
"column_break_50",
|
||||
"shipping_rule",
|
||||
"section_break_52",
|
||||
"taxes_and_charges",
|
||||
"taxes",
|
||||
"sec_tax_breakup",
|
||||
"other_charges_calculation",
|
||||
@ -569,7 +570,7 @@
|
||||
{
|
||||
"fieldname": "taxes_and_charges",
|
||||
"fieldtype": "Link",
|
||||
"label": "Taxes and Charges",
|
||||
"label": "Purchase Taxes and Charges Template",
|
||||
"oldfieldname": "purchase_other_charges",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Purchase Taxes and Charges Template",
|
||||
@ -1032,12 +1033,18 @@
|
||||
"fieldname": "update_auto_repeat_reference",
|
||||
"fieldtype": "Button",
|
||||
"label": "Update Auto Repeat Reference"
|
||||
},
|
||||
{
|
||||
"fieldname": "tax_category",
|
||||
"fieldtype": "Link",
|
||||
"label": "Tax Category",
|
||||
"options": "Tax Category"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-06-24 21:22:05.483429",
|
||||
"modified": "2019-07-11 18:25:49.509343",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
@ -26,8 +26,8 @@ def get_data():
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"name": "medical_record",
|
||||
"label": _("Patient Medical Record"),
|
||||
"name": "patient_history",
|
||||
"label": _("Patient History"),
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
|
@ -3,43 +3,6 @@ from frappe import _
|
||||
|
||||
def get_data():
|
||||
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"),
|
||||
"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"),
|
||||
"icon": "fa fa-wrench",
|
||||
|
@ -281,9 +281,9 @@ def get_data():
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Item Shortage Report",
|
||||
"route": "#Report/Bin/Item Shortage Report",
|
||||
"doctype": "Purchase Receipt"
|
||||
"doctype": "Bin"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
|
@ -21,13 +21,7 @@ def get_data():
|
||||
"type": "doctype",
|
||||
"name": "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),
|
||||
idx desc, name
|
||||
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),
|
||||
key=searchfield), {
|
||||
'txt': '%' + txt + '%',
|
||||
key=frappe.db.escape(searchfield)),
|
||||
{
|
||||
'txt': "%"+frappe.db.escape(txt)+"%",
|
||||
'_txt': txt.replace("%", ""),
|
||||
'start': start or 0,
|
||||
'page_len': page_len or 20
|
||||
|
@ -75,7 +75,7 @@ def validate_returned_items(doc):
|
||||
|
||||
items_returned = False
|
||||
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:
|
||||
frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
|
||||
.format(d.idx, d.item_code, doc.doctype, doc.return_against))
|
||||
@ -107,6 +107,9 @@ def validate_returned_items(doc):
|
||||
|
||||
items_returned = True
|
||||
|
||||
elif d.item_name:
|
||||
items_returned = True
|
||||
|
||||
if not items_returned:
|
||||
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`
|
||||
set %(target_parent_field)s = round(
|
||||
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
|
||||
from `tab%(target_dt)s` where parent="%(name)s" having sum(abs(%(target_ref_field)s)) > 0), 0), 6)
|
||||
%(update_modified)s
|
||||
|
@ -15,6 +15,9 @@ class calculate_taxes_and_totals(object):
|
||||
self.calculate()
|
||||
|
||||
def calculate(self):
|
||||
if not len(self.doc.get("items")):
|
||||
return
|
||||
|
||||
self.discount_amount_applied = False
|
||||
self._calculate()
|
||||
|
||||
|
@ -7,7 +7,9 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from requests_oauthlib import OAuth2Session
|
||||
import json, requests
|
||||
import json
|
||||
import requests
|
||||
import traceback
|
||||
from erpnext import encode_company_abbr
|
||||
|
||||
# QuickBooks requires a redirect URL, User will be redirect to this URL
|
||||
@ -32,7 +34,6 @@ def callback(*args, **kwargs):
|
||||
class QuickBooksMigrator(Document):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(QuickBooksMigrator, self).__init__(*args, **kwargs)
|
||||
from pprint import pprint
|
||||
self.oauth = OAuth2Session(
|
||||
client_id=self.client_id,
|
||||
redirect_uri=self.redirect_url,
|
||||
@ -46,7 +47,9 @@ class QuickBooksMigrator(Document):
|
||||
if self.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_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:
|
||||
self.authorization_url = self.oauth.authorization_url(self.authorization_endpoint)[0]
|
||||
|
||||
@ -218,7 +221,7 @@ class QuickBooksMigrator(Document):
|
||||
|
||||
def _fetch_general_ledger(self):
|
||||
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,
|
||||
params={
|
||||
"columns": ",".join(["tx_date", "txn_type", "credit_amt", "debt_amt"]),
|
||||
@ -493,17 +496,17 @@ class QuickBooksMigrator(Document):
|
||||
"account_currency": customer["CurrencyRef"]["value"],
|
||||
"company": self.company,
|
||||
})[0]["name"]
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
receivable_account = None
|
||||
erpcustomer = frappe.get_doc({
|
||||
"doctype": "Customer",
|
||||
"quickbooks_id": customer["Id"],
|
||||
"customer_name" : encode_company_abbr(customer["DisplayName"], self.company),
|
||||
"customer_type" : "Individual",
|
||||
"customer_group" : "Commercial",
|
||||
"customer_name": encode_company_abbr(customer["DisplayName"], self.company),
|
||||
"customer_type": "Individual",
|
||||
"customer_group": "Commercial",
|
||||
"default_currency": customer["CurrencyRef"]["value"],
|
||||
"accounts": [{"company": self.company, "account": receivable_account}],
|
||||
"territory" : "All Territories",
|
||||
"territory": "All Territories",
|
||||
"company": self.company,
|
||||
}).insert()
|
||||
if "BillAddr" in customer:
|
||||
@ -521,7 +524,7 @@ class QuickBooksMigrator(Document):
|
||||
item_dict = {
|
||||
"doctype": "Item",
|
||||
"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",
|
||||
"is_stock_item": 0,
|
||||
"item_group": "All Item Groups",
|
||||
@ -549,14 +552,14 @@ class QuickBooksMigrator(Document):
|
||||
erpsupplier = frappe.get_doc({
|
||||
"doctype": "Supplier",
|
||||
"quickbooks_id": vendor["Id"],
|
||||
"supplier_name" : encode_company_abbr(vendor["DisplayName"], self.company),
|
||||
"supplier_group" : "All Supplier Groups",
|
||||
"supplier_name": encode_company_abbr(vendor["DisplayName"], self.company),
|
||||
"supplier_group": "All Supplier Groups",
|
||||
"company": self.company,
|
||||
}).insert()
|
||||
if "BillAddr" in vendor:
|
||||
self._create_address(erpsupplier, "Supplier", vendor["BillAddr"], "Billing")
|
||||
if "ShipAddr" in vendor:
|
||||
self._create_address(erpsupplier, "Supplier",vendor["ShipAddr"], "Shipping")
|
||||
self._create_address(erpsupplier, "Supplier", vendor["ShipAddr"], "Shipping")
|
||||
except Exception as e:
|
||||
self._log_error(e)
|
||||
|
||||
@ -829,7 +832,7 @@ class QuickBooksMigrator(Document):
|
||||
"currency": invoice["CurrencyRef"]["value"],
|
||||
"conversion_rate": invoice.get("ExchangeRate", 1),
|
||||
"posting_date": invoice["TxnDate"],
|
||||
"due_date": invoice.get("DueDate", invoice["TxnDate"]),
|
||||
"due_date": invoice.get("DueDate", invoice["TxnDate"]),
|
||||
"credit_to": credit_to_account,
|
||||
"supplier": frappe.get_all("Supplier",
|
||||
filters={
|
||||
@ -1200,7 +1203,7 @@ class QuickBooksMigrator(Document):
|
||||
|
||||
|
||||
def _create_address(self, entity, doctype, address, address_type):
|
||||
try :
|
||||
try:
|
||||
if not frappe.db.exists({"doctype": "Address", "quickbooks_id": address["Id"]}):
|
||||
frappe.get_doc({
|
||||
"doctype": "Address",
|
||||
@ -1252,8 +1255,6 @@ class QuickBooksMigrator(Document):
|
||||
|
||||
|
||||
def _log_error(self, execption, data=""):
|
||||
import json, traceback
|
||||
traceback.print_exc()
|
||||
frappe.log_error(title="QuickBooks Migration Error",
|
||||
message="\n".join([
|
||||
"Data",
|
||||
|
@ -21,9 +21,9 @@ frappe.ui.form.on('Patient', {
|
||||
});
|
||||
}
|
||||
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.set_route("medical_record");
|
||||
frappe.set_route("patient_history");
|
||||
},"View");
|
||||
}
|
||||
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){
|
||||
frm.add_custom_button(__('Medical Record'), function() {
|
||||
frm.add_custom_button(__('Patient History'), function() {
|
||||
frappe.route_options = {"patient": frm.doc.patient};
|
||||
frappe.set_route("medical_record");
|
||||
frappe.set_route("patient_history");
|
||||
},__("View"));
|
||||
}
|
||||
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) {
|
||||
frappe.route_options = {"patient": frm.doc.patient};
|
||||
frappe.set_route("medical_record");
|
||||
frappe.set_route("patient_history");
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
.medical_record-row > * {
|
||||
z-index: -999;
|
||||
}
|
||||
|
||||
.date-indicator {
|
||||
background:none;
|
||||
font-size:12px;
|
||||
@ -35,6 +39,61 @@
|
||||
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 {
|
||||
background: #5e64ff;
|
||||
}
|
||||
@ -65,8 +124,3 @@
|
||||
#page-medical_record .list-filters {
|
||||
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,
|
||||
"creation": "2016-06-09 11:33:14.025787",
|
||||
"creation": "2018-08-08 17:09:13.816199",
|
||||
"docstatus": 0,
|
||||
"doctype": "Page",
|
||||
"icon": "icon-play",
|
||||
"icon": "",
|
||||
"idx": 0,
|
||||
"modified": "2018-08-06 11:40:39.705660",
|
||||
"modified": "2018-08-08 17:09:55.969424",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "medical_record",
|
||||
"name": "patient_history",
|
||||
"owner": "Administrator",
|
||||
"page_name": "medical_record",
|
||||
"page_name": "patient_history",
|
||||
"restrict_to_domain": "Healthcare",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Healthcare Administrator"
|
||||
},
|
||||
{
|
||||
"role": "Physician"
|
||||
}
|
||||
@ -21,5 +24,5 @@
|
||||
"standard": "Yes",
|
||||
"style": null,
|
||||
"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)
|
||||
each["occupied_out_of_vacant"] = occupancy_msg
|
||||
return hc_service_units
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_patient_vitals(patient, from_date=None, to_date=None):
|
||||
if not patient: return
|
||||
vitals = frappe.db.sql("""select * from `tabVital Signs` where \
|
||||
docstatus=1 and patient=%s order by signs_date, signs_time""", \
|
||||
(patient), as_dict=1)
|
||||
if vitals and vitals[0]:
|
||||
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):
|
||||
def test_employee_onboarding_incomplete_task(self):
|
||||
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()
|
||||
applicant = get_job_applicant()
|
||||
onboarding = frappe.new_doc('Employee Onboarding')
|
||||
@ -39,9 +39,10 @@ class TestEmployeeOnboarding(unittest.TestCase):
|
||||
|
||||
# complete the task
|
||||
project = frappe.get_doc('Project', onboarding.project)
|
||||
project.load_tasks()
|
||||
project.tasks[0].status = 'Completed'
|
||||
project.save()
|
||||
for task in frappe.get_all('Task', dict(project=project.name)):
|
||||
task = frappe.get_doc('Task', task.name)
|
||||
task.status = 'Completed'
|
||||
task.save()
|
||||
|
||||
# make employee
|
||||
onboarding.reload()
|
||||
@ -71,4 +72,3 @@ def _set_up():
|
||||
project = "Employee Onboarding : Test Researcher - test@researcher.com"
|
||||
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 `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_dependencies = ['Employee']
|
||||
company_name = '_Test Company 4'
|
||||
|
||||
|
||||
class TestExpenseClaim(unittest.TestCase):
|
||||
def test_total_expense_claim_for_project(self):
|
||||
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 `tabExpense Claim` where project='_Test Project 1'")
|
||||
frappe.db.sql("update `tabExpense Claim` set project = '', task = ''")
|
||||
|
||||
frappe.get_doc({
|
||||
"project_name": "_Test Project 1",
|
||||
"doctype": "Project",
|
||||
"doctype": "Project"
|
||||
}).save()
|
||||
|
||||
task = frappe.get_doc({
|
||||
"doctype": "Task",
|
||||
"subject": "_Test Project Task 1",
|
||||
"project": "_Test Project 1"
|
||||
}).save()
|
||||
task = frappe.get_doc(dict(
|
||||
doctype = 'Task',
|
||||
subject = '_Test Project Task 1',
|
||||
status = 'Open',
|
||||
project = '_Test Project 1'
|
||||
)).insert()
|
||||
|
||||
task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"})
|
||||
payable_account = get_payable_account("Wind Power LLC")
|
||||
make_expense_claim(payable_account, 300, 200, "Wind Power LLC","Travel Expenses - WP", "_Test Project 1", task_name)
|
||||
task_name = task.name
|
||||
payable_account = get_payable_account(company_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("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("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)
|
||||
|
||||
def test_expense_claim_status(self):
|
||||
payable_account = get_payable_account("Wind Power LLC")
|
||||
expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP")
|
||||
payable_account = get_payable_account(company_name)
|
||||
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 = frappe.get_doc(je_dict)
|
||||
@ -66,9 +69,9 @@ class TestExpenseClaim(unittest.TestCase):
|
||||
self.assertEqual(expense_claim.status, "Unpaid")
|
||||
|
||||
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()
|
||||
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()
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||
@ -78,9 +81,9 @@ class TestExpenseClaim(unittest.TestCase):
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
expected_values = dict((d[0], d) for d in [
|
||||
['CGST - WP',10.0, 0.0],
|
||||
[payable_account, 0.0, 210.0],
|
||||
["Travel Expenses - WP", 200.0, 0.0]
|
||||
['CGST - _TC4',18.0, 0.0],
|
||||
[payable_account, 0.0, 218.0],
|
||||
["Travel Expenses - _TC4", 200.0, 0.0]
|
||||
])
|
||||
|
||||
for gle in gl_entries:
|
||||
@ -89,14 +92,14 @@ class TestExpenseClaim(unittest.TestCase):
|
||||
self.assertEquals(expected_values[gle.account][2], gle.credit)
|
||||
|
||||
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({
|
||||
"doctype": "Expense Claim",
|
||||
"employee": "_T-Employee-00001",
|
||||
"payable_account": payable_account,
|
||||
"approval_status": "Rejected",
|
||||
"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()
|
||||
|
||||
@ -111,9 +114,9 @@ def get_payable_account(company):
|
||||
|
||||
def generate_taxes():
|
||||
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')
|
||||
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':[{
|
||||
"account_head": account,
|
||||
"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):
|
||||
employee = frappe.db.get_value("Employee", {"status": "Active"})
|
||||
currency = frappe.db.get_value('Company', company, 'default_currency')
|
||||
expense_claim = {
|
||||
"doctype": "Expense Claim",
|
||||
"employee": employee,
|
||||
"payable_account": payable_account,
|
||||
"approval_status": "Approved",
|
||||
"company": company,
|
||||
'currency': currency,
|
||||
"expenses":
|
||||
[{"expense_type": "Travel",
|
||||
"default_account": account,
|
||||
'currency': currency,
|
||||
"amount": amount,
|
||||
"sanctioned_amount": sanctioned_amount}]}
|
||||
if taxes:
|
||||
|
@ -4,6 +4,12 @@
|
||||
frappe.provide("erpnext.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) {
|
||||
erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function (r) {
|
||||
if (!r.exc) {
|
||||
|
@ -39,31 +39,19 @@ frappe.ui.form.on('Loan', {
|
||||
},
|
||||
|
||||
refresh: function (frm) {
|
||||
if (frm.doc.docstatus == 1 && frm.doc.status == "Sanctioned") {
|
||||
frm.add_custom_button(__('Create Disbursement Entry'), function() {
|
||||
frm.trigger("make_jv");
|
||||
})
|
||||
}
|
||||
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.trigger("make_repayment_entry");
|
||||
})
|
||||
if (frm.doc.docstatus == 1) {
|
||||
if (frm.doc.status == "Sanctioned") {
|
||||
frm.add_custom_button(__('Create Disbursement Entry'), function() {
|
||||
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)) {
|
||||
frm.add_custom_button(__('Create Repayment Entry'), function() {
|
||||
frm.trigger("make_repayment_entry");
|
||||
}).addClass("btn-primary");
|
||||
}
|
||||
}
|
||||
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) {
|
||||
frappe.call({
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
@ -20,6 +21,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "applicant_type",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
@ -53,6 +55,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "applicant",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
@ -86,6 +89,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "applicant_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@ -118,6 +122,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "loan_application",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -151,6 +156,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "loan_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -184,6 +190,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@ -215,7 +222,8 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"default": "Today",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
@ -248,6 +256,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -282,6 +291,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Sanctioned",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
@ -299,7 +309,7 @@
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
@ -316,6 +326,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.applicant_type==\"Employee\"",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "repay_from_salary",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
@ -348,6 +359,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@ -380,6 +392,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "loan_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@ -415,6 +428,7 @@
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fetch_from": "loan_type.rate_of_interest",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "rate_of_interest",
|
||||
"fieldtype": "Percent",
|
||||
"hidden": 0,
|
||||
@ -448,6 +462,8 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.status==\"Disbursed\"",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "disbursement_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
@ -480,6 +496,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "repayment_start_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
@ -499,7 +516,7 @@
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
@ -512,6 +529,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@ -544,6 +562,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Repay Over Number of Periods",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "repayment_method",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
@ -579,6 +598,7 @@
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"depends_on": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "repayment_periods",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
@ -613,6 +633,7 @@
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"depends_on": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "monthly_repayment_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@ -646,6 +667,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "account_info",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@ -678,6 +700,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "mode_of_payment",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -711,6 +734,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "payment_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -744,6 +768,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@ -775,6 +800,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "loan_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -808,6 +834,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "interest_income_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -841,6 +868,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break_15",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@ -873,6 +901,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "repayment_schedule",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
@ -906,6 +935,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break_17",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@ -939,6 +969,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "total_payment",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@ -972,6 +1003,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_19",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@ -1004,6 +1036,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "total_interest_payable",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@ -1037,6 +1070,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "total_amount_paid",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@ -1070,6 +1104,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -1106,7 +1141,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-21 16:15:53.267145",
|
||||
"modified": "2019-07-10 13:04:20.953694",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Loan",
|
||||
@ -1149,7 +1184,6 @@
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"user_permission_doctypes": "[\"Employee\"]",
|
||||
"write": 0
|
||||
}
|
||||
],
|
||||
|
@ -6,29 +6,33 @@ from __future__ import unicode_literals
|
||||
import frappe, math, json
|
||||
import erpnext
|
||||
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
|
||||
|
||||
class Loan(AccountsController):
|
||||
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)
|
||||
self.set_missing_fields()
|
||||
self.make_repayment_schedule()
|
||||
self.set_repayment_period()
|
||||
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
|
||||
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.set_repayment_period()
|
||||
self.calculate_totals()
|
||||
|
||||
def make_jv_entry(self):
|
||||
self.check_permission('write')
|
||||
@ -105,20 +109,31 @@ def update_total_amount_paid(doc):
|
||||
frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid)
|
||||
|
||||
def update_disbursement_status(doc):
|
||||
disbursement = frappe.db.sql("""select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount
|
||||
from `tabGL Entry` where account = %s and against_voucher_type = 'Loan' and against_voucher = %s""",
|
||||
(doc.payment_account, doc.name), as_dict=1)[0]
|
||||
if disbursement.disbursed_amount == doc.loan_amount:
|
||||
frappe.db.set_value("Loan", doc.name , "status", "Disbursed")
|
||||
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)
|
||||
disbursement = frappe.db.sql("""
|
||||
select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount
|
||||
from `tabGL Entry`
|
||||
where account = %s and against_voucher_type = 'Loan' and against_voucher = %s
|
||||
""", (doc.payment_account, doc.name), as_dict=1)[0]
|
||||
|
||||
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:
|
||||
frappe.throw(_("Please enter Repayment Periods"))
|
||||
|
||||
@ -222,4 +237,4 @@ def make_jv_entry(loan, company, loan_account, applicant_type, applicant, loan_a
|
||||
"reference_name": loan,
|
||||
})
|
||||
journal_entry.set("accounts", account_amt_list)
|
||||
return journal_entry.as_dict()
|
||||
return journal_entry.as_dict()
|
||||
|
@ -23,9 +23,8 @@ frappe.ui.form.on('Loan Application', {
|
||||
},
|
||||
add_toolbar_buttons: function(frm) {
|
||||
if (frm.doc.status == "Approved") {
|
||||
frm.add_custom_button(__('Loan'), function() {
|
||||
frm.add_custom_button(__('Create Loan'), function() {
|
||||
frappe.call({
|
||||
type: "GET",
|
||||
method: "erpnext.hr.doctype.loan_application.loan_application.make_loan",
|
||||
args: {
|
||||
"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.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):
|
||||
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.get_repayment_details()
|
||||
|
||||
@ -29,14 +29,14 @@ class LoanApplication(Document):
|
||||
if self.repayment_method == "Repay Fixed Amount per Period":
|
||||
monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
|
||||
if monthly_interest_rate:
|
||||
self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
|
||||
self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
|
||||
math.log(self.repayment_amount - (self.loan_amount*monthly_interest_rate))) /
|
||||
(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
|
||||
@ -47,9 +47,9 @@ class LoanApplication(Document):
|
||||
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_loan(source_name, target_doc = None):
|
||||
doclist = get_mapped_doc("Loan Application", source_name, {
|
||||
|
@ -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):
|
||||
amount, additional_amount = 0, 0
|
||||
elif not row.amount:
|
||||
amount = row.default_amount + row.additional_amount
|
||||
amount = flt(row.default_amount) + flt(row.additional_amount)
|
||||
|
||||
# apply rounding
|
||||
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',
|
||||
'attendance':('is', 'not set'),
|
||||
'time':('>=', self.process_attendance_after),
|
||||
'shift_actual_start': ('<', self.last_sync_of_checkin),
|
||||
'shift_actual_end': ('<', self.last_sync_of_checkin),
|
||||
'shift': self.name
|
||||
}
|
||||
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
|
||||
|
||||
frappe.ui.form.on('Blanket Order', {
|
||||
onload: function(frm) {
|
||||
frm.trigger('set_tc_name_filter');
|
||||
},
|
||||
|
||||
setup: function(frm) {
|
||||
frm.add_fetch("customer", "customer_name", "customer_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,
|
||||
item.description,
|
||||
item.image,
|
||||
bom.project,
|
||||
item.stock_uom,
|
||||
item.allow_alternative_item,
|
||||
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']
|
||||
}
|
||||
]
|
||||
}
|
@ -10,4 +10,4 @@ def get_data():
|
||||
'items': ['Material Request', 'Stock Entry']
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
execute:frappe.delete_doc_if_exists("Page", "support-analytics")
|
||||
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.set_default_action_for_quality_inspection
|
||||
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_priority_for_support
|
||||
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
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("accounts", "doctype", "tax_category")
|
||||
frappe.reload_doc("stock", "doctype", "item_manufacturer")
|
||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||
if not company:
|
||||
return
|
||||
|
@ -0,0 +1,7 @@
|
||||
# Copyright (c) 2019
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.delete_doc("Page", "medical_record")
|
@ -33,19 +33,23 @@ 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"])
|
||||
|
||||
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:
|
||||
if service_level:
|
||||
doc = frappe.get_doc("Service Level", service_level.name)
|
||||
doc.append("priorities", {
|
||||
"priority": service_level.priority,
|
||||
"default_priority": 1,
|
||||
"response_time": service_level.response_time,
|
||||
"response_time_period": service_level.response_time_period,
|
||||
"resolution_time": service_level.resolution_time,
|
||||
"resolution_time_period": service_level.resolution_time_period
|
||||
})
|
||||
doc.save(ignore_permissions=True)
|
||||
if not doc.priorities:
|
||||
doc.append("priorities", {
|
||||
"priority": service_level.priority,
|
||||
"default_priority": 1,
|
||||
"response_time": service_level.response_time,
|
||||
"response_time_period": service_level.response_time_period,
|
||||
"resolution_time": service_level.resolution_time,
|
||||
"resolution_time_period": service_level.resolution_time_period
|
||||
})
|
||||
doc.flags.ignore_validate = True
|
||||
doc.save(ignore_permissions=True)
|
||||
except frappe.db.TableMissingError:
|
||||
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_period": service_level_agreement.resolution_time_period
|
||||
})
|
||||
doc.flags.ignore_validate = True
|
||||
doc.save(ignore_permissions=True)
|
||||
except frappe.db.TableMissingError:
|
||||
frappe.reload_doc("support", "doctype", "service_level_agreement")
|
@ -2,10 +2,9 @@ import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doctype('Task')
|
||||
frappe.reload_doctype('Project Task')
|
||||
|
||||
# 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'))
|
||||
if 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):
|
||||
if frappe.cache().hget('item_cache_build_in_progress', item_code):
|
||||
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
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
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) {
|
||||
var so = frappe.meta.get_docfield("Project", "sales_order");
|
||||
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))
|
||||
|
||||
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,
|
||||
sum(hours) as total_hours
|
||||
from `tabTimesheet Detail` where project=%s and docstatus < 2 group by activity_type
|
||||
@ -33,57 +29,19 @@ class Project(Document):
|
||||
def before_print(self):
|
||||
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):
|
||||
self.validate_weights()
|
||||
self.sync_tasks()
|
||||
self.tasks = []
|
||||
self.load_tasks()
|
||||
if not self.is_new():
|
||||
self.copy_from_template()
|
||||
self.validate_dates()
|
||||
self.send_welcome_email()
|
||||
self.update_percent_complete(from_validate=True)
|
||||
self.update_costing()
|
||||
self.update_percent_complete()
|
||||
|
||||
def copy_from_template(self):
|
||||
'''
|
||||
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
|
||||
if not self.expected_start_date:
|
||||
@ -108,104 +66,6 @@ class Project(Document):
|
||||
task_weight = task.task_weight
|
||||
)).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):
|
||||
if self.get("__islocal") or not existing_task_data: return True
|
||||
|
||||
@ -215,48 +75,43 @@ class Project(Document):
|
||||
if row.get(field) != d.get(field):
|
||||
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):
|
||||
'''Called externally by Task'''
|
||||
self.update_percent_complete()
|
||||
self.update_costing()
|
||||
self.db_update()
|
||||
|
||||
def after_insert(self):
|
||||
self.copy_from_template()
|
||||
if self.sales_order:
|
||||
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
|
||||
|
||||
def update_percent_complete(self, from_validate=False):
|
||||
if not self.tasks: return
|
||||
total = frappe.db.sql("""select count(name) from tabTask where project=%s""", self.name)[0][0]
|
||||
def update_percent_complete(self):
|
||||
total = frappe.db.count('Task', dict(project=self.name))
|
||||
|
||||
if not total and self.percent_complete:
|
||||
if not total:
|
||||
self.percent_complete = 0
|
||||
else:
|
||||
if (self.percent_complete_method == "Task Completion" and total > 0) or (
|
||||
not self.percent_complete_method and total > 0):
|
||||
completed = frappe.db.sql("""select count(name) from tabTask where
|
||||
project=%s and status in ('Cancelled', 'Completed')""", self.name)[0][0]
|
||||
self.percent_complete = flt(flt(completed) / total * 100, 2)
|
||||
|
||||
if (self.percent_complete_method == "Task Completion" and total > 0) or (
|
||||
not self.percent_complete_method and total > 0):
|
||||
completed = frappe.db.sql("""select count(name) from tabTask where
|
||||
project=%s and status in ('Cancelled', 'Completed')""", self.name)[0][0]
|
||||
self.percent_complete = flt(flt(completed) / total * 100, 2)
|
||||
if (self.percent_complete_method == "Task Progress" and total > 0):
|
||||
progress = frappe.db.sql("""select sum(progress) from tabTask where
|
||||
project=%s""", self.name)[0][0]
|
||||
self.percent_complete = flt(flt(progress) / total, 2)
|
||||
|
||||
if (self.percent_complete_method == "Task Progress" and total > 0):
|
||||
progress = frappe.db.sql("""select sum(progress) from tabTask where
|
||||
project=%s""", self.name)[0][0]
|
||||
self.percent_complete = flt(flt(progress) / total, 2)
|
||||
|
||||
if (self.percent_complete_method == "Task Weight" and total > 0):
|
||||
weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where
|
||||
project=%s""", self.name)[0][0]
|
||||
weighted_progress = frappe.db.sql("""select progress, task_weight from tabTask where
|
||||
project=%s""", self.name, as_dict=1)
|
||||
pct_complete = 0
|
||||
for row in weighted_progress:
|
||||
pct_complete += row["progress"] * frappe.utils.safe_div(row["task_weight"], weight_sum)
|
||||
self.percent_complete = flt(flt(pct_complete), 2)
|
||||
if (self.percent_complete_method == "Task Weight" and total > 0):
|
||||
weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where
|
||||
project=%s""", self.name)[0][0]
|
||||
weighted_progress = frappe.db.sql("""select progress, task_weight from tabTask where
|
||||
project=%s""", self.name, as_dict=1)
|
||||
pct_complete = 0
|
||||
for row in weighted_progress:
|
||||
pct_complete += row["progress"] * frappe.utils.safe_div(row["task_weight"], weight_sum)
|
||||
self.percent_complete = flt(flt(pct_complete), 2)
|
||||
|
||||
# don't update status if it is cancelled
|
||||
if self.status == 'Cancelled':
|
||||
@ -268,9 +123,6 @@ class Project(Document):
|
||||
else:
|
||||
self.status = "Open"
|
||||
|
||||
if not from_validate:
|
||||
self.db_update()
|
||||
|
||||
def update_costing(self):
|
||||
from_time_sheet = frappe.db.sql("""select
|
||||
sum(costing_amount) as costing_amount,
|
||||
@ -297,7 +149,6 @@ class Project(Document):
|
||||
self.update_sales_amount()
|
||||
self.update_billed_amount()
|
||||
self.calculate_gross_margin()
|
||||
self.db_update()
|
||||
|
||||
def calculate_gross_margin(self):
|
||||
expense_amount = (flt(self.total_costing_amount) + flt(self.total_expense_claim)
|
||||
@ -348,57 +199,6 @@ class Project(Document):
|
||||
content=content.format(*messages))
|
||||
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):
|
||||
'''Return timeline for attendance'''
|
||||
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.load_tasks()
|
||||
tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc')
|
||||
|
||||
task1 = project.tasks[0]
|
||||
self.assertEqual(task1.title, 'Task 1')
|
||||
task1 = tasks[0]
|
||||
self.assertEqual(task1.subject, 'Task 1')
|
||||
self.assertEqual(task1.description, 'Task 1 description')
|
||||
self.assertEqual(getdate(task1.start_date), getdate('2019-01-01'))
|
||||
self.assertEqual(getdate(task1.end_date), getdate('2019-01-04'))
|
||||
self.assertEqual(getdate(task1.exp_start_date), getdate('2019-01-01'))
|
||||
self.assertEqual(getdate(task1.exp_end_date), getdate('2019-01-04'))
|
||||
|
||||
self.assertEqual(len(project.tasks), 4)
|
||||
task4 = project.tasks[3]
|
||||
self.assertEqual(task4.title, 'Task 4')
|
||||
self.assertEqual(getdate(task4.end_date), getdate('2019-01-06'))
|
||||
self.assertEqual(len(tasks), 4)
|
||||
task4 = tasks[3]
|
||||
self.assertEqual(task4.subject, 'Task 4')
|
||||
self.assertEqual(getdate(task4.exp_end_date), getdate('2019-01-06'))
|
||||
|
||||
def get_project(name):
|
||||
template = get_project_template()
|
||||
|
@ -1,12 +1,6 @@
|
||||
[
|
||||
{
|
||||
"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):
|
||||
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()
|
||||
|
||||
def update_status(self):
|
||||
|
@ -109,7 +109,7 @@ class CallPopup {
|
||||
});
|
||||
wrapper.append(`
|
||||
<div class="caller-info flex">
|
||||
${frappe.avatar(null, 'avatar-xl', contact.name, contact.image)}
|
||||
${frappe.avatar(null, 'avatar-xl', contact.name, contact.image || '')}
|
||||
<div>
|
||||
<h5>${contact_name}</h5>
|
||||
<div>${contact.mobile_no || ''}</div>
|
||||
|
@ -52,3 +52,13 @@ $.extend(frappe.breadcrumbs.preferred, {
|
||||
"Sales Partner": "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('contact_person', erpnext.queries.contact_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) {
|
||||
var item = frappe.get_doc(cdt, cdn);
|
||||
|
||||
frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]);
|
||||
|
||||
let item_rate = item.price_list_rate;
|
||||
@ -146,6 +155,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
||||
|
||||
if (item.discount_amount) {
|
||||
item.rate = flt((item.price_list_rate) - (item.discount_amount), precision('rate', item));
|
||||
} else {
|
||||
item.rate = item_rate;
|
||||
}
|
||||
|
||||
this.calculate_taxes_and_totals();
|
||||
|
@ -383,8 +383,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
|
||||
setup_sms: function() {
|
||||
var me = this;
|
||||
let blacklist = ['Purchase Invoice', 'BOM'];
|
||||
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(); });
|
||||
}
|
||||
},
|
||||
@ -584,8 +585,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
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")) {
|
||||
me.frm.set_value("tc_name", company_doc.default_terms);
|
||||
let selling_doctypes_for_tc = ["Sales Invoice", "Quotation", "Sales Order", "Delivery Note"];
|
||||
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([
|
||||
|
@ -129,9 +129,7 @@ function get_filters(){
|
||||
}
|
||||
]
|
||||
|
||||
let dimension_filters = erpnext.get_dimension_filters();
|
||||
|
||||
dimension_filters.then((dimensions) => {
|
||||
erpnext.dimension_filters.then((dimensions) => {
|
||||
dimensions.forEach((dimension) => {
|
||||
filters.push({
|
||||
"fieldname": dimension["fieldname"],
|
||||
|
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