Merge branch 'develop' into feat-manufacturer-is-missing-contacts
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
index fcbd10f..dd20632 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
@@ -9,6 +9,26 @@
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 @@
}
});
},
-
- disabled: function(frm) {
- frappe.call({
- method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension",
- args: {
- doc: frm.doc
- }
- });
- }
});
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json
index 1e2bb92..57543a0 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json
@@ -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",
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 91d03be..15ace72 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -121,11 +121,11 @@
@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'):
diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
index 28807c4..90cdf83 100644
--- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
+++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
@@ -103,7 +103,7 @@
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 @@
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
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js
index dad75b4..0d5456e 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js
@@ -21,9 +21,29 @@
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');
}
},
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json
index 333d39a..a232a95 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.json
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json
@@ -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",
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 3132c93..8fbddb9 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -498,6 +498,7 @@
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")),
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 2c382c5..f17b2cb 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -302,7 +302,7 @@
},
() => 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 @@
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 @@
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 @@
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 @@
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 @@
$.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 @@
},
() => {
if(frm.doc.payment_type != "Internal") {
- frm.events.get_outstanding_documents(frm);
+ frm.clear_table("references");
}
}
]);
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index fcaa570..a85eccd 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -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",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index ea76126..699f046 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -574,8 +574,8 @@
# 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 @@
# 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 @@
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 @@
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 @@
"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 @@
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 @@
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 @@
"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 @@
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"]:
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 1b03896..219d989 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -337,7 +337,8 @@
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 @@
"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 \
@@ -773,7 +775,8 @@
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
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 07494a2..c3c3e26 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -187,9 +187,13 @@
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"],
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 71bdc2a..f8bb971 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -734,6 +734,7 @@
"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 \
diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js
index 70f193e..f6a561f 100644
--- a/erpnext/accounts/report/accounts_payable/accounts_payable.js
+++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js
@@ -108,3 +108,14 @@
});
}
}
+
+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"]
+ });
+ });
+});
diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
index 06499ad..ec4f0c9 100644
--- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
+++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
@@ -92,3 +92,14 @@
});
}
}
+
+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"]
+ });
+ });
+});
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
index 3661afe..2a45454 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
@@ -172,3 +172,14 @@
});
}
}
+
+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"]
+ });
+ });
+});
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 2973748..0cda2c1 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -5,6 +5,7 @@
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 @@
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):
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
index f9162ad..a7c0787 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
@@ -116,3 +116,14 @@
});
}
}
+
+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"]
+ });
+ });
+});
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
index b2072f0..f2a33a8 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
@@ -63,9 +63,7 @@
]
}
-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"]);
});
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js
index 0422111..03940f4 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.js
+++ b/erpnext/accounts/report/cash_flow/cash_flow.js
@@ -8,17 +8,19 @@
// 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"
+ }
+ );
});
\ No newline at end of file
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 32af644..ea82575 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -159,9 +159,7 @@
]
}
-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"],
diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
index 48d7361..ac11868 100644
--- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
+++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
@@ -93,4 +93,6 @@
else:
chart["type"] = "line"
+ chart["fieldtype"] = "Currency"
+
return chart
\ No newline at end of file
diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
index 80b50b9..d6864b5 100644
--- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
+++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
@@ -16,7 +16,7 @@
"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 @@
"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"]);
+ });
+ });
+});
diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
index a0d8c5f..6e9b31f 100644
--- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
+++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
@@ -24,8 +24,17 @@
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 @@
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 @@
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 @@
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}
diff --git a/erpnext/accounts/report/sales_register/sales_register.js b/erpnext/accounts/report/sales_register/sales_register.js
index 0b48882c..442aa12 100644
--- a/erpnext/accounts/report/sales_register/sales_register.js
+++ b/erpnext/accounts/report/sales_register/sales_register.js
@@ -67,3 +67,14 @@
}
]
}
+
+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"]
+ });
+ });
+});
diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py
index de60995..d08056f 100644
--- a/erpnext/accounts/report/sales_register/sales_register.py
+++ b/erpnext/accounts/report/sales_register/sales_register.py
@@ -5,6 +5,7 @@
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 @@
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):
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js
index 8bc7280..73d2ab3 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.js
+++ b/erpnext/accounts/report/trial_balance/trial_balance.js
@@ -96,9 +96,7 @@
}
});
-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"],
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 7a1f6c5..542c7e4 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -628,7 +628,7 @@
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 @@
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 @@
""".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 @@
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 @@
'invoice_amount': flt(d.invoice_amount),
'payment_amount': payment_amount,
'outstanding_amount': outstanding_amount,
- 'due_date': due_date
+ 'due_date': d.due_date
})
)
diff --git a/erpnext/config/stock.py b/erpnext/config/stock.py
index 84aa847..f5e48b3 100644
--- a/erpnext/config/stock.py
+++ b/erpnext/config/stock.py
@@ -281,9 +281,9 @@
},
{
"type": "report",
+ "is_query_report": True,
"name": "Item Shortage Report",
- "route": "#Report/Bin/Item Shortage Report",
- "doctype": "Purchase Receipt"
+ "doctype": "Bin"
},
{
"type": "report",
diff --git a/erpnext/config/support.py b/erpnext/config/support.py
index 0301bb3..36b4214 100644
--- a/erpnext/config/support.py
+++ b/erpnext/config/support.py
@@ -97,4 +97,15 @@
},
]
},
+ {
+ "label": _("Settings"),
+ "icon": "fa fa-list",
+ "items": [
+ {
+ "type": "doctype",
+ "name": "Support Settings",
+ "label": _("Support Settings"),
+ },
+ ]
+ },
]
\ No newline at end of file
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index d74bc0e..47c9f0a 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -206,10 +206,11 @@
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
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 8cf11f7..2fddcdf 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -75,7 +75,7 @@
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 @@
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"))
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index f70870b..b193ac2 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -294,7 +294,7 @@
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
diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py
index 29a1a2b..96a533e 100644
--- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py
+++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py
@@ -7,7 +7,9 @@
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 @@
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 @@
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 @@
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 @@
"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 @@
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 @@
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 @@
"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 @@
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 @@
def _log_error(self, execption, data=""):
- import json, traceback
- traceback.print_exc()
frappe.log_error(title="QuickBooks Migration Error",
message="\n".join([
"Data",
diff --git a/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py b/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py
deleted file mode 100644
index b6c6502..0000000
--- a/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py
+++ /dev/null
@@ -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
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
index 5e7f276..35c9f72 100644
--- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
+++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
@@ -12,7 +12,7 @@
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 @@
# 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 @@
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)
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
index 92fdc09..6618a4f 100644
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
@@ -10,33 +10,36 @@
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 @@
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 @@
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 @@
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 @@
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 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 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:
diff --git a/erpnext/hr/doctype/loan/loan.js b/erpnext/hr/doctype/loan/loan.js
index e1b4178..3f5c30c 100644
--- a/erpnext/hr/doctype/loan/loan.js
+++ b/erpnext/hr/doctype/loan/loan.js
@@ -39,31 +39,19 @@
},
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({
diff --git a/erpnext/hr/doctype/loan/loan.json b/erpnext/hr/doctype/loan/loan.json
index 587b301..505b601 100644
--- a/erpnext/hr/doctype/loan/loan.json
+++ b/erpnext/hr/doctype/loan/loan.json
@@ -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
}
],
diff --git a/erpnext/hr/doctype/loan/loan.py b/erpnext/hr/doctype/loan/loan.py
index 58c9b8f..a803863 100644
--- a/erpnext/hr/doctype/loan/loan.py
+++ b/erpnext/hr/doctype/loan/loan.py
@@ -6,29 +6,33 @@
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 @@
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 @@
"reference_name": loan,
})
journal_entry.set("accounts", account_amt_list)
- return journal_entry.as_dict()
\ No newline at end of file
+ return journal_entry.as_dict()
diff --git a/erpnext/hr/doctype/loan_application/loan_application.js b/erpnext/hr/doctype/loan_application/loan_application.js
index febcbd8..a73b62a 100644
--- a/erpnext/hr/doctype/loan_application/loan_application.js
+++ b/erpnext/hr/doctype/loan_application/loan_application.js
@@ -23,9 +23,8 @@
},
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 @@
}
}
});
- })
+ }).addClass("btn-primary");
}
}
});
diff --git a/erpnext/hr/doctype/loan_application/loan_application.py b/erpnext/hr/doctype/loan_application/loan_application.py
index 706c964..5dbcf15 100644
--- a/erpnext/hr/doctype/loan_application/loan_application.py
+++ b/erpnext/hr/doctype/loan_application/loan_application.py
@@ -9,11 +9,11 @@
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 @@
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 @@
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, {
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 75eb794..766f675 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -594,6 +594,7 @@
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,
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index bd41c7e..571c2dc 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -615,4 +615,7 @@
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
diff --git a/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py b/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py
new file mode 100644
index 0000000..347dec1
--- /dev/null
+++ b/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py
@@ -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 != '' """)
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_priority_for_support.py b/erpnext/patches/v12_0/set_priority_for_support.py
index cc29039..5096ed4 100644
--- a/erpnext/patches/v12_0/set_priority_for_support.py
+++ b/erpnext/patches/v12_0/set_priority_for_support.py
@@ -33,19 +33,23 @@
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 @@
"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")
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_task_status.py b/erpnext/patches/v12_0/set_task_status.py
index 32b8177..70f6509 100644
--- a/erpnext/patches/v12_0/set_task_status.py
+++ b/erpnext/patches/v12_0/set_task_status.py
@@ -2,10 +2,9 @@
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)
diff --git a/erpnext/patches/v12_0/update_due_date_in_gle.py b/erpnext/patches/v12_0/update_due_date_in_gle.py
new file mode 100644
index 0000000..3484872
--- /dev/null
+++ b/erpnext/patches/v12_0/update_due_date_in_gle.py
@@ -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))
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 528c7cd..5613f08 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -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 @@
});
},
- 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');
- },
});
diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json
index 2fc507b..b4536c0 100644
--- a/erpnext/projects/doctype/project/project.json
+++ b/erpnext/projects/doctype/project/project.json
@@ -1,1974 +1,487 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:project_name",
- "beta": 0,
"creation": "2013-03-07 11:55:07",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
- "editable_grid": 0,
"engine": "InnoDB",
+ "field_order": [
+ "project_name",
+ "status",
+ "project_type",
+ "is_active",
+ "percent_complete_method",
+ "percent_complete",
+ "column_break_5",
+ "project_template",
+ "expected_start_date",
+ "expected_end_date",
+ "priority",
+ "department",
+ "customer_details",
+ "customer",
+ "column_break_14",
+ "sales_order",
+ "users_section",
+ "users",
+ "copied_from",
+ "section_break0",
+ "notes",
+ "section_break_18",
+ "actual_start_date",
+ "actual_time",
+ "column_break_20",
+ "actual_end_date",
+ "project_details",
+ "estimated_costing",
+ "total_costing_amount",
+ "total_expense_claim",
+ "total_purchase_cost",
+ "company",
+ "column_break_28",
+ "total_sales_amount",
+ "total_billable_amount",
+ "total_billed_amount",
+ "total_consumed_material_cost",
+ "cost_center",
+ "margin",
+ "gross_margin",
+ "column_break_37",
+ "per_gross_margin",
+ "monitor_progress",
+ "collect_progress",
+ "holiday_list",
+ "frequency",
+ "from_time",
+ "to_time",
+ "first_email",
+ "second_email",
+ "daily_time_to_send",
+ "day_to_send",
+ "weekly_time_to_send",
+ "column_break_45",
+ "message"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
"fieldname": "project_name",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Project Name",
- "length": 0,
- "no_copy": 0,
"oldfieldtype": "Data",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
"reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
"unique": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"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": 0,
"in_standard_filter": 1,
"label": "Status",
- "length": 0,
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "Open\nCompleted\nCancelled",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
"reqd": 1,
- "search_index": 1,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "search_index": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "project_type",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Project Type",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "project_type",
"oldfieldtype": "Data",
- "options": "Project Type",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Project Type"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "is_active",
"fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Is Active",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "is_active",
"oldfieldtype": "Select",
- "options": "Yes\nNo",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Yes\nNo"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "Task Completion",
"fieldname": "percent_complete_method",
"fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "% Complete Method",
- "length": 0,
- "no_copy": 0,
- "options": "Task Completion\nTask Progress\nTask Weight",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "options": "Task Completion\nTask Progress\nTask Weight"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
"bold": 1,
- "collapsible": 0,
- "columns": 0,
"fieldname": "percent_complete",
"fieldtype": "Percent",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "% Completed",
- "length": 0,
"no_copy": 1,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_5",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "project_template",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "From Template",
- "length": 0,
- "no_copy": 0,
- "options": "Project Template",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Project Template"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "expected_start_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": "Expected Start Date",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "project_start_date",
- "oldfieldtype": "Date",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "oldfieldtype": "Date"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
"bold": 1,
- "collapsible": 0,
- "columns": 0,
"fieldname": "expected_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": "Expected End Date",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "completion_date",
- "oldfieldtype": "Date",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "oldfieldtype": "Date"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "priority",
"fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
"in_standard_filter": 1,
"label": "Priority",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "priority",
"oldfieldtype": "Select",
- "options": "Medium\nLow\nHigh",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Medium\nLow\nHigh"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "department",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Department",
- "length": 0,
- "no_copy": 0,
- "options": "Department",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Department"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 1,
- "columns": 0,
"fieldname": "customer_details",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Customer Details",
- "length": 0,
- "no_copy": 0,
"oldfieldtype": "Section Break",
- "options": "fa fa-user",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "fa fa-user"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "customer",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
"in_global_search": 1,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Customer",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "customer",
"oldfieldtype": "Link",
"options": "Customer",
- "permlevel": 0,
"print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 1,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "search_index": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_14",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "sales_order",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Sales Order",
- "length": 0,
- "no_copy": 0,
- "options": "Sales Order",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "options": "Sales Order"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 1,
- "columns": 0,
"fieldname": "users_section",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Users",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Users"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"description": "Project will be accessible on the website to these users",
"fieldname": "users",
"fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Users",
- "length": 0,
- "no_copy": 0,
- "options": "Project User",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Project User"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sb_milestones",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Tasks",
- "length": 0,
- "no_copy": 0,
- "oldfieldtype": "Section Break",
- "options": "fa fa-flag",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "tasks",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Tasks",
- "length": 0,
- "no_copy": 0,
- "options": "Project Task",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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": "copied_from",
"fieldtype": "Data",
"hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Copied From",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 1,
- "columns": 0,
"fieldname": "section_break0",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Notes",
- "length": 0,
- "no_copy": 0,
"oldfieldtype": "Section Break",
- "options": "fa fa-list",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "fa fa-list"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "notes",
"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": "Notes",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "notes",
- "oldfieldtype": "Text Editor",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "oldfieldtype": "Text Editor"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 1,
- "columns": 0,
"fieldname": "section_break_18",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Start and End Dates",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Start and End Dates"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "actual_start_date",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Actual Start Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "actual_time",
"fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Actual Time (in Hours)",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_20",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "actual_end_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": "Actual End Date",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "act_completion_date",
"oldfieldtype": "Date",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 1,
- "columns": 0,
"fieldname": "project_details",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Costing and Billing",
- "length": 0,
- "no_copy": 0,
"oldfieldtype": "Section Break",
- "options": "fa fa-money",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "fa fa-money"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "estimated_costing",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "Estimated Cost",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "project_value",
"oldfieldtype": "Currency",
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Company:company:default_currency"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
"fieldname": "total_costing_amount",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Total Costing Amount (via Timesheets)",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
"fieldname": "total_expense_claim",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Total Expense Claim (via Expense Claims)",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "total_purchase_cost",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Total Purchase Cost (via Purchase Invoice)",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "company",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Company",
- "length": 0,
- "no_copy": 0,
"options": "Company",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "remember_last_selected_value": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_28",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "total_sales_amount",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Total Sales Amount (via Sales Order)",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
"fieldname": "total_billable_amount",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Total Billable Amount (via Timesheets)",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "total_billed_amount",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Total Billed Amount (via Sales Invoices)",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "total_consumed_material_cost",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Total Consumed Material Cost (via Stock Entry)",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "cost_center",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Default Cost Center",
- "length": 0,
- "no_copy": 0,
- "options": "Cost Center",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Cost Center"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 1,
- "columns": 0,
"fieldname": "margin",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Margin",
- "length": 0,
- "no_copy": 0,
"oldfieldtype": "Column Break",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0,
"width": "50%"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "gross_margin",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Gross Margin",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "gross_margin_value",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_37",
- "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
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "per_gross_margin",
"fieldtype": "Percent",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Gross Margin %",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "per_gross_margin",
"oldfieldtype": "Currency",
- "options": "",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 1,
- "columns": 0,
"fieldname": "monitor_progress",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Monitor Progress",
- "length": 0,
- "no_copy": 0,
- "options": "",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Monitor Progress"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "default": "0",
"fieldname": "collect_progress",
"fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Collect Progress",
- "length": 0,
- "no_copy": 0,
- "options": "",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Collect Progress"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "collect_progress",
"fieldname": "holiday_list",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Holiday List",
- "length": 0,
- "no_copy": 0,
- "options": "Holiday List",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "options": "Holiday List"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.collect_progress == true",
"fieldname": "frequency",
"fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Frequency To Collect Progress",
- "length": 0,
- "no_copy": 0,
- "options": "Hourly\nTwice Daily\nDaily\nWeekly",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "options": "Hourly\nTwice Daily\nDaily\nWeekly"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:(doc.frequency == \"Hourly\" && doc.collect_progress)",
"fieldname": "from_time",
"fieldtype": "Time",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "From Time",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "From Time"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:(doc.frequency == \"Hourly\" && doc.collect_progress)",
"fieldname": "to_time",
"fieldtype": "Time",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "To Time",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "To Time"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:(doc.frequency == \"Twice Daily\" && doc.collect_progress == true)\n\n",
"fieldname": "first_email",
"fieldtype": "Time",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "First Email",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "First Email"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:(doc.frequency == \"Twice Daily\" && doc.collect_progress == true)",
"fieldname": "second_email",
"fieldtype": "Time",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Second Email",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Second Email"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:(doc.frequency == \"Daily\" && doc.collect_progress == true)",
"fieldname": "daily_time_to_send",
"fieldtype": "Time",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Time to send",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Time to send"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)",
"fieldname": "day_to_send",
"fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Day to Send",
- "length": 0,
- "no_copy": 0,
- "options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)",
"fieldname": "weekly_time_to_send",
"fieldtype": "Time",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Time to send",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Time to send"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_45",
- "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
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "collect_progress",
"description": "Message will sent to users to get their status on the project",
"fieldname": "message",
"fieldtype": "Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Message",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Message"
}
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
"icon": "fa fa-puzzle-piece",
"idx": 29,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
"max_attachments": 4,
- "modified": "2019-02-18 17:56:04.789560",
+ "modified": "2019-06-25 16:14:43.887151",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project",
"owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Projects User",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
},
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
"permlevel": 1,
- "print": 0,
"read": 1,
"report": 1,
- "role": "All",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
+ "role": "All"
},
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Projects Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
}
],
"quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
"search_fields": "customer, status, priority, is_active",
"show_name_in_global_search": 1,
"sort_order": "DESC",
"timeline_field": "customer",
- "track_changes": 0,
- "track_seen": 1,
- "track_views": 0
+ "track_seen": 1
}
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 55a6892..6176cf8 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -19,10 +19,6 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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(*)
diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py
index beb1f13..06c62b6 100644
--- a/erpnext/projects/doctype/project/test_project.py
+++ b/erpnext/projects/doctype/project/test_project.py
@@ -19,18 +19,18 @@
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()
diff --git a/erpnext/projects/doctype/project/test_records.json b/erpnext/projects/doctype/project/test_records.json
index 9379c22..567f359 100644
--- a/erpnext/projects/doctype/project/test_records.json
+++ b/erpnext/projects/doctype/project/test_records.json
@@ -1,12 +1,6 @@
[
{
"project_name": "_Test Project",
- "status": "Open",
- "tasks":[
- {
- "title": "_Test Task",
- "status": "Open"
- }
- ]
+ "status": "Open"
}
]
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project_task/__init__.py b/erpnext/projects/doctype/project_task/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/projects/doctype/project_task/__init__.py
+++ /dev/null
diff --git a/erpnext/projects/doctype/project_task/project_task.json b/erpnext/projects/doctype/project_task/project_task.json
deleted file mode 100644
index e26c191..0000000
--- a/erpnext/projects/doctype/project_task/project_task.json
+++ /dev/null
@@ -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
-}
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project_task/project_task.py b/erpnext/projects/doctype/project_task/project_task.py
deleted file mode 100644
index 5f9d8d7..0000000
--- a/erpnext/projects/doctype/project_task/project_task.py
+++ /dev/null
@@ -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
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index d8fc199..50557f1 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -158,12 +158,6 @@
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):
diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js
index 91dfe80..89657a1 100644
--- a/erpnext/public/js/call_popup/call_popup.js
+++ b/erpnext/public/js/call_popup/call_popup.js
@@ -109,7 +109,7 @@
});
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>
diff --git a/erpnext/public/js/conf.js b/erpnext/public/js/conf.js
index ec71df3..095e744 100644
--- a/erpnext/public/js/conf.js
+++ b/erpnext/public/js/conf.js
@@ -52,3 +52,13 @@
"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'
+});
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index 824b8d9..750900e 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -141,6 +141,7 @@
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;
@@ -154,6 +155,8 @@
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();
diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js
index d1113a4..89cb13d 100644
--- a/erpnext/public/js/financial_statements.js
+++ b/erpnext/public/js/financial_statements.js
@@ -129,9 +129,7 @@
}
]
- let dimension_filters = erpnext.get_dimension_filters();
-
- dimension_filters.then((dimensions) => {
+ erpnext.dimension_filters.then((dimensions) => {
dimensions.forEach((dimension) => {
filters.push({
"fieldname": dimension["fieldname"],
diff --git a/erpnext/public/js/sms_manager.js b/erpnext/public/js/sms_manager.js
index 6ce8bb1..a058da2 100644
--- a/erpnext/public/js/sms_manager.js
+++ b/erpnext/public/js/sms_manager.js
@@ -20,8 +20,10 @@
'Purchase Receipt' : 'Items has been received against purchase receipt: ' + doc.name
}
- if (in_list(['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice'], doc.doctype))
+ if (in_list(['Sales Order', 'Delivery Note', 'Sales Invoice'], doc.doctype))
this.show(doc.contact_person, 'Customer', doc.customer, '', default_msg[doc.doctype]);
+ else if (doc.doctype === 'Quotation')
+ this.show(doc.contact_person, 'Customer', doc.party_name, '', default_msg[doc.doctype]);
else if (in_list(['Purchase Order', 'Purchase Receipt'], doc.doctype))
this.show(doc.contact_person, 'Supplier', doc.supplier, '', default_msg[doc.doctype]);
else if (doc.doctype == 'Lead')
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index cf48be4..0cd648e 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -573,7 +573,6 @@
if(!r.exc) {
var doc = frappe.model.sync(r.message);
cur_frm.dirty();
- erpnext.utils.clear_duplicates();
cur_frm.refresh();
}
}
@@ -604,28 +603,6 @@
}
}
-erpnext.utils.clear_duplicates = function() {
- if(!cur_frm.doc.items) return;
- const unique_items = new Map();
- /*
- Create a Map of items with
- item_code => [qty, warehouse, batch_no]
- */
- let items = [];
-
- for (let item of cur_frm.doc.items) {
- if (!(unique_items.has(item.item_code) && unique_items.get(item.item_code)[0] === item.qty &&
- unique_items.get(item.item_code)[1] === item.warehouse && unique_items.get(item.item_code)[2] === item.batch_no &&
- unique_items.get(item.item_code)[3] === item.delivery_date && unique_items.get(item.item_code)[4] === item.required_date &&
- unique_items.get(item.item_code)[5] === item.rate)) {
-
- unique_items.set(item.item_code, [item.qty, item.warehouse, item.batch_no, item.delivery_date, item.required_date, item.rate]);
- items.push(item);
- }
- }
- cur_frm.doc.items = items;
-}
-
frappe.form.link_formatters['Item'] = function(value, doc) {
if(doc && doc.item_name && doc.item_name !== value) {
return value? value + ': ' + doc.item_name: doc.item_name;
diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js
index fef4507..549f95e 100644
--- a/erpnext/public/js/utils/dimension_tree_filter.js
+++ b/erpnext/public/js/utils/dimension_tree_filter.js
@@ -7,12 +7,12 @@
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
"Travel Request", "Fees", "POS Profile"];
-let dimension_filters = erpnext.get_dimension_filters();
+erpnext.dimension_filters = erpnext.get_dimension_filters();
erpnext.doctypes_with_dimensions.forEach((doctype) => {
frappe.ui.form.on(doctype, {
onload: function(frm) {
- dimension_filters.then((dimensions) => {
+ erpnext.dimension_filters.then((dimensions) => {
dimensions.forEach((dimension) => {
frappe.model.with_doctype(dimension['document_type'], () => {
if (frappe.meta.has_field(dimension['document_type'], 'is_group')) {
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index eff5780..e8c170e 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -74,7 +74,6 @@
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
invoice_details = self.invoices.get(inv)
-
for rate, items in items_based_on_rate.items():
place_of_supply = invoice_details.get("place_of_supply")
ecommerce_gstin = invoice_details.get("ecommerce_gstin")
@@ -85,7 +84,7 @@
"rate": "",
"taxable_value": 0,
"cess_amount": 0,
- "type": 0
+ "type": ""
})
row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin))
@@ -94,6 +93,7 @@
row["rate"] = rate
row["taxable_value"] += sum([abs(net_amount)
for item_code, net_amount in self.invoice_items.get(inv).items() if item_code in items])
+ row["cess_amount"] += flt(self.invoice_cess.get(inv), 2)
row["type"] = "E" if ecommerce_gstin else "OE"
for key, value in iteritems(b2cs_output):
@@ -123,6 +123,10 @@
row += [tax_rate or 0, taxable_value]
+ for column in self.other_columns:
+ if column.get('fieldname') == 'cess_amount':
+ row.append(flt(self.invoice_cess.get(invoice), 2))
+
return row, taxable_value
def get_invoice_data(self):
@@ -327,7 +331,7 @@
"fieldtype": "Data"
},
{
- "fieldname": "invoice_type",
+ "fieldname": "gst_category",
"label": "Invoice Type",
"fieldtype": "Data"
},
@@ -564,12 +568,18 @@
out = get_b2b_json(res, gstin)
gst_json["b2b"] = out
+
elif filters["type_of_business"] == "B2C Large":
for item in report_data[:-1]:
res.setdefault(item["place_of_supply"], []).append(item)
out = get_b2cl_json(res, gstin)
gst_json["b2cl"] = out
+
+ elif filters["type_of_business"] == "B2C Small":
+ out = get_b2cs_json(report_data[:-1], gstin)
+ gst_json["b2cs"] = out
+
elif filters["type_of_business"] == "EXPORT":
for item in report_data[:-1]:
res.setdefault(item["export_type"], []).append(item)
@@ -605,6 +615,45 @@
return out
+def get_b2cs_json(data, gstin):
+
+ company_state_number = gstin[0:2]
+
+ out = []
+ for d in data:
+
+ pos = d.get('place_of_supply').split('-')[0]
+ tax_details = {}
+
+ rate = d.get('rate', 0)
+ tax = flt((d["taxable_value"]*rate)/100.0, 2)
+
+ if company_state_number == pos:
+ tax_details.update({"camt": flt(tax/2.0, 2), "samt": flt(tax/2.0, 2)})
+ else:
+ tax_details.update({"iamt": tax})
+
+ inv = {
+ "sply_ty": "INTRA" if company_state_number == pos else "INTER",
+ "pos": pos,
+ "typ": d.get('type'),
+ "txval": flt(d.get('taxable_value'), 2),
+ "rt": rate,
+ "iamt": flt(tax_details.get('iamt'), 2),
+ "camt": flt(tax_details.get('camt'), 2),
+ "samt": flt(tax_details.get('samt'), 2),
+ "csamt": flt(d.get('cess_amount'), 2)
+ }
+
+ if d.get('type') == "E" and d.get('ecommerce_gstin'):
+ inv.update({
+ "etin": d.get('ecommerce_gstin')
+ })
+
+ out.append(inv)
+
+ return out
+
def get_b2cl_json(res, gstin):
out = []
for pos in res:
diff --git a/erpnext/selling/doctype/customer/customer_dashboard.py b/erpnext/selling/doctype/customer/customer_dashboard.py
index 87ca6a7..8e790bf 100644
--- a/erpnext/selling/doctype/customer/customer_dashboard.py
+++ b/erpnext/selling/doctype/customer/customer_dashboard.py
@@ -26,10 +26,6 @@
'items': ['Sales Order', 'Delivery Note', 'Sales Invoice']
},
{
- 'label': _('Service Level Agreement'),
- 'items': ['Service Level Agreement']
- },
- {
'label': _('Payments'),
'items': ['Payment Entry']
},
diff --git a/erpnext/selling/doctype/installation_note/installation_note.js b/erpnext/selling/doctype/installation_note/installation_note.js
index a8d9ae8..7fd0877 100644
--- a/erpnext/selling/doctype/installation_note/installation_note.js
+++ b/erpnext/selling/doctype/installation_note/installation_note.js
@@ -3,7 +3,7 @@
frappe.ui.form.on('Installation Note', {
setup: function(frm) {
- frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
+ frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'}
frm.set_query('customer_address', erpnext.queries.address_query);
frm.set_query('contact_person', erpnext.queries.contact_query);
frm.set_query('customer', erpnext.queries.customer);
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 26ca7c6..39dda92 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -107,7 +107,7 @@
refresh: function(doc, dt, dn) {
var me = this;
this._super();
- var allow_delivery = false;
+ let allow_delivery = false;
if(doc.docstatus==1) {
if(this.frm.has_perm("submit")) {
@@ -132,6 +132,8 @@
if(doc.status !== 'Closed') {
if(doc.status !== 'On Hold') {
+ allow_delivery = this.frm.doc.items.some(item => item.delivered_by_supplier === 0 && item.qty > flt(item.delivered_qty))
+
if (this.frm.has_perm("submit")) {
if(flt(doc.per_delivered, 6) < 100 || flt(doc.per_billed) < 100) {
// hold
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 8ad3bf0..e9b310e 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -492,13 +492,27 @@
frappe.local.message_log = []
+def get_requested_item_qty(sales_order):
+ return frappe._dict(frappe.db.sql("""
+ select sales_order_item, sum(stock_qty)
+ from `tabMaterial Request Item`
+ where docstatus = 1
+ and sales_order = %s
+ group by sales_order_item
+ """, sales_order))
+
@frappe.whitelist()
def make_material_request(source_name, target_doc=None):
+ requested_item_qty = get_requested_item_qty(source_name)
+
def postprocess(source, doc):
doc.material_request_type = "Purchase"
def update_item(source, target, source_parent):
target.project = source_parent.project
+ target.qty = source.stock_qty - requested_item_qty.get(source.name, 0)
+ target.conversion_factor = 1
+ target.stock_qty = source.stock_qty - requested_item_qty.get(source.name, 0)
doc = get_mapped_doc("Sales Order", source_name, {
"Sales Order": {
@@ -523,7 +537,7 @@
"stock_uom": "uom",
"stock_qty": "qty"
},
- "condition": lambda doc: not frappe.db.exists('Product Bundle', doc.item_code),
+ "condition": lambda doc: not frappe.db.exists('Product Bundle', doc.item_code) and doc.stock_qty > requested_item_qty.get(doc.name, 0),
"postprocess": update_item
}
}, target_doc, postprocess)
@@ -547,12 +561,6 @@
"base_grand_total" : "estimated_costing",
}
},
- "Sales Order Item": {
- "doctype": "Project Task",
- "field_map": {
- "item_code": "title",
- },
- }
}, target_doc, postprocess)
return doc
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 6bcdc2b..569a03a 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -77,8 +77,34 @@
},
+
print_without_amount: function(frm) {
erpnext.stock.delivery_note.set_print_hide(frm.doc);
+ },
+
+ refresh: function(frm) {
+ if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) {
+ frm.add_custom_button(__('Credit Note'), function() {
+ frappe.confirm(__("Are you sure you want to make credit note?"),
+ function() {
+ frm.trigger("make_credit_note");
+ }
+ );
+ }, __('Create'));
+
+ frm.page.set_inner_btn_group_as_primary(__('Create'));
+ }
+ },
+
+ make_credit_note: function(frm) {
+ frm.call({
+ method: "make_return_invoice",
+ doc: frm.doc,
+ freeze: true,
+ callback: function() {
+ frm.reload_doc();
+ }
+ });
}
});
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 1e522b8..ec7df2d 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -333,7 +333,10 @@
return_invoice.is_return = True
return_invoice.save()
return_invoice.submit()
- frappe.msgprint(_("Credit Note {0} has been created automatically").format(return_invoice.name))
+
+ credit_note_link = frappe.utils.get_link_to_form('Sales Invoice', return_invoice.name)
+
+ frappe.msgprint(_("Credit Note {0} has been created automatically").format(credit_note_link))
except:
frappe.throw(_("Could not create Credit Note automatically, please uncheck 'Issue Credit Note' and submit again"))
diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py
index d182290..30675b5 100644
--- a/erpnext/stock/doctype/item_price/item_price.py
+++ b/erpnext/stock/doctype/item_price/item_price.py
@@ -31,13 +31,16 @@
frappe.throw(_("Valid From Date must be lesser than Valid Upto Date."))
def update_price_list_details(self):
- self.buying, self.selling, self.currency = \
- frappe.db.get_value("Price List",
- {"name": self.price_list, "enabled": 1},
- ["buying", "selling", "currency"])
+ if self.price_list:
+ self.buying, self.selling, self.currency = \
+ frappe.db.get_value("Price List",
+ {"name": self.price_list, "enabled": 1},
+ ["buying", "selling", "currency"])
def update_item_details(self):
- self.item_name, self.item_description = frappe.db.get_value("Item",self.item_code,["item_name", "description"])
+ if self.item_code:
+ self.item_name, self.item_description = frappe.db.get_value("Item",
+ self.item_code,["item_name", "description"])
def check_duplicates(self):
conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s"
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 5351f32..96e31ff 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -211,6 +211,7 @@
d.stock_uom = item.stock_uom;
d.conversion_factor = 1;
d.qty = item.qty;
+ d.project = item.project;
});
}
d.hide();
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index e82aa2c..a2d3e75 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -38,6 +38,29 @@
if(frm.doc.company) {
frm.trigger("toggle_display_account_head");
}
+
+ if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) {
+ frm.add_custom_button(__('Debit Note'), function() {
+ frappe.confirm(__("Are you sure you want to make debit note?"),
+ function() {
+ frm.trigger("make_debit_note");
+ }
+ );
+ }, __('Create'));
+
+ frm.page.set_inner_btn_group_as_primary(__('Create'));
+ }
+ },
+
+ make_debit_note: function(frm) {
+ frm.call({
+ method: "make_return_invoice",
+ doc: frm.doc,
+ freeze: true,
+ callback: function() {
+ frm.reload_doc();
+ }
+ });
},
company: function(frm) {
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 67cd7f8..df28bec 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -387,6 +387,16 @@
self.load_from_db()
+ def make_return_invoice(self):
+ return_invoice = make_purchase_invoice(self.name)
+ return_invoice.is_return = True
+ return_invoice.save()
+ return_invoice.submit()
+
+ debit_note_link = frappe.utils.get_link_to_form('Purchase Invoice', return_invoice.name)
+
+ frappe.msgprint(_("Debit Note {0} has been created automatically").format(debit_note_link))
+
def update_billed_amount_based_on_po(po_detail, update_modified=True):
# Billed against Sales Order directly
billed_against_po = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item`
diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js
index 1a272d1..2d9650c 100644
--- a/erpnext/support/doctype/issue/issue.js
+++ b/erpnext/support/doctype/issue/issue.js
@@ -129,8 +129,15 @@
function set_time_to_resolve_and_response(frm) {
frm.dashboard.clear_headline();
- var time_to_respond = get_time_left(frm.doc.response_by, frm.doc.agreement_fulfilled);
- var time_to_resolve = get_time_left(frm.doc.resolution_by, frm.doc.agreement_fulfilled);
+ var time_to_respond = get_status(frm.doc.response_by_variance);
+ if (!frm.doc.first_responded_on && frm.doc.agreement_fulfilled === "Ongoing") {
+ time_to_respond = get_time_left(frm.doc.response_by, frm.doc.agreement_fulfilled);
+ }
+
+ var time_to_resolve = get_status(frm.doc.resolution_by_variance);
+ if (!frm.doc.resolution_date && frm.doc.agreement_fulfilled === "Ongoing") {
+ time_to_resolve = get_time_left(frm.doc.resolution_by, frm.doc.agreement_fulfilled);
+ }
frm.dashboard.set_headline_alert(
'<div class="row">' +
@@ -146,7 +153,15 @@
function get_time_left(timestamp, agreement_fulfilled) {
const diff = moment(timestamp).diff(moment());
- const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : moment(0, 'seconds').format('HH:mm');
- let indicator = (diff_display == '00:00' && agreement_fulfilled != "Fulfilled") ? "red" : "green";
+ const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : "Failed";
+ let indicator = (diff_display == 'Failed' && agreement_fulfilled != "Fulfilled") ? "red" : "green";
return {"diff_display": diff_display, "indicator": indicator};
}
+
+function get_status(variance) {
+ if (variance > 0) {
+ return {"diff_display": "Fulfilled", "indicator": "green"};
+ } else {
+ return {"diff_display": "Failed", "indicator": "red"};
+ }
+}
\ No newline at end of file
diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json
index b7c4166..72153dc 100644
--- a/erpnext/support/doctype/issue/issue.json
+++ b/erpnext/support/doctype/issue/issue.json
@@ -113,6 +113,7 @@
"search_index": 1
},
{
+ "default": "Medium",
"fieldname": "priority",
"fieldtype": "Link",
"in_standard_filter": 1,
@@ -143,7 +144,6 @@
},
{
"collapsible": 1,
- "depends_on": "eval: doc.service_level_agreement",
"fieldname": "service_level_section",
"fieldtype": "Section Break",
"label": "Service Level"
@@ -314,6 +314,7 @@
},
{
"default": "Ongoing",
+ "depends_on": "eval: doc.service_level_agreement",
"fieldname": "agreement_fulfilled",
"fieldtype": "Select",
"label": "Service Level Agreement Fulfilled",
@@ -321,6 +322,7 @@
"read_only": 1
},
{
+ "depends_on": "eval: doc.service_level_agreement",
"description": "in hours",
"fieldname": "response_by_variance",
"fieldtype": "Float",
@@ -328,6 +330,7 @@
"read_only": 1
},
{
+ "depends_on": "eval: doc.service_level_agreement",
"description": "in hours",
"fieldname": "resolution_by_variance",
"fieldtype": "Float",
@@ -337,7 +340,7 @@
],
"icon": "fa fa-ticket",
"idx": 7,
- "modified": "2019-06-27 15:19:00.771333",
+ "modified": "2019-06-30 13:19:38.215525",
"modified_by": "Administrator",
"module": "Support",
"name": "Issue",
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index 93f13f1..226676f 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -92,7 +92,6 @@
self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime()), 2)
self.agreement_fulfilled = "Fulfilled" if self.response_by_variance > 0 and self.resolution_by_variance > 0 else "Failed"
- self.save(ignore_permissions=True)
def create_communication(self):
communication = frappe.new_doc("Communication")
@@ -118,6 +117,17 @@
replicated_issue = deepcopy(self)
replicated_issue.subject = subject
+ replicated_issue.creation = now_datetime()
+
+ # Reset SLA
+ if replicated_issue.service_level_agreement:
+ replicated_issue.service_level_agreement = None
+ replicated_issue.agreement_fulfilled = "Ongoing"
+ replicated_issue.response_by = None
+ replicated_issue.response_by_variance = None
+ replicated_issue.resolution_by = None
+ replicated_issue.resolution_by_variance = None
+
frappe.get_doc(replicated_issue).insert()
# Replicate linked Communications
@@ -136,7 +146,8 @@
return replicated_issue.name
def before_insert(self):
- self.set_response_and_resolution_time()
+ if frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
+ self.set_response_and_resolution_time()
def set_response_and_resolution_time(self, priority=None, service_level_agreement=None):
service_level_agreement = get_active_service_level_agreement_for(priority=priority,
@@ -171,13 +182,16 @@
self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime()))
def change_service_level_agreement_and_priority(self):
- if not self.priority == frappe.db.get_value("Issue", self.name, "priority"):
- self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement)
- frappe.msgprint("Priority has been updated.")
+ if self.service_level_agreement and frappe.db.exists("Issue", self.name) and \
+ frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
- if not self.service_level_agreement == frappe.db.get_value("Issue", self.name, "service_level_agreement"):
- self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement)
- frappe.msgprint("Service Level Agreement has been updated.")
+ if not self.priority == frappe.db.get_value("Issue", self.name, "priority"):
+ self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement)
+ frappe.msgprint(_("Priority has been changed to {0}.").format(self.priority))
+
+ if not self.service_level_agreement == frappe.db.get_value("Issue", self.name, "service_level_agreement"):
+ self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement)
+ frappe.msgprint(_("Service Level Agreement has been changed to {0}.").format(self.service_level_agreement))
def get_expected_time_for(parameter, service_level, start_date_time):
current_date_time = start_date_time
@@ -258,15 +272,15 @@
if not doc.first_responded_on: # first_responded_on set when first reply is sent to customer
variance = round(time_diff_in_hours(doc.response_by, current_time), 2)
- frappe.db.set_value("Issue", doc.name, "response_by_variance", variance)
+ frappe.db.set_value(dt="Issue", dn=doc.name, field="response_by_variance", val=variance, update_modified=False)
if variance < 0:
- frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Failed")
+ frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False)
if not doc.resolution_date: # resolution_date set when issue has been closed
variance = round(time_diff_in_hours(doc.resolution_by, current_time), 2)
- frappe.db.set_value("Issue", doc.name, "resolution_by_variance", variance)
+ frappe.db.set_value(dt="Issue", dn=doc.name, field="resolution_by_variance", val=variance, update_modified=False)
if variance < 0:
- frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Failed")
+ frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False)
def get_list_context(context=None):
return {
diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py
index 75d70b1..eb1736e 100644
--- a/erpnext/support/doctype/issue/test_issue.py
+++ b/erpnext/support/doctype/issue/test_issue.py
@@ -11,6 +11,7 @@
class TestIssue(unittest.TestCase):
def test_response_time_and_resolution_time_based_on_different_sla(self):
+ frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
create_service_level_agreements_for_issues()
creation = datetime.datetime(2019, 3, 4, 12, 0)
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
index f91a80c..9a83ca7 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
@@ -5,6 +5,7 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
+ "enable",
"service_level",
"default_service_level_agreement",
"holiday_list",
@@ -149,9 +150,15 @@
"in_standard_filter": 1,
"label": "Entity Type",
"options": "\nCustomer\nCustomer Group\nTerritory"
+ },
+ {
+ "default": "1",
+ "fieldname": "enable",
+ "fieldtype": "Check",
+ "label": "Enable"
}
],
- "modified": "2019-06-20 18:04:14.293378",
+ "modified": "2019-07-09 17:22:16.402939",
"modified_by": "Administrator",
"module": "Support",
"name": "Service Level Agreement",
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
index 82c0ffb..a399c58 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
@@ -6,19 +6,23 @@
import frappe
from frappe.model.document import Document
from frappe import _
+from frappe.utils import getdate
class ServiceLevelAgreement(Document):
def validate(self):
+ if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
+ frappe.throw(_("Service Level Agreement tracking is not enabled."))
+
if self.default_service_level_agreement:
if frappe.db.exists("Service Level Agreement", {"default_service_level_agreement": "1", "name": ["!=", self.name]}):
frappe.throw(_("A Default Service Level Agreement already exists."))
else:
if self.start_date and self.end_date:
- if self.start_date >= self.end_date:
+ if getdate(self.start_date) >= getdate(self.end_date):
frappe.throw(_("Start Date of Agreement can't be greater than or equal to End Date."))
- if self.end_date < frappe.utils.getdate():
+ if getdate(self.end_date) < getdate(frappe.utils.getdate()):
frappe.throw(_("End Date of Agreement can't be less than today."))
if self.entity_type and self.entity:
@@ -44,12 +48,16 @@
for service_level_agreement in service_level_agreements:
doc = frappe.get_doc("Service Level Agreement", service_level_agreement.name)
- if doc.end_date and doc.end_date < frappe.utils.getdate():
+ if doc.end_date and getdate(doc.end_date) < getdate(frappe.utils.getdate()):
frappe.db.set_value("Service Level Agreement", service_level_agreement.name, "active", 0)
def get_active_service_level_agreement_for(priority, customer=None, service_level_agreement=None):
+ if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
+ return
+
filters = [
["Service Level Agreement", "active", "=", 1],
+ ["Service Level Agreement", "enable", "=", 1]
]
if priority:
@@ -80,6 +88,14 @@
@frappe.whitelist()
def get_service_level_agreement_filters(name, customer=None):
+ if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
+ return
+
+ filters = [
+ ["Service Level Agreement", "active", "=", 1],
+ ["Service Level Agreement", "enable", "=", 1]
+ ]
+
if not customer:
or_filters = [
["Service Level Agreement", "default_service_level_agreement", "=", 1]
@@ -93,5 +109,5 @@
return {
"priority": [priority.priority for priority in frappe.get_list("Service Level Priority", filters={"parent": name}, fields=["priority"])],
- "service_level_agreements": [d.name for d in frappe.get_list("Service Level Agreement", or_filters=or_filters)]
+ "service_level_agreements": [d.name for d in frappe.get_list("Service Level Agreement", filters=filters, or_filters=or_filters)]
}
\ No newline at end of file
diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
index 6aa5394..68b82d1 100644
--- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
@@ -10,6 +10,8 @@
class TestServiceLevelAgreement(unittest.TestCase):
def test_service_level_agreement(self):
+ frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
+
create_service_level_for_sla()
# Default Service Level Agreement
diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json
index 2b79107..2dced15 100644
--- a/erpnext/support/doctype/support_settings/support_settings.json
+++ b/erpnext/support/doctype/support_settings/support_settings.json
@@ -1,560 +1,145 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
"creation": "2017-02-17 13:07:35.686409",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
- "document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
+ "field_order": [
+ "sb_00",
+ "track_service_level_agreement",
+ "issues_sb",
+ "close_issue_after_days",
+ "portal_sb",
+ "get_started_sections",
+ "show_latest_forum_posts",
+ "forum_sb",
+ "forum_url",
+ "get_latest_query",
+ "response_key_list",
+ "column_break_10",
+ "post_title_key",
+ "post_description_key",
+ "post_route_key",
+ "post_route_string",
+ "search_apis_sb",
+ "search_apis"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "issues_sb",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Issues",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Issues"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "7",
"description": "Auto close Issue after 7 days",
"fieldname": "close_issue_after_days",
"fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Close Issue After Days",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Close Issue After Days"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "portal_sb",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Support Portal",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Support Portal"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "get_started_sections",
"fieldtype": "Code",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Get Started Sections",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Get Started Sections"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "default": "0",
"fieldname": "show_latest_forum_posts",
"fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Show Latest Forum Posts",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Show Latest Forum Posts"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "show_latest_forum_posts",
"fieldname": "forum_sb",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Forum Posts",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Forum Posts"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "forum_url",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Forum URL",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Forum URL"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "get_latest_query",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Get Latest Query",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Get Latest Query"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "response_key_list",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Response Key List",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Response Key List"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_10",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "post_title_key",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Post Title Key",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Post Title Key"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "post_description_key",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Post Description Key",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Post Description Key"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "post_route_key",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Post Route Key",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Post Route Key"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "post_route_string",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Post Route String",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Post Route String"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "search_apis_sb",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Search APIs",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "label": "Search APIs"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "search_apis",
"fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Search APIs",
- "length": 0,
- "no_copy": 0,
- "options": "Support Search Source",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_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
+ "options": "Support Search Source"
+ },
+ {
+ "fieldname": "sb_00",
+ "fieldtype": "Section Break",
+ "label": "Service Level Agreements"
+ },
+ {
+ "default": "0",
+ "fieldname": "track_service_level_agreement",
+ "fieldtype": "Check",
+ "label": "Track Service Level Agreement"
}
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
"issingle": 1,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-05-17 02:11:33.462444",
+ "modified": "2019-07-09 17:11:38.216732",
"modified_by": "Administrator",
"module": "Support",
"name": "Support Settings",
- "name_case": "",
"owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
- "report": 0,
"role": "System Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
}
],
"quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
+ "track_changes": 1
}
\ No newline at end of file