From 4448e2d66bb224deee3a6dda181a358223d3c480 Mon Sep 17 00:00:00 2001 From: NahuelOperto Date: Fri, 21 Jun 2019 08:50:37 -0300 Subject: [PATCH 001/174] fix order in general ledger --- erpnext/accounts/report/general_ledger/general_ledger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 1c5e089534e..2924907e8fd 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -11,6 +11,7 @@ from erpnext.accounts.utils import get_account_currency from erpnext.accounts.report.financial_statements import get_cost_centers_with_children from six import iteritems from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from collections import OrderedDict def execute(filters=None): if not filters: @@ -267,7 +268,7 @@ def group_by_field(group_by): return 'voucher_no' def initialize_gle_map(gl_entries, filters): - gle_map = frappe._dict() + gle_map = OrderedDict() group_by = group_by_field(filters.get('group_by')) for gle in gl_entries: From a2408a63da7057af341db5a46af600c5f4fb0f34 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 24 Jun 2019 01:52:48 +0530 Subject: [PATCH 002/174] feat: payment reconciliation enhancements --- .../doctype/journal_entry/journal_entry.js | 4 + .../doctype/journal_entry/journal_entry.py | 3 +- .../doctype/payment_entry/payment_entry.py | 14 + .../payment_reconciliation.js | 99 ++++ .../payment_reconciliation.py | 145 +++++- .../payment_reconciliation_payment.json | 486 ++++-------------- erpnext/accounts/utils.py | 14 +- 7 files changed, 366 insertions(+), 399 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index b7f383fb131..b6c48c81b96 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -228,6 +228,10 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ frappe.model.validate_missing(jvd, "account"); var party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to"; out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]); + + if (in_list(['Debit Note', 'Credit Note'], doc.voucher_type)) { + out.filters.push([jvd.reference_type, "is_return", "=", 1]); + } } if(in_list(["Sales Order", "Purchase Order"], jvd.reference_type)) { diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index d082b602117..3132c937dd7 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -331,7 +331,8 @@ class JournalEntry(AccountsController): for reference_name, total in iteritems(self.reference_totals): reference_type = self.reference_types[reference_name] - if reference_type in ("Sales Invoice", "Purchase Invoice"): + if (reference_type in ("Sales Invoice", "Purchase Invoice") and + self.voucher_type not in ['Debit Note', 'Credit Note']): invoice = frappe.db.get_value(reference_type, reference_name, ["docstatus", "outstanding_amount"], as_dict=1) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 92803a6312d..ea76126d8cc 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -535,6 +535,20 @@ class PaymentEntry(AccountsController): "amount": self.total_allocated_amount * (tax_details['tax']['rate'] / 100) } + def set_gain_or_loss(self, account_details=None): + if not self.difference_amount: + self.set_difference_amount() + + row = { + 'amount': self.difference_amount + } + + if account_details: + row.update(account_details) + + self.append('deductions', row) + self.set_unallocated_amount() + @frappe.whitelist() def get_outstanding_reference_documents(args): diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 4c24a9fc97a..df31cde551f 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -16,6 +16,20 @@ frappe.ui.form.on("Payment Reconciliation Payment", { })[0].outstanding_amount; frappe.model.set_value(cdt, cdn, "allocated_amount", Math.min(invoice_amount, row.amount)); + + frm.call({ + doc: frm.doc, + method: 'get_difference_amount', + args: { + child_row: row + }, + callback: function(r, rt) { + if(r.message) { + frappe.model.set_value(cdt, cdn, + "difference_amount", r.message); + } + } + }); } } }); @@ -104,6 +118,91 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext reconcile: function() { var me = this; + var show_dialog = me.frm.doc.payments.filter(d => d.difference_amount && !d.difference_account); + + if (show_dialog && show_dialog.length) { + + this.data = []; + const dialog = new frappe.ui.Dialog({ + title: __("Select Difference Account"), + fields: [ + { + fieldname: "payments", fieldtype: "Table", label: __("Payments"), + data: this.data, in_place_edit: true, + get_data: () => { + return this.data; + }, + fields: [{ + fieldtype:'Data', + fieldname:"docname", + in_list_view: 1, + hidden: 1 + }, { + fieldtype:'Data', + fieldname:"reference_name", + label: __("Voucher No"), + in_list_view: 1, + read_only: 1 + }, { + fieldtype:'Link', + options: 'Account', + in_list_view: 1, + label: __("Difference Account"), + fieldname: 'difference_account', + reqd: 1, + get_query: function() { + return { + filters: { + company: me.frm.doc.company, + is_group: 0 + } + } + } + }, { + fieldtype:'Currency', + in_list_view: 1, + label: __("Difference Amount"), + fieldname: 'difference_amount', + read_only: 1 + }] + }, + ], + primary_action: function() { + const args = dialog.get_values()["payments"]; + + args.forEach(d => { + frappe.model.set_value("Payment Reconciliation Payment", d.docname, + "difference_account", d.difference_account); + }) + + me.reconcile_payment_entries(); + dialog.hide(); + }, + primary_action_label: __('Reconcile Entries') + }); + + this.frm.doc.payments.forEach(d => { + if (d.difference_amount && !d.difference_account) { + dialog.fields_dict.payments.df.data.push({ + 'docname': d.name, + 'reference_name': d.reference_name, + 'difference_amount': d.difference_amount, + 'difference_account': d.difference_account, + }); + } + }); + + this.data = dialog.fields_dict.payments.df.data; + dialog.fields_dict.payments.grid.refresh(); + dialog.show(); + } else { + this.reconcile_payment_entries(); + } + }, + + reconcile_payment_entries: function() { + var me = this; + return this.frm.call({ doc: me.frm.doc, method: 'reconcile', diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 9e7c3eb55e5..64861615b0e 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -3,10 +3,11 @@ from __future__ import unicode_literals import frappe, erpnext -from frappe.utils import flt +from frappe.utils import flt, today from frappe import msgprint, _ from frappe.model.document import Document -from erpnext.accounts.utils import get_outstanding_invoices +from erpnext.accounts.utils import (get_outstanding_invoices, + update_reference_in_payment_entry, reconcile_against_document) from erpnext.controllers.accounts_controller import get_advance_payment_entries class PaymentReconciliation(Document): @@ -20,14 +21,17 @@ class PaymentReconciliation(Document): payment_entries = self.get_payment_entries() journal_entries = self.get_jv_entries() - self.add_payment_entries(payment_entries + journal_entries) + if self.party_type in ["Customer", "Supplier"]: + dr_or_cr_notes = self.get_dr_or_cr_notes() + + self.add_payment_entries(payment_entries + journal_entries + dr_or_cr_notes) def get_payment_entries(self): order_doctype = "Sales Order" if self.party_type=="Customer" else "Purchase Order" payment_entries = get_advance_payment_entries(self.party_type, self.party, self.receivable_payable_account, order_doctype, against_all_orders=True, limit=self.limit) - + return payment_entries def get_jv_entries(self): @@ -72,6 +76,34 @@ class PaymentReconciliation(Document): return list(journal_entries) + def get_dr_or_cr_notes(self): + dr_or_cr = ("credit_in_account_currency" + if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency") + + reconciled_dr_or_cr = ("debit_in_account_currency" + if dr_or_cr == "credit_in_account_currency" else "credit_in_account_currency") + + voucher_type = ('Sales Invoice' + if self.party_type == 'Customer' else "Purchase Invoice") + + return frappe.db.sql(""" SELECT `tab{doc}`.name as reference_name, %(voucher_type)s as reference_type, + (sum(`tabGL Entry`.{dr_or_cr}) - sum(`tabGL Entry`.{reconciled_dr_or_cr})) as amount + FROM `tab{doc}`, `tabGL Entry` + WHERE + (`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no) + and `tab{doc}`.is_return = 1 and `tabGL Entry`.against_voucher_type = %(voucher_type)s + and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s + and `tabGL Entry`.party_type = %(party_type)s and `tabGL Entry`.account = %(account)s + GROUP BY `tabSales Invoice`.name + Having + amount > 0 + """.format(doc=voucher_type, dr_or_cr=dr_or_cr, reconciled_dr_or_cr=reconciled_dr_or_cr), { + 'party': self.party, + 'party_type': self.party_type, + 'voucher_type': voucher_type, + 'account': self.receivable_payable_account + }, as_dict=1) + def add_payment_entries(self, entries): self.set('payments', []) for e in entries: @@ -112,36 +144,67 @@ class PaymentReconciliation(Document): if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency") lst = [] + dr_or_cr_notes = [] for e in self.get('payments'): + reconciled_entry = [] if e.invoice_number and e.allocated_amount: - lst.append(frappe._dict({ - 'voucher_type': e.reference_type, - 'voucher_no' : e.reference_name, - 'voucher_detail_no' : e.reference_row, - 'against_voucher_type' : e.invoice_type, - 'against_voucher' : e.invoice_number, - 'account' : self.receivable_payable_account, - 'party_type': self.party_type, - 'party': self.party, - 'is_advance' : e.is_advance, - 'dr_or_cr' : dr_or_cr, - 'unadjusted_amount' : flt(e.amount), - 'allocated_amount' : flt(e.allocated_amount) - })) + if e.reference_type in ['Sales Invoice', 'Purchase Invoice']: + reconciled_entry = dr_or_cr_notes + else: + reconciled_entry = lst + + reconciled_entry.append(self.get_payment_details(e, dr_or_cr)) if lst: - from erpnext.accounts.utils import reconcile_against_document reconcile_against_document(lst) - msgprint(_("Successfully Reconciled")) - self.get_unreconciled_entries() + if dr_or_cr_notes: + reconcile_dr_cr_note(dr_or_cr_notes) + + msgprint(_("Successfully Reconciled")) + self.get_unreconciled_entries() + + def get_payment_details(self, row, dr_or_cr): + return frappe._dict({ + 'voucher_type': row.reference_type, + 'voucher_no' : row.reference_name, + 'voucher_detail_no' : row.reference_row, + 'against_voucher_type' : row.invoice_type, + 'against_voucher' : row.invoice_number, + 'account' : self.receivable_payable_account, + 'party_type': self.party_type, + 'party': self.party, + 'is_advance' : row.is_advance, + 'dr_or_cr' : dr_or_cr, + 'unadjusted_amount' : flt(row.amount), + 'allocated_amount' : flt(row.allocated_amount), + 'difference_amount': row.difference_amount, + 'difference_account': row.difference_account + }) + + def get_difference_amount(self, child_row): + if child_row.get("reference_type") != 'Payment Entry': return + + child_row = frappe._dict(child_row) + + if child_row.invoice_number and " | " in child_row.invoice_number: + child_row.invoice_type, child_row.invoice_number = child_row.invoice_number.split(" | ") + + dr_or_cr = ("credit_in_account_currency" + if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency") + + row = self.get_payment_details(child_row, dr_or_cr) + + doc = frappe.get_doc(row.voucher_type, row.voucher_no) + update_reference_in_payment_entry(row, doc, do_not_save=True) + + return doc.difference_amount def check_mandatory_to_fetch(self): for fieldname in ["company", "party_type", "party", "receivable_payable_account"]: if not self.get(fieldname): frappe.throw(_("Please select {0} first").format(self.meta.get_label(fieldname))) - def validate_invoice(self): if not self.get("invoices"): frappe.throw(_("No records found in the Invoice table")) @@ -186,3 +249,41 @@ class PaymentReconciliation(Document): cond += " and `{0}` <= {1}".format(dr_or_cr, flt(self.maximum_amount)) return cond + +def reconcile_dr_cr_note(dr_cr_notes): + for d in dr_cr_notes: + voucher_type = ('Credit Note' + if d.voucher_type == 'Sales Invoice' else 'Debit Note') + + dr_or_cr = ('credit_in_account_currency' + if d.reference_type == 'Sales Invoice' else 'debit_in_account_currency') + + reconcile_dr_or_cr = ('debit_in_account_currency' + if dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency') + + jv = frappe.get_doc({ + "doctype": "Journal Entry", + "voucher_type": voucher_type, + "posting_date": today(), + "accounts": [ + { + 'account': d.account, + 'party': d.party, + 'party_type': d.party_type, + reconcile_dr_or_cr: (abs(d.allocated_amount) + if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)), + 'reference_type': d.against_voucher_type, + 'reference_name': d.against_voucher + }, + { + 'account': d.account, + 'party': d.party, + 'party_type': d.party_type, + dr_or_cr: abs(d.allocated_amount), + 'reference_type': d.voucher_type, + 'reference_name': d.voucher_no + } + ] + }) + + jv.submit() \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json index 814257c047a..018bfd028a6 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json @@ -1,389 +1,127 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2014-07-09 16:13:35.452759", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "creation": "2014-07-09 16:13:35.452759", + "doctype": "DocType", + "editable_grid": 1, + "field_order": [ + "reference_type", + "reference_name", + "posting_date", + "is_advance", + "reference_row", + "col_break1", + "invoice_number", + "amount", + "allocated_amount", + "section_break_10", + "difference_account", + "difference_amount", + "sec_break1", + "remark" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_type", - "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": "Reference Type", - "length": 0, - "no_copy": 0, - "options": "DocType", - "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 - }, + "fieldname": "reference_type", + "fieldtype": "Link", + "label": "Reference Type", + "options": "DocType", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, - "fieldname": "reference_name", - "fieldtype": "Dynamic 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": 0, - "label": "Reference Name", - "length": 0, - "no_copy": 0, - "options": "reference_type", - "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 - }, + "columns": 2, + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Reference Name", + "options": "reference_type", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "posting_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": "Posting Date", - "length": 0, - "no_copy": 0, - "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 - }, + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_advance", - "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": "Is Advance", - "length": 0, - "no_copy": 0, - "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 - }, + "fieldname": "is_advance", + "fieldtype": "Data", + "hidden": 1, + "label": "Is Advance", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_row", - "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": "Reference Row", - "length": 0, - "no_copy": 0, - "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 - }, + "fieldname": "reference_row", + "fieldtype": "Data", + "hidden": 1, + "label": "Reference Row", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break1", - "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, - "label": "", - "length": 0, - "no_copy": 0, - "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 - }, + "fieldname": "col_break1", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, - "fieldname": "invoice_number", - "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": "Invoice Number", - "length": 0, - "no_copy": 0, - "options": "", - "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": 0 - }, + "columns": 2, + "fieldname": "invoice_number", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Invoice Number", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "amount", - "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": "Amount", - "length": 0, - "no_copy": 0, - "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 - }, + "columns": 2, + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "allocated_amount", - "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": "Allocated amount", - "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 - }, + "columns": 2, + "fieldname": "allocated_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Allocated amount", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sec_break1", - "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": "", - "length": 0, - "no_copy": 0, - "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 - }, + "fieldname": "sec_break1", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "remark", - "fieldtype": "Small Text", - "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": "Remark", - "length": 0, - "no_copy": 0, - "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 + "fieldname": "remark", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Remark", + "read_only": 1 + }, + { + "columns": 2, + "fieldname": "difference_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Difference Account", + "options": "Account" + }, + { + "fieldname": "difference_amount", + "fieldtype": "Currency", + "label": "Difference Amount", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" } - ], - "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, - "menu_index": 0, - "modified": "2019-01-07 16:52:07.567027", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Payment Reconciliation Payment", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "modified": "2019-06-24 00:08:11.150796", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Payment Reconciliation Payment", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 96f64e8db3d..101a71f8e50 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -435,7 +435,7 @@ def update_reference_in_journal_entry(d, jv_obj): jv_obj.flags.ignore_validate_update_after_submit = True jv_obj.save(ignore_permissions=True) -def update_reference_in_payment_entry(d, payment_entry): +def update_reference_in_payment_entry(d, payment_entry, do_not_save=False): reference_details = { "reference_doctype": d.against_voucher_type, "reference_name": d.against_voucher, @@ -466,7 +466,17 @@ def update_reference_in_payment_entry(d, payment_entry): payment_entry.setup_party_account_field() payment_entry.set_missing_values() payment_entry.set_amounts() - payment_entry.save(ignore_permissions=True) + + if d.difference_amount and d.difference_account: + payment_entry.set_gain_or_loss(account_details={ + 'account': d.difference_account, + 'cost_center': payment_entry.cost_center or frappe.get_cached_value('Company', + payment_entry.company, "cost_center"), + 'amount': d.difference_amount + }) + + if not do_not_save: + payment_entry.save(ignore_permissions=True) def unlink_ref_doc_from_payment_entries(ref_doc): remove_ref_doc_link_from_jv(ref_doc.doctype, ref_doc.name) From 42fe68d06513a71d3f237510b32f69e29c634fdc Mon Sep 17 00:00:00 2001 From: NahuelOperto Date: Mon, 24 Jun 2019 08:29:14 -0300 Subject: [PATCH 003/174] feat: order type in trr type filter --- .../report/sales_analytics/sales_analytics.js | 2 +- .../report/sales_analytics/sales_analytics.py | 73 ++++++++++++++----- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js index fbe045bf35c..149c923d5c0 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.js +++ b/erpnext/selling/report/sales_analytics/sales_analytics.js @@ -8,7 +8,7 @@ frappe.query_reports["Sales Analytics"] = { fieldname: "tree_type", label: __("Tree Type"), fieldtype: "Select", - options: ["Customer Group","Customer","Item Group","Item","Territory"], + options: ["Customer Group","Customer","Item Group","Item","Territory","Order Type"], default: "Customer", reqd: 1 }, diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 3239fc626f9..8a5e50a61e7 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -23,15 +23,15 @@ class Analytics(object): self.get_columns() self.get_data() self.get_chart_data() - return self.columns, self.data , None, self.chart + return self.columns, self.data, None, self.chart def get_columns(self): - self.columns =[{ + self.columns = [{ "label": _(self.filters.tree_type + " ID"), - "options": self.filters.tree_type, + "options": self.filters.tree_type if self.filters.tree_type != "Order Type" else "", "fieldname": "entity", - "fieldtype": "Link", - "width": 140 + "fieldtype": "Link" if self.filters.tree_type != "Order Type" else "Data", + "width": 140 if self.filters.tree_type != "Order Type" else 200 }] if self.filters.tree_type in ["Customer", "Supplier", "Item"]: self.columns.append({ @@ -73,6 +73,28 @@ class Analytics(object): self.get_sales_transactions_based_on_item_group() self.get_rows_by_group() + elif self.filters.tree_type == "Order Type": + if self.filters.doc_type != "Sales Order": + self.data = [] + return + self.get_sales_transactions_based_on_order_type() + self.get_rows_by_group() + + def get_sales_transactions_based_on_order_type(self): + if self.filters["value_quantity"] == 'Value': + value_field = "base_net_total" + else: + value_field = "total_qty" + + self.entries = frappe.db.sql(""" select s.order_type as entity, s.{value_field} as value_field, s.{date_field} + from `tab{doctype}` s where s.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s + and ifnull(s.order_type, '') != '' order by s.order_type + """ + .format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type), + (self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1) + + self.get_teams() + def get_sales_transactions_based_on_customers_or_suppliers(self): if self.filters["value_quantity"] == 'Value': value_field = "base_net_total as value_field" @@ -88,7 +110,7 @@ class Analytics(object): self.entries = frappe.get_all(self.filters.doc_type, fields=[entity, entity_name, value_field, self.date_field], - filters = { + filters={ "docstatus": 1, "company": self.filters.company, self.date_field: ('between', [self.filters.from_date, self.filters.to_date]) @@ -112,7 +134,7 @@ class Analytics(object): where s.name = i.parent and i.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s """ - .format(date_field=self.date_field, value_field = value_field, doctype=self.filters.doc_type), + .format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type), (self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1) self.entity_names = {} @@ -135,7 +157,7 @@ class Analytics(object): self.entries = frappe.get_all(self.filters.doc_type, fields=[entity_field, value_field, self.date_field], - filters = { + filters={ "docstatus": 1, "company": self.filters.company, self.date_field: ('between', [self.filters.from_date, self.filters.to_date]) @@ -154,13 +176,13 @@ class Analytics(object): from `tab{doctype} Item` i , `tab{doctype}` s where s.name = i.parent and i.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s - """.format(date_field=self.date_field, value_field = value_field, doctype=self.filters.doc_type), + """.format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type), (self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1) self.get_groups() def get_rows(self): - self.data=[] + self.data = [] self.get_periodic_data() for entity, period_data in iteritems(self.entity_periodic_data): @@ -192,7 +214,7 @@ class Analytics(object): period = self.get_period(end_date) amount = flt(self.entity_periodic_data.get(d.name, {}).get(period, 0.0)) row[scrub(period)] = amount - if d.parent: + if d.parent and (self.filters.tree_type != "Order Type" or d.parent == "Order Types"): self.entity_periodic_data.setdefault(d.parent, frappe._dict()).setdefault(period, 0.0) self.entity_periodic_data[d.parent][period] += amount total += amount @@ -216,7 +238,7 @@ class Analytics(object): elif self.filters.range == 'Monthly': period = str(self.months[posting_date.month - 1]) + " " + str(posting_date.year) elif self.filters.range == 'Quarterly': - period = "Quarter " + str(((posting_date.month-1)//3)+1) +" " + str(posting_date.year) + period = "Quarter " + str(((posting_date.month - 1) // 3) + 1) + " " + str(posting_date.year) else: year = get_fiscal_year(posting_date, company=self.filters.company) period = str(year[0]) @@ -234,7 +256,7 @@ class Analytics(object): }.get(self.filters.range, 1) if self.filters.range in ['Monthly', 'Quarterly']: - from_date = from_date.replace(day = 1) + from_date = from_date.replace(day=1) elif self.filters.range == "Yearly": from_date = get_fiscal_year(from_date)[1] else: @@ -270,7 +292,22 @@ class Analytics(object): self.group_entries = frappe.db.sql("""select name, lft, rgt , {parent} as parent from `tab{tree}` order by lft""" - .format(tree=self.filters.tree_type, parent=parent), as_dict=1) + .format(tree=self.filters.tree_type, parent=parent), as_dict=1) + + for d in self.group_entries: + if d.parent: + self.depth_map.setdefault(d.name, self.depth_map.get(d.parent) + 1) + else: + self.depth_map.setdefault(d.name, 0) + + def get_teams(self): + self.depth_map = frappe._dict() + + self.group_entries = frappe.db.sql(""" select * from (select "Order Types" as name, 0 as lft, + 2 as rgt, '' as parent union select distinct order_type as name, 1 as lft, 1 as rgt, "Order Types" as parent + from `tab{doctype}` where ifnull(order_type, '') != '') as b order by lft, name + """ + .format(doctype=self.filters.doc_type), as_dict=1) for d in self.group_entries: if d.parent: @@ -285,13 +322,13 @@ class Analytics(object): length = len(self.columns) if self.filters.tree_type in ["Customer", "Supplier", "Item"]: - labels = [d.get("label") for d in self.columns[2:length-1]] + labels = [d.get("label") for d in self.columns[2:length - 1]] else: - labels = [d.get("label") for d in self.columns[1:length-1]] + labels = [d.get("label") for d in self.columns[1:length - 1]] self.chart = { "data": { 'labels': labels, - 'datasets':[] + 'datasets': [] }, "type": "line" - } \ No newline at end of file + } From c41403c8a987b632c776da9b8b5f07cf5d9e7db3 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 27 Jun 2019 16:33:19 +0530 Subject: [PATCH 004/174] fix: remove move and add buttons from stock summary --- erpnext/stock/dashboard/item_dashboard.js | 99 +------------------ .../stock/dashboard/item_dashboard_list.html | 15 --- 2 files changed, 1 insertion(+), 113 deletions(-) diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index 157dbfe1749..f820b7aa86f 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -16,18 +16,6 @@ erpnext.stock.ItemDashboard = Class.extend({ this.content = $(frappe.render_template('item_dashboard')).appendTo(this.parent); this.result = this.content.find('.result'); - // move - this.content.on('click', '.btn-move', function() { - erpnext.stock.move_item(unescape($(this).attr('data-item')), $(this).attr('data-warehouse'), - null, $(this).attr('data-actual_qty'), null, function() { me.refresh(); }); - }); - - this.content.on('click', '.btn-add', function() { - erpnext.stock.move_item(unescape($(this).attr('data-item')), null, $(this).attr('data-warehouse'), - $(this).attr('data-actual_qty'), $(this).attr('data-rate'), - function() { me.refresh(); }); - }); - // more this.content.find('.btn-more').on('click', function() { me.start += 20; @@ -111,89 +99,4 @@ erpnext.stock.ItemDashboard = Class.extend({ show_item: show_item || false } } -}) - -erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callback) { - var dialog = new frappe.ui.Dialog({ - title: target ? __('Add Item') : __('Move Item'), - fields: [ - {fieldname: 'item_code', label: __('Item'), - fieldtype: 'Link', options: 'Item', read_only: 1}, - {fieldname: 'source', label: __('Source Warehouse'), - fieldtype: 'Link', options: 'Warehouse', read_only: 1}, - {fieldname: 'target', label: __('Target Warehouse'), - fieldtype: 'Link', options: 'Warehouse', reqd: 1}, - {fieldname: 'qty', label: __('Quantity'), reqd: 1, - fieldtype: 'Float', description: __('Available {0}', [actual_qty]) }, - {fieldname: 'rate', label: __('Rate'), fieldtype: 'Currency', hidden: 1 }, - ], - }) - dialog.show(); - dialog.get_field('item_code').set_input(item); - - if(source) { - dialog.get_field('source').set_input(source); - } else { - dialog.get_field('source').df.hidden = 1; - dialog.get_field('source').refresh(); - } - - if(rate) { - dialog.get_field('rate').set_value(rate); - dialog.get_field('rate').df.hidden = 0; - dialog.get_field('rate').refresh(); - } - - if(target) { - dialog.get_field('target').df.read_only = 1; - dialog.get_field('target').value = target; - dialog.get_field('target').refresh(); - } - - dialog.set_primary_action(__('Submit'), function() { - var values = dialog.get_values(); - if(!values) { - return; - } - if(source && values.qty > actual_qty) { - frappe.msgprint(__('Quantity must be less than or equal to {0}', [actual_qty])); - return; - } - if(values.source === values.target) { - frappe.msgprint(__('Source and target warehouse must be different')); - } - - frappe.call({ - method: 'erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry', - args: values, - freeze: true, - callback: function(r) { - frappe.show_alert(__('Stock Entry {0} created', - ['' + r.message.name+ ''])); - dialog.hide(); - callback(r); - }, - }); - }); - - $('

' - + __("Add more items or open full form") + '

') - .appendTo(dialog.body) - .find('.link-open') - .on('click', function() { - frappe.model.with_doctype('Stock Entry', function() { - var doc = frappe.model.get_new_doc('Stock Entry'); - doc.from_warehouse = dialog.get_value('source'); - doc.to_warehouse = dialog.get_value('target'); - var row = frappe.model.add_child(doc, 'items'); - row.item_code = dialog.get_value('item_code'); - row.f_warehouse = dialog.get_value('target'); - row.t_warehouse = dialog.get_value('target'); - row.qty = dialog.get_value('qty'); - row.conversion_factor = 1; - row.transfer_qty = dialog.get_value('qty'); - row.basic_rate = dialog.get_value('rate'); - frappe.set_route('Form', doc.doctype, doc.name); - }) - }); -} \ No newline at end of file +}) \ No newline at end of file diff --git a/erpnext/stock/dashboard/item_dashboard_list.html b/erpnext/stock/dashboard/item_dashboard_list.html index 5a3fa2ed485..f0e87b1c53a 100644 --- a/erpnext/stock/dashboard/item_dashboard_list.html +++ b/erpnext/stock/dashboard/item_dashboard_list.html @@ -39,21 +39,6 @@ - {% if can_write %} -
- {% if d.actual_qty %} -
- {% endif %} {% endfor %} From a596a7fd2bbf986c45d5d9df180dec6223351913 Mon Sep 17 00:00:00 2001 From: Govind S Menokee Date: Sat, 29 Jun 2019 04:08:26 +0530 Subject: [PATCH 005/174] feat: Add batch operation times in BOM Operation --- erpnext/manufacturing/doctype/bom/bom.py | 2 + .../doctype/bom_operation/bom_operation.json | 460 +++------- .../doctype/work_order/work_order.py | 9 +- .../work_order_operation.json | 847 ++++-------------- 4 files changed, 305 insertions(+), 1013 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 75eb7943868..6925ed12aa1 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -578,6 +578,8 @@ class BOM(WebsiteGenerator): for d in self.operations: if not d.description: d.description = frappe.db.get_value('Operation', d.operation, 'description') + if not d.is_batch_operation: + d.batch_size = 1 def get_list_context(context): context.title = _("Bill of Materials") diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json index 08c4f4fce68..298d097f332 100644 --- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json +++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json @@ -1,361 +1,127 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-02-22 01:27:49", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", + "creation": "2013-02-22 01:27:49", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "operation", + "workstation", + "description", + "col_break1", + "hour_rate", + "time_in_mins", + "is_batch_operation", + "batch_size", + "operating_cost", + "base_hour_rate", + "base_operating_cost", + "image" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "operation", - "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": 0, - "label": "Operation", - "length": 0, - "no_copy": 0, - "oldfieldname": "operation_no", - "oldfieldtype": "Data", - "options": "Operation", - "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": 0 - }, + "fieldname": "operation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Operation", + "oldfieldname": "operation_no", + "oldfieldtype": "Data", + "options": "Operation", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "workstation", - "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": 0, - "label": "Workstation", - "length": 0, - "no_copy": 0, - "oldfieldname": "workstation", - "oldfieldtype": "Link", - "options": "Workstation", - "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 - }, + "fieldname": "workstation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Workstation", + "oldfieldname": "workstation", + "oldfieldtype": "Link", + "options": "Workstation" + }, { - "allow_bulk_edit": 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": 1, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "opn_description", - "oldfieldtype": "Text", - "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 - }, + "fieldname": "description", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Description", + "oldfieldname": "opn_description", + "oldfieldtype": "Text" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break1", - "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, - "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 - }, + "fieldname": "col_break1", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "hour_rate", - "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": "Hour Rate", - "length": 0, - "no_copy": 0, - "oldfieldname": "hour_rate", - "oldfieldtype": "Currency", - "options": "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 - }, + "fieldname": "hour_rate", + "fieldtype": "Currency", + "label": "Hour Rate", + "oldfieldname": "hour_rate", + "oldfieldtype": "Currency", + "options": "currency" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "In minutes", - "fieldname": "time_in_mins", - "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": "Operation Time ", - "length": 0, - "no_copy": 0, - "oldfieldname": "time_in_mins", - "oldfieldtype": "Currency", - "options": "", - "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": 0 - }, + "description": "In minutes", + "fieldname": "time_in_mins", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Operation Time ", + "oldfieldname": "time_in_mins", + "oldfieldtype": "Currency", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "operating_cost", - "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": "Operating Cost", - "length": 0, - "no_copy": 0, - "oldfieldname": "operating_cost", - "oldfieldtype": "Currency", - "options": "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 - }, + "fieldname": "operating_cost", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Operating Cost", + "oldfieldname": "operating_cost", + "oldfieldtype": "Currency", + "options": "currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_hour_rate", - "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": "Base Hour Rate(Company Currency)", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "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 - }, + "fieldname": "base_hour_rate", + "fieldtype": "Currency", + "label": "Base Hour Rate(Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "5", - "fieldname": "base_operating_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": "Operating Cost(Company Currency)", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "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 - }, + "default": "5", + "fieldname": "base_operating_cost", + "fieldtype": "Currency", + "label": "Operating Cost(Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "image", - "fieldtype": "Attach", - "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": "Image", - "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 + "fieldname": "image", + "fieldtype": "Attach", + "label": "Image" + }, + { + "default": "0", + "fieldname": "is_batch_operation", + "fieldtype": "Check", + "label": "Is Batch Operation" + }, + { + "default": "1", + "depends_on": "eval:doc.is_batch_operation==1;", + "fieldname": "batch_size", + "fieldtype": "Int", + "label": "Batch Size" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-03-26 09:55:28.107451", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "BOM Operation", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "track_changes": 0, - "track_seen": 0 + ], + "idx": 1, + "istable": 1, + "modified": "2019-06-29 03:35:32.213562", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "BOM Operation", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 0e8f69145b0..24eb4e63ae3 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe import json +import math from frappe import _ from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate from frappe.model.document import Document @@ -323,7 +324,8 @@ class WorkOrder(Document): select operation, description, workstation, idx, base_hour_rate as hour_rate, time_in_mins, - "Pending" as status, parent as bom + "Pending" as status, parent as bom, + is_batch_operation, batch_size from `tabBOM Operation` where @@ -348,7 +350,10 @@ class WorkOrder(Document): bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity") for d in self.get("operations"): - d.time_in_mins = flt(d.time_in_mins) / flt(bom_qty) * flt(self.qty) + if d.is_batch_operation: + d.time_in_mins = flt(d.time_in_mins) / flt(bom_qty) * math.ceil(flt(self.qty)/flt(d.batch_size)) + else: + d.time_in_mins = flt(d.time_in_mins) / flt(bom_qty) * flt(self.qty) self.calculate_operating_cost() diff --git a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json index 9c1c95383bb..69735bfb28a 100644 --- a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json +++ b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json @@ -1,690 +1,209 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2014-10-16 14:35:41.950175", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "creation": "2014-10-16 14:35:41.950175", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "details", + "operation", + "bom", + "description", + "col_break1", + "completed_qty", + "status", + "workstation", + "estimated_time_and_cost", + "planned_start_time", + "planned_end_time", + "column_break_10", + "time_in_mins", + "hour_rate", + "is_batch_operation", + "batch_size", + "planned_operating_cost", + "section_break_9", + "actual_start_time", + "actual_end_time", + "column_break_11", + "actual_operation_time", + "actual_operating_cost" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "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": "", - "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, - "unique": 0 - }, + "fieldname": "details", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "operation", - "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": 0, - "label": "Operation", - "length": 0, - "no_copy": 0, - "oldfieldname": "operation_no", - "oldfieldtype": "Data", - "options": "Operation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "operation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Operation", + "oldfieldname": "operation_no", + "oldfieldtype": "Data", + "options": "Operation", + "read_only": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bom", - "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": "BOM", - "length": 0, - "no_copy": 1, - "options": "BOM", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "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, - "unique": 0 - }, + "fieldname": "bom", + "fieldtype": "Link", + "label": "BOM", + "no_copy": 1, + "options": "BOM", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 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": "Operation Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "opn_description", - "oldfieldtype": "Text", - "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, - "unique": 0 - }, + "fieldname": "description", + "fieldtype": "Text Editor", + "label": "Operation Description", + "oldfieldname": "opn_description", + "oldfieldtype": "Text", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break1", - "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, - "unique": 0 - }, + "fieldname": "col_break1", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Operation completed for how many finished goods?", - "fieldname": "completed_qty", - "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": "Completed Qty", - "length": 0, - "no_copy": 1, - "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, - "unique": 0 - }, + "description": "Operation completed for how many finished goods?", + "fieldname": "completed_qty", + "fieldtype": "Float", + "label": "Completed Qty", + "no_copy": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Pending", - "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": "Pending\nWork in Progress\nCompleted", - "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, - "unique": 0 - }, + "default": "Pending", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "no_copy": 1, + "options": "Pending\nWork in Progress\nCompleted", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "workstation", - "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": 0, - "label": "Workstation", - "length": 0, - "no_copy": 0, - "oldfieldname": "workstation", - "oldfieldtype": "Link", - "options": "Workstation", - "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, - "unique": 0 - }, + "fieldname": "workstation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Workstation", + "oldfieldname": "workstation", + "oldfieldtype": "Link", + "options": "Workstation" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "estimated_time_and_cost", - "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": "Estimated Time and Cost", - "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, - "unique": 0 - }, + "fieldname": "estimated_time_and_cost", + "fieldtype": "Section Break", + "label": "Estimated Time and Cost" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "planned_start_time", - "fieldtype": "Datetime", - "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": "Planned Start Time", - "length": 0, - "no_copy": 1, - "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, - "unique": 0 - }, + "fieldname": "planned_start_time", + "fieldtype": "Datetime", + "label": "Planned Start Time", + "no_copy": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "planned_end_time", - "fieldtype": "Datetime", - "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": "Planned End Time", - "length": 0, - "no_copy": 1, - "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, - "unique": 0 - }, + "fieldname": "planned_end_time", + "fieldtype": "Datetime", + "label": "Planned End Time", + "no_copy": 1, + "read_only": 1 + }, { - "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, - "unique": 0 - }, + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "in Minutes", - "fieldname": "time_in_mins", - "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": "Operation Time", - "length": 0, - "no_copy": 0, - "oldfieldname": "time_in_mins", - "oldfieldtype": "Currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "description": "in Minutes", + "fieldname": "time_in_mins", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Operation Time", + "oldfieldname": "time_in_mins", + "oldfieldtype": "Currency", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "hour_rate", - "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": "Hour Rate", - "length": 0, - "no_copy": 0, - "oldfieldname": "hour_rate", - "oldfieldtype": "Currency", - "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, - "unique": 0 - }, + "fieldname": "hour_rate", + "fieldtype": "Float", + "label": "Hour Rate", + "oldfieldname": "hour_rate", + "oldfieldtype": "Currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "planned_operating_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": "Planned Operating Cost", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "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, - "unique": 0 - }, + "fieldname": "planned_operating_cost", + "fieldtype": "Currency", + "label": "Planned Operating Cost", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_9", - "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": "Actual Time and Cost", - "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, - "unique": 0 - }, + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "label": "Actual Time and Cost" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "actual_start_time", - "fieldtype": "Datetime", - "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 Time", - "length": 0, - "no_copy": 1, - "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, - "unique": 0 - }, + "fieldname": "actual_start_time", + "fieldtype": "Datetime", + "label": "Actual Start Time", + "no_copy": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Updated via 'Time Log'", - "fieldname": "actual_end_time", - "fieldtype": "Datetime", - "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 Time", - "length": 0, - "no_copy": 1, - "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, - "unique": 0 - }, + "description": "Updated via 'Time Log'", + "fieldname": "actual_end_time", + "fieldtype": "Datetime", + "label": "Actual End Time", + "no_copy": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_11", - "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, - "unique": 0 - }, + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "in Minutes\nUpdated via 'Time Log'", - "fieldname": "actual_operation_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 Operation Time", - "length": 0, - "no_copy": 1, - "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, - "unique": 0 - }, + "description": "in Minutes\nUpdated via 'Time Log'", + "fieldname": "actual_operation_time", + "fieldtype": "Float", + "label": "Actual Operation Time", + "no_copy": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "(Hour Rate / 60) * Actual Operation Time", - "fieldname": "actual_operating_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": "Actual Operating Cost", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "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, - "unique": 0 + "description": "(Hour Rate / 60) * Actual Operation Time", + "fieldname": "actual_operating_cost", + "fieldtype": "Currency", + "label": "Actual Operating Cost", + "no_copy": 1, + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_batch_operation", + "fieldtype": "Check", + "label": "Is Batch Operation", + "read_only": 1 + }, + { + "depends_on": "eval:doc.is_batch_operation==1;", + "fieldname": "batch_size", + "fieldtype": "Int", + "label": "Batch Size", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-02-13 02:58:11.328693", - "modified_by": "Administrator", - "module": "Manufacturing", + ], + "istable": 1, + "modified": "2019-06-29 02:54:14.714995", + "modified_by": "Administrator", + "module": "Manufacturing", "name": "Work Order Operation", - "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": 1, - "track_seen": 0 + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file From 09b44345dfca2e15ecc84ff185b6ac9a457cee6d Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 19 Jun 2019 12:52:07 +0530 Subject: [PATCH 006/174] fix: as limit 100 was set in the query therefore not all item's default value moved to the Item Default table --- erpnext/patches.txt | 2 +- ...efaults_to_child_table_for_multicompany.py | 84 +++++++++++++------ 2 files changed, 58 insertions(+), 28 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 36a31cfadc1..be9baf4583a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -533,7 +533,7 @@ erpnext.patches.v11_0.create_department_records_for_each_company erpnext.patches.v11_0.make_location_from_warehouse erpnext.patches.v11_0.make_asset_finance_book_against_old_entries erpnext.patches.v11_0.check_buying_selling_in_currency_exchange -erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany #02-07-2018 +erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany #02-07-2018 #19-06-2019 erpnext.patches.v11_0.refactor_erpnext_shopify #2018-09-07 erpnext.patches.v11_0.rename_overproduction_percent_field erpnext.patches.v11_0.update_backflush_subcontract_rm_based_on_bom diff --git a/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py b/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py index 01f84a03136..c7c76355400 100644 --- a/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py +++ b/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py @@ -17,10 +17,8 @@ def execute(): frappe.reload_doc('stock', 'doctype', 'item_default') frappe.reload_doc('stock', 'doctype', 'item') - if frappe.db.a_row_exists('Item Default'): return - companies = frappe.get_all("Company") - if len(companies) == 1: + if len(companies) == 1 and not frappe.get_all("Item Default", limit=1): try: frappe.db.sql(''' INSERT INTO `tabItem Default` @@ -35,32 +33,64 @@ def execute(): except: pass else: - item_details = frappe.get_all("Item", fields=["name", "default_warehouse", "buying_cost_center", - "expense_account", "selling_cost_center", "income_account"], limit=100) + item_details = frappe.db.sql(""" SELECT name, default_warehouse, + buying_cost_center, expense_account, selling_cost_center, income_account + FROM tabItem + WHERE + name not in (select distinct parent from `tabItem Default`) and ifnull(disabled, 0) = 0""" + , as_dict=1) - for item in item_details: - item_defaults = [] + items_default_data = {} + for item_data in item_details: + for d in [["default_warehouse", "Warehouse"], ["expense_account", "Account"], + ["income_account", "Account"], ["buying_cost_center", "Cost Center"], + ["selling_cost_center", "Cost Center"]]: + if item_data.get(d[0]): + company = frappe.get_value(d[1], item_data.get(d[0]), "company", cache=True) - def insert_into_item_defaults(doc_field_name, doc_field_value, company): - for d in item_defaults: - if d.get("company") == company: - d[doc_field_name] = doc_field_value - return - item_defaults.append({ - "company": company, - doc_field_name: doc_field_value - }) + if item_data.name not in items_default_data: + items_default_data[item_data.name] = {} - for d in [ - ["default_warehouse", "Warehouse"], ["expense_account", "Account"], ["income_account", "Account"], - ["buying_cost_center", "Cost Center"], ["selling_cost_center", "Cost Center"] - ]: - if item.get(d[0]): - company = frappe.get_value(d[1], item.get(d[0]), "company", cache=True) - insert_into_item_defaults(d[0], item.get(d[0]), company) + company_wise_data = items_default_data[item_data.name] - doc = frappe.get_doc("Item", item.name) - doc.extend("item_defaults", item_defaults) + if company not in company_wise_data: + company_wise_data[company] = {} - for child_doc in doc.item_defaults: - child_doc.db_insert() \ No newline at end of file + default_data = company_wise_data[company] + default_data[d[0]] = item_data.get(d[0]) + + to_insert_data = [] + + # items_default_data data structure will be as follow + # { + # 'item_code 1': {'company 1': {'default_warehouse': 'Test Warehouse 1'}}, + # 'item_code 2': { + # 'company 1': {'default_warehouse': 'Test Warehouse 1'}, + # 'company 2': {'default_warehouse': 'Test Warehouse 1'} + # } + # } + + for item_code, companywise_item_data in items_default_data.items(): + for company, item_default_data in companywise_item_data.items(): + to_insert_data.append(( + frappe.generate_hash("", 10), + item_code, + 'Item', + 'item_defaults', + company, + item_default_data.get('default_warehouse'), + item_default_data.get('expense_account'), + item_default_data.get('income_account'), + item_default_data.get('buying_cost_center'), + item_default_data.get('selling_cost_center'), + )) + + if to_insert_data: + frappe.db.sql(''' + INSERT INTO `tabItem Default` + ( + `name`, `parent`, `parenttype`, `parentfield`, `company`, `default_warehouse`, + `expense_account`, `income_account`, `buying_cost_center`, `selling_cost_center` + ) + VALUES {} + '''.format(', '.join(['%s'] * len(to_insert_data))), tuple(to_insert_data)) \ No newline at end of file From 126a02e2084cd7c5e9d3035d38692a068e2ff8f8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 2 Jul 2019 15:28:57 +0530 Subject: [PATCH 007/174] fix: set invoice status considering invoice discounting --- .../discounted_invoice.json | 147 +--- .../invoice_discounting.json | 674 ++---------------- .../invoice_discounting.py | 33 +- .../doctype/journal_entry/journal_entry.py | 18 +- .../doctype/sales_invoice/sales_invoice.js | 13 +- .../doctype/sales_invoice/sales_invoice.json | 13 +- .../doctype/sales_invoice/sales_invoice.py | 66 +- .../sales_invoice/sales_invoice_list.js | 23 +- .../doctype/purchase_order/purchase_order.py | 1 - erpnext/controllers/status_updater.py | 2 + 10 files changed, 206 insertions(+), 784 deletions(-) diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json index 0d04b19fd1b..8d7ed74eb9e 100644 --- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json +++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json @@ -1,177 +1,64 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, "creation": "2019-03-07 12:07:09.416101", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "sales_invoice", + "customer", + "column_break_3", + "posting_date", + "outstanding_amount" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sales_invoice", "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": 0, "label": "Invoice", - "length": 0, - "no_copy": 0, "options": "Sales Invoice", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "sales_invoice.customer", "fieldname": "customer", "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": 0, "label": "Customer", - "length": 0, - "no_copy": 0, "options": "Customer", - "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, "fetch_from": "sales_invoice.posting_date", "fieldname": "posting_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": "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, "fetch_from": "sales_invoice.grand_total", "fieldname": "outstanding_amount", "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": "Outstanding Amount", - "length": 0, - "no_copy": 0, "options": "Company:company:default_currency", - "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 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" } ], - "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-03-07 16:38:03.622666", + "modified": "2019-05-30 19:27:29.436153", "modified_by": "Administrator", "module": "Accounts", "name": "Discounted Invoice", - "name_case": "", "owner": "Administrator", "permissions": [], "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_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json index 8927ca708d7..3bfe2594326 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json @@ -1,744 +1,177 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, "allow_import": 1, - "allow_rename": 0, "autoname": "ACC-INV-DISC-.YYYY.-.#####", - "beta": 0, "creation": "2019-03-07 12:01:56.296952", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "posting_date", + "loan_start_date", + "loan_period", + "loan_end_date", + "column_break_3", + "status", + "company", + "section_break_5", + "invoices", + "section_break_7", + "total_amount", + "column_break_9", + "bank_charges", + "section_break_6", + "short_term_loan", + "bank_account", + "bank_charges_account", + "column_break_15", + "accounts_receivable_credit", + "accounts_receivable_discounted", + "accounts_receivable_unpaid", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "Today", "fieldname": "posting_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": "Posting 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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "loan_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": "Loan 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 + "label": "Loan Start Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "loan_period", "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": "Loan Period", - "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": "Loan Period (Days)" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "loan_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": "Loan End Date", - "length": 0, "no_copy": 1, - "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_3", - "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": "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": 0, "label": "Status", - "length": 0, "no_copy": 1, "options": "Draft\nSanctioned\nDisbursed\nSettled\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": 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": 1, - "in_standard_filter": 0, "label": "Company", - "length": 0, - "no_copy": 0, "options": "Company", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_5", - "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 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "invoices", "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": "Invoices", - "length": 0, - "no_copy": 0, "options": "Discounted Invoice", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_7", - "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 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_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 Amount", - "length": 0, - "no_copy": 0, "options": "Company:company:default_currency", - "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_9", - "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": "bank_charges", "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": "Bank Charges", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "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": "Company:company:default_currency" }, { - "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 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "short_term_loan", "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": "Short Term Loan Account", - "length": 0, - "no_copy": 0, "options": "Account", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "bank_account", "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": "Bank Account", - "length": 0, - "no_copy": 0, "options": "Account", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "bank_charges_account", "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": "Bank Charges Account", - "length": 0, - "no_copy": 0, "options": "Account", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_15", - "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": "accounts_receivable_credit", "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": "Accounts Receivable Credit Account", - "length": 0, - "no_copy": 0, "options": "Account", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "accounts_receivable_discounted", "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": "Accounts Receivable Discounted Account", - "length": 0, - "no_copy": 0, "options": "Account", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "accounts_receivable_unpaid", "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": "Accounts Receivable Unpaid Account", - "length": 0, - "no_copy": 0, "options": "Account", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "amended_from", "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": "Amended From", - "length": 0, "no_copy": 1, "options": "Invoice Discounting", - "permlevel": 0, "print_hide": 1, - "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 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-03-08 14:24:31.222027", + "modified": "2019-05-30 19:08:21.199759", "modified_by": "Administrator", "module": "Accounts", "name": "Invoice Discounting", - "name_case": "", "owner": "Administrator", "permissions": [ { @@ -748,26 +181,17 @@ "delete": 1, "email": 1, "export": 1, - "if_owner": 0, "import": 1, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 } ], - "quick_entry": 0, - "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_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py index c8756af7d75..29475d5644d 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py @@ -28,18 +28,39 @@ class InvoiceDiscounting(AccountsController): self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices]) def on_submit(self): + self.update_sales_invoice() self.make_gl_entries() def on_cancel(self): self.set_status() + self.update_sales_invoice() self.make_gl_entries() - def set_status(self): - self.status = "Draft" - if self.docstatus == 1: - self.status = "Sanctioned" - elif self.docstatus == 2: - self.status = "Cancelled" + def set_status(self, status=None): + if status: + self.status = status + self.db_set("status", status) + for d in self.invoices: + frappe.get_doc("Sales Invoice", d.sales_invoice).set_status(update=True, update_modified=False) + else: + self.status = "Draft" + if self.docstatus == 1: + self.status = "Sanctioned" + elif self.docstatus == 2: + self.status = "Cancelled" + + def update_sales_invoice(self): + for d in self.invoices: + if self.docstatus == 1: + is_discounted = 1 + else: + discounted_invoice = frappe.db.exists({ + "doctype": "Discounted Invoice", + "sales_invoice": d.sales_invoice, + "docstatus": 1 + }) + is_discounted = 1 if discounted_invoice else 0 + frappe.db.set_value("Sales Invoice", d.sales_invoice, "is_discounted", is_discounted) def make_gl_entries(self): company_currency = frappe.get_cached_value('Company', self.company, "default_currency") diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index d082b602117..a9929c379dc 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -105,24 +105,28 @@ class JournalEntry(AccountsController): invoice_discounting_list = list(set([d.reference_name for d in self.accounts if d.reference_type=="Invoice Discounting"])) for inv_disc in invoice_discounting_list: - short_term_loan_account, id_status = frappe.db.get_value("Invoice Discounting", inv_disc, ["short_term_loan", "status"]) + inv_disc_doc = frappe.get_doc("Invoice Discounting", inv_disc) + status = None for d in self.accounts: - if d.account == short_term_loan_account and d.reference_name == inv_disc: + if d.account == inv_disc_doc.short_term_loan and d.reference_name == inv_disc: if self.docstatus == 1: if d.credit > 0: - _validate_invoice_discounting_status(inv_disc, id_status, "Sanctioned", d.idx) + _validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Sanctioned", d.idx) status = "Disbursed" elif d.debit > 0: - _validate_invoice_discounting_status(inv_disc, id_status, "Disbursed", d.idx) + _validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Disbursed", d.idx) status = "Settled" else: if d.credit > 0: - _validate_invoice_discounting_status(inv_disc, id_status, "Disbursed", d.idx) + _validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Disbursed", d.idx) status = "Sanctioned" elif d.debit > 0: - _validate_invoice_discounting_status(inv_disc, id_status, "Settled", d.idx) + _validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Settled", d.idx) status = "Disbursed" - frappe.db.set_value("Invoice Discounting", inv_disc, "status", status) + break + if status: + inv_disc_doc.set_status(status=status) + def unlink_advance_entry_reference(self): for d in self.get("accounts"): diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 07494a27d6d..eaf59d3bf5d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -83,10 +83,14 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte } } - if (doc.outstanding_amount>0 && !cint(doc.is_return)) { + if (doc.outstanding_amount>0) { cur_frm.add_custom_button(__('Payment Request'), function() { me.make_payment_request(); }, __('Create')); + + cur_frm.add_custom_button(__('Invoice Discounting'), function() { + cur_frm.events.create_invoice_discounting(cur_frm); + }, __('Create')); } if (doc.docstatus === 1) { @@ -804,6 +808,13 @@ frappe.ui.form.on('Sales Invoice', { frm.set_df_property("patient_name", "hidden", 1); frm.set_df_property("ref_practitioner", "hidden", 1); } + }, + + create_invoice_discounting: function(frm) { + frappe.model.open_mapped_doc({ + method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_invoice_discounting", + frm: frm + }); } }) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 292d95d7b66..c0a1abd7852 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -155,6 +155,7 @@ "inter_company_invoice_reference", "customer_group", "campaign", + "is_discounted", "col_break23", "status", "source", @@ -1336,7 +1337,7 @@ "in_standard_filter": 1, "label": "Status", "no_copy": 1, - "options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled", + "options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nOverdue\nCancelled", "print_hide": 1, "read_only": 1 }, @@ -1553,12 +1554,20 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "is_discounted", + "fieldtype": "Check", + "label": "Is Discounted", + "no_copy": 1, + "read_only": 1 } ], "icon": "fa fa-file-text", "idx": 181, "is_submittable": 1, - "modified": "2019-05-25 22:05:03.474745", + "modified": "2019-07-02 15:21:40.257028", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index d6cf5d8b908..54e127d2f45 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1174,6 +1174,56 @@ class SalesInvoice(SellingController): self.set_missing_values(for_validate = True) + def get_discounting_status(self): + status = None + if self.is_discounted: + invoice_discounting_list = frappe.db.sql(""" + select status + from `tabInvoice Discounting` id, `tabDiscounted Invoice` d + where + id.name = d.parent + and d.sales_invoice=%s + and id.docstatus=1 + and status in ('Disbursed', 'Settled') + """, self.name) + for d in invoice_discounting_list: + status = d[0] + if status == "Disbursed": + break + return status + + def set_status(self, update=False, status=None, update_modified=True): + if self.is_new(): + if self.get('amended_from'): + self.status = 'Draft' + return + + if not status: + if self.docstatus == 2: + status = "Cancelled" + elif self.docstatus == 1: + if flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + self.status = "Overdue and Discounted" + elif self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()): + self.status = "Overdue" + elif self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + self.status = "Unpaid and Discounted" + elif self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()): + self.status = "Unpaid" + elif self.outstanding_amount < 0 and self.is_return==0 and get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): + self.status = "Credit Note Issued" + elif self.outstanding_amount<=0 and self.is_return==0: + self.status = "Paid" + elif self.is_return == 1: + self.status = "Return" + else: + self.status = "Submitted" + else: + self.status = "Draft" + + if update: + self.db_set('status', self.status, update_modified = update_modified) + def validate_inter_company_party(doctype, party, company, inter_company_reference): if not party: return @@ -1428,4 +1478,18 @@ def get_loyalty_programs(customer): frappe.db.set(customer, 'loyalty_program', lp_details[0]) return [] else: - return lp_details \ No newline at end of file + return lp_details + +@frappe.whitelist() +def create_invoice_discounting(source_name, target_doc=None): + invoice = frappe.get_doc("Sales Invoice", source_name) + invoice_discounting = frappe.new_doc("Invoice Discounting") + invoice_discounting.company = invoice.company + invoice_discounting.append("invoices", { + "sales_invoice": source_name, + "customer": invoice.customer, + "posting_date": invoice.posting_date, + "outstanding_amount": invoice.outstanding_amount + }) + + return invoice_discounting \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js index 3c9c4b428da..05d49df711a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js @@ -6,17 +6,18 @@ frappe.listview_settings['Sales Invoice'] = { add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company", "currency", "is_return"], get_indicator: function(doc) { - if(cint(doc.is_return)==1) { - return [__("Return"), "darkgrey", "is_return,=,Yes"]; - } else if(flt(doc.outstanding_amount)==0) { - return [__("Paid"), "green", "outstanding_amount,=,0"] - } else if(flt(doc.outstanding_amount) < 0) { - return [__("Credit Note Issued"), "darkgrey", "outstanding_amount,<,0"] - }else if (flt(doc.outstanding_amount) > 0 && doc.due_date >= frappe.datetime.get_today()) { - return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>,Today"] - } else if (flt(doc.outstanding_amount) > 0 && doc.due_date < frappe.datetime.get_today()) { - return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<=,Today"] - } + var status_color = { + "Draft": "grey", + "Unpaid": "orange", + "Paid": "green", + "Return": "darkgrey", + "Credit Note Issued": "darkgrey", + "Unpaid and Discounted": "orange", + "Overdue and Discounted": "red", + "Overdue": "red" + + }; + return [__(doc.status), status_color[doc.status], "status,=,"+doc.status]; }, right_column: "grand_total" }; diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index a9a2094f083..8117d9d5149 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -510,4 +510,3 @@ def update_status(status, name): def make_inter_company_sales_order(source_name, target_doc=None): from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction return make_inter_company_transaction("Purchase Order", source_name, target_doc) - diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 42e0a43e6e2..c7542a8951e 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -50,7 +50,9 @@ status_map = { ["Paid", "eval:self.outstanding_amount<=0 and self.docstatus==1 and self.is_return==0"], ["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1 and self.is_return==0 and get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], + ["Unpaid and Discounted", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1 and self.is_discounted and self.discounting_status=='Disbursed'"], ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], + ["Overdue and Discounted", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1 and self.is_discounted and self.discounting_status=='Disbursed'"], ["Cancelled", "eval:self.docstatus==2"], ], "Purchase Invoice": [ From 3bbcd491e4e1ccabddef3051796beae93834c16c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 2 Jul 2019 15:31:24 +0530 Subject: [PATCH 008/174] fix: set invoice status considering invoice discounting --- erpnext/controllers/status_updater.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index c7542a8951e..db6460f00cb 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -43,18 +43,6 @@ status_map = { ["Closed", "eval:self.status=='Closed'"], ["On Hold", "eval:self.status=='On Hold'"], ], - "Sales Invoice": [ - ["Draft", None], - ["Submitted", "eval:self.docstatus==1"], - ["Return", "eval:self.is_return==1 and self.docstatus==1"], - ["Paid", "eval:self.outstanding_amount<=0 and self.docstatus==1 and self.is_return==0"], - ["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1 and self.is_return==0 and get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], - ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], - ["Unpaid and Discounted", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1 and self.is_discounted and self.discounting_status=='Disbursed'"], - ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], - ["Overdue and Discounted", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1 and self.is_discounted and self.discounting_status=='Disbursed'"], - ["Cancelled", "eval:self.docstatus==2"], - ], "Purchase Invoice": [ ["Draft", None], ["Submitted", "eval:self.docstatus==1"], From 05fee62314957890b0772c2d2fa3f4f8266303a5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 2 Jul 2019 17:55:26 +0530 Subject: [PATCH 009/174] fix: inv status --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 54e127d2f45..43bbfab1307 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1210,7 +1210,7 @@ class SalesInvoice(SellingController): self.status = "Unpaid and Discounted" elif self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()): self.status = "Unpaid" - elif self.outstanding_amount < 0 and self.is_return==0 and get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): + elif self.outstanding_amount < 0 and self.is_return==0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): self.status = "Credit Note Issued" elif self.outstanding_amount<=0 and self.is_return==0: self.status = "Paid" From 3a83c7bd7d5340ef960e71b8535f868e1ef6f715 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Tue, 2 Jul 2019 19:56:33 +0530 Subject: [PATCH 010/174] fix: Multiple fixes in GSTR-3b Report --- .../gstr_3b_report/gstr_3b_report.html | 16 ++-- .../doctype/gstr_3b_report/gstr_3b_report.py | 79 ++++++++++++------- 2 files changed, 58 insertions(+), 37 deletions(-) diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html index 77a9b63dfef..2da79a63648 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html @@ -222,17 +222,17 @@   (1) {{__("As per rules 42 & 43 of CGST Rules")}} - - - - + {{ flt(data.itc_elg.itc_rev[0].iamt, 2) }} + {{ flt(data.itc_elg.itc_rev[0].camt, 2) }} + {{ flt(data.itc_elg.itc_rev[0].samt, 2) }} + {{ flt(data.itc_elg.itc_rev[0].csamt, 2) }}   (2) {{__("Others")}} - - - - + {{ flt(data.itc_elg.itc_rev[1].iamt, 2) }} + {{ flt(data.itc_elg.itc_rev[1].camt, 2) }} + {{ flt(data.itc_elg.itc_rev[1].samt, 2) }} + {{ flt(data.itc_elg.itc_rev[1].csamt, 2) }} (C) {{__("Net ITC Available(A) - (B)")}} diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 65698336596..448abe8f280 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -91,10 +91,10 @@ class GSTR3BReport(Document): }, { "ty": "ISD", - "iamt": 1, - "camt": 1, - "samt": 1, - "csamt": 1 + "iamt": 0, + "camt": 0, + "samt": 0, + "csamt": 0 }, { "samt": 0, @@ -104,6 +104,22 @@ class GSTR3BReport(Document): "iamt": 0 } ], + "itc_rev": [ + { + "ty": "RUL", + "iamt": 0, + "camt": 0, + "samt": 0, + "csamt": 0 + }, + { + "ty": "OTH", + "iamt": 0, + "camt": 0, + "samt": 0, + "csamt": 0 + } + ], "itc_net": { "samt": 0, "csamt": 0, @@ -173,6 +189,10 @@ class GSTR3BReport(Document): net_itc = self.report_dict["itc_elg"]["itc_net"] for d in self.report_dict["itc_elg"]["itc_avl"]: + + itc_type = itc_type_map.get(d["ty"]) + gst_category = "Registered Regular" + if d["ty"] == 'ISRC': reverse_charge = "Y" else: @@ -180,24 +200,22 @@ class GSTR3BReport(Document): for account_head in self.account_heads: - d["iamt"] = flt(itc_details.get((itc_type_map.get(d["ty"]), reverse_charge, account_head.get('igst_account')), {}).get("amount"), 2) - net_itc["iamt"] += flt(d["iamt"], 2) + d["iamt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('igst_account')), {}).get("amount"), 2) + d["camt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('cgst_account')), {}).get("amount"), 2) + d["samt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('sgst_account')), {}).get("amount"), 2) + d["csamt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('cess_account')), {}).get("amount"), 2) - d["camt"] = flt(itc_details.get((itc_type_map.get(d["ty"]), reverse_charge, account_head.get('cgst_account')), {}).get("amount"), 2) - net_itc["camt"] += flt(d["camt"], 2) - - d["samt"] = flt(itc_details.get((itc_type_map.get(d["ty"]), reverse_charge, account_head.get('sgst_account')), {}).get("amount"), 2) - net_itc["samt"] += flt(d["samt"], 2) - - d["csamt"] = flt(itc_details.get((itc_type_map.get(d["ty"]), reverse_charge, account_head.get('cess_account')), {}).get("amount"), 2) - net_itc["csamt"] += flt(d["csamt"], 2) + net_itc["iamt"] += flt(d["iamt"], 2) + net_itc["camt"] += flt(d["camt"], 2) + net_itc["samt"] += flt(d["samt"], 2) + net_itc["csamt"] += flt(d["csamt"], 2) for account_head in self.account_heads: - - self.report_dict["itc_elg"]["itc_inelg"][1]["iamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("igst_account")), {}).get("amount"), 2) - self.report_dict["itc_elg"]["itc_inelg"][1]["camt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cgst_account")), {}).get("amount"), 2) - self.report_dict["itc_elg"]["itc_inelg"][1]["samt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("sgst_account")), {}).get("amount"), 2) - self.report_dict["itc_elg"]["itc_inelg"][1]["csamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cess_account")), {}).get("amount"), 2) + itc_inelg = self.report_dict["itc_elg"]["itc_inelg"][1] + itc_inelg["iamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("igst_account")), {}).get("amount"), 2) + itc_inelg["camt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cgst_account")), {}).get("amount"), 2) + itc_inelg["samt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("sgst_account")), {}).get("amount"), 2) + itc_inelg["csamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cess_account")), {}).get("amount"), 2) def prepare_data(self, doctype, tax_details, supply_type, supply_category, gst_category_list, reverse_charge="N"): @@ -226,20 +244,22 @@ class GSTR3BReport(Document): def set_inter_state_supply(self, inter_state_supply): + osup_det = self.report_dict["sup_details"]["osup_det"] + for d in inter_state_supply.get("Unregistered", []): self.report_dict["inter_sup"]["unreg_details"].append(d) - self.report_dict["sup_details"]["osup_det"]["txval"] += flt(d["txval"], 2) - self.report_dict["sup_details"]["osup_det"]["iamt"] += flt(d["iamt"], 2) + osup_det["txval"] = flt(osup_det["txval"] + d["txval"], 2) + osup_det["iamt"] = flt(osup_det["iamt"] + d["iamt"], 2) for d in inter_state_supply.get("Registered Composition", []): self.report_dict["inter_sup"]["comp_details"].append(d) - self.report_dict["sup_details"]["osup_det"]["txval"] += flt(d["txval"], 2) - self.report_dict["sup_details"]["osup_det"]["iamt"] += flt(d["iamt"], 2) + osup_det["txval"] = flt(osup_det["txval"] + d["txval"], 2) + osup_det["iamt"] = flt(osup_det["iamt"] + d["iamt"], 2) for d in inter_state_supply.get("UIN Holders", []): self.report_dict["inter_sup"]["uin_details"].append(d) - self.report_dict["sup_details"]["osup_det"]["txval"] += flt(d["txval"], 2) - self.report_dict["sup_details"]["osup_det"]["iamt"] += flt(d["iamt"], 2) + osup_det["txval"] = flt(osup_det["txval"] + d["txval"], 2) + osup_det["iamt"] = flt(osup_det["iamt"] + d["iamt"], 2) def get_total_taxable_value(self, doctype, reverse_charge): @@ -268,7 +288,7 @@ class GSTR3BReport(Document): itc_details = {} for d in itc_amount: - itc_details.setdefault((d.eligibility_for_itc, d.reverse_charge, d.account_head),{ + itc_details.setdefault((d.gst_category, d.eligibility_for_itc, d.reverse_charge, d.account_head),{ "amount": d.tax_amount }) @@ -315,9 +335,10 @@ class GSTR3BReport(Document): "iamt": flt(inter_state_supply_tax_mapping.get(d.place_of_supply), 2) }) else: - self.report_dict["sup_details"]["osup_det"]["txval"] += flt(d.total, 2) - self.report_dict["sup_details"]["osup_det"]["camt"] += flt(inter_state_supply_tax_mapping.get(d.place_of_supply)/2, 2) - self.report_dict["sup_details"]["osup_det"]["samt"] += flt(inter_state_supply_tax_mapping.get(d.place_of_supply)/2, 2) + osup_det = self.report_dict["sup_details"]["osup_det"] + osup_det["txval"] = flt(osup_det["txval"] + d.total, 2) + osup_det["camt"] = flt(osup_det["camt"] + inter_state_supply_tax_mapping.get(d.place_of_supply)/2, 2) + osup_det["samt"] = flt(osup_det["samt"] + inter_state_supply_tax_mapping.get(d.place_of_supply)/2, 2) return inter_state_supply_details From 247d50e5528f2eafae0e10af6b5ca080e2aee9bf Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 3 Jul 2019 10:47:35 +0530 Subject: [PATCH 011/174] refactor: Remove unwanted debug flag --- erpnext/crm/doctype/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/utils.py b/erpnext/crm/doctype/utils.py index bd8b678d3b7..9cfab159958 100644 --- a/erpnext/crm/doctype/utils.py +++ b/erpnext/crm/doctype/utils.py @@ -87,7 +87,7 @@ def get_employee_emails_for_popup(communication_medium): 'parent': communication_medium, 'from_time': ['<=', now_time], 'to_time': ['>=', now_time], - }, fields=['employee_group'], debug=1) + }, fields=['employee_group']) available_employee_groups = tuple([emp.employee_group for emp in available_employee_groups]) From f078031c609f4d0ead95f5a8a292a26bd4612bc7 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 3 Jul 2019 11:43:38 +0530 Subject: [PATCH 012/174] fix: multiple minor issues in delayed order report (#18134) --- .../delayed_item_report/delayed_item_report.js | 2 +- .../delayed_item_report/delayed_item_report.py | 15 ++++++++++++--- .../delayed_order_report/delayed_order_report.js | 2 +- .../delayed_order_report/delayed_order_report.py | 13 ++++++++++--- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/report/delayed_item_report/delayed_item_report.js b/erpnext/stock/report/delayed_item_report/delayed_item_report.js index f1ead2c9fbb..40e6abefeb0 100644 --- a/erpnext/stock/report/delayed_item_report/delayed_item_report.js +++ b/erpnext/stock/report/delayed_item_report/delayed_item_report.js @@ -55,7 +55,7 @@ frappe.query_reports["Delayed Item Report"] = { label: __("Based On"), fieldtype: "Select", options: ["Delivery Note", "Sales Invoice"], - default: "Delivery Note", + default: "Sales Invoice", reqd: 1 }, ] diff --git a/erpnext/stock/report/delayed_item_report/delayed_item_report.py b/erpnext/stock/report/delayed_item_report/delayed_item_report.py index 7b968b89a7a..4fc4027200d 100644 --- a/erpnext/stock/report/delayed_item_report/delayed_item_report.py +++ b/erpnext/stock/report/delayed_item_report/delayed_item_report.py @@ -46,7 +46,8 @@ class DelayedItemReport(object): self.transactions = frappe.db.sql(""" SELECT `tab{child_doc}`.item_code, `tab{child_doc}`.item_name, `tab{child_doc}`.item_group, `tab{child_doc}`.qty, `tab{child_doc}`.rate, `tab{child_doc}`.amount, `tab{child_doc}`.so_detail, `tab{child_doc}`.{so_field} as sales_order, - `tab{doctype}`.customer, `tab{doctype}`.posting_date, `tab{doctype}`.name, `tab{doctype}`.grand_total + `tab{doctype}`.shipping_address_name, `tab{doctype}`.po_no, `tab{doctype}`.customer, + `tab{doctype}`.posting_date, `tab{doctype}`.name, `tab{doctype}`.grand_total FROM `tab{child_doc}`, `tab{doctype}` WHERE `tab{child_doc}`.parent = `tab{doctype}`.name and `tab{doctype}`.docstatus = 1 and @@ -97,12 +98,20 @@ class DelayedItemReport(object): "fieldtype": "Link", "options": based_on, "width": 100 - },{ + }, + { "label": _("Customer"), "fieldname": "customer", "fieldtype": "Link", "options": "Customer", - "width": 100 + "width": 200 + }, + { + "label": _("Shipping Address"), + "fieldname": "shipping_address_name", + "fieldtype": "Link", + "options": "Address", + "width": 120 }, { "label": _("Expected Delivery Date"), diff --git a/erpnext/stock/report/delayed_order_report/delayed_order_report.js b/erpnext/stock/report/delayed_order_report/delayed_order_report.js index 5b02a58fafc..aab0f3d0d1f 100644 --- a/erpnext/stock/report/delayed_order_report/delayed_order_report.js +++ b/erpnext/stock/report/delayed_order_report/delayed_order_report.js @@ -55,7 +55,7 @@ frappe.query_reports["Delayed Order Report"] = { label: __("Based On"), fieldtype: "Select", options: ["Delivery Note", "Sales Invoice"], - default: "Delivery Note", + default: "Sales Invoice", reqd: 1 }, ] diff --git a/erpnext/stock/report/delayed_order_report/delayed_order_report.py b/erpnext/stock/report/delayed_order_report/delayed_order_report.py index d2a1a30d9e2..79dc5d88219 100644 --- a/erpnext/stock/report/delayed_order_report/delayed_order_report.py +++ b/erpnext/stock/report/delayed_order_report/delayed_order_report.py @@ -42,7 +42,14 @@ class DelayedOrderReport(DelayedItemReport): "fieldname": "customer", "fieldtype": "Link", "options": "Customer", - "width": 100 + "width": 200 + }, + { + "label": _("Shipping Address"), + "fieldname": "shipping_address_name", + "fieldtype": "Link", + "options": "Address", + "width": 140 }, { "label": _("Expected Delivery Date"), @@ -73,11 +80,11 @@ class DelayedOrderReport(DelayedItemReport): "fieldname": "sales_order", "fieldtype": "Link", "options": "Sales Order", - "width": 100 + "width": 150 }, { "label": _("Customer PO"), "fieldname": "po_no", "fieldtype": "Data", - "width": 100 + "width": 110 }] \ No newline at end of file From 841d852f416f00a4346a1be787e3858896ac3bea Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 3 Jul 2019 15:15:08 +0530 Subject: [PATCH 013/174] refactor: added missing translation functions (#18143) * fix: Translating Error and Messages * Update erpnext/controllers/item_variant.py Co-Authored-By: Shivam Mishra * Update erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py Co-Authored-By: Shivam Mishra --- .../doctype/accounting_period/accounting_period.py | 5 +++-- .../bank_statement_transaction_entry.py | 8 ++++---- .../doctype/purchase_invoice/purchase_invoice.js | 2 +- .../doctype/purchase_invoice/purchase_invoice.py | 2 +- .../doctype/subscription_plan/subscription_plan.py | 5 +++-- erpnext/controllers/item_variant.py | 2 +- erpnext/education/doctype/question/question.py | 2 +- erpnext/education/doctype/quiz/quiz.py | 7 ++++--- .../student_report_generation_tool.py | 5 +++-- erpnext/education/utils.py | 10 +++++----- .../connectors/shopify_connection.py | 4 ++-- .../clinical_procedure_template.py | 2 +- .../doctype/patient_appointment/patient_appointment.js | 2 +- .../doctype/patient_encounter/patient_encounter.js | 2 +- .../employee_benefit_claim/employee_benefit_claim.py | 2 +- erpnext/hr/doctype/hr_settings/hr_settings.js | 2 +- erpnext/hub_node/api.py | 6 +++--- erpnext/projects/doctype/project/project.py | 2 +- erpnext/public/js/education/lms/quiz.js | 2 +- .../regional/doctype/gstr_3b_report/gstr_3b_report.py | 5 +++-- 20 files changed, 41 insertions(+), 36 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py index f7190b75e2c..de45f3a2521 100644 --- a/erpnext/accounts/doctype/accounting_period/accounting_period.py +++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from frappe import _ class AccountingPeriod(Document): def validate(self): @@ -16,7 +17,7 @@ class AccountingPeriod(Document): def autoname(self): company_abbr = frappe.get_cached_value('Company', self.company, "abbr") self.name = " - ".join([self.period_name, company_abbr]) - + def validate_overlap(self): existing_accounting_period = frappe.db.sql("""select name from `tabAccounting Period` where ( @@ -33,7 +34,7 @@ class AccountingPeriod(Document): }, as_dict=True) if len(existing_accounting_period) > 0: - frappe.throw("Accounting Period overlaps with {0}".format(existing_accounting_period[0].get("name"))) + frappe.throw(_("Accounting Period overlaps with {0}".format(existing_accounting_period[0].get("name")))) def get_doctypes_for_closing(self): docs_for_closing = [] diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py index 101b9f2194d..1318cf18d76 100644 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py +++ b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py @@ -48,7 +48,7 @@ class BankStatementTransactionEntry(Document): def get_statement_headers(self): if not self.bank_settings: - frappe.throw("Bank Data mapper doesn't exist") + frappe.throw(_("Bank Data mapper doesn't exist")) mapper_doc = frappe.get_doc("Bank Statement Settings", self.bank_settings) headers = {entry.mapped_header:entry.stmt_header for entry in mapper_doc.header_items} return headers @@ -57,7 +57,7 @@ class BankStatementTransactionEntry(Document): if self.bank_statement is None: return filename = self.bank_statement.split("/")[-1] if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0): - frappe.throw("Transactions already retreived from the statement") + frappe.throw(_("Transactions already retreived from the statement")) date_format = frappe.get_value("Bank Statement Settings", self.bank_settings, "date_format") if (date_format is None): @@ -314,7 +314,7 @@ class BankStatementTransactionEntry(Document): try: reconcile_against_document(lst) except: - frappe.throw("Exception occurred while reconciling {0}".format(payment.reference_name)) + frappe.throw(_("Exception occurred while reconciling {0}".format(payment.reference_name))) def submit_payment_entries(self): for payment in self.new_transaction_items: @@ -414,7 +414,7 @@ def get_transaction_entries(filename, headers): elif (filename.lower().endswith("xls")): rows = get_rows_from_xls_file(filename) else: - frappe.throw("Only .csv and .xlsx files are supported currently") + frappe.throw(_("Only .csv and .xlsx files are supported currently")) stmt_headers = headers.values() for row in rows: diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index dd4f51ca4e8..f4b656d3f68 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -157,7 +157,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ can_change_release_date: function(date) { const diff = frappe.datetime.get_diff(date, frappe.datetime.nowdate()); if (diff < 0) { - frappe.throw('New release date should be in the future'); + frappe.throw(__('New release date should be in the future')); return false; } else { return true; diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 1bd833b5cea..a6f6acea66b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -105,7 +105,7 @@ class PurchaseInvoice(BuyingController): def validate_release_date(self): if self.release_date and getdate(nowdate()) >= getdate(self.release_date): - frappe.msgprint('Release date must be in the future', raise_exception=True) + frappe.throw(_('Release date must be in the future')) def validate_cash(self): if not self.cash_bank_account and flt(self.paid_amount): diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py index d3fef6023ba..625979bee1f 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.model.document import Document from erpnext.utilities.product import get_price @@ -13,7 +14,7 @@ class SubscriptionPlan(Document): def validate_interval_count(self): if self.billing_interval_count < 1: - frappe.throw('Billing Interval Count cannot be less than 1') + frappe.throw(_('Billing Interval Count cannot be less than 1')) @frappe.whitelist() def get_plan_rate(plan, quantity=1, customer=None): @@ -26,7 +27,7 @@ def get_plan_rate(plan, quantity=1, customer=None): customer_group = frappe.db.get_value("Customer", customer, "customer_group") else: customer_group = None - + price = get_price(item_code=plan.item, price_list=plan.price_list, customer_group=customer_group, company=None, qty=quantity) if not price: return 0 diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 9152c316cf1..669aa9be7e7 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -176,7 +176,7 @@ def enqueue_multiple_variant_creation(item, args): for key in variants: total_variants *= len(variants[key]) if total_variants >= 600: - frappe.msgprint("Please do not create more than 500 items at a time", raise_exception=1) + frappe.throw(_("Please do not create more than 500 items at a time")) return if total_variants < 10: return create_multiple_variants(item, args) diff --git a/erpnext/education/doctype/question/question.py b/erpnext/education/doctype/question/question.py index b8221081fab..9a973c76154 100644 --- a/erpnext/education/doctype/question/question.py +++ b/erpnext/education/doctype/question/question.py @@ -38,7 +38,7 @@ class Question(Document): options = self.options answers = [item.name for item in options if item.is_correct == True] if len(answers) == 0: - frappe.throw("No correct answer is set for {0}".format(self.name)) + frappe.throw(_("No correct answer is set for {0}".format(self.name))) return None elif len(answers) == 1: return answers[0] diff --git a/erpnext/education/doctype/quiz/quiz.py b/erpnext/education/doctype/quiz/quiz.py index 8e54745464b..ae1cb6ce429 100644 --- a/erpnext/education/doctype/quiz/quiz.py +++ b/erpnext/education/doctype/quiz/quiz.py @@ -4,12 +4,13 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.model.document import Document class Quiz(Document): def validate(self): if self.passing_score > 100: - frappe.throw("Passing Score value should be between 0 and 100") + frappe.throw(_("Passing Score value should be between 0 and 100")) def allowed_attempt(self, enrollment, quiz_name): if self.max_attempts == 0: @@ -17,7 +18,7 @@ class Quiz(Document): try: if len(frappe.get_all("Quiz Activity", {'enrollment': enrollment.name, 'quiz': quiz_name})) >= self.max_attempts: - frappe.msgprint("Maximum attempts for this quiz reached!") + frappe.msgprint(_("Maximum attempts for this quiz reached!")) return False else: return True @@ -56,5 +57,5 @@ def compare_list_elementwise(*args): else: return False except TypeError: - frappe.throw("Compare List function takes on list arguments") + frappe.throw(_("Compare List function takes on list arguments")) diff --git a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py b/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py index f9979752f78..16933dcfe09 100644 --- a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py +++ b/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe, json +from frappe import _ from frappe.model.document import Document from erpnext.education.api import get_grade from frappe.utils.pdf import get_pdf @@ -79,7 +80,7 @@ def get_attendance_count(student, academic_year, academic_term=None): from_date, to_date = frappe.db.get_value("Academic Term", academic_term, ["term_start_date", "term_end_date"]) if from_date and to_date: attendance = dict(frappe.db.sql('''select status, count(student) as no_of_days - from `tabStudent Attendance` where student = %s + from `tabStudent Attendance` where student = %s and date between %s and %s group by status''', (student, from_date, to_date))) if "Absent" not in attendance.keys(): @@ -88,4 +89,4 @@ def get_attendance_count(student, academic_year, academic_term=None): attendance["Present"] = 0 return attendance else: - frappe.throw("Provide the academic year and set the starting and ending date.") \ No newline at end of file + frappe.throw(_("Provide the academic year and set the starting and ending date.")) \ No newline at end of file diff --git a/erpnext/education/utils.py b/erpnext/education/utils.py index 8cd5bbb95ba..e0b278c2b1d 100644 --- a/erpnext/education/utils.py +++ b/erpnext/education/utils.py @@ -148,7 +148,7 @@ def enroll_in_program(program_name, student=None): # Check if self enrollment in allowed program = frappe.get_doc('Program', program_name) if not program.allow_self_enroll: - return frappe.throw("You are not allowed to enroll for this course") + return frappe.throw(_("You are not allowed to enroll for this course")) student = get_current_student() if not student: @@ -162,7 +162,7 @@ def enroll_in_program(program_name, student=None): # Check if self enrollment in allowed program = frappe.get_doc('Program', program_name) if not program.allow_self_enroll: - return frappe.throw("You are not allowed to enroll for this course") + return frappe.throw(_("You are not allowed to enroll for this course")) # Enroll in program program_enrollment = student.enroll_in_program(program_name) @@ -185,7 +185,7 @@ def add_activity(course, content_type, content, program): student = get_current_student() if not student: - return frappe.throw("Student with email {0} does not exist".format(frappe.session.user), frappe.DoesNotExistError) + return frappe.throw(_("Student with email {0} does not exist".format(frappe.session.user)), frappe.DoesNotExistError) enrollment = get_or_create_course_enrollment(course, program) if content_type == 'Quiz': @@ -220,7 +220,7 @@ def get_quiz(quiz_name, course): quiz = frappe.get_doc("Quiz", quiz_name) questions = quiz.get_questions() except: - frappe.throw("Quiz {0} does not exist".format(quiz_name)) + frappe.throw(_("Quiz {0} does not exist".format(quiz_name))) return None questions = [{ @@ -347,7 +347,7 @@ def get_or_create_course_enrollment(course, program): if not course_enrollment: program_enrollment = get_enrollment('program', program, student.name) if not program_enrollment: - frappe.throw("You are not enrolled in program {0}".format(program)) + frappe.throw(_("You are not enrolled in program {0}".format(program))) return return student.enroll_in_course(course_name=course, program_enrollment=get_enrollment('program', program, student.name)) else: diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index 88078ab74f6..1d6e8917f50 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -124,7 +124,7 @@ def create_sales_order(shopify_order, shopify_settings, company=None): else: so = frappe.get_doc("Sales Order", so) - + frappe.db.commit() return so @@ -252,6 +252,6 @@ def get_tax_account_head(tax): {"parent": "Shopify Settings", "shopify_tax": tax_title}, "tax_account") if not tax_account: - frappe.throw("Tax Account not specified for Shopify Tax {0}".format(tax.get("title"))) + frappe.throw(_("Tax Account not specified for Shopify Tax {0}".format(tax.get("title")))) return tax_account diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py index 90bf95770fb..141329b3db1 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py @@ -30,7 +30,7 @@ class ClinicalProcedureTemplate(Document): try: frappe.delete_doc("Item",self.item) except Exception: - frappe.throw("""Not permitted. Please disable the Procedure Template""") + frappe.throw(_("""Not permitted. Please disable the Procedure Template""")) def get_item_details(self, args=None): item = frappe.db.sql("""select stock_uom, item_name diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index 2f328de1bc7..b3cbd1f753d 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -298,7 +298,7 @@ var get_procedure_prescribed = function(frm){ }); } else{ - frappe.msgprint("Please select Patient to get prescribed procedure"); + frappe.msgprint(__("Please select Patient to get prescribed procedure")); } }; diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index c7df5b7cd94..7ea45688fd9 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -160,7 +160,7 @@ var btn_create_vital_signs = function (frm) { var btn_create_procedure = function (frm) { if(!frm.doc.patient){ - frappe.throw("Please select patient"); + frappe.throw(__("Please select patient")); } frappe.route_options = { "patient": frm.doc.patient, diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py index 3a80b303659..abb82f2cdd1 100644 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py +++ b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py @@ -84,7 +84,7 @@ def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct): pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"]) except TypeError: # show the error in tests? - frappe.throw("Unable to find Salary Component {0}".format(sal_struct_row.salary_component)) + frappe.throw(_("Unable to find Salary Component {0}".format(sal_struct_row.salary_component))) if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1: total_pro_rata_max += max_benefit_amount if total_pro_rata_max > 0: diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js index d8be46bee70..9e5effe6a34 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.js +++ b/erpnext/hr/doctype/hr_settings/hr_settings.js @@ -15,7 +15,7 @@ frappe.ui.form.on('HR Settings', { let policy = frm.doc.password_policy; if (policy) { if (policy.includes(' ') || policy.includes('--')) { - frappe.msgprint("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically"); + frappe.msgprint(_("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically")); } frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-')); } diff --git a/erpnext/hub_node/api.py b/erpnext/hub_node/api.py index 0c94df31596..0d01c67650b 100644 --- a/erpnext/hub_node/api.py +++ b/erpnext/hub_node/api.py @@ -120,7 +120,7 @@ def get_valid_items(search_value=''): def publish_selected_items(items_to_publish): items_to_publish = json.loads(items_to_publish) if not len(items_to_publish): - frappe.throw('No items to publish') + frappe.throw(_('No items to publish')) for item in items_to_publish: item_code = item.get('item_code') @@ -165,7 +165,7 @@ def item_sync_preprocess(intended_item_publish_count): frappe.db.set_value("Marketplace Settings", "Marketplace Settings", "sync_in_progress", 1) return response else: - frappe.throw('Unable to update remote activity') + frappe.throw(_('Unable to update remote activity')) def item_sync_postprocess(): @@ -173,7 +173,7 @@ def item_sync_postprocess(): if response: frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'last_sync_datetime', frappe.utils.now()) else: - frappe.throw('Unable to update remote activity') + frappe.throw(_('Unable to update remote activity')) frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'sync_in_progress', 0) diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index ded1e5388ce..55a689259ae 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -645,7 +645,7 @@ def set_project_status(project, status): set status for project and all related tasks ''' if not status in ('Completed', 'Cancelled'): - frappe.throw('Status must be Cancelled or Completed') + frappe.throw(_('Status must be Cancelled or Completed')) project = frappe.get_doc('Project', project) frappe.has_permission(doc = project, throw = True) diff --git a/erpnext/public/js/education/lms/quiz.js b/erpnext/public/js/education/lms/quiz.js index 1b520eb9f54..52481291e02 100644 --- a/erpnext/public/js/education/lms/quiz.js +++ b/erpnext/public/js/education/lms/quiz.js @@ -68,7 +68,7 @@ class Quiz { }).then(res => { this.submit_btn.remove() if (!res.message) { - frappe.throw("Something went wrong while evaluating the quiz.") + frappe.throw(__("Something went wrong while evaluating the quiz.")) } let indicator = 'red' diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 448abe8f280..aad267e90a5 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.model.document import Document import json from six import iteritems @@ -414,7 +415,7 @@ class GSTR3BReport(Document): if gst_details: return gst_details[0] else: - frappe.throw("Please enter GSTIN and state for the Company Address {0}".format(self.company_address)) + frappe.throw(_("Please enter GSTIN and state for the Company Address {0}".format(self.company_address))) def get_account_heads(self): @@ -427,7 +428,7 @@ class GSTR3BReport(Document): if account_heads: return account_heads else: - frappe.throw("Please set account heads in GST Settings for Compnay {0}".format(self.company)) + frappe.throw(_("Please set account heads in GST Settings for Compnay {0}".format(self.company))) def get_missing_field_invoices(self): From 55fbddbee6cc71b81b4b529759f8a208c4554199 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 3 Jul 2019 16:31:06 +0530 Subject: [PATCH 014/174] Add missing semicolon --- .../doctype/payment_reconciliation/payment_reconciliation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index df31cde551f..d3992d51115 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -173,7 +173,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext args.forEach(d => { frappe.model.set_value("Payment Reconciliation Payment", d.docname, "difference_account", d.difference_account); - }) + }); me.reconcile_payment_entries(); dialog.hide(); From 7dedd9a8cd9f0d842dbba6bcd02bef378609c434 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 3 Jul 2019 17:29:29 +0530 Subject: [PATCH 015/174] refactor: better error message when changing variant value (#18144) --- erpnext/controllers/item_variant.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 669aa9be7e7..dee71dc9448 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -98,8 +98,8 @@ def validate_item_attribute_value(attributes_list, attribute, attribute_value, i if allow_rename_attribute_value: pass elif attribute_value not in attributes_list: - frappe.throw(_("Value {0} for Attribute {1} does not exist in the list of valid Item Attribute Values for Item {2}").format( - attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Invalid Attribute')) + frappe.throw(_("The value {0} is already assigned to an exisiting Item {2}.").format( + attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Rename Not Allowed')) def get_attribute_values(item): if not frappe.flags.attribute_values: From 58b4644c1a219bc5f9a2f0238dd48eec71e54325 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Wed, 3 Jul 2019 18:08:41 +0530 Subject: [PATCH 016/174] fix: Add filtesr for accounting dimensions in trial balance report --- .../report/trial_balance/trial_balance.js | 13 ++++++++ .../report/trial_balance/trial_balance.py | 31 +++++++++++++------ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index cdc77456d0a..8bc72807b33 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -96,3 +96,16 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { } }); +let dimension_filters = erpnext.get_dimension_filters(); + +dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); + diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 5758b0bc6b1..b6ddaa8e850 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -7,6 +7,7 @@ from frappe import _ from frappe.utils import flt, getdate, formatdate, cstr from erpnext.accounts.report.financial_statements \ import filter_accounts, set_gl_entries_by_account, filter_out_zero_value_rows +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions value_fields = ("opening_debit", "opening_credit", "debit", "credit", "closing_debit", "closing_credit") @@ -109,6 +110,25 @@ def get_rootwise_opening_balances(filters, report_type): additional_conditions += fb_conditions + accounting_dimensions = get_accounting_dimensions() + + query_filters = { + "company": filters.company, + "from_date": filters.from_date, + "report_type": report_type, + "year_start_date": filters.year_start_date, + "finance_book": filters.finance_book, + "company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book') + } + + if accounting_dimensions: + for dimension in accounting_dimensions: + additional_conditions += """ and {0} in (%({0})s) """.format(dimension) + + query_filters.update({ + dimension: filters.get(dimension) + }) + gle = frappe.db.sql(""" select account, sum(debit) as opening_debit, sum(credit) as opening_credit @@ -118,16 +138,7 @@ def get_rootwise_opening_balances(filters, report_type): {additional_conditions} and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes') and account in (select name from `tabAccount` where report_type=%(report_type)s) - group by account""".format(additional_conditions=additional_conditions), - { - "company": filters.company, - "from_date": filters.from_date, - "report_type": report_type, - "year_start_date": filters.year_start_date, - "finance_book": filters.finance_book, - "company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book') - }, - as_dict=True) + group by account""".format(additional_conditions=additional_conditions), query_filters , as_dict=True) opening = frappe._dict() for d in gle: From bb02c5105e1d0b90bb4f931d9b99c755f4bdfcc9 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 3 Jul 2019 19:22:21 +0530 Subject: [PATCH 017/174] fix: format values for charts --- .../report/accounts_receivable/accounts_receivable.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 4cba978b888..29737484c70 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -599,9 +599,12 @@ class ReceivablePayableReport(object): rows = [] for d in data: + values = d[self.ageing_col_idx_start : self.ageing_col_idx_start+5] + precision = cint(frappe.db.get_default("float_precision")) or 2 + formatted_values = [frappe.utils.rounded(val, precision) for val in values] rows.append( { - 'values': d[self.ageing_col_idx_start : self.ageing_col_idx_start+5] + 'values': formatted_values } ) From 76afcf4cbac7c069fb60470db1d094283e0ed7de Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 3 Jul 2019 19:22:49 +0530 Subject: [PATCH 018/174] fix: Honor price list in Shopping Cart Settings --- erpnext/shopping_cart/cart.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 95bd9ba6361..4019e07e4e4 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -251,11 +251,13 @@ def _get_cart_quotation(party=None): if quotation: qdoc = frappe.get_doc("Quotation", quotation[0].name) else: + [company, price_list] = frappe.db.get_value("Shopping Cart Settings", None, ["company", "price_list"]) qdoc = frappe.get_doc({ "doctype": "Quotation", "naming_series": get_shopping_cart_settings().quotation_series or "QTN-CART-", "quotation_to": party.doctype, - "company": frappe.db.get_value("Shopping Cart Settings", None, "company"), + "company": company, + "selling_price_list": price_list, "order_type": "Shopping Cart", "status": "Draft", "docstatus": 0, From 359a73e1aa0bfa3b0db24a89cef5e1b8faa30274 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 4 Jul 2019 11:37:20 +0530 Subject: [PATCH 019/174] fix: invoice cancellation (#18152) --- erpnext/regional/italy/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py index 2d7ffa65bd1..407677f9767 100644 --- a/erpnext/regional/italy/utils.py +++ b/erpnext/regional/italy/utils.py @@ -326,6 +326,9 @@ def get_company_country(company): return frappe.get_cached_value('Company', company, 'country') def get_e_invoice_attachments(invoice): + if not invoice.company_tax_id: + return [] + out = [] attachments = get_attachments(invoice.doctype, invoice.name) company_tax_id = invoice.company_tax_id if invoice.company_tax_id.startswith("IT") else "IT" + invoice.company_tax_id From bdec7fea82bdce5ebdfe2a5080f79a76741341e4 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Thu, 4 Jul 2019 12:28:01 +0530 Subject: [PATCH 020/174] feat: purchase order against sales order (#17975) * feat: purchase order against sales order * fix: remove unwanted checks * fix:Related tests --- .../doctype/sales_order/sales_order.js | 76 +++++++++++++------ .../doctype/sales_order/sales_order.py | 7 +- .../doctype/sales_order/test_sales_order.py | 4 +- 3 files changed, 58 insertions(+), 29 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index f4bb0709a51..26ca7c6e228 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -107,7 +107,6 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( refresh: function(doc, dt, dn) { var me = this; this._super(); - var allow_purchase = false; var allow_delivery = false; if(doc.docstatus==1) { @@ -129,28 +128,9 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( me.frm.cscript.update_status('Re-open', 'Draft') }, __("Status")); } - } + } if(doc.status !== 'Closed') { if(doc.status !== 'On Hold') { - for (var i in this.frm.doc.items) { - var item = this.frm.doc.items[i]; - if(item.delivered_by_supplier === 1 || item.supplier){ - if(item.qty > flt(item.ordered_qty) - && item.qty > flt(item.delivered_qty)) { - allow_purchase = true; - } - } - - if (item.delivered_by_supplier===0) { - if(item.qty > flt(item.delivered_qty)) { - allow_delivery = true; - } - } - - if (allow_delivery && allow_purchase) { - break; - } - } if (this.frm.has_perm("submit")) { if(flt(doc.per_delivered, 6) < 100 || flt(doc.per_billed) < 100) { @@ -180,9 +160,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( } // make purchase order - if(flt(doc.per_delivered, 6) < 100 && allow_purchase) { this.frm.add_custom_button(__('Purchase Order'), () => this.make_purchase_order(), __('Create')); - } + // maintenance if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) { @@ -543,6 +522,42 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( filters: {'parent': me.frm.doc.name} } }}, + {fieldname: 'items_for_po', fieldtype: 'Table', label: 'Select Items', + fields: [ + { + fieldtype:'Data', + fieldname:'item_code', + label: __('Item'), + read_only:1, + in_list_view:1 + }, + { + fieldtype:'Data', + fieldname:'item_name', + label: __('Item name'), + read_only:1, + in_list_view:1 + }, + { + fieldtype:'Float', + fieldname:'qty', + label: __('Quantity'), + read_only: 1, + in_list_view:1 + }, + { + fieldtype:'Link', + read_only:1, + fieldname:'uom', + label: __('UOM'), + in_list_view:1 + } + ], + data: cur_frm.doc.items, + get_data: function() { + return cur_frm.doc.items + } + }, {"fieldtype": "Button", "label": __('Create Purchase Order'), "fieldname": "make_purchase_order", "cssClass": "btn-primary"}, ] @@ -550,13 +565,22 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( dialog.fields_dict.make_purchase_order.$input.click(function() { var args = dialog.get_values(); + let selected_items = dialog.fields_dict.items_for_po.grid.get_selected_children() + if(selected_items.length == 0) { + frappe.throw({message: 'Please select Item form Table', title: __('Message'), indicator:'blue'}) + } + let selected_items_list = [] + for(let i in selected_items){ + selected_items_list.push(selected_items[i].item_code) + } dialog.hide(); return frappe.call({ type: "GET", - method: "erpnext.selling.doctype.sales_order.sales_order.make_purchase_order_for_drop_shipment", + method: "erpnext.selling.doctype.sales_order.sales_order.make_purchase_order", args: { "source_name": me.frm.doc.name, - "for_supplier": args.supplier + "for_supplier": args.supplier, + "selected_items": selected_items_list }, freeze: true, callback: function(r) { @@ -576,6 +600,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( } }) }); + dialog.get_field("items_for_po").grid.only_sortable() + dialog.get_field("items_for_po").refresh() dialog.show(); }, hold_sales_order: function(){ diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 493da99303e..8ad3bf06071 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -764,7 +764,10 @@ def get_events(start, end, filters=None): return data @frappe.whitelist() -def make_purchase_order_for_drop_shipment(source_name, for_supplier=None, target_doc=None): +def make_purchase_order(source_name, for_supplier=None, selected_items=[], target_doc=None): + if isinstance(selected_items, string_types): + selected_items = json.loads(selected_items) + def set_missing_values(source, target): target.supplier = supplier target.apply_discount_on = "" @@ -843,7 +846,7 @@ def make_purchase_order_for_drop_shipment(source_name, for_supplier=None, target "price_list_rate" ], "postprocess": update_item, - "condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == supplier + "condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == supplier and doc.item_code in selected_items } }, target_doc, set_missing_values) if not for_supplier: diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index e7697e2b0e6..569c53f628d 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -449,7 +449,7 @@ class TestSalesOrder(unittest.TestCase): frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1) def test_drop_shipping(self): - from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order_for_drop_shipment + from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order from erpnext.buying.doctype.purchase_order.purchase_order import update_status make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100) @@ -495,7 +495,7 @@ class TestSalesOrder(unittest.TestCase): so = make_sales_order(item_list=so_items, do_not_submit=True) so.submit() - po = make_purchase_order_for_drop_shipment(so.name, '_Test Supplier') + po = make_purchase_order(so.name, '_Test Supplier', selected_items=[so_items[0]['item_code']]) po.submit() dn = create_dn_against_so(so.name, delivered_qty=1) From 100af0a6356316a322bc8a48cde8aeda152ad479 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 4 Jul 2019 12:29:26 +0530 Subject: [PATCH 021/174] fix(salary-slip): Nonetype error on amount calculation (#18147) * fix: amount calculation * Update salary_slip.py --- erpnext/hr/doctype/salary_slip/salary_slip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index db0a3a5ecea..6d25c063936 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -618,7 +618,7 @@ class SalarySlip(TransactionBase): elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(row.depends_on_payment_days): amount, additional_amount = 0, 0 elif not row.amount: - amount = row.default_amount + row.additional_amount + amount = flt(row.default_amount) + flt(row.additional_amount) # apply rounding if frappe.get_cached_value("Salary Component", row.salary_component, "round_to_the_nearest_integer"): From 29734dae181279c8d478d7c58dab291e81c69abf Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 4 Jul 2019 12:50:16 +0530 Subject: [PATCH 022/174] patch: reloads newly created docs in v12 (#18148) --- erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py b/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py index 9925b70a963..3d4a9952c77 100644 --- a/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py +++ b/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py @@ -3,6 +3,8 @@ import frappe from erpnext.regional.india.setup import make_custom_fields def execute(): + frappe.reload_doc("accounts", "doctype", "tax_category") + frappe.reload_doc("stock", "doctype", "item_manufacturer") company = frappe.get_all('Company', filters = {'country': 'India'}) if not company: return From a9ea49c9767c50bd5c69d6fbc8a04a47a3bc12bc Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Thu, 4 Jul 2019 15:56:34 +0530 Subject: [PATCH 023/174] fix: Additonal discount not get set in sales transactions --- erpnext/selling/sales_common.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 9bae58b3098..ad3f27c9c1e 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -145,6 +145,11 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ }, discount_amount: function(doc, cdt, cdn) { + + if(doc.name === cdn) { + return; + } + var item = frappe.get_doc(cdt, cdn); item.discount_percentage = 0.0; this.apply_discount_on_item(doc, cdt, cdn, 'discount_amount'); From 95054a5a02ba19c70841ab2da5d151daa281d452 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 4 Jul 2019 16:13:56 +0530 Subject: [PATCH 024/174] fix: Try Faster Travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a8a0d826145..4a297e11c0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ install: - nvm install 10 - pip install python-coveralls - wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py - - sudo python install.py --develop --user travis --without-bench-setup + - sudo python install.py --develop --user travis --without-bench-setup --frappe-branch-url https://github.com/adityahase/frappe --frappe-branch faster-travis - sudo pip install -e ~/bench - rm $TRAVIS_BUILD_DIR/.git/shallow From 697f995b27b4717823c13521e4a534ae04531783 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 4 Jul 2019 16:19:22 +0530 Subject: [PATCH 025/174] fix: Send --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4a297e11c0c..200668d79b5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ install: - nvm install 10 - pip install python-coveralls - wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py - - sudo python install.py --develop --user travis --without-bench-setup --frappe-branch-url https://github.com/adityahase/frappe --frappe-branch faster-travis + - sudo python install.py --develop --user travis --without-bench-setup --frappe-repo-url https://github.com/adityahase/frappe --frappe-branch faster-travis - sudo pip install -e ~/bench - rm $TRAVIS_BUILD_DIR/.git/shallow From d7291dbcad7dd9036eaad279ba6a30815a7486e6 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 4 Jul 2019 16:51:53 +0530 Subject: [PATCH 026/174] Revert "fix: Send" This reverts commit 697f995b27b4717823c13521e4a534ae04531783. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 200668d79b5..4a297e11c0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ install: - nvm install 10 - pip install python-coveralls - wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py - - sudo python install.py --develop --user travis --without-bench-setup --frappe-repo-url https://github.com/adityahase/frappe --frappe-branch faster-travis + - sudo python install.py --develop --user travis --without-bench-setup --frappe-branch-url https://github.com/adityahase/frappe --frappe-branch faster-travis - sudo pip install -e ~/bench - rm $TRAVIS_BUILD_DIR/.git/shallow From e5dc834ddb1f312a4d9514df6af9ddcffa0ad9a4 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 4 Jul 2019 16:52:01 +0530 Subject: [PATCH 027/174] Revert "fix: Try Faster Travis" This reverts commit 95054a5a02ba19c70841ab2da5d151daa281d452. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4a297e11c0c..a8a0d826145 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ install: - nvm install 10 - pip install python-coveralls - wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py - - sudo python install.py --develop --user travis --without-bench-setup --frappe-branch-url https://github.com/adityahase/frappe --frappe-branch faster-travis + - sudo python install.py --develop --user travis --without-bench-setup - sudo pip install -e ~/bench - rm $TRAVIS_BUILD_DIR/.git/shallow From a5d975d867a0f09cdf97d93b2a579658bde1717b Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 4 Jul 2019 17:47:28 +0530 Subject: [PATCH 028/174] fix: Don't show Send SMS in BOM --- erpnext/public/js/controllers/transaction.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index bbd1f1c17bd..741f21e7127 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -383,8 +383,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ setup_sms: function() { var me = this; + let blacklist = ['Purchase Invoice', 'BOM']; if(this.frm.doc.docstatus===1 && !in_list(["Lost", "Stopped", "Closed"], this.frm.doc.status) - && this.frm.doctype != "Purchase Invoice") { + && !blacklist.includes(this.frm.doctype)) { this.frm.page.add_menu_item(__('Send SMS'), function() { me.send_sms(); }); } }, From 1d3008750c6de8a83b5ecbde0bb261ebde3fc8c4 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 4 Jul 2019 17:53:10 +0530 Subject: [PATCH 029/174] fix(UX): Move Bill of Materials section to first --- erpnext/config/manufacturing.py | 74 ++++++++++++++++----------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/erpnext/config/manufacturing.py b/erpnext/config/manufacturing.py index da605502216..c79c5b8b117 100644 --- a/erpnext/config/manufacturing.py +++ b/erpnext/config/manufacturing.py @@ -3,43 +3,6 @@ from frappe import _ def get_data(): return [ - { - "label": _("Production"), - "icon": "fa fa-star", - "items": [ - { - "type": "doctype", - "name": "Work Order", - "description": _("Orders released for production."), - "onboard": 1, - "dependencies": ["Item", "BOM"] - }, - { - "type": "doctype", - "name": "Production Plan", - "description": _("Generate Material Requests (MRP) and Work Orders."), - "onboard": 1, - "dependencies": ["Item", "BOM"] - }, - { - "type": "doctype", - "name": "Stock Entry", - "onboard": 1, - "dependencies": ["Item"] - }, - { - "type": "doctype", - "name": "Timesheet", - "description": _("Time Sheet for manufacturing."), - "onboard": 1, - "dependencies": ["Activity Type"] - }, - { - "type": "doctype", - "name": "Job Card" - } - ] - }, { "label": _("Bill of Materials"), "items": [ @@ -85,6 +48,43 @@ def get_data(): ] }, + { + "label": _("Production"), + "icon": "fa fa-star", + "items": [ + { + "type": "doctype", + "name": "Work Order", + "description": _("Orders released for production."), + "onboard": 1, + "dependencies": ["Item", "BOM"] + }, + { + "type": "doctype", + "name": "Production Plan", + "description": _("Generate Material Requests (MRP) and Work Orders."), + "onboard": 1, + "dependencies": ["Item", "BOM"] + }, + { + "type": "doctype", + "name": "Stock Entry", + "onboard": 1, + "dependencies": ["Item"] + }, + { + "type": "doctype", + "name": "Timesheet", + "description": _("Time Sheet for manufacturing."), + "onboard": 1, + "dependencies": ["Activity Type"] + }, + { + "type": "doctype", + "name": "Job Card" + } + ] + }, { "label": _("Tools"), "icon": "fa fa-wrench", From 2b969a469a05c2e4962cc080e71a6b6acf716616 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 4 Jul 2019 18:52:32 +0530 Subject: [PATCH 030/174] fix: Build item cache in long worker (#18165) --- erpnext/portal/product_configurator/item_variants_cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/portal/product_configurator/item_variants_cache.py b/erpnext/portal/product_configurator/item_variants_cache.py index f33c8d605b4..fc294ce58bb 100644 --- a/erpnext/portal/product_configurator/item_variants_cache.py +++ b/erpnext/portal/product_configurator/item_variants_cache.py @@ -110,4 +110,4 @@ def build_cache(item_code): def enqueue_build_cache(item_code): if frappe.cache().hget('item_cache_build_in_progress', item_code): return - frappe.enqueue(build_cache, item_code=item_code, queue='short') + frappe.enqueue(build_cache, item_code=item_code, queue='long') From 23c80658bae91468f18b6305430ed328ad582f5f Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 4 Jul 2019 18:54:04 +0530 Subject: [PATCH 031/174] fix: Add links in validation message --- erpnext/stock/doctype/stock_entry/stock_entry.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 464101e672b..0abcbb328aa 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -324,8 +324,10 @@ class StockEntry(StockController): completed_qty = d.completed_qty + (allowance_percentage/100 * d.completed_qty) if total_completed_qty > flt(completed_qty): job_card = frappe.db.get_value('Job Card', {'operation_id': d.name}, 'name') - frappe.throw(_("Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order # {3}. Please update operation status via Job Card # {4}") - .format(d.idx, d.operation, total_completed_qty, self.work_order, job_card), OperationsNotCompleteError) + work_order_link = frappe.utils.get_link_to_form('Work Order', self.work_order) + job_card_link = frappe.utils.get_link_to_form('Job Card', job_card) + frappe.throw(_("Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.") + .format(d.idx, frappe.bold(d.operation), frappe.bold(total_completed_qty), work_order_link, job_card_link), OperationsNotCompleteError) def check_duplicate_entry_for_work_order(self): other_ste = [t[0] for t in frappe.db.get_values("Stock Entry", { From 7fc6021ca5ec621232354666477dc95e40c12626 Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Thu, 4 Jul 2019 22:46:16 +0530 Subject: [PATCH 032/174] feat(setup): adding selling buying filter in terms and conditions --- .../doctype/pos_profile/pos_profile.js | 4 + erpnext/hr/doctype/job_offer/job_offer.js | 6 + .../doctype/blanket_order/blanket_order.js | 23 + erpnext/patches.txt | 1 + ...default_buying_selling_terms_in_company.py | 19 + erpnext/public/js/controllers/buying.js | 8 + erpnext/public/js/controllers/transaction.js | 13 +- erpnext/selling/sales_common.js | 6 + erpnext/setup/doctype/company/company.js | 8 + erpnext/setup/doctype/company/company.json | 1543 +++++++++-------- .../terms_and_conditions.json | 246 +-- .../terms_and_conditions.py | 4 + erpnext/startup/boot.py | 2 +- 13 files changed, 916 insertions(+), 967 deletions(-) create mode 100644 erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js index 10f127a53f6..5e94118d60b 100755 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.js +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js @@ -8,6 +8,10 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) { return { filters: { selling: 1 } }; }); + frm.set_query("tc_name", function() { + return { filters: { selling: 1 } }; + }); + erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); }); diff --git a/erpnext/hr/doctype/job_offer/job_offer.js b/erpnext/hr/doctype/job_offer/job_offer.js index 1ee35afe109..c3d83c48cca 100755 --- a/erpnext/hr/doctype/job_offer/job_offer.js +++ b/erpnext/hr/doctype/job_offer/job_offer.js @@ -4,6 +4,12 @@ frappe.provide("erpnext.job_offer"); frappe.ui.form.on("Job Offer", { + onload: function (frm) { + frm.set_query("select_terms", function() { + return { filters: { hr: 1 } }; + }); + }, + select_terms: function (frm) { erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function (r) { if (!r.exc) { diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js index 89efb9369d3..d1bef3fcd03 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js @@ -2,6 +2,10 @@ // For license information, please see license.txt frappe.ui.form.on('Blanket Order', { + onload: function(frm) { + frm.trigger('set_tc_name_filter'); + }, + setup: function(frm) { frm.add_fetch("customer", "customer_name", "customer_name"); frm.add_fetch("supplier", "supplier_name", "supplier_name"); @@ -44,4 +48,23 @@ frappe.ui.form.on('Blanket Order', { } }); }, + + set_tc_name_filter: function(frm) { + if (frm.doc.blanket_order_type === 'Selling'){ + frm.set_query("tc_name", function() { + return { filters: { selling: 1 } }; + }); + } + if (frm.doc.blanket_order_type === 'Purchasing'){ + frm.set_query("tc_name", function() { + return { filters: { buying: 1 } }; + }); + } + }, + + blanket_order_type: function (frm) { + frm.trigger('set_tc_name_filter'); + } }); + + diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 72db8ad063e..bd41c7e8840 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -615,3 +615,4 @@ erpnext.patches.v11_1.set_missing_opportunity_from erpnext.patches.v12_0.set_quotation_status erpnext.patches.v12_0.set_priority_for_support erpnext.patches.v12_0.delete_priority_property_setter +erpnext.patches.v12_0.add_default_buying_selling_terms_in_company diff --git a/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py b/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py new file mode 100644 index 00000000000..484f81a7aca --- /dev/null +++ b/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py @@ -0,0 +1,19 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe +from frappe.model.utils.rename_field import rename_field + +def execute(): + frappe.reload_doc("setup", "doctype", "company") + if frappe.db.has_column('Company', 'default_terms'): + rename_field('Company', "default_terms", "default_selling_terms") + + for company in frappe.get_all("Company", ["name", "default_selling_terms", "default_buying_terms"]): + if company.default_selling_terms and not company.default_buying_terms: + frappe.db.set_value("Company", company.name, "default_buying_terms", company.default_selling_terms) + + frappe.reload_doc("setup", "doctype", "terms_and_conditions") + frappe.db.sql("update `tabTerms and Conditions` set selling=1, buying=1, hr=1") diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 97c823d0535..824b8d98d2b 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -61,6 +61,14 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ }); } + if(this.frm.fields_dict.tc_name) { + this.frm.set_query("tc_name", function() { + return{ + filters: { 'buying': 1 } + } + }); + } + me.frm.set_query('supplier', erpnext.queries.supplier); me.frm.set_query('contact_person', erpnext.queries.contact_query); me.frm.set_query('supplier_address', erpnext.queries.address_query); diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index a3ecea168d5..11fdb8b178b 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -585,8 +585,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ me.frm.set_value("letter_head", company_doc.default_letter_head); } } - if (company_doc.default_terms && me.frm.doc.doctype != "Purchase Invoice" && frappe.meta.has_field(me.frm.doc.doctype, "tc_name")) { - me.frm.set_value("tc_name", company_doc.default_terms); + let selling_doctypes_for_tc = ["Sales Invoice", "Quotation", "Sales Order", "Delivery Note"]; + if (company_doc.default_selling_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") && + selling_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) { + me.frm.set_value("tc_name", company_doc.default_selling_terms); + } + let buying_doctypes_for_tc = ["Request for Quotation", "Supplier Quotation", "Purchase Order", + "Material Request", "Purchase Receipt"]; + // Purchase Invoice is excluded as per issue #3345 + if (company_doc.default_buying_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") && + buying_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) { + me.frm.set_value("tc_name", company_doc.default_buying_terms); } frappe.run_serially([ diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 9bae58b3098..00efb4f2198 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -59,6 +59,12 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ }); } + if(this.frm.fields_dict.tc_name) { + this.frm.set_query("tc_name", function() { + return { filters: { selling: 1 } }; + }); + } + if(!this.frm.fields_dict["items"]) { return; } diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 1e6056ec860..313de677fc5 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -17,6 +17,14 @@ frappe.ui.form.on("Company", { filters: {"is_group": 1} } }); + + frm.set_query("default_selling_terms", function() { + return { filters: { selling: 1 } }; + }); + + frm.set_query("default_buying_terms", function() { + return { filters: { buying: 1 } }; + }); }, company_name: function(frm) { diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index bb652ca6bf7..bc3418997dd 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -1,769 +1,776 @@ { - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:company_name", - "creation": "2013-04-10 08:35:39", - "description": "Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Organization.", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "details", - "company_name", - "abbr", - "change_abbr", - "is_group", - "cb0", - "domain", - "parent_company", - "charts_section", - "default_currency", - "default_letter_head", - "default_holiday_list", - "default_finance_book", - "standard_working_hours", - "default_terms", - "default_warehouse_for_sales_return", - "column_break_10", - "country", - "create_chart_of_accounts_based_on", - "chart_of_accounts", - "existing_company", - "tax_id", - "date_of_establishment", - "sales_settings", - "monthly_sales_target", - "sales_monthly_history", - "column_break_goals", - "transactions_annual_history", - "total_monthly_sales", - "default_settings", - "default_bank_account", - "default_cash_account", - "default_receivable_account", - "round_off_account", - "round_off_cost_center", - "write_off_account", - "discount_allowed_account", - "discount_received_account", - "exchange_gain_loss_account", - "unrealized_exchange_gain_loss_account", - "column_break0", - "allow_account_creation_against_child_company", - "default_payable_account", - "default_employee_advance_account", - "default_expense_account", - "default_income_account", - "default_deferred_revenue_account", - "default_deferred_expense_account", - "default_payroll_payable_account", - "default_expense_claim_payable_account", - "section_break_22", - "cost_center", - "column_break_26", - "credit_limit", - "payment_terms", - "auto_accounting_for_stock_settings", - "enable_perpetual_inventory", - "default_inventory_account", - "stock_adjustment_account", - "column_break_32", - "stock_received_but_not_billed", - "expenses_included_in_valuation", - "fixed_asset_depreciation_settings", - "accumulated_depreciation_account", - "depreciation_expense_account", - "series_for_depreciation_entry", - "expenses_included_in_asset_valuation", - "column_break_40", - "disposal_account", - "depreciation_cost_center", - "capital_work_in_progress_account", - "asset_received_but_not_billed", - "budget_detail", - "exception_budget_approver_role", - "company_info", - "company_logo", - "date_of_incorporation", - "address_html", - "date_of_commencement", - "phone_no", - "fax", - "email", - "website", - "column_break1", - "company_description", - "registration_info", - "registration_details", - "delete_company_transactions", - "lft", - "rgt", - "old_parent" - ], - "fields": [ - { - "fieldname": "details", - "fieldtype": "Section Break", - "oldfieldtype": "Section Break" - }, - { - "fieldname": "company_name", - "fieldtype": "Data", - "label": "Company", - "oldfieldname": "company_name", - "oldfieldtype": "Data", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "abbr", - "fieldtype": "Data", - "label": "Abbr", - "oldfieldname": "abbr", - "oldfieldtype": "Data", - "reqd": 1 - }, - { - "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")", - "fieldname": "change_abbr", - "fieldtype": "Button", - "label": "Change Abbreviation" - }, - { - "bold": 1, - "default": "0", - "fieldname": "is_group", - "fieldtype": "Check", - "label": "Is Group" - }, - { - "fieldname": "default_finance_book", - "fieldtype": "Link", - "label": "Default Finance Book", - "options": "Finance Book" - }, - { - "fieldname": "cb0", - "fieldtype": "Column Break" - }, - { - "fieldname": "domain", - "fieldtype": "Link", - "label": "Domain", - "options": "Domain" - }, - { - "fieldname": "parent_company", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Parent Company", - "options": "Company" - }, - { - "fieldname": "company_logo", - "fieldtype": "Attach Image", - "hidden": 1, - "label": "Company Logo" - }, - { - "fieldname": "company_description", - "fieldtype": "Text Editor", - "label": "Company Description" - }, - { - "collapsible": 1, - "fieldname": "sales_settings", - "fieldtype": "Section Break", - "label": "Sales Settings" - }, - { - "fieldname": "sales_monthly_history", - "fieldtype": "Small Text", - "hidden": 1, - "label": "Sales Monthly History", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "transactions_annual_history", - "fieldtype": "Code", - "hidden": 1, - "label": "Transactions Annual History", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "monthly_sales_target", - "fieldtype": "Currency", - "label": "Monthly Sales Target", - "options": "default_currency" - }, - { - "fieldname": "column_break_goals", - "fieldtype": "Column Break" - }, - { - "fieldname": "total_monthly_sales", - "fieldtype": "Currency", - "label": "Total Monthly Sales", - "no_copy": 1, - "options": "default_currency", - "read_only": 1 - }, - { - "fieldname": "charts_section", - "fieldtype": "Section Break", - "label": "Default Values" - }, - { - "fieldname": "default_currency", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Currency", - "options": "Currency", - "reqd": 1 - }, - { - "fieldname": "default_letter_head", - "fieldtype": "Link", - "label": "Default Letter Head", - "options": "Letter Head" - }, - { - "fieldname": "default_holiday_list", - "fieldtype": "Link", - "label": "Default Holiday List", - "options": "Holiday List" - }, - { - "fieldname": "standard_working_hours", - "fieldtype": "Float", - "label": "Standard Working Hours" - }, - { - "fieldname": "default_terms", - "fieldtype": "Link", - "label": "Default Terms", - "options": "Terms and Conditions" - }, - { - "fieldname": "default_warehouse_for_sales_return", - "fieldtype": "Link", - "label": "Default warehouse for Sales Return", - "options": "Warehouse" - }, - { - "fieldname": "column_break_10", - "fieldtype": "Column Break" - }, - { - "fieldname": "country", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Country", - "options": "Country", - "reqd": 1 - }, - { - "fieldname": "create_chart_of_accounts_based_on", - "fieldtype": "Select", - "label": "Create Chart Of Accounts Based On", - "options": "\nStandard Template\nExisting Company" - }, - { - "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"", - "fieldname": "chart_of_accounts", - "fieldtype": "Select", - "label": "Chart Of Accounts Template", - "no_copy": 1 - }, - { - "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"", - "fieldname": "existing_company", - "fieldtype": "Link", - "label": "Existing Company ", - "no_copy": 1, - "options": "Company" - }, - { - "fieldname": "tax_id", - "fieldtype": "Data", - "label": "Tax ID" - }, - { - "fieldname": "date_of_establishment", - "fieldtype": "Date", - "label": "Date of Establishment" - }, - { - "fieldname": "default_settings", - "fieldtype": "Section Break", - "label": "Accounts Settings", - "oldfieldtype": "Section Break" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_bank_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Bank Account", - "no_copy": 1, - "oldfieldname": "default_bank_account", - "oldfieldtype": "Link", - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_cash_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Cash Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_receivable_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Receivable Account", - "no_copy": 1, - "oldfieldname": "receivables_group", - "oldfieldtype": "Link", - "options": "Account" - }, - { - "fieldname": "round_off_account", - "fieldtype": "Link", - "label": "Round Off Account", - "options": "Account" - }, - { - "fieldname": "round_off_cost_center", - "fieldtype": "Link", - "label": "Round Off Cost Center", - "options": "Cost Center" - }, - { - "fieldname": "write_off_account", - "fieldtype": "Link", - "label": "Write Off Account", - "options": "Account" - }, - { - "fieldname": "discount_allowed_account", - "fieldtype": "Link", - "label": "Discount Allowed Account", - "options": "Account" - }, - { - "fieldname": "discount_received_account", - "fieldtype": "Link", - "label": "Discount Received Account", - "options": "Account" - }, - { - "fieldname": "exchange_gain_loss_account", - "fieldtype": "Link", - "label": "Exchange Gain / Loss Account", - "options": "Account" - }, - { - "fieldname": "unrealized_exchange_gain_loss_account", - "fieldtype": "Link", - "label": "Unrealized Exchange Gain/Loss Account", - "options": "Account" - }, - { - "fieldname": "column_break0", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "width": "50%" - }, - { - "default": "0", - "depends_on": "eval:doc.parent_company", - "fieldname": "allow_account_creation_against_child_company", - "fieldtype": "Check", - "label": "Allow Account Creation Against Child Company" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_payable_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Payable Account", - "no_copy": 1, - "oldfieldname": "payables_group", - "oldfieldtype": "Link", - "options": "Account" - }, - { - "fieldname": "default_employee_advance_account", - "fieldtype": "Link", - "label": "Default Employee Advance Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_expense_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Cost of Goods Sold Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_income_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Income Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_deferred_revenue_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Deferred Revenue Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_deferred_expense_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Deferred Expense Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_payroll_payable_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Payroll Payable Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_expense_claim_payable_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Expense Claim Payable Account", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "section_break_22", - "fieldtype": "Section Break" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "cost_center", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Cost Center", - "no_copy": 1, - "options": "Cost Center" - }, - { - "fieldname": "column_break_26", - "fieldtype": "Column Break" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "credit_limit", - "fieldtype": "Currency", - "label": "Credit Limit", - "oldfieldname": "credit_limit", - "oldfieldtype": "Currency", - "options": "default_currency" - }, - { - "fieldname": "payment_terms", - "fieldtype": "Link", - "label": "Default Payment Terms Template", - "options": "Payment Terms Template" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "auto_accounting_for_stock_settings", - "fieldtype": "Section Break", - "label": "Stock Settings" - }, - { - "default": "1", - "fieldname": "enable_perpetual_inventory", - "fieldtype": "Check", - "label": "Enable Perpetual Inventory" - }, - { - "fieldname": "default_inventory_account", - "fieldtype": "Link", - "label": "Default Inventory Account", - "options": "Account" - }, - { - "fieldname": "stock_adjustment_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Stock Adjustment Account", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "column_break_32", - "fieldtype": "Column Break" - }, - { - "fieldname": "stock_received_but_not_billed", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Stock Received But Not Billed", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "expenses_included_in_valuation", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Expenses Included In Valuation", - "no_copy": 1, - "options": "Account" - }, - { - "collapsible": 1, - "fieldname": "fixed_asset_depreciation_settings", - "fieldtype": "Section Break", - "label": "Fixed Asset Depreciation Settings" - }, - { - "fieldname": "accumulated_depreciation_account", - "fieldtype": "Link", - "label": "Accumulated Depreciation Account", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "depreciation_expense_account", - "fieldtype": "Link", - "label": "Depreciation Expense Account", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "series_for_depreciation_entry", - "fieldtype": "Data", - "label": "Series for Asset Depreciation Entry (Journal Entry)" - }, - { - "fieldname": "expenses_included_in_asset_valuation", - "fieldtype": "Link", - "label": "Expenses Included In Asset Valuation", - "options": "Account" - }, - { - "fieldname": "column_break_40", - "fieldtype": "Column Break" - }, - { - "fieldname": "disposal_account", - "fieldtype": "Link", - "label": "Gain/Loss Account on Asset Disposal", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "depreciation_cost_center", - "fieldtype": "Link", - "label": "Asset Depreciation Cost Center", - "no_copy": 1, - "options": "Cost Center" - }, - { - "fieldname": "capital_work_in_progress_account", - "fieldtype": "Link", - "label": "Capital Work In Progress Account", - "options": "Account" - }, - { - "fieldname": "asset_received_but_not_billed", - "fieldtype": "Link", - "label": "Asset Received But Not Billed", - "options": "Account" - }, - { - "collapsible": 1, - "fieldname": "budget_detail", - "fieldtype": "Section Break", - "label": "Budget Detail" - }, - { - "fieldname": "exception_budget_approver_role", - "fieldtype": "Link", - "label": "Exception Budget Approver Role", - "options": "Role" - }, - { - "collapsible": 1, - "description": "For reference only.", - "fieldname": "company_info", - "fieldtype": "Section Break", - "label": "Company Info" - }, - { - "fieldname": "date_of_incorporation", - "fieldtype": "Date", - "label": "Date of Incorporation" - }, - { - "fieldname": "address_html", - "fieldtype": "HTML" - }, - { - "fieldname": "column_break1", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "width": "50%" - }, - { - "depends_on": "eval:doc.date_of_incorporation", - "fieldname": "date_of_commencement", - "fieldtype": "Date", - "label": "Date of Commencement" - }, - { - "fieldname": "phone_no", - "fieldtype": "Data", - "label": "Phone No", - "oldfieldname": "phone_no", - "oldfieldtype": "Data", - "options": "Phone" - }, - { - "fieldname": "fax", - "fieldtype": "Data", - "label": "Fax", - "oldfieldname": "fax", - "oldfieldtype": "Data", - "options": "Phone" - }, - { - "fieldname": "email", - "fieldtype": "Data", - "label": "Email", - "oldfieldname": "email", - "oldfieldtype": "Data", - "options": "Email" - }, - { - "fieldname": "website", - "fieldtype": "Data", - "label": "Website", - "oldfieldname": "website", - "oldfieldtype": "Data" - }, - { - "fieldname": "registration_info", - "fieldtype": "Section Break", - "oldfieldtype": "Section Break", - "width": "50%" - }, - { - "description": "Company registration numbers for your reference. Tax numbers etc.", - "fieldname": "registration_details", - "fieldtype": "Code", - "label": "Registration Details", - "oldfieldname": "registration_details", - "oldfieldtype": "Code" - }, - { - "fieldname": "delete_company_transactions", - "fieldtype": "Button", - "label": "Delete Company Transactions" - }, - { - "fieldname": "lft", - "fieldtype": "Int", - "hidden": 1, - "label": "Lft", - "print_hide": 1, - "read_only": 1, - "search_index": 1 - }, - { - "fieldname": "rgt", - "fieldtype": "Int", - "hidden": 1, - "label": "Rgt", - "print_hide": 1, - "read_only": 1, - "search_index": 1 - }, - { - "fieldname": "old_parent", - "fieldtype": "Data", - "hidden": 1, - "label": "old_parent", - "print_hide": 1, - "read_only": 1 - } - ], - "icon": "fa fa-building", - "idx": 1, - "image_field": "company_logo", - "modified": "2019-06-14 14:36:11.363309", - "modified_by": "Administrator", - "module": "Setup", - "name": "Company", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "email": 1, - "print": 1, - "read": 1, - "role": "Accounts User" - }, - { - "read": 1, - "role": "Employee" - }, - { - "read": 1, - "role": "Sales User" - }, - { - "read": 1, - "role": "Purchase User" - }, - { - "read": 1, - "role": "Stock User" - }, - { - "read": 1, - "role": "Projects User" - } - ], - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "ASC", - "track_changes": 1 - } \ No newline at end of file + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:company_name", + "creation": "2013-04-10 08:35:39", + "description": "Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Organization.", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "details", + "company_name", + "abbr", + "change_abbr", + "is_group", + "cb0", + "domain", + "parent_company", + "charts_section", + "default_currency", + "default_letter_head", + "default_holiday_list", + "default_finance_book", + "standard_working_hours", + "default_selling_terms", + "default_buying_terms", + "default_warehouse_for_sales_return", + "column_break_10", + "country", + "create_chart_of_accounts_based_on", + "chart_of_accounts", + "existing_company", + "tax_id", + "date_of_establishment", + "sales_settings", + "monthly_sales_target", + "sales_monthly_history", + "column_break_goals", + "transactions_annual_history", + "total_monthly_sales", + "default_settings", + "default_bank_account", + "default_cash_account", + "default_receivable_account", + "round_off_account", + "round_off_cost_center", + "write_off_account", + "discount_allowed_account", + "discount_received_account", + "exchange_gain_loss_account", + "unrealized_exchange_gain_loss_account", + "column_break0", + "allow_account_creation_against_child_company", + "default_payable_account", + "default_employee_advance_account", + "default_expense_account", + "default_income_account", + "default_deferred_revenue_account", + "default_deferred_expense_account", + "default_payroll_payable_account", + "default_expense_claim_payable_account", + "section_break_22", + "cost_center", + "column_break_26", + "credit_limit", + "payment_terms", + "auto_accounting_for_stock_settings", + "enable_perpetual_inventory", + "default_inventory_account", + "stock_adjustment_account", + "column_break_32", + "stock_received_but_not_billed", + "expenses_included_in_valuation", + "fixed_asset_depreciation_settings", + "accumulated_depreciation_account", + "depreciation_expense_account", + "series_for_depreciation_entry", + "expenses_included_in_asset_valuation", + "column_break_40", + "disposal_account", + "depreciation_cost_center", + "capital_work_in_progress_account", + "asset_received_but_not_billed", + "budget_detail", + "exception_budget_approver_role", + "company_info", + "company_logo", + "date_of_incorporation", + "address_html", + "date_of_commencement", + "phone_no", + "fax", + "email", + "website", + "column_break1", + "company_description", + "registration_info", + "registration_details", + "delete_company_transactions", + "lft", + "rgt", + "old_parent" + ], + "fields": [ + { + "fieldname": "details", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "company_name", + "fieldtype": "Data", + "label": "Company", + "oldfieldname": "company_name", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "abbr", + "fieldtype": "Data", + "label": "Abbr", + "oldfieldname": "abbr", + "oldfieldtype": "Data", + "reqd": 1 + }, + { + "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")", + "fieldname": "change_abbr", + "fieldtype": "Button", + "label": "Change Abbreviation" + }, + { + "bold": 1, + "default": "0", + "fieldname": "is_group", + "fieldtype": "Check", + "label": "Is Group" + }, + { + "fieldname": "default_finance_book", + "fieldtype": "Link", + "label": "Default Finance Book", + "options": "Finance Book" + }, + { + "fieldname": "cb0", + "fieldtype": "Column Break" + }, + { + "fieldname": "domain", + "fieldtype": "Link", + "label": "Domain", + "options": "Domain" + }, + { + "fieldname": "parent_company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Parent Company", + "options": "Company" + }, + { + "fieldname": "company_logo", + "fieldtype": "Attach Image", + "hidden": 1, + "label": "Company Logo" + }, + { + "fieldname": "company_description", + "fieldtype": "Text Editor", + "label": "Company Description" + }, + { + "collapsible": 1, + "fieldname": "sales_settings", + "fieldtype": "Section Break", + "label": "Sales Settings" + }, + { + "fieldname": "sales_monthly_history", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Sales Monthly History", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "transactions_annual_history", + "fieldtype": "Code", + "hidden": 1, + "label": "Transactions Annual History", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "monthly_sales_target", + "fieldtype": "Currency", + "label": "Monthly Sales Target", + "options": "default_currency" + }, + { + "fieldname": "column_break_goals", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_monthly_sales", + "fieldtype": "Currency", + "label": "Total Monthly Sales", + "no_copy": 1, + "options": "default_currency", + "read_only": 1 + }, + { + "fieldname": "charts_section", + "fieldtype": "Section Break", + "label": "Default Values" + }, + { + "fieldname": "default_currency", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Currency", + "options": "Currency", + "reqd": 1 + }, + { + "fieldname": "default_letter_head", + "fieldtype": "Link", + "label": "Default Letter Head", + "options": "Letter Head" + }, + { + "fieldname": "default_holiday_list", + "fieldtype": "Link", + "label": "Default Holiday List", + "options": "Holiday List" + }, + { + "fieldname": "standard_working_hours", + "fieldtype": "Float", + "label": "Standard Working Hours" + }, + { + "fieldname": "default_warehouse_for_sales_return", + "fieldtype": "Link", + "label": "Default warehouse for Sales Return", + "options": "Warehouse" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "country", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Country", + "options": "Country", + "reqd": 1 + }, + { + "fieldname": "create_chart_of_accounts_based_on", + "fieldtype": "Select", + "label": "Create Chart Of Accounts Based On", + "options": "\nStandard Template\nExisting Company" + }, + { + "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"", + "fieldname": "chart_of_accounts", + "fieldtype": "Select", + "label": "Chart Of Accounts Template", + "no_copy": 1 + }, + { + "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"", + "fieldname": "existing_company", + "fieldtype": "Link", + "label": "Existing Company ", + "no_copy": 1, + "options": "Company" + }, + { + "fieldname": "tax_id", + "fieldtype": "Data", + "label": "Tax ID" + }, + { + "fieldname": "date_of_establishment", + "fieldtype": "Date", + "label": "Date of Establishment" + }, + { + "fieldname": "default_settings", + "fieldtype": "Section Break", + "label": "Accounts Settings", + "oldfieldtype": "Section Break" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_bank_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Bank Account", + "no_copy": 1, + "oldfieldname": "default_bank_account", + "oldfieldtype": "Link", + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_cash_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Cash Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_receivable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Receivable Account", + "no_copy": 1, + "oldfieldname": "receivables_group", + "oldfieldtype": "Link", + "options": "Account" + }, + { + "fieldname": "round_off_account", + "fieldtype": "Link", + "label": "Round Off Account", + "options": "Account" + }, + { + "fieldname": "round_off_cost_center", + "fieldtype": "Link", + "label": "Round Off Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "write_off_account", + "fieldtype": "Link", + "label": "Write Off Account", + "options": "Account" + }, + { + "fieldname": "discount_allowed_account", + "fieldtype": "Link", + "label": "Discount Allowed Account", + "options": "Account" + }, + { + "fieldname": "discount_received_account", + "fieldtype": "Link", + "label": "Discount Received Account", + "options": "Account" + }, + { + "fieldname": "exchange_gain_loss_account", + "fieldtype": "Link", + "label": "Exchange Gain / Loss Account", + "options": "Account" + }, + { + "fieldname": "unrealized_exchange_gain_loss_account", + "fieldtype": "Link", + "label": "Unrealized Exchange Gain/Loss Account", + "options": "Account" + }, + { + "fieldname": "column_break0", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "default": "0", + "depends_on": "eval:doc.parent_company", + "fieldname": "allow_account_creation_against_child_company", + "fieldtype": "Check", + "label": "Allow Account Creation Against Child Company" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_payable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Payable Account", + "no_copy": 1, + "oldfieldname": "payables_group", + "oldfieldtype": "Link", + "options": "Account" + }, + { + "fieldname": "default_employee_advance_account", + "fieldtype": "Link", + "label": "Default Employee Advance Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_expense_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Cost of Goods Sold Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_income_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Income Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_deferred_revenue_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Deferred Revenue Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_deferred_expense_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Deferred Expense Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_payroll_payable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Payroll Payable Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_expense_claim_payable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Expense Claim Payable Account", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "section_break_22", + "fieldtype": "Section Break" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "cost_center", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Cost Center", + "no_copy": 1, + "options": "Cost Center" + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "credit_limit", + "fieldtype": "Currency", + "label": "Credit Limit", + "oldfieldname": "credit_limit", + "oldfieldtype": "Currency", + "options": "default_currency" + }, + { + "fieldname": "payment_terms", + "fieldtype": "Link", + "label": "Default Payment Terms Template", + "options": "Payment Terms Template" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "auto_accounting_for_stock_settings", + "fieldtype": "Section Break", + "label": "Stock Settings" + }, + { + "default": "1", + "fieldname": "enable_perpetual_inventory", + "fieldtype": "Check", + "label": "Enable Perpetual Inventory" + }, + { + "fieldname": "default_inventory_account", + "fieldtype": "Link", + "label": "Default Inventory Account", + "options": "Account" + }, + { + "fieldname": "stock_adjustment_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Stock Adjustment Account", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "column_break_32", + "fieldtype": "Column Break" + }, + { + "fieldname": "stock_received_but_not_billed", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Stock Received But Not Billed", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "expenses_included_in_valuation", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Expenses Included In Valuation", + "no_copy": 1, + "options": "Account" + }, + { + "collapsible": 1, + "fieldname": "fixed_asset_depreciation_settings", + "fieldtype": "Section Break", + "label": "Fixed Asset Depreciation Settings" + }, + { + "fieldname": "accumulated_depreciation_account", + "fieldtype": "Link", + "label": "Accumulated Depreciation Account", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "depreciation_expense_account", + "fieldtype": "Link", + "label": "Depreciation Expense Account", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "series_for_depreciation_entry", + "fieldtype": "Data", + "label": "Series for Asset Depreciation Entry (Journal Entry)" + }, + { + "fieldname": "expenses_included_in_asset_valuation", + "fieldtype": "Link", + "label": "Expenses Included In Asset Valuation", + "options": "Account" + }, + { + "fieldname": "column_break_40", + "fieldtype": "Column Break" + }, + { + "fieldname": "disposal_account", + "fieldtype": "Link", + "label": "Gain/Loss Account on Asset Disposal", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "depreciation_cost_center", + "fieldtype": "Link", + "label": "Asset Depreciation Cost Center", + "no_copy": 1, + "options": "Cost Center" + }, + { + "fieldname": "capital_work_in_progress_account", + "fieldtype": "Link", + "label": "Capital Work In Progress Account", + "options": "Account" + }, + { + "fieldname": "asset_received_but_not_billed", + "fieldtype": "Link", + "label": "Asset Received But Not Billed", + "options": "Account" + }, + { + "collapsible": 1, + "fieldname": "budget_detail", + "fieldtype": "Section Break", + "label": "Budget Detail" + }, + { + "fieldname": "exception_budget_approver_role", + "fieldtype": "Link", + "label": "Exception Budget Approver Role", + "options": "Role" + }, + { + "collapsible": 1, + "description": "For reference only.", + "fieldname": "company_info", + "fieldtype": "Section Break", + "label": "Company Info" + }, + { + "fieldname": "date_of_incorporation", + "fieldtype": "Date", + "label": "Date of Incorporation" + }, + { + "fieldname": "address_html", + "fieldtype": "HTML" + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "depends_on": "eval:doc.date_of_incorporation", + "fieldname": "date_of_commencement", + "fieldtype": "Date", + "label": "Date of Commencement" + }, + { + "fieldname": "phone_no", + "fieldtype": "Data", + "label": "Phone No", + "oldfieldname": "phone_no", + "oldfieldtype": "Data", + "options": "Phone" + }, + { + "fieldname": "fax", + "fieldtype": "Data", + "label": "Fax", + "oldfieldname": "fax", + "oldfieldtype": "Data", + "options": "Phone" + }, + { + "fieldname": "email", + "fieldtype": "Data", + "label": "Email", + "oldfieldname": "email", + "oldfieldtype": "Data", + "options": "Email" + }, + { + "fieldname": "website", + "fieldtype": "Data", + "label": "Website", + "oldfieldname": "website", + "oldfieldtype": "Data" + }, + { + "fieldname": "registration_info", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "width": "50%" + }, + { + "description": "Company registration numbers for your reference. Tax numbers etc.", + "fieldname": "registration_details", + "fieldtype": "Code", + "label": "Registration Details", + "oldfieldname": "registration_details", + "oldfieldtype": "Code" + }, + { + "fieldname": "delete_company_transactions", + "fieldtype": "Button", + "label": "Delete Company Transactions" + }, + { + "fieldname": "lft", + "fieldtype": "Int", + "hidden": 1, + "label": "Lft", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "rgt", + "fieldtype": "Int", + "hidden": 1, + "label": "Rgt", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "old_parent", + "fieldtype": "Data", + "hidden": 1, + "label": "old_parent", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "default_selling_terms", + "fieldtype": "Link", + "label": "Default Selling Terms", + "options": "Terms and Conditions" + }, + { + "fieldname": "default_buying_terms", + "fieldtype": "Link", + "label": "Default Buying Terms", + "options": "Terms and Conditions" + } + ], + "icon": "fa fa-building", + "idx": 1, + "image_field": "company_logo", + "modified": "2019-07-04 22:20:45.104307", + "modified_by": "Administrator", + "module": "Setup", + "name": "Company", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "Accounts User" + }, + { + "read": 1, + "role": "Employee" + }, + { + "read": 1, + "role": "Sales User" + }, + { + "read": 1, + "role": "Purchase User" + }, + { + "read": 1, + "role": "Stock User" + }, + { + "read": 1, + "role": "Projects User" + } + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json index c1d459f2653..aba6a791a4e 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json @@ -1,288 +1,142 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, "autoname": "field:title", - "beta": 0, "creation": "2013-01-10 16:34:24", - "custom": 0, "description": "Standard Terms and Conditions that can be added to Sales and Purchases.\n\nExamples:\n\n1. Validity of the offer.\n1. Payment Terms (In Advance, On Credit, part advance etc).\n1. What is extra (or payable by the Customer).\n1. Safety / usage warning.\n1. Warranty if any.\n1. Returns Policy.\n1. Terms of shipping, if applicable.\n1. Ways of addressing disputes, indemnity, liability, etc.\n1. Address and Contact of your Company.", - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, + "engine": "InnoDB", + "field_order": [ + "title", + "disabled", + "applicable_modules_section", + "selling", + "buying", + "hr", + "section_break_7", + "terms", + "terms_and_conditions_help" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "title", "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": "Title", - "length": 0, "no_copy": 1, "oldfieldname": "title", "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": "0", "fieldname": "disabled", "fieldtype": "Check", - "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": "Disabled", - "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": "Disabled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "allow_in_quick_entry": 1, "fieldname": "terms", "fieldtype": "Text Editor", - "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": "Terms and Conditions", - "length": 0, - "no_copy": 0, "oldfieldname": "terms", - "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": 0, - "columns": 0, "fieldname": "terms_and_conditions_help", "fieldtype": "HTML", - "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": "Terms and Conditions Help", - "length": 0, - "no_copy": 0, - "options": "

Standard Terms and Conditions Example

\n\n
Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n
\n\n

How to get fieldnames

\n\n

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

\n\n

Templating

\n\n

Templates are compiled using the Jinja Templating Langauge. To learn more about Jinja, read this documentation.

", - "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": "

Standard Terms and Conditions Example

\n\n
Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n
\n\n

How to get fieldnames

\n\n

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

\n\n

Templating

\n\n

Templates are compiled using the Jinja Templating Langauge. To learn more about Jinja, read this documentation.

" + }, + { + "fieldname": "applicable_modules_section", + "fieldtype": "Section Break", + "label": "Applicable Modules" + }, + { + "default": "1", + "fieldname": "selling", + "fieldtype": "Check", + "label": "Selling" + }, + { + "default": "1", + "fieldname": "buying", + "fieldtype": "Check", + "label": "Buying" + }, + { + "default": "1", + "fieldname": "hr", + "fieldtype": "Check", + "label": "HR" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "icon-legal", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-29 06:36:33.131473", + "modified": "2019-07-04 13:31:30.393425", "modified_by": "Administrator", "module": "Setup", "name": "Terms and Conditions", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, "import": 1, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Sales Master Manager", - "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": 0, - "print": 0, "read": 1, - "report": 0, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales User" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, - "report": 0, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Purchase User" }, { - "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": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "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": "Accounts 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": 0, - "print": 0, "read": 1, - "report": 0, - "role": "Stock User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Stock User" } ], "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, "show_name_in_global_search": 1, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "sort_field": "modified", + "sort_order": "ASC" } \ No newline at end of file diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py index a2152a6eb52..372cc6d3e3e 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py @@ -3,9 +3,11 @@ from __future__ import unicode_literals import frappe +from frappe import _, throw import json from frappe.model.document import Document from frappe.utils.jinja import validate_template +from frappe.utils import cint from six import string_types @@ -13,6 +15,8 @@ class TermsandConditions(Document): def validate(self): if self.terms: validate_template(self.terms) + if not cint(self.buying) and not cint(self.selling) and not cint(self.hr) and not cint(self.disabled): + throw(_("At least one of the Applicable Modules should be selected")) @frappe.whitelist() def get_terms_and_conditions(template_name, doc): diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py index a5bd93fc2c2..4ca43a89b8f 100644 --- a/erpnext/startup/boot.py +++ b/erpnext/startup/boot.py @@ -33,7 +33,7 @@ def boot_session(bootinfo): FROM `tabCompany` LIMIT 1""") and 'Yes' or 'No' - bootinfo.docs += frappe.db.sql("""select name, default_currency, cost_center, default_terms, + bootinfo.docs += frappe.db.sql("""select name, default_currency, cost_center, default_selling_terms, default_buying_terms, default_letter_head, default_bank_account, enable_perpetual_inventory, country from `tabCompany`""", as_dict=1, update={"doctype":":Company"}) From 830f2b642bcaa7fd82f60b598d1a26a096523a4b Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Fri, 5 Jul 2019 09:48:38 +0530 Subject: [PATCH 033/174] chore(manufacturing): added dashboard for doctypes --- .../blanket_order/blanket_order_dashboard.py | 12 +++++++++ .../doctype/bom/bom_dashboard.py | 27 +++++++++++++++++++ .../doctype/operation/operation_dashboard.py | 13 +++++++++ .../doctype/routing/routing_dashboard.py | 12 +++++++++ .../workstation/workstation_dashboard.py | 13 +++++++++ erpnext/stock/doctype/item/item_dashboard.py | 2 +- 6 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py create mode 100644 erpnext/manufacturing/doctype/bom/bom_dashboard.py create mode 100644 erpnext/manufacturing/doctype/operation/operation_dashboard.py create mode 100644 erpnext/manufacturing/doctype/routing/routing_dashboard.py create mode 100644 erpnext/manufacturing/doctype/workstation/workstation_dashboard.py diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py new file mode 100644 index 00000000000..0005439cb1a --- /dev/null +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'blanket_order', + 'transactions': [ + { + 'items': ['Purchase Order', 'Sales Order'] + } + ] + } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom/bom_dashboard.py b/erpnext/manufacturing/doctype/bom/bom_dashboard.py new file mode 100644 index 00000000000..af710b04644 --- /dev/null +++ b/erpnext/manufacturing/doctype/bom/bom_dashboard.py @@ -0,0 +1,27 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'bom_no', + 'non_standard_fieldnames': { + 'Item': 'default_bom', + 'Purchase Order': 'bom', + 'Purchase Receipt': 'bom', + 'Purchase Invoice': 'bom' + }, + 'transactions': [ + { + 'label': _('Stock'), + 'items': ['Item', 'Stock Entry', 'Quality Inspection'] + }, + { + 'label': _('Manufacture'), + 'items': ['BOM', 'Work Order', 'Job Card', 'Production Plan'] + }, + { + 'label': _('Purchase'), + 'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'] + } + ] + } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/operation/operation_dashboard.py b/erpnext/manufacturing/doctype/operation/operation_dashboard.py new file mode 100644 index 00000000000..9e53a20ad58 --- /dev/null +++ b/erpnext/manufacturing/doctype/operation/operation_dashboard.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'operation', + 'transactions': [ + { + 'label': _('Manufacture'), + 'items': ['BOM', 'Work Order', 'Job Card', 'Timesheet'] + } + ] + } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/routing/routing_dashboard.py b/erpnext/manufacturing/doctype/routing/routing_dashboard.py new file mode 100644 index 00000000000..ab309cc9d50 --- /dev/null +++ b/erpnext/manufacturing/doctype/routing/routing_dashboard.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'routing', + 'transactions': [ + { + 'items': ['BOM'] + } + ] + } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py new file mode 100644 index 00000000000..c5683134f21 --- /dev/null +++ b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'workstation', + 'transactions': [ + { + 'label': _('Manufacture'), + 'items': ['BOM', 'Routing', 'Work Order', 'Job Card', 'Operation', 'Timesheet'] + } + ] + } \ No newline at end of file diff --git a/erpnext/stock/doctype/item/item_dashboard.py b/erpnext/stock/doctype/item/item_dashboard.py index b3733d357ba..4bf3c38e222 100644 --- a/erpnext/stock/doctype/item/item_dashboard.py +++ b/erpnext/stock/doctype/item/item_dashboard.py @@ -41,7 +41,7 @@ def get_data(): }, { 'label': _('Manufacture'), - 'items': ['Work Order', 'Item Manufacturer'] + 'items': ['Production Plan', 'Work Order', 'Item Manufacturer'] } ] } \ No newline at end of file From 38b930b638f10888b629c02284813212dc36e5d7 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 5 Jul 2019 10:38:48 +0530 Subject: [PATCH 034/174] refactor: Move and Add buttons open new stock entry --- erpnext/stock/dashboard/item_dashboard.js | 25 +++++++++++++++++++ .../stock/dashboard/item_dashboard_list.html | 15 +++++++++++ 2 files changed, 40 insertions(+) diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index f820b7aa86f..ed325a16b8a 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -16,6 +16,31 @@ erpnext.stock.ItemDashboard = Class.extend({ this.content = $(frappe.render_template('item_dashboard')).appendTo(this.parent); this.result = this.content.find('.result'); + this.content.on('click', '.btn-move', function() { + let item = unescape($(this).attr('data-item')); + let warehouse = unescape($(this).attr('data-warehouse')); + open_stock_entry(item, warehouse, "Material Transfer"); + }); + + this.content.on('click', '.btn-add', function() { + let item = unescape($(this).attr('data-item')); + let warehouse = unescape($(this).attr('data-warehouse')); + open_stock_entry(item, warehouse); + }); + + function open_stock_entry(item, warehouse, entry_type) { + frappe.model.with_doctype('Stock Entry', function() { + var doc = frappe.model.get_new_doc('Stock Entry'); + if (entry_type) doc.stock_entry_type = entry_type; + + var row = frappe.model.add_child(doc, 'items'); + row.item_code = item; + row.s_warehouse = warehouse; + + frappe.set_route('Form', doc.doctype, doc.name); + }) + } + // more this.content.find('.btn-more').on('click', function() { me.start += 20; diff --git a/erpnext/stock/dashboard/item_dashboard_list.html b/erpnext/stock/dashboard/item_dashboard_list.html index f0e87b1c53a..5a3fa2ed485 100644 --- a/erpnext/stock/dashboard/item_dashboard_list.html +++ b/erpnext/stock/dashboard/item_dashboard_list.html @@ -39,6 +39,21 @@ + {% if can_write %} +
+ {% if d.actual_qty %} +
+ {% endif %} {% endfor %} From ad33c195100abfd35fd1d5ed16998c8ac1a3563b Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Fri, 5 Jul 2019 11:28:59 +0530 Subject: [PATCH 035/174] fix: converting spaces to tabs on a few lines --- .../doctype/blanket_order/blanket_order_dashboard.py | 2 +- erpnext/manufacturing/doctype/bom/bom_dashboard.py | 10 +++++----- .../doctype/job_card/job_card_dashboard.py | 2 +- .../doctype/operation/operation_dashboard.py | 2 +- .../doctype/workstation/workstation_dashboard.py | 2 +- erpnext/stock/doctype/item/item_dashboard.py | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py index 0005439cb1a..ed319a0cefd 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Purchase Order', 'Sales Order'] } ] - } \ No newline at end of file + } diff --git a/erpnext/manufacturing/doctype/bom/bom_dashboard.py b/erpnext/manufacturing/doctype/bom/bom_dashboard.py index af710b04644..803ece7c789 100644 --- a/erpnext/manufacturing/doctype/bom/bom_dashboard.py +++ b/erpnext/manufacturing/doctype/bom/bom_dashboard.py @@ -6,9 +6,9 @@ def get_data(): 'fieldname': 'bom_no', 'non_standard_fieldnames': { 'Item': 'default_bom', - 'Purchase Order': 'bom', - 'Purchase Receipt': 'bom', - 'Purchase Invoice': 'bom' + 'Purchase Order': 'bom', + 'Purchase Receipt': 'bom', + 'Purchase Invoice': 'bom' }, 'transactions': [ { @@ -19,9 +19,9 @@ def get_data(): 'label': _('Manufacture'), 'items': ['BOM', 'Work Order', 'Job Card', 'Production Plan'] }, - { + { 'label': _('Purchase'), 'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'] } ] - } \ No newline at end of file + } diff --git a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py index d48bccf9d42..c2aa2bd968a 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py +++ b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['Material Request', 'Stock Entry'] } ] - } \ No newline at end of file + } diff --git a/erpnext/manufacturing/doctype/operation/operation_dashboard.py b/erpnext/manufacturing/doctype/operation/operation_dashboard.py index 9e53a20ad58..8deb9ec6e0b 100644 --- a/erpnext/manufacturing/doctype/operation/operation_dashboard.py +++ b/erpnext/manufacturing/doctype/operation/operation_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['BOM', 'Work Order', 'Job Card', 'Timesheet'] } ] - } \ No newline at end of file + } diff --git a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py index c5683134f21..9e0d1d17394 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py +++ b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['BOM', 'Routing', 'Work Order', 'Job Card', 'Operation', 'Timesheet'] } ] - } \ No newline at end of file + } diff --git a/erpnext/stock/doctype/item/item_dashboard.py b/erpnext/stock/doctype/item/item_dashboard.py index 4bf3c38e222..dd4676a037a 100644 --- a/erpnext/stock/doctype/item/item_dashboard.py +++ b/erpnext/stock/doctype/item/item_dashboard.py @@ -44,4 +44,4 @@ def get_data(): 'items': ['Production Plan', 'Work Order', 'Item Manufacturer'] } ] - } \ No newline at end of file + } From cdb75b56c5a9c858b2a4b8b87aaa632f1e1a3ec6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 5 Jul 2019 12:20:02 +0530 Subject: [PATCH 036/174] fix: removed item manaufacturer child table from item master --- erpnext/stock/doctype/item/item.json | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 3936bf7524f..164ffa443b2 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -76,7 +76,6 @@ "is_customer_provided_item", "customer", "supplier_details", - "manufacturers", "delivered_by_supplier", "column_break2", "supplier_items", @@ -1022,12 +1021,6 @@ "fieldtype": "Check", "label": "Synced With Hub", "read_only": 1 - }, - { - "fieldname": "manufacturers", - "fieldtype": "Table", - "label": "Manufacturers", - "options": "Item Manufacturer" } ], "has_web_view": 1, @@ -1035,7 +1028,7 @@ "idx": 2, "image_field": "image", "max_attachments": 1, - "modified": "2019-06-02 04:45:59.911507", + "modified": "2019-07-05 12:18:13.977931", "modified_by": "Administrator", "module": "Stock", "name": "Item", @@ -1097,4 +1090,4 @@ "sort_order": "DESC", "title_field": "item_name", "track_changes": 1 - } \ No newline at end of file + } From 82e2bac89112b74d577db1c917dece714439b651 Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Fri, 5 Jul 2019 12:41:19 +0530 Subject: [PATCH 037/174] fix(coding style): adding a few spaces --- erpnext/manufacturing/doctype/blanket_order/blanket_order.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js index d1bef3fcd03..0bbf689d4a5 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js @@ -50,12 +50,12 @@ frappe.ui.form.on('Blanket Order', { }, set_tc_name_filter: function(frm) { - if (frm.doc.blanket_order_type === 'Selling'){ + if (frm.doc.blanket_order_type === 'Selling') { frm.set_query("tc_name", function() { return { filters: { selling: 1 } }; }); } - if (frm.doc.blanket_order_type === 'Purchasing'){ + if (frm.doc.blanket_order_type === 'Purchasing') { frm.set_query("tc_name", function() { return { filters: { buying: 1 } }; }); From 7a7c66e95e281181520ed0f5ea8c1ca5d10a1aa2 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 5 Jul 2019 13:06:53 +0530 Subject: [PATCH 038/174] refactor: added throw if supplier is not default for any item --- .../material_request/material_request.py | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index ef5f24e59d1..2b079e7995f 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -370,19 +370,20 @@ def make_purchase_order_based_on_supplier(source_name, target_doc=None): def get_material_requests_based_on_supplier(supplier): supplier_items = [d.parent for d in frappe.db.get_all("Item Default", {"default_supplier": supplier}, 'parent')] - if supplier_items: - material_requests = frappe.db.sql_list("""select distinct mr.name - from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item - where mr.name = mr_item.parent - and mr_item.item_code in (%s) - and mr.material_request_type = 'Purchase' - and mr.per_ordered < 99.99 - and mr.docstatus = 1 - and mr.status != 'Stopped' - order by mr_item.item_code ASC""" % ', '.join(['%s']*len(supplier_items)), - tuple(supplier_items)) - else: - material_requests = [] + if not supplier_items: + frappe.throw(_("{0} is not the default supplier for any items.".format(supplier))) + + material_requests = frappe.db.sql_list("""select distinct mr.name + from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item + where mr.name = mr_item.parent + and mr_item.item_code in (%s) + and mr.material_request_type = 'Purchase' + and mr.per_ordered < 99.99 + and mr.docstatus = 1 + and mr.status != 'Stopped' + order by mr_item.item_code ASC""" % ', '.join(['%s']*len(supplier_items)), + tuple(supplier_items)) + return material_requests, supplier_items @frappe.whitelist() From 2f3f7507c4d131d204b473938f9f33a4338983fd Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 28 Apr 2019 23:30:00 +0530 Subject: [PATCH 039/174] fix: browser hangs while making payment entry for large number of outstaning invoices --- .../doctype/payment_entry/payment_entry.js | 87 ++++++++++++------- .../doctype/payment_entry/payment_entry.json | 12 ++- .../doctype/payment_entry/payment_entry.py | 30 ++++--- erpnext/accounts/utils.py | 10 ++- 4 files changed, 86 insertions(+), 53 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 2c382c58f6a..986457fac82 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -302,7 +302,7 @@ frappe.ui.form.on('Payment Entry', { }, () => frm.set_value("party_balance", r.message.party_balance), () => frm.set_value("party_name", r.message.party_name), - () => frm.events.get_outstanding_documents(frm), + () => frm.clear_table("references"), () => frm.events.hide_unhide_fields(frm), () => frm.events.set_dynamic_labels(frm), () => { @@ -323,9 +323,7 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_account_currency_and_balance(frm, frm.doc.paid_from, "paid_from_account_currency", "paid_from_account_balance", function(frm) { - if (frm.doc.payment_type == "Receive") { - frm.events.get_outstanding_documents(frm); - } else if (frm.doc.payment_type == "Pay") { + if (frm.doc.payment_type == "Pay") { frm.events.paid_amount(frm); } } @@ -337,9 +335,7 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_account_currency_and_balance(frm, frm.doc.paid_to, "paid_to_account_currency", "paid_to_account_balance", function(frm) { - if(frm.doc.payment_type == "Pay") { - frm.events.get_outstanding_documents(frm); - } else if (frm.doc.payment_type == "Receive") { + if (frm.doc.payment_type == "Receive") { if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) { if(frm.doc.source_exchange_rate) { frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate); @@ -533,26 +529,49 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_unallocated_amount(frm); }, - get_outstanding_documents: function(frm) { + get_outstanding_invoice: function(frm) { + const fields = [ + {fieldtype:"Date", label: __("Posting From Date"), fieldname:"from_date"}, + {fieldtype:"Column Break"}, + {fieldtype:"Date", label: __("Posting To Date"), fieldname:"to_date"}, + {fieldtype:"Section Break"}, + ]; + + frappe.prompt(fields, function(data){ + frappe.flags.allocate_payment_amount = true; + frm.events.get_outstanding_documents(frm, data); + }, __("Select Date"), __("Get Outstanding Invoices")); + }, + + get_outstanding_documents: function(frm, date_args) { 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 + } + + if(date_args) { + args["from_date"] = date_args["from_date"]; + args["to_date"] = date_args["to_date"]; + } + 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,24 +627,26 @@ frappe.ui.form.on('Payment Entry', { frm.events.allocate_party_amount_against_ref_docs(frm, (frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount)); + + frappe.flags.allocate_payment_amount = false; } }); }, - allocate_payment_amount: function(frm) { - if(frm.doc.payment_type == 'Internal Transfer'){ - return - } + // 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); - } - }, + // 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; @@ -677,7 +698,7 @@ frappe.ui.form.on('Payment Entry', { $.each(frm.doc.references || [], function(i, row) { row.allocated_amount = 0 //If allocate payment amount checkbox is unchecked, set zero to allocate amount - if(frm.doc.allocate_payment_amount){ + if(frappe.flags.allocate_payment_amount){ if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) { if(row.outstanding_amount >= allocated_positive_outstanding) { row.allocated_amount = allocated_positive_outstanding; @@ -958,7 +979,7 @@ frappe.ui.form.on('Payment Entry', { }, () => { 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 fcaa570331f..4950bd12d38 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -325,19 +325,17 @@ "reqd": 1 }, { - "collapsible": 1, - "collapsible_depends_on": "references", + "collapsible": 0, + "collapsible_depends_on": "", "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", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index ea76126d8cc..bb0d44e9d4e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -574,8 +574,8 @@ def get_outstanding_reference_documents(args): # Get negative outstanding sales /purchase invoices negative_outstanding_invoices = [] if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"): - negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), - args.get("party"), args.get("party_account"), party_account_currency, company_currency) + negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"), + args.get("party_account"), args.get("company"), party_account_currency, company_currency) # Get positive outstanding sales /purchase invoices/ Fees condition = "" @@ -585,10 +585,16 @@ def get_outstanding_reference_documents(args): # Add cost center condition if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account(): - condition += " and cost_center='%s'" % args.get("cost_center") + condition += " and cost_center='%s'" % args.get("cost_center") + + if args.get("from_date") and args.get("to_date"): + condition += " and posting_date between '{0}' and '{1}'".format(args.get("from_date"), args.get("to_date")) + + 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"), condition=condition, limit=100) for d in outstanding_invoices: d["exchange_rate"] = 1 @@ -606,12 +612,13 @@ def get_outstanding_reference_documents(args): orders_to_be_billed = [] if (args.get("party_type") != "Student"): orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), - args.get("party"), party_account_currency, company_currency) + args.get("party"), args.get("company"), party_account_currency, company_currency) return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed -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): if party_type == "Customer": voucher_type = 'Sales Order' elif party_type == "Supplier": @@ -641,6 +648,7 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre where {party_type} = %s and docstatus = 1 + and company = %s and ifnull(status, "") != "Closed" and {ref_field} > advance_paid and abs(100 - per_billed) > 0.01 @@ -652,7 +660,7 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre "voucher_type": voucher_type, "party_type": scrub(party_type), "condition": condition - }), party, as_dict=True) + }), (party, company), as_dict=True) order_list = [] for d in orders: @@ -663,7 +671,8 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre return order_list -def get_negative_outstanding_invoices(party_type, party, party_account, party_account_currency, company_currency, cost_center=None): +def get_negative_outstanding_invoices(party_type, party, party_account, + company, party_account_currency, company_currency, cost_center=None): voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice" supplier_condition = "" if voucher_type == "Purchase Invoice": @@ -684,7 +693,8 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac from `tab{voucher_type}` where - {party_type} = %s and {party_account} = %s and docstatus = 1 and outstanding_amount < 0 + {party_type} = %s and {party_account} = %s and docstatus = 1 and + company = %s and outstanding_amount < 0 {supplier_condition} order by posting_date, name @@ -696,7 +706,7 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac "party_type": scrub(party_type), "party_account": "debit_to" if party_type == "Customer" else "credit_to", "cost_center": cost_center - }), (party, party_account), as_dict=True) + }), (party, party_account, company), as_dict=True) @frappe.whitelist() diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 7a1f6c57c2c..d1861785afc 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -632,6 +632,10 @@ def get_outstanding_invoices(party_type, party, account, condition=None): outstanding_invoices = [] precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2 + limit_cond = '' + if limit: + limit_cond = " limit {}".format(limit) + if erpnext.get_party_account_type(party_type) == 'Receivable': dr_or_cr = "debit_in_account_currency - credit_in_account_currency" payment_dr_or_cr = "credit_in_account_currency - debit_in_account_currency" @@ -673,11 +677,11 @@ def get_outstanding_invoices(party_type, party, account, condition=None): and account = %(account)s and {payment_dr_or_cr} > 0 and against_voucher is not null and against_voucher != '' - group by against_voucher_type, against_voucher - """.format(payment_dr_or_cr=payment_dr_or_cr), { + group by against_voucher_type, against_voucher {limit_cond} + """.format(payment_dr_or_cr=payment_dr_or_cr, limit_cond= limit_cond), { "party_type": party_type, "party": party, - "account": account, + "account": account }, as_dict=True) pe_map = frappe._dict() From 0f065d5de163a0eeb778a0e0c3d3d1af1808a1bc Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 2 May 2019 00:06:04 +0530 Subject: [PATCH 040/174] Added due date in the gl entry --- .../accounts/doctype/gl_entry/gl_entry.json | 35 ++++++++- .../doctype/journal_entry/journal_entry.py | 1 + .../doctype/payment_entry/payment_entry.js | 72 ++++++++++++------- .../doctype/payment_entry/payment_entry.json | 6 +- .../doctype/payment_entry/payment_entry.py | 32 ++++++--- .../purchase_invoice/purchase_invoice.py | 1 + .../doctype/sales_invoice/sales_invoice.py | 1 + erpnext/accounts/utils.py | 23 +++--- erpnext/patches.txt | 1 + .../patches/v12_0/update_due_date_in_gle.py | 17 +++++ 10 files changed, 139 insertions(+), 50 deletions(-) create mode 100644 erpnext/patches/v12_0/update_due_date_in_gle.py diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json index 333d39aae65..a232a953f31 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 3132c937dd7..8fbddb9b0cc 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -498,6 +498,7 @@ class JournalEntry(AccountsController): self.get_gl_dict({ "account": d.account, "party_type": d.party_type, + "due_date": self.due_date, "party": d.party, "against": d.against_account, "debit": flt(d.debit, d.precision("debit")), diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 986457fac82..f17b2cbeda4 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -530,20 +530,57 @@ frappe.ui.form.on('Payment Entry', { }, get_outstanding_invoice: function(frm) { + const today = frappe.datetime.get_today(); const fields = [ - {fieldtype:"Date", label: __("Posting From Date"), fieldname:"from_date"}, + {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: __("Posting To Date"), fieldname:"to_date"}, + {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(data){ + frappe.prompt(fields, function(filters){ frappe.flags.allocate_payment_amount = true; - frm.events.get_outstanding_documents(frm, data); - }, __("Select Date"), __("Get Outstanding Invoices")); + frm.events.validate_filters_data(frm, filters); + frm.events.get_outstanding_documents(frm, filters); + }, __("Filters"), __("Get Outstanding Invoices")); }, - get_outstanding_documents: function(frm, date_args) { + 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) { @@ -563,11 +600,12 @@ frappe.ui.form.on('Payment Entry', { "cost_center": frm.doc.cost_center } - if(date_args) { - args["from_date"] = date_args["from_date"]; - args["to_date"] = date_args["to_date"]; + 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: { @@ -628,26 +666,10 @@ frappe.ui.form.on('Payment Entry', { frm.events.allocate_party_amount_against_ref_docs(frm, (frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount)); - frappe.flags.allocate_payment_amount = false; } }); }, - // 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; diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 4950bd12d38..a85eccd30af 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,8 +325,6 @@ "reqd": 1 }, { - "collapsible": 0, - "collapsible_depends_on": "", "depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)", "fieldname": "section_break_14", "fieldtype": "Section Break", @@ -568,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 bb0d44e9d4e..699f04675fe 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -587,14 +587,21 @@ def get_outstanding_reference_documents(args): if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account(): condition += " and cost_center='%s'" % args.get("cost_center") - if args.get("from_date") and args.get("to_date"): - condition += " and posting_date between '{0}' and '{1}'".format(args.get("from_date"), args.get("to_date")) + 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"))) + 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, limit=100) + args.get("party_account"), filters=args, condition=condition, limit=100) for d in outstanding_invoices: d["exchange_rate"] = 1 @@ -612,13 +619,19 @@ def get_outstanding_reference_documents(args): orders_to_be_billed = [] if (args.get("party_type") != "Student"): orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), - args.get("party"), args.get("company"), 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} {1}.") + .format(args.get("party_type").lower(), args.get("party"))) + + return data def get_orders_to_be_billed(posting_date, party_type, party, - company, party_account_currency, company_currency, cost_center=None): + company, party_account_currency, company_currency, cost_center=None, filters=None): if party_type == "Customer": voucher_type = 'Sales Order' elif party_type == "Supplier": @@ -664,6 +677,10 @@ def get_orders_to_be_billed(posting_date, party_type, party, 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) @@ -934,7 +951,6 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= pe.paid_to_account_currency = party_account_currency if payment_type=="Pay" else bank.account_currency pe.paid_amount = paid_amount pe.received_amount = received_amount - pe.allocate_payment_amount = 1 pe.letter_head = doc.get("letter_head") if pe.party_type in ["Customer", "Supplier"]: diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index a6f6acea66b..1a49be3399b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -416,6 +416,7 @@ class PurchaseInvoice(BuyingController): "account": self.credit_to, "party_type": "Supplier", "party": self.supplier, + "due_date": self.due_date, "against": self.against_expense_account, "credit": grand_total_in_company_currency, "credit_in_account_currency": grand_total_in_company_currency \ diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index b725c73c8b0..6d44811f193 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -734,6 +734,7 @@ class SalesInvoice(SellingController): "account": self.debit_to, "party_type": "Customer", "party": self.customer, + "due_date": self.due_date, "against": self.against_income_account, "debit": grand_total_in_company_currency, "debit_in_account_currency": grand_total_in_company_currency \ diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index d1861785afc..542c7e4e524 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -628,14 +628,10 @@ def get_held_invoices(party_type, party): return held_invoices -def get_outstanding_invoices(party_type, party, account, condition=None): +def get_outstanding_invoices(party_type, party, account, condition=None, filters=None): outstanding_invoices = [] precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2 - limit_cond = '' - if limit: - limit_cond = " limit {}".format(limit) - if erpnext.get_party_account_type(party_type) == 'Receivable': dr_or_cr = "debit_in_account_currency - credit_in_account_currency" payment_dr_or_cr = "credit_in_account_currency - debit_in_account_currency" @@ -648,7 +644,8 @@ def get_outstanding_invoices(party_type, party, account, condition=None): invoice_list = frappe.db.sql(""" select - voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount + voucher_no, voucher_type, posting_date, due_date, + ifnull(sum({dr_or_cr}), 0) as invoice_amount from `tabGL Entry` where @@ -677,8 +674,8 @@ def get_outstanding_invoices(party_type, party, account, condition=None): and account = %(account)s and {payment_dr_or_cr} > 0 and against_voucher is not null and against_voucher != '' - group by against_voucher_type, against_voucher {limit_cond} - """.format(payment_dr_or_cr=payment_dr_or_cr, limit_cond= limit_cond), { + group by against_voucher_type, against_voucher + """.format(payment_dr_or_cr=payment_dr_or_cr), { "party_type": party_type, "party": party, "account": account @@ -692,10 +689,12 @@ def get_outstanding_invoices(party_type, party, account, condition=None): payment_amount = pe_map.get((d.voucher_type, d.voucher_no), 0) outstanding_amount = flt(d.invoice_amount - payment_amount, precision) if outstanding_amount > 0.5 / (10**precision): - if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices: - due_date = frappe.db.get_value( - d.voucher_type, d.voucher_no, "posting_date" if party_type == "Employee" else "due_date") + if (filters.get("outstanding_amt_greater_than") and + not (outstanding_amount >= filters.get("outstanding_amt_greater_than") and + outstanding_amount <= filters.get("outstanding_amt_less_than"))): + continue + if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices: outstanding_invoices.append( frappe._dict({ 'voucher_no': d.voucher_no, @@ -704,7 +703,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None): 'invoice_amount': flt(d.invoice_amount), 'payment_amount': payment_amount, 'outstanding_amount': outstanding_amount, - 'due_date': due_date + 'due_date': d.due_date }) ) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 72db8ad063e..b35a6da5861 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -615,3 +615,4 @@ erpnext.patches.v11_1.set_missing_opportunity_from erpnext.patches.v12_0.set_quotation_status erpnext.patches.v12_0.set_priority_for_support erpnext.patches.v12_0.delete_priority_property_setter +erpnext.patches.v12_0.update_due_date_in_gle 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 00000000000..9e2be6fcee7 --- /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 account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable') )""" + .format(doctype=doctype)) \ No newline at end of file From 909d7734626d65e8e179b28c8a8545abbbfab401 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 4 Jul 2019 19:32:46 +0530 Subject: [PATCH 041/174] fix: on credit note / debit note deferred reversed instead of income --- .../accounts/doctype/purchase_invoice/purchase_invoice.py | 6 +++++- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index a6f6acea66b..1b03896dab5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -484,9 +484,13 @@ class PurchaseInvoice(BuyingController): "credit": flt(item.rm_supp_cost) }, warehouse_account[self.supplier_warehouse]["account_currency"], item=item)) elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()): + + expense_account = (item.expense_account + if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account) + gl_entries.append( self.get_gl_dict({ - "account": item.expense_account if not item.enable_deferred_expense else item.deferred_expense_account, + "account": expense_account, "against": self.supplier, "debit": flt(item.base_net_amount, item.precision("base_net_amount")), "debit_in_account_currency": (flt(item.base_net_amount, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index b725c73c8b0..71bdc2aafed 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -783,10 +783,13 @@ class SalesInvoice(SellingController): asset.db_set("disposal_date", self.posting_date) asset.set_status("Sold" if self.docstatus==1 else None) else: - account_currency = get_account_currency(item.income_account) + income_account = (item.income_account + if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account) + + account_currency = get_account_currency(income_account) gl_entries.append( self.get_gl_dict({ - "account": item.income_account if not item.enable_deferred_revenue else item.deferred_revenue_account, + "account": income_account, "against": self.customer, "credit": flt(item.base_net_amount, item.precision("base_net_amount")), "credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount")) From ee01ba587902ee15b7e08ee9cb0a98a2b55cdd3a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 5 Jul 2019 15:21:45 +0530 Subject: [PATCH 042/174] fix: linting --- erpnext/patches/v12_0/update_due_date_in_gle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/update_due_date_in_gle.py b/erpnext/patches/v12_0/update_due_date_in_gle.py index 9e2be6fcee7..4c47a82dccd 100644 --- a/erpnext/patches/v12_0/update_due_date_in_gle.py +++ b/erpnext/patches/v12_0/update_due_date_in_gle.py @@ -13,5 +13,5 @@ def execute(): 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 account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable') )""" - .format(doctype=doctype)) \ No newline at end of file + and account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable') )""" #nosec + .format(doctype=doctype)) From 6277966105a8765178ddc69e4487ccbfd4aa3a72 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 5 Jul 2019 15:58:13 +0530 Subject: [PATCH 043/174] chore: added check for auto-reorder in stock settings (#18174) * chore: added check for auto-reorder in stock settings * style: removed explicit return --- erpnext/stock/doctype/item/item.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 80d4e172a5a..6484b93485a 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -122,6 +122,7 @@ class Item(WebsiteGenerator): self.validate_item_defaults() self.validate_customer_provided_part() self.update_defaults_from_item_group() + self.validate_auto_reorder_enabled_in_stock_settings() self.cant_change() if not self.get("__islocal"): @@ -859,6 +860,12 @@ class Item(WebsiteGenerator): filters={"production_item": self.name, "docstatus": 1}): return True + def validate_auto_reorder_enabled_in_stock_settings(self): + if self.reorder_levels: + enabled = frappe.db.get_single_value('Stock Settings', 'auto_indent') + if not enabled: + frappe.msgprint(msg=_("You have to enable auto re-order in Stock Settings to maintain re-order levels."), title=_("Enable Auto Re-Order"), indicator="orange") + def get_timeline_data(doctype, name): '''returns timeline data based on stock ledger entry''' out = {} From 5d4d70b75f1404e28bfca4c435fcfb6d992d19fe Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 5 Jul 2019 16:59:27 +0530 Subject: [PATCH 044/174] feat: provision to make debit / credit note against the stock returned entry --- .../purchase_invoice/purchase_invoice.py | 6 +++-- erpnext/controllers/status_updater.py | 2 +- .../doctype/delivery_note/delivery_note.js | 26 +++++++++++++++++++ .../doctype/delivery_note/delivery_note.py | 5 +++- .../purchase_receipt/purchase_receipt.js | 23 ++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 10 +++++++ 6 files changed, 68 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 4d87edf375d..18a38d8c204 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -337,7 +337,8 @@ class PurchaseInvoice(BuyingController): if not self.is_return: self.update_against_document_in_jv() self.update_billing_status_for_zero_amount_refdoc("Purchase Order") - self.update_billing_status_in_pr() + + self.update_billing_status_in_pr() # Updating stock ledger should always be called after updating prevdoc status, # because updating ordered qty in bin depends upon updated ordered qty in PO @@ -769,7 +770,8 @@ class PurchaseInvoice(BuyingController): if not self.is_return: self.update_billing_status_for_zero_amount_refdoc("Purchase Order") - self.update_billing_status_in_pr() + + self.update_billing_status_in_pr() # Updating stock ledger should always be called after updating prevdoc status, # because updating ordered qty in bin depends upon updated ordered qty in PO diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 42e0a43e6e2..62be2e4e2a6 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -290,7 +290,7 @@ class StatusUpdater(Document): frappe.db.sql("""update `tab%(target_parent_dt)s` set %(target_parent_field)s = round( ifnull((select - ifnull(sum(if(%(target_ref_field)s > %(target_field)s, abs(%(target_field)s), abs(%(target_ref_field)s))), 0) + ifnull(sum(if(abs(%(target_ref_field)s) > abs(%(target_field)s), abs(%(target_field)s), abs(%(target_ref_field)s))), 0) / sum(abs(%(target_ref_field)s)) * 100 from `tab%(target_dt)s` where parent="%(name)s" having sum(abs(%(target_ref_field)s)) > 0), 0), 6) %(update_modified)s diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 78bc06a47bf..8c4a5cd11cd 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -77,8 +77,34 @@ frappe.ui.form.on("Delivery Note", { }, + 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 1e522b834ea..ec7df2da6d5 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -333,7 +333,10 @@ class DeliveryNote(SellingController): 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/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index e82aa2c63ec..a2d3e75f239 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -38,6 +38,29 @@ frappe.ui.form.on("Purchase Receipt", { 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 cdca44d60be..11e60b04385 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -396,6 +396,16 @@ class PurchaseReceipt(BuyingController): 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` From db3c5625aedc97b6b4b43438960fe0944371d14f Mon Sep 17 00:00:00 2001 From: Bhavishya Sharma Date: Sat, 6 Jul 2019 13:54:30 +0530 Subject: [PATCH 045/174] feat: Manufacturer is Missing Contacts --- .../doctype/manufacturer/manufacturer.js | 10 +- .../doctype/manufacturer/manufacturer.json | 358 +++++------------- .../doctype/manufacturer/manufacturer.py | 5 +- 3 files changed, 118 insertions(+), 255 deletions(-) diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.js b/erpnext/stock/doctype/manufacturer/manufacturer.js index 67df1bf4c0b..dcbe2a05f03 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.js +++ b/erpnext/stock/doctype/manufacturer/manufacturer.js @@ -3,6 +3,14 @@ frappe.ui.form.on('Manufacturer', { refresh: function(frm) { - + frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Manufacturer' } + if (frm.doc.__islocal) { + hide_field(['address_html','contact_html']); + frappe.contacts.clear_address_and_contact(frm); + } + else { + unhide_field(['address_html','contact_html']); + frappe.contacts.render_address_and_contact(frm); + } } }); diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.json b/erpnext/stock/doctype/manufacturer/manufacturer.json index 50259178364..3a64fbeceb1 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.json +++ b/erpnext/stock/doctype/manufacturer/manufacturer.json @@ -1,268 +1,120 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:short_name", - "beta": 0, - "creation": "2016-01-17 11:04:52.761731", - "custom": 0, - "description": "Manufacturers used in Items", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:short_name", + "creation": "2016-01-17 11:04:52.761731", + "description": "Manufacturers used in Items", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "short_name", + "full_name", + "website", + "country", + "logo", + "address_contacts", + "address_html", + "column_break_8", + "contact_html", + "section_break_10", + "notes" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Limited to 12 characters", - "fieldname": "short_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": "Short Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "description": "Limited to 12 characters", + "fieldname": "short_name", + "fieldtype": "Data", + "label": "Short Name", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "full_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Full Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "full_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Full Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "website", - "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": "Website", - "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, - "unique": 0 - }, + "fieldname": "website", + "fieldtype": "Data", + "label": "Website" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "country", - "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": "Country", - "length": 0, - "no_copy": 0, - "options": "Country", - "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, - "unique": 0 - }, + "fieldname": "country", + "fieldtype": "Link", + "label": "Country", + "options": "Country" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "logo", - "fieldtype": "Attach Image", - "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": "Logo", - "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, - "unique": 0 - }, + "fieldname": "logo", + "fieldtype": "Attach Image", + "label": "Logo" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "notes", - "fieldtype": "Small 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": "Notes", - "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, - "unique": 0 + "fieldname": "notes", + "fieldtype": "Small Text", + "label": "Notes" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "address_contacts", + "fieldtype": "Section Break", + "label": "Address and Contacts", + "options": "fa fa-map-marker" + }, + { + "fieldname": "address_html", + "fieldtype": "HTML", + "label": "Address HTML", + "read_only": 1 + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "fieldname": "contact_html", + "fieldtype": "HTML", + "label": "Contact HTML", + "read_only": 1 + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-certificate", - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-08-03 06:27:57.182666", - "modified_by": "Administrator", - "module": "Stock", - "name": "Manufacturer", - "name_case": "", - "owner": "Administrator", + ], + "icon": "fa fa-certificate", + "modified": "2019-07-06 13:06:47.237014", + "modified_by": "Administrator", + "module": "Stock", + "name": "Manufacturer", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "share": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "short_name, full_name", - "show_name_in_global_search": 1, - "sort_field": "", - "sort_order": "DESC", - "title_field": "short_name", - "track_changes": 0, - "track_seen": 0 + ], + "search_fields": "short_name, full_name", + "show_name_in_global_search": 1, + "sort_order": "DESC", + "title_field": "short_name" } \ No newline at end of file diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.py b/erpnext/stock/doctype/manufacturer/manufacturer.py index 7b85b05aa14..b624f73b773 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.py +++ b/erpnext/stock/doctype/manufacturer/manufacturer.py @@ -4,7 +4,10 @@ from __future__ import unicode_literals import frappe +from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address from frappe.model.document import Document class Manufacturer(Document): - pass + def onload(self): + """Load address and contacts in `__onload`""" + load_address_and_contact(self) From 607611ebdc68186d051f2d8c4b72006e061b3bf7 Mon Sep 17 00:00:00 2001 From: Bhavishya Sharma Date: Sat, 6 Jul 2019 15:02:57 +0530 Subject: [PATCH 046/174] codacy fix --- erpnext/stock/doctype/manufacturer/manufacturer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.js b/erpnext/stock/doctype/manufacturer/manufacturer.js index dcbe2a05f03..bb7e314e14e 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.js +++ b/erpnext/stock/doctype/manufacturer/manufacturer.js @@ -3,7 +3,7 @@ frappe.ui.form.on('Manufacturer', { refresh: function(frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Manufacturer' } + frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Manufacturer' }; if (frm.doc.__islocal) { hide_field(['address_html','contact_html']); frappe.contacts.clear_address_and_contact(frm); From 7ce199807cafc3c25c634adeb13b8924a081c578 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Sun, 7 Jul 2019 14:25:08 +0530 Subject: [PATCH 047/174] fix: GSTR-1 B2CS Json file generation and cess amount fixes --- erpnext/regional/report/gstr_1/gstr_1.py | 55 ++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index eff578001f4..e8c170e7215 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -74,7 +74,6 @@ class Gstr1Report(object): 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 @@ class Gstr1Report(object): "rate": "", "taxable_value": 0, "cess_amount": 0, - "type": 0 + "type": "" }) row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin)) @@ -94,6 +93,7 @@ class Gstr1Report(object): 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 @@ class Gstr1Report(object): 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 @@ class Gstr1Report(object): "fieldtype": "Data" }, { - "fieldname": "invoice_type", + "fieldname": "gst_category", "label": "Invoice Type", "fieldtype": "Data" }, @@ -564,12 +568,18 @@ def get_json(): 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 @@ def get_b2b_json(res, gstin): 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: From 6bb5ade667120dd5ec01cb2ea5d2aed9aa86eb6b Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Sun, 7 Jul 2019 21:24:45 +0530 Subject: [PATCH 048/174] fix: Add accounting dimensions to various reports and fixes --- .../accounting_dimension.js | 29 +++++++++++++------ .../accounting_dimension.json | 5 ++-- .../accounting_dimension.py | 6 ++-- .../accounts_payable/accounts_payable.js | 11 +++++++ .../accounts_payable_summary.js | 11 +++++++ .../accounts_receivable.js | 11 +++++++ .../accounts_receivable.py | 9 ++++++ .../accounts_receivable_summary.js | 11 +++++++ .../budget_variance_report.js | 4 +-- .../report/general_ledger/general_ledger.js | 4 +-- .../profitability_analysis.js | 9 ++++-- .../profitability_analysis.py | 19 ++++++++---- .../report/sales_register/sales_register.js | 11 +++++++ .../report/sales_register/sales_register.py | 11 +++++++ .../report/trial_balance/trial_balance.js | 4 +-- erpnext/public/js/financial_statements.js | 4 +-- .../public/js/utils/dimension_tree_filter.js | 2 +- 17 files changed, 127 insertions(+), 34 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js index fcbd10f606f..dd20632a651 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js @@ -9,6 +9,26 @@ frappe.ui.form.on('Accounting Dimension', { frappe.set_route("List", frm.doc.document_type); }); } + + let button = frm.doc.disabled ? "Enable" : "Disable"; + + frm.add_custom_button(__(button), function() { + + frm.set_value('disabled', 1 - frm.doc.disabled); + + frappe.call({ + method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension", + args: { + doc: frm.doc + }, + freeze: true, + callback: function(r) { + let message = frm.doc.disabled ? "Dimension Disabled" : "Dimension Enabled"; + frm.save(); + frappe.show_alert({message:__(message), indicator:'green'}); + } + }); + }); }, document_type: function(frm) { @@ -21,13 +41,4 @@ frappe.ui.form.on('Accounting Dimension', { } }); }, - - disabled: function(frm) { - frappe.call({ - method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension", - args: { - doc: frm.doc - } - }); - } }); diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json index 1e2bb925a1a..57543a0ef48 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 91d03be493f..15ace7239ea 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -121,11 +121,11 @@ def delete_accounting_dimension(doc): @frappe.whitelist() def disable_dimension(doc): if frappe.flags.in_test: - frappe.enqueue(start_dimension_disabling, doc=doc) + toggle_disabling(doc=doc) else: - start_dimension_disabling(doc=doc) + frappe.enqueue(toggle_disabling, doc=doc) -def start_dimension_disabling(doc): +def toggle_disabling(doc): doc = json.loads(doc) if doc.get('disabled'): diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 70f193e787b..f6a561f04f6 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -108,3 +108,14 @@ frappe.query_reports["Accounts Payable"] = { }); } } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); 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 06499adeeaf..ec4f0c983f3 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 @@ frappe.query_reports["Accounts Payable Summary"] = { }); } } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 3661afe7977..2a45454baca 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -172,3 +172,14 @@ frappe.query_reports["Accounts Receivable"] = { }); } } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 29737484c70..0cda2c15dde 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _, scrub from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions class ReceivablePayableReport(object): def __init__(self, filters=None): @@ -553,6 +554,14 @@ class ReceivablePayableReport(object): conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts))) values += accounts + accounting_dimensions = get_accounting_dimensions() + + if accounting_dimensions: + for dimension in accounting_dimensions: + if self.filters.get(dimension): + conditions.append("{0} = %s".format(dimension)) + values.append(self.filters.get(dimension)) + return " and ".join(conditions), values def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher): 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 f9162adabd0..a7c0787fcd7 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 @@ frappe.query_reports["Accounts Receivable Summary"] = { }); } } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); 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 b2072f06f12..f2a33a83ee3 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 @@ frappe.query_reports["Budget Variance Report"] = { ] } -let dimension_filters = erpnext.get_dimension_filters(); - -dimension_filters.then((dimensions) => { +erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]); }); diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 32af6440212..ea82575b808 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -159,9 +159,7 @@ frappe.query_reports["General Ledger"] = { ] } -let dimension_filters = erpnext.get_dimension_filters(); - -dimension_filters.then((dimensions) => { +erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{ "fieldname": dimension["fieldname"], diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js index 80b50b92c36..d6864b54f74 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js @@ -16,7 +16,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "fieldname": "based_on", "label": __("Based On"), "fieldtype": "Select", - "options": "Cost Center\nProject", + "options": ["Cost Center", "Project"], "default": "Cost Center", "reqd": 1 }, @@ -104,5 +104,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "parent_field": "parent_account", "initial_depth": 3 } -}); + erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Profitability Analysis"].filters[1].options.push(dimension["document_type"]); + }); + }); +}); diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py index a0d8c5f0e4e..6e9b31f2f6d 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py @@ -24,8 +24,17 @@ def get_accounts_data(based_on, company): if based_on == 'cost_center': return frappe.db.sql("""select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt from `tabCost Center` where company=%s order by name""", company, as_dict=True) - else: + elif based_on == 'project': return frappe.get_all('Project', fields = ["name"], filters = {'company': company}, order_by = 'name') + else: + filters = {} + doctype = frappe.unscrub(based_on) + has_company = frappe.db.has_column(doctype, 'company') + + if has_company: + filters.update({'company': company}) + + return frappe.get_all(doctype, fields = ["name"], filters = filters, order_by = 'name') def get_data(accounts, filters, based_on): if not accounts: @@ -42,7 +51,7 @@ def get_data(accounts, filters, based_on): accumulate_values_into_parents(accounts, accounts_by_name) data = prepare_data(accounts, filters, total_row, parent_children_map, based_on) - data = filter_out_zero_value_rows(data, parent_children_map, + data = filter_out_zero_value_rows(data, parent_children_map, show_zero_values=filters.get("show_zero_values")) return data @@ -112,14 +121,14 @@ def prepare_data(accounts, filters, total_row, parent_children_map, based_on): for key in value_fields: row[key] = flt(d.get(key, 0.0), 3) - + if abs(row[key]) >= 0.005: # ignore zero values has_value = True row["has_value"] = has_value data.append(row) - + data.extend([{},total_row]) return data @@ -174,7 +183,7 @@ def set_gl_entries_by_account(company, from_date, to_date, based_on, gl_entries_ if from_date: additional_conditions.append("and posting_date >= %(from_date)s") - gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit, + gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit, is_opening, (select root_type from `tabAccount` where name = account) as type from `tabGL Entry` where company=%(company)s {additional_conditions} diff --git a/erpnext/accounts/report/sales_register/sales_register.js b/erpnext/accounts/report/sales_register/sales_register.js index 0b48882ca90..442aa1262e3 100644 --- a/erpnext/accounts/report/sales_register/sales_register.js +++ b/erpnext/accounts/report/sales_register/sales_register.js @@ -67,3 +67,14 @@ frappe.query_reports["Sales Register"] = { } ] } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index de60995ca22..d08056f6f9b 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import flt from frappe import msgprint, _ +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions def execute(filters=None): return _execute(filters) @@ -163,6 +164,16 @@ def get_conditions(filters): where parent=`tabSales Invoice`.name and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)""" + accounting_dimensions = get_accounting_dimensions() + + if accounting_dimensions: + for dimension in accounting_dimensions: + if filters.get(dimension): + conditions += """ and exists(select name from `tabSales Invoice Item` + where parent=`tabSales Invoice`.name + and ifnull(`tabSales Invoice Item`.{0}, '') = %({0})s)""".format(dimension) + + return conditions def get_invoices(filters, additional_query_columns): diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 8bc72807b33..73d2ab38989 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -96,9 +96,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { } }); -let dimension_filters = erpnext.get_dimension_filters(); - -dimension_filters.then((dimensions) => { +erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{ "fieldname": dimension["fieldname"], diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index d1113a4ca4a..89cb13d9810 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -129,9 +129,7 @@ function get_filters(){ } ] - let dimension_filters = erpnext.get_dimension_filters(); - - dimension_filters.then((dimensions) => { + erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { filters.push({ "fieldname": dimension["fieldname"], diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index fef450795bd..a9122d8dff1 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -7,7 +7,7 @@ erpnext.doctypes_with_dimensions = ["GL Entry", "Sales Invoice", "Purchase Invoi "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, { From 35f15148fb60df17d709925ee81155d98c7422c6 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Mon, 8 Jul 2019 04:55:28 +0000 Subject: [PATCH 049/174] fix: do not update modified (#18193) --- erpnext/support/doctype/issue/issue.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 93f13f1f9f4..ad1c2632508 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -258,15 +258,15 @@ def set_service_level_agreement_variance(issue=None): 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 { From 334335a2efdcb86a41230a57cb6e41f1e1342c25 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 8 Jul 2019 10:26:29 +0530 Subject: [PATCH 050/174] fix: not able to make credit note for the sales invoice in which item code is not set (#18184) --- erpnext/controllers/sales_and_purchase_return.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 8cf11f785be..2fddcdf24c5 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -75,7 +75,7 @@ def validate_returned_items(doc): items_returned = False for d in doc.get("items"): - if flt(d.qty) < 0 or d.get('received_qty') < 0: + if d.item_code and (flt(d.qty) < 0 or d.get('received_qty') < 0): if d.item_code not in valid_items: frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}") .format(d.idx, d.item_code, doc.doctype, doc.return_against)) @@ -107,6 +107,9 @@ def validate_returned_items(doc): items_returned = True + elif d.item_name: + items_returned = True + if not items_returned: frappe.throw(_("Atleast one item should be entered with negative quantity in return document")) From d86f027ce011bd194c092998d531f2192d9c599f Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 8 Jul 2019 10:29:26 +0530 Subject: [PATCH 051/174] fix: default supplier was not set from the patch in item defaults for multi company instance (#18178) --- erpnext/patches.txt | 1 + ...pdate_default_supplier_in_item_defaults.py | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0dd88e94e07..70bad342091 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -615,5 +615,6 @@ erpnext.patches.v11_1.set_missing_opportunity_from erpnext.patches.v12_0.set_quotation_status erpnext.patches.v12_0.set_priority_for_support erpnext.patches.v12_0.delete_priority_property_setter +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 00000000000..347dec1f74d --- /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 From bef897602d5ab745bcd0051da7843f11fcf6b3fd Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Mon, 8 Jul 2019 10:30:26 +0530 Subject: [PATCH 052/174] fix: Use db_set since it triggers on_update event (#18175) --- .../doctype/bank_reconciliation/bank_reconciliation.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 28807c41180..90cdf834c59 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -103,7 +103,7 @@ class BankReconciliation(Document): for d in self.get('payment_entries'): if d.clearance_date: if not d.payment_document: - frappe.throw(_("Row #{0}: Payment document is required to complete the trasaction")) + frappe.throw(_("Row #{0}: Payment document is required to complete the transaction")) if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date): frappe.throw(_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}") @@ -113,10 +113,8 @@ class BankReconciliation(Document): if not d.clearance_date: d.clearance_date = None - frappe.db.set_value(d.payment_document, d.payment_entry, "clearance_date", d.clearance_date) - frappe.db.sql("""update `tab{0}` set clearance_date = %s, modified = %s - where name=%s""".format(d.payment_document), - (d.clearance_date, nowdate(), d.payment_entry)) + payment_entry = frappe.get_doc(d.payment_document, d.payment_entry) + payment_entry.db_set('clearance_date', d.clearance_date) clearance_date_updated = True From 8309fcfbbc6cc5a97568aa988b993ebc8157e4e7 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 8 Jul 2019 10:39:30 +0530 Subject: [PATCH 053/174] BREAKING CHANGE: Remove anti-pattern "Project Task" (#18059) * BREAKING CHANGE: Remove anti-pattern "Project Task" * fix(tests): remove `tasks` from project/test_records.json * fix(tests) * fix(test): test_employee_onboarding.py * fix(tests): test_expense_claim.py * fix(refactor): cleanup project.py validate/update * fix(refactor): cleanup project.py validate/update * fix(test): test_expense_claim * fix(test): test_expense_claim * fix(test): test_expense_claim, try Test Company 4 * Update project.py --- .../test_employee_onboarding.py | 10 +- .../expense_claim/test_expense_claim.py | 52 +- erpnext/patches.txt | 1 + erpnext/patches/v12_0/set_task_status.py | 3 +- erpnext/projects/doctype/project/project.js | 71 - erpnext/projects/doctype/project/project.json | 1715 ++--------------- erpnext/projects/doctype/project/project.py | 254 +-- .../projects/doctype/project/test_project.py | 18 +- .../doctype/project/test_records.json | 8 +- .../projects/doctype/project_task/__init__.py | 0 .../doctype/project_task/project_task.json | 430 ----- .../doctype/project_task/project_task.py | 9 - erpnext/projects/doctype/task/task.py | 6 - .../doctype/sales_order/sales_order.py | 6 - 14 files changed, 187 insertions(+), 2396 deletions(-) delete mode 100644 erpnext/projects/doctype/project_task/__init__.py delete mode 100644 erpnext/projects/doctype/project_task/project_task.json delete mode 100644 erpnext/projects/doctype/project_task/project_task.py diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py index 5e7f276ccc8..35c9f728b61 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 @@ from erpnext.hr.doctype.employee_onboarding.employee_onboarding import Incomplet class TestEmployeeOnboarding(unittest.TestCase): def test_employee_onboarding_incomplete_task(self): if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}): - return frappe.get_doc('Employee Onboarding', {'employee_name': 'Test Researcher'}) + frappe.delete_doc('Employee Onboarding', {'employee_name': 'Test Researcher'}) _set_up() applicant = get_job_applicant() onboarding = frappe.new_doc('Employee Onboarding') @@ -39,9 +39,10 @@ class TestEmployeeOnboarding(unittest.TestCase): # complete the task project = frappe.get_doc('Project', onboarding.project) - project.load_tasks() - project.tasks[0].status = 'Completed' - project.save() + for task in frappe.get_all('Task', dict(project=project.name)): + task = frappe.get_doc('Task', task.name) + task.status = 'Completed' + task.save() # make employee onboarding.reload() @@ -71,4 +72,3 @@ def _set_up(): project = "Employee Onboarding : Test Researcher - test@researcher.com" frappe.db.sql("delete from tabProject where name=%s", project) frappe.db.sql("delete from tabTask where project=%s", project) - frappe.db.sql("delete from `tabProject Task` where parent=%s", project) diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index 92fdc094431..6618a4f7c57 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 @@ from erpnext.accounts.doctype.account.test_account import create_account test_records = frappe.get_test_records('Expense Claim') test_dependencies = ['Employee'] +company_name = '_Test Company 4' + class TestExpenseClaim(unittest.TestCase): def test_total_expense_claim_for_project(self): frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """) - frappe.db.sql("""delete from `tabProject Task` where parent = "_Test Project 1" """) frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """) - frappe.db.sql("delete from `tabExpense Claim` where project='_Test Project 1'") + frappe.db.sql("update `tabExpense Claim` set project = '', task = ''") frappe.get_doc({ "project_name": "_Test Project 1", - "doctype": "Project", + "doctype": "Project" }).save() - task = frappe.get_doc({ - "doctype": "Task", - "subject": "_Test Project Task 1", - "project": "_Test Project 1" - }).save() + task = frappe.get_doc(dict( + doctype = 'Task', + subject = '_Test Project Task 1', + status = 'Open', + project = '_Test Project 1' + )).insert() - task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"}) - payable_account = get_payable_account("Wind Power LLC") - make_expense_claim(payable_account, 300, 200, "Wind Power LLC","Travel Expenses - WP", "_Test Project 1", task_name) + task_name = task.name + payable_account = get_payable_account(company_name) + + make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", "_Test Project 1", task_name) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200) - expense_claim2 = make_expense_claim(payable_account, 600, 500, "Wind Power LLC", "Travel Expenses - WP","_Test Project 1", task_name) + expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4","_Test Project 1", task_name) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700) @@ -48,8 +51,8 @@ class TestExpenseClaim(unittest.TestCase): self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200) def test_expense_claim_status(self): - payable_account = get_payable_account("Wind Power LLC") - expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP") + payable_account = get_payable_account(company_name) + expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4") je_dict = make_bank_entry("Expense Claim", expense_claim.name) je = frappe.get_doc(je_dict) @@ -66,9 +69,9 @@ class TestExpenseClaim(unittest.TestCase): self.assertEqual(expense_claim.status, "Unpaid") def test_expense_claim_gl_entry(self): - payable_account = get_payable_account("Wind Power LLC") + payable_account = get_payable_account(company_name) taxes = generate_taxes() - expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP", do_not_submit=True, taxes=taxes) + expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", do_not_submit=True, taxes=taxes) expense_claim.submit() gl_entries = frappe.db.sql("""select account, debit, credit @@ -78,9 +81,9 @@ class TestExpenseClaim(unittest.TestCase): self.assertTrue(gl_entries) expected_values = dict((d[0], d) for d in [ - ['CGST - WP',10.0, 0.0], - [payable_account, 0.0, 210.0], - ["Travel Expenses - WP", 200.0, 0.0] + ['CGST - _TC4',18.0, 0.0], + [payable_account, 0.0, 218.0], + ["Travel Expenses - _TC4", 200.0, 0.0] ]) for gle in gl_entries: @@ -89,14 +92,14 @@ class TestExpenseClaim(unittest.TestCase): self.assertEquals(expected_values[gle.account][2], gle.credit) def test_rejected_expense_claim(self): - payable_account = get_payable_account("Wind Power LLC") + payable_account = get_payable_account(company_name) expense_claim = frappe.get_doc({ "doctype": "Expense Claim", "employee": "_T-Employee-00001", "payable_account": payable_account, "approval_status": "Rejected", "expenses": - [{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "amount": 300, "sanctioned_amount": 200 }] + [{ "expense_type": "Travel", "default_account": "Travel Expenses - _TC4", "amount": 300, "sanctioned_amount": 200 }] }) expense_claim.submit() @@ -111,9 +114,9 @@ def get_payable_account(company): def generate_taxes(): parent_account = frappe.db.get_value('Account', - {'company': "Wind Power LLC", 'is_group':1, 'account_type': 'Tax'}, + {'company': company_name, 'is_group':1, 'account_type': 'Tax'}, 'name') - account = create_account(company="Wind Power LLC", account_name="CGST", account_type="Tax", parent_account=parent_account) + account = create_account(company=company_name, account_name="CGST", account_type="Tax", parent_account=parent_account) return {'taxes':[{ "account_head": account, "rate": 0, @@ -124,15 +127,18 @@ def generate_taxes(): def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None): employee = frappe.db.get_value("Employee", {"status": "Active"}) + currency = frappe.db.get_value('Company', company, 'default_currency') expense_claim = { "doctype": "Expense Claim", "employee": employee, "payable_account": payable_account, "approval_status": "Approved", "company": company, + 'currency': currency, "expenses": [{"expense_type": "Travel", "default_account": account, + 'currency': currency, "amount": amount, "sanctioned_amount": sanctioned_amount}]} if taxes: diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 70bad342091..571c2dc75b4 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -615,6 +615,7 @@ erpnext.patches.v11_1.set_missing_opportunity_from erpnext.patches.v12_0.set_quotation_status erpnext.patches.v12_0.set_priority_for_support erpnext.patches.v12_0.delete_priority_property_setter +execute:frappe.delete_doc("DocType", "Project Task") erpnext.patches.v11_1.update_default_supplier_in_item_defaults erpnext.patches.v12_0.update_due_date_in_gle erpnext.patches.v12_0.add_default_buying_selling_terms_in_company diff --git a/erpnext/patches/v12_0/set_task_status.py b/erpnext/patches/v12_0/set_task_status.py index 32b8177130b..70f65097dc3 100644 --- a/erpnext/patches/v12_0/set_task_status.py +++ b/erpnext/patches/v12_0/set_task_status.py @@ -2,10 +2,9 @@ import frappe def execute(): frappe.reload_doctype('Task') - frappe.reload_doctype('Project Task') # add "Completed" if customized - for doctype in ('Task', 'Project Task'): + for doctype in ('Task'): property_setter_name = frappe.db.exists('Property Setter', dict(doc_type = doctype, field_name = 'status', property = 'options')) if property_setter_name: property_setter = frappe.get_doc('Property Setter', property_setter_name) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 528c7cd0c71..5613f088e16 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 @@ frappe.ui.form.on("Project", { }); }, - tasks_refresh: function (frm) { - var grid = frm.get_field('tasks').grid; - grid.wrapper.find('select[data-fieldname="status"]').each(function () { - if ($(this).val() === 'Open') { - $(this).addClass('input-indicator-open'); - } else { - $(this).removeClass('input-indicator-open'); - } - }); - }, - - status: function(frm) { - if (frm.doc.status === 'Cancelled') { - frappe.confirm(__('Set tasks in this project as cancelled?'), () => { - frm.doc.tasks = frm.doc.tasks.map(task => { - task.status = 'Cancelled'; - return task; - }); - frm.refresh_field('tasks'); - }); - } - } -}); - -frappe.ui.form.on("Project Task", { - edit_task: function(frm, doctype, name) { - var doc = frappe.get_doc(doctype, name); - if(doc.task_id) { - frappe.set_route("Form", "Task", doc.task_id); - } else { - frappe.msgprint(__("Save the document first.")); - } - }, - - edit_timesheet: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - frappe.route_options = {"project": frm.doc.project_name, "task": child.task_id}; - frappe.set_route("List", "Timesheet"); - }, - - make_timesheet: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - frappe.model.with_doctype('Timesheet', function() { - var doc = frappe.model.get_new_doc('Timesheet'); - var row = frappe.model.add_child(doc, 'time_logs'); - row.project = frm.doc.project_name; - row.task = child.task_id; - frappe.set_route('Form', doc.doctype, doc.name); - }) - }, - - status: function(frm, doctype, name) { - frm.trigger('tasks_refresh'); - }, }); diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 2fc507b252c..b4536c085c5 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 55a689259ae..6176cf89b4f 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -19,10 +19,6 @@ class Project(Document): return '{0}: {1}'.format(_(self.status), frappe.safe_decode(self.project_name)) def onload(self): - """Load project tasks for quick view""" - if not self.get('__unsaved') and not self.get("tasks"): - self.load_tasks() - self.set_onload('activity_summary', frappe.db.sql('''select activity_type, sum(hours) as total_hours from `tabTimesheet Detail` where project=%s and docstatus < 2 group by activity_type @@ -33,57 +29,19 @@ class Project(Document): def before_print(self): self.onload() - def load_tasks(self): - """Load `tasks` from the database""" - if frappe.flags.in_import: - return - project_task_custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task"}, "fieldname") - - self.tasks = [] - for task in self.get_tasks(): - task_map = { - "title": task.subject, - "status": task.status, - "start_date": task.exp_start_date, - "end_date": task.exp_end_date, - "description": task.description, - "task_id": task.name, - "task_weight": task.task_weight - } - - self.map_custom_fields(task, task_map, project_task_custom_fields) - - self.append("tasks", task_map) - - def get_tasks(self): - if self.name is None: - return {} - else: - filters = {"project": self.name} - - if self.get("deleted_task_list"): - filters.update({ - 'name': ("not in", self.deleted_task_list) - }) - - return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc, status asc") def validate(self): - self.validate_weights() - self.sync_tasks() - self.tasks = [] - self.load_tasks() if not self.is_new(): self.copy_from_template() - self.validate_dates() self.send_welcome_email() - self.update_percent_complete(from_validate=True) + self.update_costing() + self.update_percent_complete() def copy_from_template(self): ''' Copy tasks from template ''' - if self.project_template and not len(self.tasks or []): + if self.project_template and not frappe.db.get_all('Task', dict(project = self.name), limit=1): # has a template, and no loaded tasks, so lets create if not self.expected_start_date: @@ -108,104 +66,6 @@ class Project(Document): task_weight = task.task_weight )).insert() - # reload tasks after project - self.load_tasks() - - def validate_dates(self): - if self.tasks: - for d in self.tasks: - if self.expected_start_date: - if d.start_date and getdate(d.start_date) < getdate(self.expected_start_date): - frappe.throw(_("Start date of task {0} cannot be less than {1} expected start date {2}") - .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 {0} cannot be less than {1} expected start date {2}") - .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 {0} cannot be greater than {1} expected end date {2}") - .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 {0} cannot be greater than {1} expected end date {2}") - .format(d.title, self.name, self.expected_end_date)) - - if self.expected_start_date and self.expected_end_date: - if getdate(self.expected_end_date) < getdate(self.expected_start_date): - frappe.throw(_("Expected End Date can not be less than Expected Start Date")) - - def validate_weights(self): - for task in self.tasks: - if task.task_weight is not None: - if task.task_weight < 0: - frappe.throw(_("Task weight cannot be negative")) - - def sync_tasks(self): - """sync tasks and remove table""" - if not hasattr(self, "deleted_task_list"): - self.set("deleted_task_list", []) - - if self.flags.dont_sync_tasks: return - task_names = [] - - existing_task_data = {} - - fields = ["title", "status", "start_date", "end_date", "description", "task_weight", "task_id"] - exclude_fieldtype = ["Button", "Column Break", - "Section Break", "Table", "Read Only", "Attach", "Attach Image", "Color", "Geolocation", "HTML", "Image"] - - custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task", - "fieldtype": ("not in", exclude_fieldtype)}, "fieldname") - - for d in custom_fields: - fields.append(d.fieldname) - - for d in frappe.get_all('Project Task', - fields = fields, - filters = {'parent': self.name}): - existing_task_data.setdefault(d.task_id, d) - - for t in self.tasks: - if t.task_id: - task = frappe.get_doc("Task", t.task_id) - else: - task = frappe.new_doc("Task") - task.project = self.name - - if not t.task_id or self.is_row_updated(t, existing_task_data, fields): - task.update({ - "subject": t.title, - "status": t.status, - "exp_start_date": t.start_date, - "exp_end_date": t.end_date, - "description": t.description, - "task_weight": t.task_weight - }) - - self.map_custom_fields(t, task, custom_fields) - - task.flags.ignore_links = True - task.flags.from_project = True - task.flags.ignore_feed = True - - if t.task_id: - task.update({ - "modified_by": frappe.session.user, - "modified": now() - }) - - task.run_method("validate") - task.db_update() - else: - task.save(ignore_permissions = True) - task_names.append(task.name) - else: - task_names.append(task.name) - - # delete - for t in frappe.get_all("Task", ["name"], {"project": self.name, "name": ("not in", task_names)}): - self.deleted_task_list.append(t.name) - def is_row_updated(self, row, existing_task_data, fields): if self.get("__islocal") or not existing_task_data: return True @@ -215,48 +75,43 @@ class Project(Document): if row.get(field) != d.get(field): return True - def map_custom_fields(self, source, target, custom_fields): - for field in custom_fields: - target.update({ - field.fieldname: source.get(field.fieldname) - }) - def update_project(self): + '''Called externally by Task''' self.update_percent_complete() self.update_costing() + self.db_update() def after_insert(self): self.copy_from_template() if self.sales_order: frappe.db.set_value("Sales Order", self.sales_order, "project", self.name) - def update_percent_complete(self, from_validate=False): - if not self.tasks: return - total = frappe.db.sql("""select count(name) from tabTask where project=%s""", self.name)[0][0] + def update_percent_complete(self): + total = frappe.db.count('Task', dict(project=self.name)) - if not total and self.percent_complete: + if not total: self.percent_complete = 0 + else: + if (self.percent_complete_method == "Task Completion" and total > 0) or ( + not self.percent_complete_method and total > 0): + completed = frappe.db.sql("""select count(name) from tabTask where + project=%s and status in ('Cancelled', 'Completed')""", self.name)[0][0] + self.percent_complete = flt(flt(completed) / total * 100, 2) - if (self.percent_complete_method == "Task Completion" and total > 0) or ( - not self.percent_complete_method and total > 0): - completed = frappe.db.sql("""select count(name) from tabTask where - project=%s and status in ('Cancelled', 'Completed')""", self.name)[0][0] - self.percent_complete = flt(flt(completed) / total * 100, 2) + if (self.percent_complete_method == "Task Progress" and total > 0): + progress = frappe.db.sql("""select sum(progress) from tabTask where + project=%s""", self.name)[0][0] + self.percent_complete = flt(flt(progress) / total, 2) - if (self.percent_complete_method == "Task Progress" and total > 0): - progress = frappe.db.sql("""select sum(progress) from tabTask where - project=%s""", self.name)[0][0] - self.percent_complete = flt(flt(progress) / total, 2) - - if (self.percent_complete_method == "Task Weight" and total > 0): - weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where - project=%s""", self.name)[0][0] - weighted_progress = frappe.db.sql("""select progress, task_weight from tabTask where - project=%s""", self.name, as_dict=1) - pct_complete = 0 - for row in weighted_progress: - pct_complete += row["progress"] * frappe.utils.safe_div(row["task_weight"], weight_sum) - self.percent_complete = flt(flt(pct_complete), 2) + if (self.percent_complete_method == "Task Weight" and total > 0): + weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where + project=%s""", self.name)[0][0] + weighted_progress = frappe.db.sql("""select progress, task_weight from tabTask where + project=%s""", self.name, as_dict=1) + pct_complete = 0 + for row in weighted_progress: + pct_complete += row["progress"] * frappe.utils.safe_div(row["task_weight"], weight_sum) + self.percent_complete = flt(flt(pct_complete), 2) # don't update status if it is cancelled if self.status == 'Cancelled': @@ -268,9 +123,6 @@ class Project(Document): else: self.status = "Open" - if not from_validate: - self.db_update() - def update_costing(self): from_time_sheet = frappe.db.sql("""select sum(costing_amount) as costing_amount, @@ -297,7 +149,6 @@ class Project(Document): self.update_sales_amount() self.update_billed_amount() self.calculate_gross_margin() - self.db_update() def calculate_gross_margin(self): expense_amount = (flt(self.total_costing_amount) + flt(self.total_expense_claim) @@ -348,57 +199,6 @@ class Project(Document): content=content.format(*messages)) user.welcome_email_sent = 1 - def on_update(self): - self.delete_task() - self.load_tasks() - self.update_project() - self.update_dependencies_on_duplicated_project() - - def delete_task(self): - if not self.get('deleted_task_list'): return - - for d in self.get('deleted_task_list'): - # unlink project - frappe.db.set_value('Task', d, 'project', '') - - self.deleted_task_list = [] - - def update_dependencies_on_duplicated_project(self): - if self.flags.dont_sync_tasks: return - if not self.copied_from: - self.copied_from = self.name - - if self.name != self.copied_from and self.get('__unsaved'): - # duplicated project - dependency_map = {} - for task in self.tasks: - _task = frappe.db.get_value( - 'Task', - {"subject": task.title, "project": self.copied_from}, - ['name', 'depends_on_tasks'], - as_dict=True - ) - - if _task is None: - continue - - name = _task.name - - dependency_map[task.title] = [x['subject'] for x in frappe.get_list( - 'Task Depends On', {"parent": name}, ['subject'])] - - for key, value in iteritems(dependency_map): - task_name = frappe.db.get_value('Task', {"subject": key, "project": self.name }) - - task_doc = frappe.get_doc('Task', task_name) - - for dt in value: - dt_name = frappe.db.get_value('Task', {"subject": dt, "project": self.name}) - task_doc.append('depends_on', {"task": dt_name}) - - task_doc.db_update() - - def get_timeline_data(doctype, name): '''Return timeline for attendance''' return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*) diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py index beb1f130f5b..06c62b62d2f 100644 --- a/erpnext/projects/doctype/project/test_project.py +++ b/erpnext/projects/doctype/project/test_project.py @@ -19,18 +19,18 @@ class TestProject(unittest.TestCase): project = get_project('Test Project with Template') - project.load_tasks() + tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc') - task1 = project.tasks[0] - self.assertEqual(task1.title, 'Task 1') + task1 = tasks[0] + self.assertEqual(task1.subject, 'Task 1') self.assertEqual(task1.description, 'Task 1 description') - self.assertEqual(getdate(task1.start_date), getdate('2019-01-01')) - self.assertEqual(getdate(task1.end_date), getdate('2019-01-04')) + self.assertEqual(getdate(task1.exp_start_date), getdate('2019-01-01')) + self.assertEqual(getdate(task1.exp_end_date), getdate('2019-01-04')) - self.assertEqual(len(project.tasks), 4) - task4 = project.tasks[3] - self.assertEqual(task4.title, 'Task 4') - self.assertEqual(getdate(task4.end_date), getdate('2019-01-06')) + self.assertEqual(len(tasks), 4) + task4 = tasks[3] + self.assertEqual(task4.subject, 'Task 4') + self.assertEqual(getdate(task4.exp_end_date), getdate('2019-01-06')) def get_project(name): template = get_project_template() diff --git a/erpnext/projects/doctype/project/test_records.json b/erpnext/projects/doctype/project/test_records.json index 9379c22b5c6..567f359b50d 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 e69de29bb2d..00000000000 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 e26c191e0b8..00000000000 --- 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 5f9d8d76229..00000000000 --- 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 d8fc199ec24..50557f1551d 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -158,12 +158,6 @@ class Task(NestedSet): if check_if_child_exists(self.name): throw(_("Child Task exists for this Task. You can not delete this Task.")) - if self.project: - tasks = frappe.get_doc('Project', self.project).tasks - for task in tasks: - if task.get('task_id') == self.name: - frappe.delete_doc('Project Task', task.name) - self.update_nsm_model() def update_status(self): diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 8ad3bf06071..6c0b02dd483 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -547,12 +547,6 @@ def make_project(source_name, target_doc=None): "base_grand_total" : "estimated_costing", } }, - "Sales Order Item": { - "doctype": "Project Task", - "field_map": { - "item_code": "title", - }, - } }, target_doc, postprocess) return doc From 6a7969117f1ec438f25ec5f8bfbbec10a04ef01d Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 8 Jul 2019 10:40:40 +0530 Subject: [PATCH 054/174] fix(bom): escape name with wildcard character (#18164) --- erpnext/controllers/queries.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index d74bc0ea18c..47c9f0a4ce3 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -206,10 +206,11 @@ def bom(doctype, txt, searchfield, start, page_len, filters): if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), idx desc, name limit %(start)s, %(page_len)s """.format( - fcond=get_filters_cond(doctype, filters, conditions), + fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'), mcond=get_match_cond(doctype), - key=searchfield), { - 'txt': '%' + txt + '%', + key=frappe.db.escape(searchfield)), + { + 'txt': "%"+frappe.db.escape(txt)+"%", '_txt': txt.replace("%", ""), 'start': start or 0, 'page_len': page_len or 20 From de13faf19a123998fa9dd1578b4d2d3d966faddc Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 9 Jul 2019 11:53:31 +0530 Subject: [PATCH 055/174] fix: sending sms from quotation --- erpnext/public/js/sms_manager.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/sms_manager.js b/erpnext/public/js/sms_manager.js index 6ce8bb11500..a7003a272d0 100644 --- a/erpnext/public/js/sms_manager.js +++ b/erpnext/public/js/sms_manager.js @@ -20,8 +20,10 @@ erpnext.SMSManager = function SMSManager(doc) { '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 (in_list(['Quotation'], doc.doctype)) + 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') From 890ea195f3cc6487ab0809926c3a3cbea2837d12 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Tue, 9 Jul 2019 16:56:56 +0530 Subject: [PATCH 056/174] fix: Tree filter fixes for erpnext dimensions --- erpnext/public/js/utils/dimension_tree_filter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index a9122d8dff1..549f95e039f 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -12,7 +12,7 @@ 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')) { From e9dd9b842e289a9683743a92b8974cba2c968df8 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 9 Jul 2019 17:54:00 +0530 Subject: [PATCH 057/174] Update erpnext/public/js/sms_manager.js Co-Authored-By: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- erpnext/public/js/sms_manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/sms_manager.js b/erpnext/public/js/sms_manager.js index a7003a272d0..a058da23ac4 100644 --- a/erpnext/public/js/sms_manager.js +++ b/erpnext/public/js/sms_manager.js @@ -22,7 +22,7 @@ erpnext.SMSManager = function SMSManager(doc) { 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 (in_list(['Quotation'], 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]); From 444091b35013f9747a2fa4048f4388cf4d0154a5 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 10 Jul 2019 10:22:08 +0530 Subject: [PATCH 058/174] refactor: disable quick entry for batched and serialized items --- erpnext/stock/dashboard/item_dashboard.js | 102 +++++++++++++++++- erpnext/stock/dashboard/item_dashboard.py | 4 +- .../stock/dashboard/item_dashboard_list.html | 2 + 3 files changed, 104 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index ed325a16b8a..c84acc54b37 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -19,13 +19,24 @@ erpnext.stock.ItemDashboard = Class.extend({ this.content.on('click', '.btn-move', function() { let item = unescape($(this).attr('data-item')); let warehouse = unescape($(this).attr('data-warehouse')); - open_stock_entry(item, warehouse, "Material Transfer"); + let actual_qty = unescape($(this).attr('data-actual_qty')); + let disable_quick_entry = Number(unescape($(this).attr('data-disable_quick_entry'))); + + if (disable_quick_entry) open_stock_entry(item, warehouse, "Material Transfer"); + + else erpnext.stock.move_item(item, warehouse, null, actual_qty, null, function() { me.refresh(); }) }); this.content.on('click', '.btn-add', function() { let item = unescape($(this).attr('data-item')); let warehouse = unescape($(this).attr('data-warehouse')); - open_stock_entry(item, warehouse); + let actual_qty = unescape($(this).attr('data-actual_qty')); + let disable_quick_entry = Number(unescape($(this).attr('data-disable_quick_entry'))); + let rate = unescape($(this).attr('data-rate')); + + if (disable_quick_entry) open_stock_entry(item, warehouse); + + else erpnext.stock.move_item(item, null, warehouse, actual_qty, rate, function() { me.refresh(); }) }); function open_stock_entry(item, warehouse, entry_type) { @@ -124,4 +135,89 @@ erpnext.stock.ItemDashboard = Class.extend({ show_item: show_item || false } } -}) \ No newline at end of file +}) + +erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callback) { + var dialog = new frappe.ui.Dialog({ + title: target ? __('Add Item') : __('Move Item'), + fields: [ + {fieldname: 'item_code', label: __('Item'), + fieldtype: 'Link', options: 'Item', read_only: 1}, + {fieldname: 'source', label: __('Source Warehouse'), + fieldtype: 'Link', options: 'Warehouse', read_only: 1}, + {fieldname: 'target', label: __('Target Warehouse'), + fieldtype: 'Link', options: 'Warehouse', reqd: 1}, + {fieldname: 'qty', label: __('Quantity'), reqd: 1, + fieldtype: 'Float', description: __('Available {0}', [actual_qty]) }, + {fieldname: 'rate', label: __('Rate'), fieldtype: 'Currency', hidden: 1 }, + ], + }) + dialog.show(); + dialog.get_field('item_code').set_input(item); + + if(source) { + dialog.get_field('source').set_input(source); + } else { + dialog.get_field('source').df.hidden = 1; + dialog.get_field('source').refresh(); + } + + if(rate) { + dialog.get_field('rate').set_value(rate); + dialog.get_field('rate').df.hidden = 0; + dialog.get_field('rate').refresh(); + } + + if(target) { + dialog.get_field('target').df.read_only = 1; + dialog.get_field('target').value = target; + dialog.get_field('target').refresh(); + } + + dialog.set_primary_action(__('Submit'), function() { + var values = dialog.get_values(); + if(!values) { + return; + } + if(source && values.qty > actual_qty) { + frappe.msgprint(__('Quantity must be less than or equal to {0}', [actual_qty])); + return; + } + if(values.source === values.target) { + frappe.msgprint(__('Source and target warehouse must be different')); + } + + frappe.call({ + method: 'erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry', + args: values, + freeze: true, + callback: function(r) { + frappe.show_alert(__('Stock Entry {0} created', + ['' + r.message.name+ ''])); + dialog.hide(); + callback(r); + }, + }); + }); + + $('

' + + __("Add more items or open full form") + '

') + .appendTo(dialog.body) + .find('.link-open') + .on('click', function() { + frappe.model.with_doctype('Stock Entry', function() { + var doc = frappe.model.get_new_doc('Stock Entry'); + doc.from_warehouse = dialog.get_value('source'); + doc.to_warehouse = dialog.get_value('target'); + var row = frappe.model.add_child(doc, 'items'); + row.item_code = dialog.get_value('item_code'); + row.f_warehouse = dialog.get_value('target'); + row.t_warehouse = dialog.get_value('target'); + row.qty = dialog.get_value('qty'); + row.conversion_factor = 1; + row.transfer_qty = dialog.get_value('qty'); + row.basic_rate = dialog.get_value('rate'); + frappe.set_route('Form', doc.doctype, doc.name); + }) + }); +} \ No newline at end of file diff --git a/erpnext/stock/dashboard/item_dashboard.py b/erpnext/stock/dashboard/item_dashboard.py index 487c7656595..7634ff0a281 100644 --- a/erpnext/stock/dashboard/item_dashboard.py +++ b/erpnext/stock/dashboard/item_dashboard.py @@ -44,7 +44,9 @@ def get_data(item_code=None, warehouse=None, item_group=None, for item in items: item.update({ - 'item_name': frappe.get_cached_value("Item", item.item_code, 'item_name') + 'item_name': frappe.get_cached_value("Item", item.item_code, 'item_name'), + 'disable_quick_entry': frappe.get_cached_value("Item", item.item_code, 'has_batch_no') + or frappe.get_cached_value("Item", item.item_code, 'has_serial_no'), }) return items diff --git a/erpnext/stock/dashboard/item_dashboard_list.html b/erpnext/stock/dashboard/item_dashboard_list.html index 5a3fa2ed485..e1914ed76a2 100644 --- a/erpnext/stock/dashboard/item_dashboard_list.html +++ b/erpnext/stock/dashboard/item_dashboard_list.html @@ -43,11 +43,13 @@
{% if d.actual_qty %}