From 5d5f026a0d4ac2d088b722f254f7b58db27b8a4f Mon Sep 17 00:00:00 2001 From: Anupam K Date: Fri, 24 Jul 2020 02:32:45 +0530 Subject: [PATCH 01/23] fix: Target variance report signs --- .../item_group_wise_sales_target_variance.py | 20 +++++++++---------- ...ner_target_variance_based_on_item_group.js | 17 +++++++++++++++- ...son_target_variance_based_on_item_group.js | 17 +++++++++++++++- ...ory_target_variance_based_on_item_group.js | 17 +++++++++++++++- 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py index 857b9823e0..ae216ca5d6 100644 --- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py +++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py @@ -63,13 +63,13 @@ def get_columns(filters, period_list, partner_doctype): "label": _(partner_doctype), "fieldtype": "Link", "options": partner_doctype, - "width": 100 + "width": 150 }, { "fieldname": "item_group", "label": _("Item Group"), "fieldtype": "Link", "options": "Item Group", - "width": 100 + "width": 150 }] for period in period_list: @@ -81,19 +81,19 @@ def get_columns(filters, period_list, partner_doctype): "label": _("Target ({})").format(period.label), "fieldtype": fieldtype, "options": options, - "width": 100 + "width": 150 }, { "fieldname": period.key, "label": _("Achieved ({})").format(period.label), "fieldtype": fieldtype, "options": options, - "width": 100 + "width": 150 }, { "fieldname": variance_key, "label": _("Variance ({})").format(period.label), "fieldtype": fieldtype, "options": options, - "width": 100 + "width": 150 }]) columns.extend([{ @@ -101,19 +101,19 @@ def get_columns(filters, period_list, partner_doctype): "label": _("Total Target"), "fieldtype": fieldtype, "options": options, - "width": 100 + "width": 150 }, { "fieldname": "total_achieved", "label": _("Total Achieved"), "fieldtype": fieldtype, "options": options, - "width": 100 + "width": 150 }, { "fieldname": "total_variance", "label": _("Total Variance"), "fieldtype": fieldtype, "options": options, - "width": 100 + "width": 150 }]) return columns @@ -154,10 +154,10 @@ def prepare_data(filters, sales_users_data, actual_data, date_field, period_list if (r.get(sales_field) == d.parent and r.item_group == d.item_group and period.from_date <= r.get(date_field) and r.get(date_field) <= period.to_date): details[p_key] += r.get(qty_or_amount_field, 0) - details[variance_key] = details.get(target_key) - details.get(p_key) + details[variance_key] = details.get(p_key) - details.get(target_key) details["total_achieved"] += details.get(p_key) - details["total_variance"] = details.get("total_target") - details.get("total_achieved") + details["total_variance"] = details.get("total_achieved") - details.get("total_target") return rows diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js index f99f68c524..38bb127e23 100644 --- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js +++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js @@ -44,5 +44,20 @@ frappe.query_reports["Sales Partner Target Variance based on Item Group"] = { options: "Quantity\nAmount", default: "Quantity" }, - ] + ], + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (column.fieldname.includes('variance')) { + + if (data[column.fieldname] < 0) { + value = "" + value + ""; + } + else if (data[column.fieldname] > 0) { + value = "" + value + ""; + } + } + + return value; + } } diff --git a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js index 9f6bfc41df..a8e2fad373 100644 --- a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js +++ b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js @@ -44,5 +44,20 @@ frappe.query_reports["Sales Person Target Variance Based On Item Group"] = { options: "Quantity\nAmount", default: "Quantity" }, - ] + ], + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (column.fieldname.includes('variance')) { + + if (data[column.fieldname] < 0) { + value = "" + value + ""; + } + else if (data[column.fieldname] > 0) { + value = "" + value + ""; + } + } + + return value; + } } diff --git a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js index dd9607ffbd..263391a7f7 100644 --- a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js +++ b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js @@ -44,5 +44,20 @@ frappe.query_reports["Territory Target Variance Based On Item Group"] = { options: "Quantity\nAmount", default: "Quantity" }, - ] + ], + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (column.fieldname.includes('variance')) { + + if (data[column.fieldname] < 0) { + value = "" + value + ""; + } + else if (data[column.fieldname] > 0) { + value = "" + value + ""; + } + } + + return value; + } } From 8718daa11e11c17ec64d550df661b3f3ac7c2eac Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sat, 25 Jul 2020 23:56:11 +0530 Subject: [PATCH 02/23] Adding formatter in budget variance report --- .../budget_variance_report.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) 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 3ec4d306c3..30415d1e65 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js @@ -71,7 +71,22 @@ frappe.query_reports["Budget Variance Report"] = { fieldtype: "Check", default: 0, }, - ] + ], + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (column.fieldname.includes('variance')) { + + if (data[column.fieldname] < 0) { + value = "" + value + ""; + } + else if (data[column.fieldname] > 0) { + value = "" + value + ""; + } + } + + return value; + } } erpnext.dimension_filters.forEach((dimension) => { From bfb219612e143139b535d273556898b7d0ecaeaf Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 13 Aug 2020 11:34:05 +0530 Subject: [PATCH 03/23] feat: enable total row in Gross Profit Report --- .../report/gross_profit/gross_profit.json | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.json b/erpnext/accounts/report/gross_profit/gross_profit.json index 9cfb0627d3..cd6bac2d77 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.json +++ b/erpnext/accounts/report/gross_profit/gross_profit.json @@ -1,24 +1,23 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2013-02-25 17:03:34", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2017-02-24 20:12:22.464240", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Gross Profit", - "owner": "Administrator", - "ref_doctype": "Sales Invoice", - "report_name": "Gross Profit", - "report_type": "Script Report", + "add_total_row": 1, + "creation": "2013-02-25 17:03:34", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 3, + "is_standard": "Yes", + "modified": "2020-08-13 11:26:39.112352", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Gross Profit", + "owner": "Administrator", + "ref_doctype": "Sales Invoice", + "report_name": "Gross Profit", + "report_type": "Script Report", "roles": [ { "role": "Accounts Manager" - }, + }, { "role": "Accounts User" } From b3bd780d46cd9d9d48033d9bb8b074a25942e598 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Thu, 13 Aug 2020 17:07:15 +0530 Subject: [PATCH 04/23] fix: codasy dix --- .../report/budget_variance_report/budget_variance_report.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 30415d1e65..f547ca619b 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js @@ -74,9 +74,9 @@ frappe.query_reports["Budget Variance Report"] = { ], "formatter": function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - + if (column.fieldname.includes('variance')) { - + if (data[column.fieldname] < 0) { value = "" + value + ""; } From d7b69e57aeb62cd5ea5dc83a9702b8072e755313 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Mon, 17 Aug 2020 11:20:56 +0530 Subject: [PATCH 05/23] fix: deleting unused Stock Entry Type --- erpnext/patches/v13_0/stock_entry_enhancements.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/stock_entry_enhancements.py b/erpnext/patches/v13_0/stock_entry_enhancements.py index dcc4f956f7..0bdcc9c0e8 100644 --- a/erpnext/patches/v13_0/stock_entry_enhancements.py +++ b/erpnext/patches/v13_0/stock_entry_enhancements.py @@ -24,4 +24,8 @@ def execute(): if not frappe.db.exists('Warehouse Type', 'Transit'): doc = frappe.new_doc('Warehouse Type') doc.name = 'Transit' - doc.insert() \ No newline at end of file + doc.insert() + + frappe.reload_doc("stock", "doctype", "stock_entry_type") + frappe.delete_doc_if_exists("Stock Entry Type", "Send to Warehouse") + frappe.delete_doc_if_exists("Stock Entry Type", "Receive at Warehouse") \ No newline at end of file From eb241c6c424e7bbdd50c33edf31a8883c1126686 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 17 Aug 2020 12:44:52 +0530 Subject: [PATCH 06/23] fix: General Ledger filter validation --- .../accounts/report/general_ledger/general_ledger.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index fcd36e4e6e..779cdbde9b 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -43,8 +43,11 @@ def execute(filters=None): def validate_filters(filters, account_details): - if not filters.get('company'): - frappe.throw(_('{0} is mandatory').format(_('Company'))) + if not filters.get("company"): + frappe.throw(_("{0} is mandatory").format(_("Company"))) + + if not filters.get("from_date") and not filters.get("to_date"): + frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date")))) if filters.get("account") and not account_details.get(filters.account): frappe.throw(_("Account {0} does not exists").format(filters.account)) @@ -132,7 +135,7 @@ def get_gl_entries(filters): if filters and filters.get('cost_center'): select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency, credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency """ - + distributed_cost_center_query = """ UNION ALL SELECT name as gl_entry, @@ -146,7 +149,7 @@ def get_gl_entries(filters): against_voucher_type, against_voucher, account_currency, - remarks, against, + remarks, against, is_opening, `tabGL Entry`.creation {select_fields_with_percentage} FROM `tabGL Entry`, ( From 205df14a480fb305504eca16957c891c99f079e2 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 17 Aug 2020 16:23:44 +0530 Subject: [PATCH 07/23] fix: Credit Limit Email not working --- erpnext/selling/doctype/customer/customer.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 93d4832173..1f955fcd52 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -184,10 +184,10 @@ class Customer(TransactionBase): def validate_credit_limit_on_change(self): if self.get("__islocal") or not self.credit_limits: return - + past_credit_limits = [d.credit_limit for d in frappe.db.get_all("Customer Credit Limit", filters={'parent': self.name}, fields=["credit_limit"], order_by="company")] - + current_credit_limits = [d.credit_limit for d in sorted(self.credit_limits, key=lambda k: k.company)] if past_credit_limits == current_credit_limits: @@ -396,13 +396,12 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, credit_controller_users = get_users_with_role(credit_controller_role or "Sales Master Manager") # form a list of emails and names to show to the user - credit_controller_users = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users] - - if not credit_controller_users: + credit_controller_users_formatted = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users] + if not credit_controller_users_formatted: frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer))) message = """Please contact any of the following users to extend the credit limits for {0}: -

  • {1}
""".format(customer, '
  • '.join(credit_controller_users)) +

    • {1}
    """.format(customer, '
  • '.join(credit_controller_users_formatted)) # if the current user does not have permissions to override credit limit, # prompt them to send out an email to the controller users @@ -427,7 +426,7 @@ def send_emails(args): subject = (_("Credit limit reached for customer {0}").format(args.get('customer'))) message = (_("Credit limit has been crossed for customer {0} ({1}/{2})") .format(args.get('customer'), args.get('customer_outstanding'), args.get('credit_limit'))) - frappe.sendmail(recipients=[args.get('credit_controller_users_list')], subject=subject, message=message) + frappe.sendmail(recipients=args.get('credit_controller_users_list'), subject=subject, message=message) def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None): # Outstanding based on GL Entries From e7ab53a711a822e801f4d9c2c8b1006a5e4c3c9c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 22 Jun 2020 12:36:23 +0530 Subject: [PATCH 08/23] fix: completed qty not updated in work order --- erpnext/manufacturing/doctype/job_card/job_card.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index c29d4ba3d5..8dd90e612b 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -233,7 +233,7 @@ class JobCard(Document): work_order_field = "name" if field == "operation_id" else field for data in wo.operations: - if data.get(work_order_field) == self.get(field) and data.workstation == self.workstation: + if data.get(work_order_field) == self.get(field): data.completed_qty = for_quantity data.actual_operation_time = time_in_mins data.actual_start_time = time_data[0].start_time if time_data else None From a5963e1b2c94cb7f028f49cb2957154c76cff9a4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 24 Aug 2020 17:55:23 +0530 Subject: [PATCH 09/23] fix: user created manual job card not linking job card operations with work order operations --- .../doctype/job_card/job_card.js | 61 ++++++++++++++++++- .../doctype/job_card/job_card.json | 10 ++- .../doctype/job_card/job_card.py | 34 +++++++++++ .../doctype/job_card/test_job_card.py | 24 +++++++- 4 files changed, 124 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index bab0dfb6b4..7ac0b11ecb 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -2,6 +2,17 @@ // For license information, please see license.txt frappe.ui.form.on('Job Card', { + setup: function(frm) { + frm.set_query('operation', function() { + return { + query: 'erpnext.manufacturing.doctype.job_card.job_card.get_operations', + filters: { + 'work_order': frm.doc.work_order + } + }; + }); + }, + refresh: function(frm) { frappe.flags.pause_job = 0; frappe.flags.resume_job = 0; @@ -20,12 +31,60 @@ frappe.ui.form.on('Job Card', { } } + frm.trigger("toggle_operation_number"); + if (frm.doc.docstatus == 0 && (frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity) - && (!frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) { + && (!frm.doc.items || !frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) { frm.trigger("prepare_timer_buttons"); } }, + operation: function(frm) { + frm.trigger("toggle_operation_number"); + + if (frm.doc.operation && frm.doc.work_order) { + frappe.call({ + method: "erpnext.manufacturing.doctype.job_card.job_card.get_operation_details", + args: { + "work_order":frm.doc.work_order, + "operation":frm.doc.operation + }, + callback: function (r) { + if (r.message) { + if (r.message.length == 1) { + frm.set_value("operation_id", r.message[0].name); + } else { + let args = []; + + r.message.forEach((row) => { + args.push({ "label": row.idx, "value": row.name }); + }); + + let description = __("Operation {0} added multiple times in the work order {1}", + [frm.doc.operation, frm.doc.work_order]); + + frm.set_df_property("operation_row_number", "options", args); + frm.set_df_property("operation_row_number", "description", description); + } + + frm.trigger("toggle_operation_number"); + } + } + }) + } + }, + + operation_row_number(frm) { + if (frm.doc.operation_row_number) { + frm.set_value("operation_id", frm.doc.operation_row_number); + } + }, + + toggle_operation_number(frm) { + frm.toggle_display("operation_row_number", !frm.doc.operation_id && frm.doc.operation); + frm.toggle_reqd("operation_row_number", !frm.doc.operation_id && frm.doc.operation); + }, + prepare_timer_buttons: function(frm) { frm.trigger("make_dashboard"); if (!frm.doc.job_started) { diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json index fba670c1c1..087ab6b484 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.json +++ b/erpnext/manufacturing/doctype/job_card/job_card.json @@ -11,6 +11,7 @@ "bom_no", "workstation", "operation", + "operation_row_number", "column_break_4", "posting_date", "company", @@ -291,11 +292,15 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "operation_row_number", + "fieldtype": "Select", + "label": "Operation Row Number" } ], "is_submittable": 1, - "links": [], - "modified": "2020-04-20 15:14:00.273441", + "modified": "2020-08-24 15:21:21.398267", "modified_by": "Administrator", "module": "Manufacturing", "name": "Job Card", @@ -347,7 +352,6 @@ "write": 1 } ], - "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", "title_field": "operation", diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 8dd90e612b..a87e6e5037 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -15,10 +15,13 @@ from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings class OverlapError(frappe.ValidationError): pass +class OperationMismatchError(frappe.ValidationError): pass + class JobCard(Document): def validate(self): self.validate_time_logs() self.set_status() + self.validate_operation_id() def validate_time_logs(self): self.total_completed_qty = 0.0 @@ -306,6 +309,37 @@ class JobCard(Document): if update_status: self.db_set('status', self.status) + def validate_operation_id(self): + if (self.get("operation_id") and self.get("operation_row_number") and self.operation and self.work_order and + frappe.get_cached_value("Work Order Operation", self.operation_row_number, "name") != self.operation_id): + work_order = frappe.bold(get_link_to_form("Work Order", self.work_order)) + frappe.throw(_("Operation {0} does not belong to the work order {1}") + .format(frappe.bold(self.operation), work_order), OperationMismatchError) + +@frappe.whitelist() +def get_operation_details(work_order, operation): + if work_order and operation: + return frappe.get_all("Work Order Operation", fields = ["name", "idx"], + filters = { + "parent": work_order, + "operation": operation + } + ) + +@frappe.whitelist() +def get_operations(doctype, txt, searchfield, start, page_len, filters): + if filters.get("work_order"): + args = {"parent": filters.get("work_order")} + if txt: + args["operation"] = ("like", "%{0}%".format(txt)) + + return frappe.get_all("Work Order Operation", + filters = args, + fields = ["distinct operation as operation"], + limit_start = start, + limit_page_length = page_len, + order_by="idx asc", as_list=1) + @frappe.whitelist() def make_material_request(source_name, target_doc=None): def update_item(obj, target, source_parent): diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py index ca05fea0f6..2a6c35fc04 100644 --- a/erpnext/manufacturing/doctype/job_card/test_job_card.py +++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py @@ -4,6 +4,28 @@ from __future__ import unicode_literals import unittest +import frappe +from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record +from erpnext.manufacturing.doctype.job_card.job_card import OperationMismatchError class TestJobCard(unittest.TestCase): - pass + def test_job_card(self): + data = frappe.get_cached_value('BOM', + {'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item']) + + if data: + bom, bom_item = data + + work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom) + + job_cards = frappe.get_all('Job Card', + filters = {'work_order': work_order.name}, fields = ["operation_id", "name"]) + + if job_cards: + job_card = job_cards[0] + frappe.db.set_value("Job Card", job_card.name, "operation_row_number", job_card.operation_id) + + doc = frappe.get_doc("Job Card", job_card.name) + doc.operation_id = "Test Data" + self.assertRaises(OperationMismatchError, doc.save) + From d0aafece32bc779963cb28951cf73617f1d36ef4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 31 Aug 2020 11:57:57 +0530 Subject: [PATCH 10/23] fix: incorrect completed qty against operation in work order if workstation is different in job card --- .../doctype/job_card/job_card.py | 12 +++---- .../doctype/job_card/test_job_card.py | 36 +++++++++++++++++++ .../doctype/workstation/test_workstation.py | 15 ++++++++ 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index a87e6e5037..8855e0acf5 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -212,11 +212,10 @@ class JobCard(Document): for_quantity, time_in_mins = 0, 0 from_time_list, to_time_list = [], [] - field = "operation_id" if self.operation_id else "operation" + field = "operation_id" data = frappe.get_all('Job Card', fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"], - filters = {"docstatus": 1, "work_order": self.work_order, - "workstation": self.workstation, field: self.get(field)}) + filters = {"docstatus": 1, "work_order": self.work_order, field: self.get(field)}) if data and len(data) > 0: for_quantity = data[0].completed_qty @@ -229,14 +228,13 @@ class JobCard(Document): FROM `tabJob Card` jc, `tabJob Card Time Log` jctl WHERE jctl.parent = jc.name and jc.work_order = %s - and jc.workstation = %s and jc.{0} = %s and jc.docstatus = 1 - """.format(field), (self.work_order, self.workstation, self.get(field)), as_dict=1) + and jc.{0} = %s and jc.docstatus = 1 + """.format(field), (self.work_order, self.get(field)), as_dict=1) wo = frappe.get_doc('Work Order', self.work_order) - work_order_field = "name" if field == "operation_id" else field for data in wo.operations: - if data.get(work_order_field) == self.get(field): + if data.get("name") == self.get(field): data.completed_qty = for_quantity data.actual_operation_time = time_in_mins data.actual_start_time = time_data[0].start_time if time_data else None diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py index 2a6c35fc04..353f6d281a 100644 --- a/erpnext/manufacturing/doctype/job_card/test_job_card.py +++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py @@ -5,6 +5,8 @@ from __future__ import unicode_literals import unittest import frappe +from frappe.utils import random_string +from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record from erpnext.manufacturing.doctype.job_card.job_card import OperationMismatchError @@ -29,3 +31,37 @@ class TestJobCard(unittest.TestCase): doc.operation_id = "Test Data" self.assertRaises(OperationMismatchError, doc.save) + def test_job_card_with_different_work_station(self): + data = frappe.get_cached_value('BOM', + {'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item']) + + if data: + bom, bom_item = data + + work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom) + + job_card = frappe.get_all('Job Card', + filters = {'work_order': work_order.name}, + fields = ["operation_id", "workstation", "name", "for_quantity"])[0] + + if job_card: + workstation = frappe.db.get_value("Workstation", + {"name": ("not in", [job_card.workstation])}, "name") + + if not workstation or job_card.workstation == workstation: + workstation = make_workstation(workstation_name=random_string(5)).name + + doc = frappe.get_doc("Job Card", job_card.name) + doc.workstation = workstation + doc.append("time_logs", { + "from_time": "2009-01-01 12:06:25", + "to_time": "2009-01-01 12:37:25", + "time_in_mins": "31.00002", + "completed_qty": job_card.for_quantity + }) + doc.submit() + + completed_qty = frappe.db.get_value("Work Order Operation", job_card.operation_id, "completed_qty") + self.assertEqual(completed_qty, job_card.for_quantity) + + doc.cancel() \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.py b/erpnext/manufacturing/doctype/workstation/test_workstation.py index 2169260854..8266cf7b77 100644 --- a/erpnext/manufacturing/doctype/workstation/test_workstation.py +++ b/erpnext/manufacturing/doctype/workstation/test_workstation.py @@ -20,3 +20,18 @@ class TestWorkstation(unittest.TestCase): "_Test Workstation 1", "Operation 1", "2013-02-02 05:00:00", "2013-02-02 20:00:00") self.assertRaises(WorkstationHolidayError, check_if_within_operating_hours, "_Test Workstation 1", "Operation 1", "2013-02-01 10:00:00", "2013-02-02 20:00:00") + +def make_workstation(**args): + args = frappe._dict(args) + + try: + doc = frappe.get_doc({ + "doctype": "Workstation", + "workstation_name": args.workstation_name + }) + + doc.insert() + + return doc + except frappe.DuplicateEntryError: + return frappe.get_doc("Workstation", args.workstation_name) \ No newline at end of file From f9f26d2fa419aaec5c27b6c51086c979bb78907a Mon Sep 17 00:00:00 2001 From: Anupam K Date: Mon, 31 Aug 2020 14:00:27 +0530 Subject: [PATCH 11/23] fix: contact us button issue --- .../generators/item/item_add_to_cart.html | 31 ++++++++++--------- .../generators/item/item_configure.html | 7 ++--- .../generators/item/item_inquiry.html | 11 +++++++ 3 files changed, 30 insertions(+), 19 deletions(-) create mode 100644 erpnext/templates/generators/item/item_inquiry.html diff --git a/erpnext/templates/generators/item/item_add_to_cart.html b/erpnext/templates/generators/item/item_add_to_cart.html index 2a70d8dbe9..40bc0c749b 100644 --- a/erpnext/templates/generators/item/item_add_to_cart.html +++ b/erpnext/templates/generators/item/item_add_to_cart.html @@ -27,22 +27,25 @@ {% endif %} {% endif %} - {% if product_info.price and (cart_settings.allow_items_not_in_stock or product_info.in_stock) %}
    - - {{ _("View in Cart") }} - - + {% if product_info.price and (cart_settings.allow_items_not_in_stock or product_info.in_stock) %} + + {{ _("View in Cart") }} + + + {% endif %} + {% if cart_settings.show_contact_us_button %} + {% include "templates/generators/item/item_inquiry.html" %} + {% endif %}
    - {% endif %} diff --git a/erpnext/templates/generators/item/item_configure.html b/erpnext/templates/generators/item/item_configure.html index b8b0d98bdc..73f9ec99b3 100644 --- a/erpnext/templates/generators/item/item_configure.html +++ b/erpnext/templates/generators/item/item_configure.html @@ -10,14 +10,11 @@ {{ _('Configure') }} {% endif %} - {% if cart_settings.show_contact_us_button | int %} - + {% if cart_settings.show_contact_us_button %} + {% include "templates/generators/item/item_inquiry.html" %} {% endif %} {% endif %} diff --git a/erpnext/templates/generators/item/item_inquiry.html b/erpnext/templates/generators/item/item_inquiry.html new file mode 100644 index 0000000000..83653b6821 --- /dev/null +++ b/erpnext/templates/generators/item/item_inquiry.html @@ -0,0 +1,11 @@ +{% if shopping_cart and shopping_cart.cart_settings.enabled %} +{% set cart_settings = shopping_cart.cart_settings %} + {% if cart_settings.show_contact_us_button | int %} + + {% endif %} + +{% endif %} From eb4b2aa14252161433bb3f3f5e2be45476f0af8b Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 1 Sep 2020 15:01:32 +0530 Subject: [PATCH 12/23] fix: get_items from product bundle for purchase order (#22821) * fix: get_items from product bundle for purchase order * fix: Don't overwrite doctype while setting attributes in child row Co-authored-by: Marica --- erpnext/public/js/controllers/buying.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index a4cc68b3e2..cb76c87b62 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -503,11 +503,11 @@ erpnext.buying.get_items_from_product_bundle = function(frm) { if(!r.exc && r.message) { remove_empty_first_row(frm); - for ( var i=0; i< r.message.length; i++ ) { + for (var i=0; i< r.message.length; i++) { var d = frm.add_child("items"); var item = r.message[i]; - for ( var key in item) { - if ( !is_null(item[key]) ) { + for (var key in item) { + if (!is_null(item[key]) && key !== "doctype") { d[key] = item[key]; } } From cebf8034552821a4c54104ac3170b54ffc7a8504 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 1 Sep 2020 16:15:28 +0530 Subject: [PATCH 13/23] fix: Update tax template on supplier address change --- erpnext/regional/india/taxes.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js index fbccc6b078..3b6a28f52c 100644 --- a/erpnext/regional/india/taxes.js +++ b/erpnext/regional/india/taxes.js @@ -6,6 +6,9 @@ erpnext.setup_auto_gst_taxation = (doctype) => { shipping_address: function(frm) { frm.trigger('get_tax_template'); }, + supplier_address: function(frm) { + frm.trigger('get_tax_template'); + }, tax_category: function(frm) { frm.trigger('get_tax_template'); }, From 86a190652e4643a59862b9663f7dfdd10343d1ef Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 1 Sep 2020 14:02:34 +0530 Subject: [PATCH 14/23] fix: incorrect job card timer issue --- erpnext/manufacturing/doctype/job_card/job_card.js | 12 ++++++------ .../doctype/job_card/test_job_card.py | 14 +++++++++++--- .../doctype/work_order/test_work_order.py | 2 +- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index 7ac0b11ecb..b051b3243f 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -34,7 +34,7 @@ frappe.ui.form.on('Job Card', { frm.trigger("toggle_operation_number"); if (frm.doc.docstatus == 0 && (frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity) - && (!frm.doc.items || !frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) { + && (frm.doc.items || !frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) { frm.trigger("prepare_timer_buttons"); } }, @@ -94,9 +94,9 @@ frappe.ui.form.on('Job Card', { fieldname: 'employee'}, d => { if (d.employee) { frm.set_value("employee", d.employee); + } else { + frm.events.start_job(frm); } - - frm.events.start_job(frm); }, __("Enter Value"), __("Start")); } else { frm.events.start_job(frm); @@ -141,9 +141,7 @@ frappe.ui.form.on('Job Card', { frm.set_value('current_time' , 0); } - frm.save("Save", () => {}, "", () => { - frm.doc.time_logs.pop(-1); - }); + frm.save(); }, complete_job: function(frm, completed_time, completed_qty) { @@ -175,6 +173,8 @@ frappe.ui.form.on('Job Card', { employee: function(frm) { if (frm.doc.job_started && !frm.doc.current_time) { frm.trigger("reset_timer"); + } else { + frm.events.start_job(frm); } }, diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py index 353f6d281a..b6a6c33d37 100644 --- a/erpnext/manufacturing/doctype/job_card/test_job_card.py +++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py @@ -31,6 +31,9 @@ class TestJobCard(unittest.TestCase): doc.operation_id = "Test Data" self.assertRaises(OperationMismatchError, doc.save) + for d in job_cards: + frappe.delete_doc("Job Card", d.name) + def test_job_card_with_different_work_station(self): data = frappe.get_cached_value('BOM', {'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item']) @@ -40,9 +43,11 @@ class TestJobCard(unittest.TestCase): work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom) - job_card = frappe.get_all('Job Card', + job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name}, - fields = ["operation_id", "workstation", "name", "for_quantity"])[0] + fields = ["operation_id", "workstation", "name", "for_quantity"]) + + job_card = job_cards[0] if job_card: workstation = frappe.db.get_value("Workstation", @@ -64,4 +69,7 @@ class TestJobCard(unittest.TestCase): completed_qty = frappe.db.get_value("Work Order Operation", job_card.operation_id, "completed_qty") self.assertEqual(completed_qty, job_card.for_quantity) - doc.cancel() \ No newline at end of file + doc.cancel() + + for d in job_cards: + frappe.delete_doc("Job Card", d.name) \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 2260befb3f..b7c7c32869 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -7,7 +7,7 @@ import unittest import frappe from frappe.utils import flt, time_diff_in_hours, now, add_months, cint, today from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory -from erpnext.manufacturing.doctype.work_order.work_order import (make_stock_entry, +from erpnext.manufacturing.doctype.work_order.work_order import (make_stock_entry, ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError, CapacityError) from erpnext.stock.doctype.stock_entry import test_stock_entry from erpnext.stock.utils import get_bin From d2881ba4dde0eb3157b52ef905517996de17191f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 2 Sep 2020 11:52:59 +0530 Subject: [PATCH 15/23] fix: set conversion factor while creating RFQ from Opportunity --- erpnext/crm/doctype/opportunity/opportunity.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 6096053136..ad12941fda 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -267,6 +267,12 @@ def make_quotation(source_name, target_doc=None): @frappe.whitelist() def make_request_for_quotation(source_name, target_doc=None): + def set_missing_values(source, target): + rfq = frappe.get_doc(target) + for item in rfq.items: + # opportunity item is not multi-uom + item.conversion_factor = 1.0 + doclist = get_mapped_doc("Opportunity", source_name, { "Opportunity": { "doctype": "Request for Quotation" @@ -279,7 +285,7 @@ def make_request_for_quotation(source_name, target_doc=None): ["uom", "uom"] ] } - }, target_doc) + }, target_doc, set_missing_values) return doclist From a1b5d570768d5b71c4a199a02ea7038c7ac409de Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 2 Sep 2020 13:14:01 +0530 Subject: [PATCH 16/23] test: make rfq from opportunity --- .../test_request_for_quotation.py | 19 +++++++++++++++++++ .../doctype/opportunity/test_opportunity.py | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py index 3de9526c4f..019cefc0bd 100644 --- a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py @@ -11,6 +11,8 @@ from erpnext.stock.doctype.item.test_item import make_item from erpnext.templates.pages.rfq import check_supplier_has_docname_access from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation +from erpnext.crm.doctype.opportunity.test_opportunity import make_opportunity +from erpnext.crm.doctype.opportunity.opportunity import make_request_for_quotation as make_rfq class TestRequestforQuotation(unittest.TestCase): def test_quote_status(self): @@ -110,6 +112,23 @@ class TestRequestforQuotation(unittest.TestCase): self.assertEqual(supplier_quotation.items[0].qty, 5) self.assertEqual(supplier_quotation.items[0].stock_qty, 10) + def test_make_rfq_from_opportunity(self): + opportunity = make_opportunity(with_items=1) + supplier_data = get_supplier_data() + rfq = make_rfq(opportunity.name) + + self.assertEqual(len(rfq.get("items")), len(opportunity.get("items"))) + rfq.message_for_supplier = 'Please supply the specified items at the best possible rates.' + + for item in rfq.items: + item.warehouse = "_Test Warehouse - _TC" + + for data in supplier_data: + rfq.append('suppliers', data) + + rfq.status = 'Draft' + rfq.submit() + def make_request_for_quotation(**args): """ :param supplier_data: List containing supplier data diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index 33d90076c4..04cd8a26ca 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -82,7 +82,8 @@ def make_opportunity(**args): if args.with_items: opp_doc.append('items', { "item_code": args.item_code or "_Test Item", - "qty": args.qty or 1 + "qty": args.qty or 1, + "uom": "_Test UOM" }) opp_doc.insert() From c3984691b39cd1209a323f5e83e8f50576b1b4f8 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 2 Sep 2020 20:01:14 +0530 Subject: [PATCH 17/23] fix: Sales funnel data is inconsistent (#23110) * fix: Sales funnel data is inconsistent * fix: data inconsistency * fix: Converted Count Co-authored-by: Marica --- .../selling/page/sales_funnel/sales_funnel.py | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.py b/erpnext/selling/page/sales_funnel/sales_funnel.py index dba24ef5b0..b613718c7e 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.py +++ b/erpnext/selling/page/sales_funnel/sales_funnel.py @@ -20,29 +20,28 @@ def get_funnel_data(from_date, to_date, company): validate_filters(from_date, to_date, company) active_leads = frappe.db.sql("""select count(*) from `tabLead` - where (date(`modified`) between %s and %s) - and status != "Do Not Contact" and company=%s""", (from_date, to_date, company))[0][0] - - active_leads += frappe.db.sql("""select count(distinct contact.name) from `tabContact` contact - left join `tabDynamic Link` dl on (dl.parent=contact.name) where dl.link_doctype='Customer' - and (date(contact.modified) between %s and %s) and status != "Passive" """, (from_date, to_date))[0][0] + where (date(`creation`) between %s and %s) + and company=%s""", (from_date, to_date, company))[0][0] opportunities = frappe.db.sql("""select count(*) from `tabOpportunity` where (date(`creation`) between %s and %s) - and status != "Lost" and company=%s""", (from_date, to_date, company))[0][0] + and opportunity_from='Lead' and company=%s""", (from_date, to_date, company))[0][0] quotations = frappe.db.sql("""select count(*) from `tabQuotation` where docstatus = 1 and (date(`creation`) between %s and %s) - and status != "Lost" and company=%s""", (from_date, to_date, company))[0][0] + and (opportunity!="" or quotation_to="Lead") and company=%s""", (from_date, to_date, company))[0][0] + + converted = frappe.db.sql("""select count(*) from `tabCustomer` + JOIN `tabLead` ON `tabLead`.name = `tabCustomer`.lead_name + WHERE (date(`tabCustomer`.creation) between %s and %s) + and `tabLead`.company=%s""", (from_date, to_date, company))[0][0] - sales_orders = frappe.db.sql("""select count(*) from `tabSales Order` - where docstatus = 1 and (date(`creation`) between %s and %s) and company=%s""", (from_date, to_date, company))[0][0] return [ - { "title": _("Active Leads / Customers"), "value": active_leads, "color": "#B03B46" }, + { "title": _("Active Leads"), "value": active_leads, "color": "#B03B46" }, { "title": _("Opportunities"), "value": opportunities, "color": "#F09C00" }, { "title": _("Quotations"), "value": quotations, "color": "#006685" }, - { "title": _("Sales Orders"), "value": sales_orders, "color": "#00AD65" } + { "title": _("Converted"), "value": converted, "color": "#00AD65" } ] @frappe.whitelist() From b872035fd9bbe362847c12604a7d8021ce82491d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2020 17:22:19 +0000 Subject: [PATCH 18/23] chore(deps): bump bl from 3.0.0 to 3.0.1 Bumps [bl](https://github.com/rvagg/bl) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/rvagg/bl/releases) - [Commits](https://github.com/rvagg/bl/compare/v3.0.0...v3.0.1) Signed-off-by: dependabot[bot] --- yarn.lock | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/yarn.lock b/yarn.lock index b19f566fd0..97a063597d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -282,9 +282,9 @@ balanced-match@^1.0.0: integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= bl@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88" - integrity sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A== + version "3.0.1" + resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.1.tgz#1cbb439299609e419b5a74d7fce2f8b37d8e5c6f" + integrity sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ== dependencies: readable-stream "^3.0.1" @@ -866,12 +866,12 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -inherits@2.0.4, inherits@~2.0.1: +inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1447,9 +1447,9 @@ readable-stream@2, readable-stream@~2.3.6: util-deprecate "~1.0.1" readable-stream@^3.0.1, readable-stream@^3.1.1: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.5.0.tgz#465d70e6d1087f6162d079cd0b5db7fbebfd1606" - integrity sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA== + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -1505,9 +1505,9 @@ safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-buffer@~5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== "safer-buffer@>= 2.1.2 < 3": version "2.1.2" From 0857e6d6699570443afaf52ff55ed0ae53b60b21 Mon Sep 17 00:00:00 2001 From: michellealva Date: Thu, 3 Sep 2020 10:37:43 +0530 Subject: [PATCH 19/23] fix: Add "Bank Clearance" and "Bank Reconciliation" in desk --- erpnext/accounts/desk_page/accounting/accounting.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index a2497838ee..2c5231491c 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -43,7 +43,7 @@ { "hidden": 0, "label": "Bank Statement", - "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"label\": \"Bank Clearance\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -98,7 +98,7 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-06-19 12:42:44.054598", + "modified": "2020-09-03 10:37:07.865801", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", @@ -158,4 +158,4 @@ "type": "Dashboard" } ] -} +} \ No newline at end of file From 979ad180fa640536f8c8c831517aecb7ba2a5e2f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 3 Sep 2020 11:03:22 +0530 Subject: [PATCH 20/23] fix: update rfq item in postprocess --- erpnext/crm/doctype/opportunity/opportunity.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index ad12941fda..47b05f306b 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -267,11 +267,8 @@ def make_quotation(source_name, target_doc=None): @frappe.whitelist() def make_request_for_quotation(source_name, target_doc=None): - def set_missing_values(source, target): - rfq = frappe.get_doc(target) - for item in rfq.items: - # opportunity item is not multi-uom - item.conversion_factor = 1.0 + def update_item(obj, target, source_parent): + target.conversion_factor = 1.0 doclist = get_mapped_doc("Opportunity", source_name, { "Opportunity": { @@ -283,9 +280,10 @@ def make_request_for_quotation(source_name, target_doc=None): ["name", "opportunity_item"], ["parent", "opportunity"], ["uom", "uom"] - ] + ], + "postprocess": update_item } - }, target_doc, set_missing_values) + }, target_doc) return doclist From 54cd194b4127dfc7b767c96ddd108a5466f9f176 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Thu, 3 Sep 2020 13:51:26 +0530 Subject: [PATCH 21/23] feat: removed roles for some reginal report and added via setup --- erpnext/patches.txt | 1 + ..._custom_roles_for_some_regional_reports.py | 10 +++++ erpnext/regional/india/setup.py | 13 ++++++ erpnext/regional/report/gstr_1/gstr_1.json | 14 +----- erpnext/regional/report/gstr_2/gstr_2.json | 44 +++++++------------ .../hsn_wise_summary_of_outward_supplies.json | 42 +++++++----------- 6 files changed, 59 insertions(+), 65 deletions(-) create mode 100644 erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4b9c566f04..771babef6a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -724,3 +724,4 @@ erpnext.patches.v12_0.update_state_code_for_daman_and_diu erpnext.patches.v12_0.rename_lost_reason_detail erpnext.patches.v13_0.drop_razorpay_payload_column erpnext.patches.v13_0.update_start_end_date_for_old_shift_assignment +erpnext.patches.v13_0.setting_custom_roles_for_some_regional_reports diff --git a/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py b/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py new file mode 100644 index 0000000000..ecc7822e1d --- /dev/null +++ b/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py @@ -0,0 +1,10 @@ +from __future__ import unicode_literals +import frappe +from erpnext.regional.india.setup import add_custom_roles_for_reports + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + add_custom_roles_for_reports() \ No newline at end of file diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 290694a789..cbcd6e3203 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -73,6 +73,19 @@ def add_custom_roles_for_reports(): ] )).insert() + for report_name in ('HSN-wise-summary of outward supplies', 'GSTR-1', 'GSTR-2'): + + if not frappe.db.get_value('Custom Role', dict(report=report_name)): + frappe.get_doc(dict( + doctype='Custom Role', + report=report_name, + roles= [ + dict(role='Accounts User'), + dict(role='Accounts Manager'), + dict(role='Auditor') + ] + )).insert() + def add_permissions(): for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate'): add_permission(doctype, 'All', 0) diff --git a/erpnext/regional/report/gstr_1/gstr_1.json b/erpnext/regional/report/gstr_1/gstr_1.json index 2012bb8840..75aed8cffc 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.json +++ b/erpnext/regional/report/gstr_1/gstr_1.json @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2019-06-30 19:33:59.769385", + "modified": "2019-09-03 19:33:59.769385", "modified_by": "Administrator", "module": "Regional", "name": "GSTR-1", @@ -16,15 +16,5 @@ "ref_doctype": "GL Entry", "report_name": "GSTR-1", "report_type": "Script Report", - "roles": [ - { - "role": "Accounts User" - }, - { - "role": "Accounts Manager" - }, - { - "role": "Auditor" - } - ] + "roles": [] } \ No newline at end of file diff --git a/erpnext/regional/report/gstr_2/gstr_2.json b/erpnext/regional/report/gstr_2/gstr_2.json index 929ed914d8..b70d0f9416 100644 --- a/erpnext/regional/report/gstr_2/gstr_2.json +++ b/erpnext/regional/report/gstr_2/gstr_2.json @@ -1,29 +1,19 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2018-01-29 12:59:55.650445", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2018-01-29 12:59:55.650445", - "modified_by": "Administrator", - "module": "Regional", - "name": "GSTR-2", - "owner": "Administrator", - "ref_doctype": "GL Entry", - "report_name": "GSTR-2", - "report_type": "Script Report", - "roles": [ - { - "role": "Accounts User" - }, - { - "role": "Accounts Manager" - }, - { - "role": "Auditor" - } - ] + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2018-01-29 12:59:55.650445", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2018-09-03 12:59:55.650445", + "modified_by": "Administrator", + "module": "Regional", + "name": "GSTR-2", + "owner": "Administrator", + "ref_doctype": "GL Entry", + "report_name": "GSTR-2", + "report_type": "Script Report", + "roles": [] } \ No newline at end of file diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.json b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.json index 124a720134..cc6ad574af 100644 --- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.json +++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.json @@ -1,28 +1,18 @@ { - "add_total_row": 0, - "creation": "2018-04-26 10:49:29.159400", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2019-04-26 12:59:38.603649", - "modified_by": "Administrator", - "module": "Regional", - "name": "HSN-wise-summary of outward supplies", - "owner": "Administrator", - "ref_doctype": "Sales Invoice", - "report_name": "HSN-wise-summary of outward supplies", - "report_type": "Script Report", - "roles": [ - { - "role": "Accounts User" - }, - { - "role": "Accounts Manager" - }, - { - "role": "Auditor" - } - ] + "add_total_row": 0, + "creation": "2018-04-26 10:49:29.159400", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2019-09-03 12:59:38.603649", + "modified_by": "Administrator", + "module": "Regional", + "name": "HSN-wise-summary of outward supplies", + "owner": "Administrator", + "ref_doctype": "Sales Invoice", + "report_name": "HSN-wise-summary of outward supplies", + "report_type": "Script Report", + "roles": [] } \ No newline at end of file From 68b0108301cc8ecab255e6f2c3afc3edbf145723 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 10 Jul 2020 14:23:12 +0530 Subject: [PATCH 22/23] fix: incorrect qty after transaction in stock ledger entry --- .../stock_reconciliation/stock_reconciliation.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 43fbc00466..b81f8a086d 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -258,6 +258,7 @@ class StockReconciliation(StockController): sl_entries.append(args) + qty_after_transaction = 0 for serial_no in serial_nos: args = self.get_sle_for_items(row, [serial_no]) @@ -271,11 +272,19 @@ class StockReconciliation(StockController): if previous_sle and row.warehouse != previous_sle.get("warehouse"): # If serial no exists in different warehouse + warehouse = previous_sle.get("warehouse", '') or row.warehouse + + if not qty_after_transaction: + qty_after_transaction = get_stock_balance(row.item_code, + warehouse, self.posting_date, self.posting_time) + + qty_after_transaction -= 1 + new_args = args.copy() new_args.update({ 'actual_qty': -1, - 'qty_after_transaction': cint(previous_sle.get('qty_after_transaction')) - 1, - 'warehouse': previous_sle.get("warehouse", '') or row.warehouse, + 'qty_after_transaction': qty_after_transaction, + 'warehouse': warehouse, 'valuation_rate': previous_sle.get("valuation_rate") }) From 9b681770d06ed09213da30603a9b2524bc2137a4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 3 Sep 2020 18:09:02 +0530 Subject: [PATCH 23/23] fix: Account filter in Process Deferred Accounting doctype --- .../process_deferred_accounting.js | 6 ++++-- .../process_deferred_accounting.json | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js index 2800c195ce..1ec6805ae0 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js +++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js @@ -10,13 +10,15 @@ frappe.ui.form.on('Process Deferred Accounting', { } }; }); + }, - if (frm.doc.company) { + type: function(frm) { + if (frm.doc.company && frm.doc.type) { frm.set_query("account", function() { return { filters: { 'company': frm.doc.company, - 'root_type': 'Liability', + 'root_type': frm.doc.type === 'Income' ? 'Liability' : 'Asset', 'is_group': 0 } }; diff --git a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.json b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.json index 4daafef3ec..457e98ca54 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.json +++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.json @@ -60,6 +60,7 @@ "reqd": 1 }, { + "depends_on": "eval: doc.type", "fieldname": "account", "fieldtype": "Link", "label": "Account", @@ -73,9 +74,10 @@ "reqd": 1 } ], + "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-02-06 18:18:09.852844", + "modified": "2020-09-03 18:07:02.463754", "modified_by": "Administrator", "module": "Accounts", "name": "Process Deferred Accounting",