From 8b144a38cc82484133953026cee72d80684dfff2 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Tue, 19 May 2020 14:27:01 +0530 Subject: [PATCH 001/129] fix: add naming series (#21770) --- .../doctype/quality_meeting/quality_meeting.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json index 0849fd7aeb0..3818f566c17 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json @@ -1,10 +1,11 @@ { - "autoname": "format:MTNG-{date}", + "autoname": "naming_series:", "creation": "2018-10-15 16:25:41.548432", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "naming_series", "date", "cb_00", "status", @@ -53,9 +54,15 @@ "fieldname": "sb_01", "fieldtype": "Section Break", "label": "Minutes" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "MTNG-.YYYY.-.MM.-.DD.-" } ], - "modified": "2019-07-13 19:57:40.500541", + "modified": "2020-05-19 13:18:59.821740", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Meeting", From 113048347cfc3350b01c5c170f7481c8903e8e7f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 19 May 2020 14:36:29 +0530 Subject: [PATCH 002/129] feat: save shipping address and contact to woocommerece customer (bp #21654) (#21771) --- .../connectors/woocommerce_connection.py | 87 +++++++++++++------ erpnext/tests/test_woocommerce.py | 2 +- 2 files changed, 63 insertions(+), 26 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 618865200cf..44f87e0462e 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -49,12 +49,13 @@ def _order(*args, **kwargs): if event == "created": sys_lang = frappe.get_single("System Settings").language or 'en' raw_billing_data = order.get("billing") + raw_shipping_data = order.get("shipping") customer_name = raw_billing_data.get("first_name") + " " + raw_billing_data.get("last_name") - link_customer_and_address(raw_billing_data, customer_name) + link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name) link_items(order.get("line_items"), woocommerce_settings, sys_lang) create_sales_order(order, woocommerce_settings, customer_name, sys_lang) -def link_customer_and_address(raw_billing_data, customer_name): +def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name): customer_woo_com_email = raw_billing_data.get("email") customer_exists = frappe.get_value("Customer", {"woocommerce_email": customer_woo_com_email}) if not customer_exists: @@ -68,38 +69,74 @@ def link_customer_and_address(raw_billing_data, customer_name): customer.customer_name = customer_name customer.woocommerce_email = customer_woo_com_email customer.flags.ignore_mandatory = True - customer.save() + customer.save() if customer_exists: frappe.rename_doc("Customer", old_name, customer_name) - address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email}) + billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"}) + shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"}) + rename_address(billing_address, customer) + rename_address(shipping_address, customer) else: - address = frappe.new_doc("Address") + create_address(raw_billing_data, customer, "Billing") + create_address(raw_shipping_data, customer, "Shipping") + create_contact(raw_billing_data, customer) - address.address_line1 = raw_billing_data.get("address_1", "Not Provided") - address.address_line2 = raw_billing_data.get("address_2", "Not Provided") - address.city = raw_billing_data.get("city", "Not Provided") - address.woocommerce_email = customer_woo_com_email - address.address_type = "Billing" - address.country = frappe.get_value("Country", {"code": raw_billing_data.get("country", "IN").lower()}) - address.state = raw_billing_data.get("state") - address.pincode = raw_billing_data.get("postcode") - address.phone = raw_billing_data.get("phone") - address.email_id = customer_woo_com_email +def create_contact(data, customer): + email = data.get("email", None) + phone = data.get("phone", None) + + if not email and not phone: + return + + contact = frappe.new_doc("Contact") + contact.first_name = data.get("first_name") + contact.last_name = data.get("last_name") + contact.is_primary_contact = 1 + contact.is_billing_contact = 1 + + if phone: + contact.add_phone(phone, is_primary_mobile_no=1, is_primary_phone=1) + + if email: + contact.add_email(email, is_primary=1) + + contact.append("links", { + "link_doctype": "Customer", + "link_name": customer.name + }) + + contact.flags.ignore_mandatory = True + contact.save() + +def create_address(raw_data, customer, address_type): + address = frappe.new_doc("Address") + + address.address_line1 = raw_data.get("address_1", "Not Provided") + address.address_line2 = raw_data.get("address_2", "Not Provided") + address.city = raw_data.get("city", "Not Provided") + address.woocommerce_email = customer.woocommerce_email + address.address_type = address_type + address.country = frappe.get_value("Country", {"code": raw_data.get("country", "IN").lower()}) + address.state = raw_data.get("state") + address.pincode = raw_data.get("postcode") + address.phone = raw_data.get("phone") + address.email_id = customer.woocommerce_email address.append("links", { "link_doctype": "Customer", - "link_name": customer.customer_name + "link_name": customer.name }) + address.flags.ignore_mandatory = True - address = address.save() + address.save() - if customer_exists: - old_address_title = address.name - new_address_title = customer.customer_name + "-billing" - address.address_title = customer.customer_name - address.save() +def rename_address(address, customer): + old_address_title = address.name + new_address_title = customer.name + "-" + address.address_type + address.address_title = customer.customer_name + address.save() - frappe.rename_doc("Address", old_address_title, new_address_title) + frappe.rename_doc("Address", old_address_title, new_address_title) def link_items(items_list, woocommerce_settings, sys_lang): for item_data in items_list: @@ -111,7 +148,7 @@ def link_items(items_list, woocommerce_settings, sys_lang): else: #Create Item item = frappe.new_doc("Item") - + item.item_name = item_data.get("name") item.item_code = _("woocommerce - {0}", sys_lang).format(item_data.get("product_id")) item.woocommerce_id = item_data.get("product_id") @@ -171,7 +208,7 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l add_tax_details(new_sales_order, order.get("shipping_tax"), "Shipping Tax", woocommerce_settings.f_n_f_account) add_tax_details(new_sales_order, order.get("shipping_total"), "Shipping Total", woocommerce_settings.f_n_f_account) - + def add_tax_details(sales_order, price, desc, tax_account_head): sales_order.append("taxes", { "charge_type":"Actual", diff --git a/erpnext/tests/test_woocommerce.py b/erpnext/tests/test_woocommerce.py index ce0f47d685f..df715ab2027 100644 --- a/erpnext/tests/test_woocommerce.py +++ b/erpnext/tests/test_woocommerce.py @@ -24,7 +24,7 @@ class TestWoocommerce(unittest.TestCase): woo_settings.creation_user = "Administrator" woo_settings.save(ignore_permissions=True) - def test_sales_order_for_woocommerece(self): + def test_sales_order_for_woocommerce(self): frappe.flags.woocomm_test_order_data = {"id":75,"parent_id":0,"number":"74","order_key":"wc_order_5aa1281c2dacb","created_via":"checkout","version":"3.3.3","status":"processing","currency":"INR","date_created":"2018-03-08T12:10:04","date_created_gmt":"2018-03-08T12:10:04","date_modified":"2018-03-08T12:10:04","date_modified_gmt":"2018-03-08T12:10:04","discount_total":"0.00","discount_tax":"0.00","shipping_total":"150.00","shipping_tax":"0.00","cart_tax":"0.00","total":"649.00","total_tax":"0.00","prices_include_tax":False,"customer_id":12,"customer_ip_address":"103.54.99.5","customer_user_agent":"mozilla\\/5.0 (x11; linux x86_64) applewebkit\\/537.36 (khtml, like gecko) chrome\\/64.0.3282.186 safari\\/537.36","customer_note":"","billing":{"first_name":"Tony","last_name":"Stark","company":"Woocommerce","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN","email":"tony@gmail.com","phone":"123457890"},"shipping":{"first_name":"Tony","last_name":"Stark","company":"","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN"},"payment_method":"cod","payment_method_title":"Cash on delivery","transaction_id":"","date_paid":"","date_paid_gmt":"","date_completed":"","date_completed_gmt":"","cart_hash":"8e76b020d5790066496f244860c4703f","meta_data":[],"line_items":[{"id":80,"name":"Marvel","product_id":56,"variation_id":0,"quantity":1,"tax_class":"","subtotal":"499.00","subtotal_tax":"0.00","total":"499.00","total_tax":"0.00","taxes":[],"meta_data":[],"sku":"","price":499}],"tax_lines":[],"shipping_lines":[{"id":81,"method_title":"Flat rate","method_id":"flat_rate:1","total":"150.00","total_tax":"0.00","taxes":[],"meta_data":[{"id":623,"key":"Items","value":"Marvel × 1"}]}],"fee_lines":[],"coupon_lines":[],"refunds":[]} order() From 72892a40d6123614ac4fc939fb7ab54058cb34cd Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 19 May 2020 16:36:25 +0530 Subject: [PATCH 003/129] refactor: use text editor in issue web form (#21772) --- erpnext/support/web_form/issues/issues.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/web_form/issues/issues.json b/erpnext/support/web_form/issues/issues.json index 0f15e4737fd..1c0c8ff1584 100644 --- a/erpnext/support/web_form/issues/issues.json +++ b/erpnext/support/web_form/issues/issues.json @@ -18,7 +18,7 @@ "is_standard": 1, "login_required": 1, "max_attachment_size": 0, - "modified": "2020-03-06 05:24:05.749664", + "modified": "2020-05-19 15:08:45.361878", "modified_by": "Administrator", "module": "Support", "name": "issues", @@ -76,7 +76,7 @@ { "allow_read_on_all_link_options": 0, "fieldname": "description", - "fieldtype": "Text", + "fieldtype": "Text Editor", "hidden": 0, "label": "Description", "max_length": 0, From b384ed74cd9f8bcee83e441340948c8fef569e24 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 May 2020 18:53:08 +0530 Subject: [PATCH 004/129] fix: Instate Invoice not appearing in GSTR-1 report --- erpnext/regional/report/gstr_1/gstr_1.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index a4d0bf59c11..13a9f0bf4ad 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -60,6 +60,9 @@ 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(): + if inv in self.cgst_igst_invoices: + rate = rate/2 + row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) if self.filters.get("type_of_business") == "CDNR": @@ -118,11 +121,11 @@ class Gstr1Report(object): row.append(invoice_details.get(fieldname)) taxable_value = 0 for item_code, net_amount in self.invoice_items.get(invoice).items(): - if item_code in items: - if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []): - taxable_value += abs(net_amount) - elif not self.item_tax_rate.get(invoice): - taxable_value += abs(net_amount) + if item_code in items: + if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []): + taxable_value += abs(net_amount) + elif not self.item_tax_rate.get(invoice): + taxable_value += abs(net_amount) row += [tax_rate or 0, taxable_value] @@ -196,7 +199,7 @@ class Gstr1Report(object): if d.item_code not in self.invoice_items.get(d.parent, {}): self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, sum(i.get('base_net_amount', 0) for i in items - if i.item_code == d.item_code and i.parent == d.parent)) + if i.item_code == d.item_code and i.parent == d.parent)) item_tax_rate = {} @@ -221,6 +224,8 @@ class Gstr1Report(object): self.items_based_on_tax_rate = {} self.invoice_cess = frappe._dict() + self.cgst_igst_invoices = [] + unidentified_gst_accounts = [] for parent, account, item_wise_tax_detail, tax_amount in self.tax_details: if account in self.gst_accounts.cess_account: @@ -243,6 +248,8 @@ class Gstr1Report(object): tax_rate = tax_amounts[0] if cgst_or_sgst: tax_rate *= 2 + if parent not in self.cgst_igst_invoices: + self.cgst_igst_invoices.append(parent) rate_based_dict = self.items_based_on_tax_rate\ .setdefault(parent, {}).setdefault(tax_rate, []) From 07924d3c7cb1567ff737656552c8a7fe6046a7f3 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 19 May 2020 19:05:17 +0530 Subject: [PATCH 005/129] refactor: changed the fieldtype from data to small text (#21783) --- .../accounts/doctype/sales_invoice/sales_invoice.json | 4 ++-- .../stock/doctype/delivery_note/delivery_note.json | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index ea78c27333e..6e0a30d48e0 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -397,7 +397,7 @@ { "allow_on_submit": 1, "fieldname": "po_no", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Customer's Purchase Order", "no_copy": 1, "print_hide": 1 @@ -1570,7 +1570,7 @@ "idx": 181, "is_submittable": 1, "links": [], - "modified": "2020-04-29 13:37:09.355300", + "modified": "2020-05-19 17:00:57.208696", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 424a7f3c22f..8a2fa397731 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -24,10 +24,10 @@ "return_against", "customer_po_details", "po_no", - "section_break_18", - "pick_list", "column_break_17", "po_date", + "section_break_18", + "pick_list", "contact_info", "shipping_address_name", "shipping_address", @@ -294,7 +294,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "po_no", "fieldname": "customer_po_details", "fieldtype": "Section Break", "label": "Customer PO Details" @@ -302,7 +301,7 @@ { "allow_on_submit": 1, "fieldname": "po_no", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Customer's Purchase Order No", "no_copy": 1, "oldfieldname": "po_no", @@ -316,7 +315,6 @@ "fieldtype": "Column Break" }, { - "depends_on": "eval:doc.po_no", "fieldname": "po_date", "fieldtype": "Date", "label": "Customer's Purchase Order Date", @@ -324,7 +322,6 @@ "oldfieldtype": "Data", "print_hide": 1, "print_width": "100px", - "read_only": 1, "width": "100px" }, { @@ -1240,7 +1237,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2020-04-17 12:51:41.288600", + "modified": "2020-05-19 17:03:45.880106", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", From e2e5ba345c3647f51f0a14f91ca7b5bd45113db9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 May 2020 19:56:51 +0530 Subject: [PATCH 006/129] fix: Tax amount in GSTR-1 JSON --- erpnext/regional/report/gstr_1/gstr_1.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 13a9f0bf4ad..fae9dc6e9d8 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -60,9 +60,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(): - if inv in self.cgst_igst_invoices: - rate = rate/2 - row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) if self.filters.get("type_of_business") == "CDNR": @@ -120,9 +117,15 @@ class Gstr1Report(object): else: row.append(invoice_details.get(fieldname)) taxable_value = 0 + + if invoice in self.cgst_igst_invoices: + division_factor = 2 + else: + division_factor = 1 + for item_code, net_amount in self.invoice_items.get(invoice).items(): if item_code in items: - if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []): + if self.item_tax_rate.get(invoice) and tax_rate/division_factor in self.item_tax_rate.get(invoice, {}).get(item_code, []): taxable_value += abs(net_amount) elif not self.item_tax_rate.get(invoice): taxable_value += abs(net_amount) From 05aae4341eb7ef826bd973afc3e1a75ed6daef32 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 May 2020 20:59:51 +0530 Subject: [PATCH 007/129] fix: Against voucher in General Ledger --- erpnext/accounts/report/general_ledger/general_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index ae407231caa..bb9cfcd886f 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -293,7 +293,7 @@ def get_accountwise_gle(filters, gl_entries, gle_map): data[key].debit_in_account_currency += flt(gle.debit_in_account_currency) data[key].credit_in_account_currency += flt(gle.credit_in_account_currency) - if data[key].against_voucher: + if data[key].against_voucher and gle.against_voucher: data[key].against_voucher += ', ' + gle.against_voucher from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) From 9245133b99e9f6f18a5d7393889aa3374e120278 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Tue, 19 May 2020 18:41:06 +0530 Subject: [PATCH 008/129] tax id is not fetched in when creating sales order from quoation --- erpnext/selling/doctype/sales_order/sales_order.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 6462d3bc8c3..b03b6a6fa7e 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -282,6 +282,7 @@ "width": "100px" }, { + "fetch_from": "customer.tax_id", "fieldname": "tax_id", "fieldtype": "Data", "label": "Tax Id", @@ -1196,7 +1197,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-04-17 12:50:39.640534", + "modified": "2020-05-19 21:36:57.437325", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", From d53d6cabb463798e6dc0edec431e8d50963cb5e5 Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 20 May 2020 16:13:38 +0530 Subject: [PATCH 009/129] fix: Validate Payment Gateway only if it exists in Payment Request. (#21806) --- erpnext/accounts/doctype/payment_request/payment_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 4e4eac0610f..c863ce8e7f7 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -69,7 +69,7 @@ class PaymentRequest(Document): elif self.payment_request_type == 'Inward': self.db_set('status', 'Requested') - send_mail = self.payment_gateway_validation() + send_mail = self.payment_gateway_validation() if self.payment_gateway else None ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \ From ee56de309172e7b53c1d73efb2c7d1be56cb5de5 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 20 May 2020 16:15:16 +0530 Subject: [PATCH 010/129] enable Allow Rename in sales stage (#21800) --- .../crm/doctype/sales_stage/sales_stage.json | 120 +++++------------- 1 file changed, 34 insertions(+), 86 deletions(-) diff --git a/erpnext/crm/doctype/sales_stage/sales_stage.json b/erpnext/crm/doctype/sales_stage/sales_stage.json index 4374bb58314..85cc9599983 100644 --- a/erpnext/crm/doctype/sales_stage/sales_stage.json +++ b/erpnext/crm/doctype/sales_stage/sales_stage.json @@ -1,96 +1,44 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:stage_name", - "beta": 0, - "creation": "2018-10-01 09:28:16.399518", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_rename": 1, + "autoname": "field:stage_name", + "creation": "2018-10-01 09:28:16.399518", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "stage_name" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stage_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": "Stage 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, - "translatable": 0, + "fieldname": "stage_name", + "fieldtype": "Data", + "label": "Stage Name", "unique": 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": 0, - "max_attachments": 0, - "modified": "2018-10-01 09:29:43.230378", - "modified_by": "Administrator", - "module": "CRM", - "name": "Sales Stage", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-05-20 12:37:17.431879", + "modified_by": "Administrator", + "module": "CRM", + "name": "Sales Stage", + "owner": "Administrator", "permissions": [ { - "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": "Sales Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file From c75303b0abf55c212eaee19dcc46a0277889af2c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 20 May 2020 18:24:05 +0530 Subject: [PATCH 011/129] feat: remove website settings from boot (#21801) (#21809) (cherry picked from commit e091789332662a4e27d4c99cc788cb2cf321e7b4) Co-authored-by: Shivam Mishra --- erpnext/startup/boot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py index 4ca43a89b8f..2b80fb8dfa1 100644 --- a/erpnext/startup/boot.py +++ b/erpnext/startup/boot.py @@ -10,7 +10,6 @@ def boot_session(bootinfo): """boot session - send website info if guest""" bootinfo.custom_css = frappe.db.get_value('Style Settings', None, 'custom_css') or '' - bootinfo.website_settings = frappe.get_doc('Website Settings') if frappe.session['user']!='Guest': update_page_info(bootinfo) From 67c9f00917e8eb2a9ea059a9f0d113c24269f52c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 20 May 2020 22:15:12 +0530 Subject: [PATCH 012/129] fix: Project filter in Trial Baalance Report --- erpnext/accounts/report/trial_balance/trial_balance.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 8bd4399e608..5a699b6580a 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -71,7 +71,8 @@ def get_data(filters): opening_balances = get_opening_balances(filters) #add filter inside list so that the query in financial_statements.py doesn't break - filters.project = [filters.project] + if filters.project: + filters.project = [filters.project] set_gl_entries_by_account(filters.company, filters.from_date, filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry)) From 2caac240716e502f9e2dad961ec40ce825572b71 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 21 May 2020 10:09:42 +0530 Subject: [PATCH 013/129] fix: item tax template not applied if valid from is blank --- erpnext/accounts/page/pos/pos.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 06575ac3a69..28c9149c561 100755 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -1459,7 +1459,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.child.serial_no = (this.item_serial_no[this.child.item_code] ? this.item_serial_no[this.child.item_code][0] : ''); - const tax_template_is_valid = frappe.datetime.get_diff(frappe.datetime.now_date(), this.items[0].valid_from) > 0; + const tax_template_is_valid = true; + if (this.items && this.items[0].valid_from) { + tax_template_is_valid = frappe.datetime.get_diff(frappe.datetime.now_date(), + this.items[0].valid_from) > 0; + } this.child.item_tax_template = tax_template_is_valid ? this.items[0].item_tax_template : ''; this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_tax_template]); From ee1c3831bc0f1ecfd39962d0a1de1032b13007ce Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 21 May 2020 12:08:04 +0530 Subject: [PATCH 014/129] fix: plc conversion rate set infinitely (#21821) --- erpnext/manufacturing/doctype/bom/bom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index cfcab7aea68..74169c80d35 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -228,7 +228,7 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({ plc_conversion_rate: function(doc) { if (!this.in_apply_price_list) { - this.apply_price_list(); + this.apply_price_list(null, true); } }, From a8b2f222eaccf09cc599187544c6641443ab51cd Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 21 May 2020 13:11:48 +0530 Subject: [PATCH 015/129] fix: Fetch customer into Delivery Note from Pick List --- erpnext/stock/doctype/pick_list/pick_list.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 917524929e1..c79025c1fb7 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -300,6 +300,7 @@ def create_delivery_note(source_name, target_doc=None): set_delivery_note_missing_values(delivery_note) delivery_note.pick_list = pick_list.name + delivery_note.customer = pick_list.customer if pick_list.customer else None return delivery_note From d1458c6c0a63ae43fa253842ea6699acbdc65ecf Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 21 May 2020 14:03:29 +0530 Subject: [PATCH 016/129] fix: Supplier Invoice No not fetched in Import Supplier Invoice --- .../import_supplier_invoice/import_supplier_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py index 72fe17fb379..203ea8c865a 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py @@ -58,7 +58,7 @@ class ImportSupplierInvoice(Document): "naming_series": self.invoice_series, "document_type": line.TipoDocumento.text, "bill_date": get_datetime_str(line.Data.text), - "invoice_no": line.Numero.text, + "bill_no": line.Numero.text, "total_discount": 0, "items": [], "buying_price_list": self.default_buying_price_list @@ -249,7 +249,7 @@ def create_supplier(supplier_group, args): return existing_supplier_name else: - + new_supplier = frappe.new_doc("Supplier") new_supplier.supplier_name = args.supplier new_supplier.supplier_group = supplier_group From da23bb6bf46e63c3cded8832dd647de86623a418 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 21 May 2020 18:46:38 +0530 Subject: [PATCH 017/129] fix(set_serial_no_status): auto commit on many writes (#21843) --- erpnext/patches/v12_0/set_serial_no_status.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py index 4ec84ef0f9e..abba37d48ef 100644 --- a/erpnext/patches/v12_0/set_serial_no_status.py +++ b/erpnext/patches/v12_0/set_serial_no_status.py @@ -5,8 +5,12 @@ from frappe.utils import getdate, nowdate def execute(): frappe.reload_doc('stock', 'doctype', 'serial_no') - for serial_no in frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` - where (status is NULL OR status='')""", as_dict = 1): + serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` + where (status is NULL OR status='')""", as_dict = 1) + if len(serial_no_list) > 20000: + frappe.db.auto_commit_on_many_writes = True + + for serial_no in serial_no_list: if serial_no.get("delivery_document_type"): status = "Delivered" elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()): @@ -14,4 +18,7 @@ def execute(): else: status = "Active" - frappe.db.set_value("Serial No", serial_no.get("name"), "status", status) \ No newline at end of file + frappe.db.set_value("Serial No", serial_no.get("name"), "status", status) + + if frappe.db.auto_commit_on_many_writes: + frappe.db.auto_commit_on_many_writes = False From 4c779300fddea78c1ddb8ac1a1337d933761cb6d Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Thu, 21 May 2020 18:47:30 +0530 Subject: [PATCH 018/129] fix: convert goals point to flt (#21840) --- .../hr/doctype/appraisal_template/appraisal_template.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template.py b/erpnext/hr/doctype/appraisal_template/appraisal_template.py index e5d3c42e1bd..d0dfad4be31 100644 --- a/erpnext/hr/doctype/appraisal_template/appraisal_template.py +++ b/erpnext/hr/doctype/appraisal_template/appraisal_template.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import cint +from frappe.utils import cint, flt from frappe import _ from frappe.model.document import Document @@ -11,11 +11,11 @@ from frappe.model.document import Document class AppraisalTemplate(Document): def validate(self): self.check_total_points() - - def check_total_points(self): + + def check_total_points(self): total_points = 0 for d in self.get("goals"): - total_points += int(d.per_weightage or 0) + total_points += flt(d.per_weightage) if cint(total_points) != 100: frappe.throw(_("Sum of points for all goals should be 100. It is {0}").format(total_points)) From 030abc14a45cc7c6185f67b846fb369f95d6971e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 21 May 2020 18:57:24 +0530 Subject: [PATCH 019/129] fix(patch): Handle single value in patch (#21823) (#21833) (cherry picked from commit 5cef8db4db7b7ddf4d2f789cd10dcab13ce18987) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- .../patches/v12_0/remove_duplicate_leave_ledger_entries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py index 98a2fcf27ea..6353304d7aa 100644 --- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -40,5 +40,5 @@ def get_duplicate_records(): def delete_duplicate_ledger_entries(duplicate_records_list): """Delete duplicate leave ledger entries.""" - if duplicate_records_list: - frappe.db.sql(''' DELETE FROM `tabLeave Ledger Entry` WHERE name in {0}'''.format(tuple(duplicate_records_list))) #nosec \ No newline at end of file + if not duplicate_records_list: return + frappe.db.sql('''DELETE FROM `tabLeave Ledger Entry` WHERE name in %s''', ((tuple(duplicate_records_list)), )) \ No newline at end of file From 576374a8a730e6e77a0abc417bbd6590d6f80685 Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 22 May 2020 10:49:00 +0530 Subject: [PATCH 020/129] fix: Added Inactive serial no status (#21849) --- erpnext/patches.txt | 2 +- erpnext/patches/v12_0/set_serial_no_status.py | 4 +++- erpnext/stock/doctype/serial_no/serial_no.json | 4 ++-- erpnext/stock/doctype/serial_no/serial_no.py | 2 ++ erpnext/stock/doctype/serial_no/serial_no_list.js | 2 ++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ea12e2f2210..78e9bf5e313 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -665,5 +665,5 @@ erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price -erpnext.patches.v12_0.set_serial_no_status +erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py index abba37d48ef..3b5f5ef3407 100644 --- a/erpnext/patches/v12_0/set_serial_no_status.py +++ b/erpnext/patches/v12_0/set_serial_no_status.py @@ -5,7 +5,7 @@ from frappe.utils import getdate, nowdate def execute(): frappe.reload_doc('stock', 'doctype', 'serial_no') - serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` + serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date, warehouse from `tabSerial No` where (status is NULL OR status='')""", as_dict = 1) if len(serial_no_list) > 20000: frappe.db.auto_commit_on_many_writes = True @@ -15,6 +15,8 @@ def execute(): status = "Delivered" elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()): status = "Expired" + elif not serial_no.get("warehouse"): + status = "Inactive" else: status = "Active" diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json index 731a7302797..d9f8b627545 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.json +++ b/erpnext/stock/doctype/serial_no/serial_no.json @@ -420,14 +420,14 @@ "fieldtype": "Select", "in_standard_filter": 1, "label": "Status", - "options": "\nActive\nDelivered\nExpired", + "options": "\nActive\nInactive\nDelivered\nExpired", "read_only": 1 } ], "icon": "fa fa-barcode", "idx": 1, "links": [], - "modified": "2020-04-08 13:29:58.517772", + "modified": "2020-05-21 19:29:58.517772", "modified_by": "Administrator", "module": "Stock", "name": "Serial No", diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 6fca584c2f2..2cb43ae2db3 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -42,6 +42,8 @@ class SerialNo(StockController): self.status = "Delivered" elif self.warranty_expiry_date and getdate(self.warranty_expiry_date) <= getdate(nowdate()): self.status = "Expired" + elif not self.warehouse: + self.status = "Inactive" else: self.status = "Active" diff --git a/erpnext/stock/doctype/serial_no/serial_no_list.js b/erpnext/stock/doctype/serial_no/serial_no_list.js index 651f790583d..7526d1d8a5c 100644 --- a/erpnext/stock/doctype/serial_no/serial_no_list.js +++ b/erpnext/stock/doctype/serial_no/serial_no_list.js @@ -5,6 +5,8 @@ frappe.listview_settings['Serial No'] = { return [__("Delivered"), "green", "delivery_document_type,is,set"]; } else if (doc.warranty_expiry_date && frappe.datetime.get_diff(doc.warranty_expiry_date, frappe.datetime.nowdate()) <= 0) { return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set"]; + } else if (!doc.warehouse) { + return [__("Inactive"), "grey", "warehouse,is,not set"]; } else { return [__("Active"), "green", "delivery_document_type,is,not set"]; } From 7522e598db613438fb63080f3f75193e8b633511 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 22 May 2020 10:59:16 +0530 Subject: [PATCH 021/129] fix: set customer and supplier details using sql (#21846) (#21854) * fix: set customer and supplier details using sql instead of slowing down the query with get_doc and save() we can just use sql to update the required values for customer and supplier Signed-off-by: Chinmay D. Pai * chore: remove extra quote Co-authored-by: Himanshu * fix: update sql query to include tabPrice List Signed-off-by: Chinmay D. Pai Co-authored-by: Himanshu (cherry picked from commit baef43977be81a9db11480cdbce7a3514093e108) Co-authored-by: Chinmay Pai --- ...er_supplier_based_on_type_of_item_price.py | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py index 60aec05466a..b8efb210a03 100644 --- a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py +++ b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py @@ -1,15 +1,29 @@ from __future__ import unicode_literals import frappe + def execute(): - invalid_selling_item_price = frappe.db.sql( - """SELECT name FROM `tabItem Price` WHERE selling = 1 and buying = 0 and (supplier IS NOT NULL or supplier = '')""" - ) - invalid_buying_item_price = frappe.db.sql( - """SELECT name FROM `tabItem Price` WHERE selling = 0 and buying = 1 and (customer IS NOT NULL or customer = '')""" - ) - docs_to_modify = invalid_buying_item_price + invalid_selling_item_price - for d in docs_to_modify: - # saving the doc will auto reset invalid customer/supplier field - doc = frappe.get_doc("Item Price", d[0]) - doc.save() \ No newline at end of file + """ + set proper customer and supplier details for item price + based on selling and buying values + """ + + # update for selling + frappe.db.sql( + """UPDATE `tabItem Price` ip, `tabPrice List` pl + SET ip.`reference` = ip.`customer`, ip.`supplier` = NULL + WHERE ip.`selling` = 1 + AND ip.`buying` = 0 + AND (ip.`supplier` IS NOT NULL OR ip.`supplier` = '') + AND ip.`price_list` = pl.`name` + AND pl.`enabled` = 1""") + + # update for buying + frappe.db.sql( + """UPDATE `tabItem Price` ip, `tabPrice List` pl + SET ip.`reference` = ip.`supplier`, ip.`customer` = NULL + WHERE ip.`selling` = 0 + AND ip.`buying` = 1 + AND (ip.`customer` IS NOT NULL OR ip.`customer` = '') + AND ip.`price_list` = pl.`name` + AND pl.`enabled` = 1""") From 44d6ce49b57673a6b407822cc2e4eb159c533ef2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 22 May 2020 11:06:19 +0530 Subject: [PATCH 022/129] fix: expense account error message in DN (#21851) (#21856) Changed error message if expense account not set for item in Delivery Note. Earlier: Expense or Difference account is mandatory for Item IT - 6 as it impacts overall stock value After fix: Expense Account not set for Item IT - 6. Please set an Expense Account for the item in the Items table (cherry picked from commit 1763d3e7067c95678089afe17dbb268f4ec9c4df) Co-authored-by: Michelle Alva <50285544+michellealva@users.noreply.github.com> --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index f5c42b49927..d4b0a8d8466 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -224,7 +224,7 @@ class StockController(AccountsController): def check_expense_account(self, item): if not item.get("expense_account"): - frappe.throw(_("Expense or Difference account is mandatory for Item {0} as it impacts overall stock value").format(item.item_code)) + frappe.throw(_("Expense Account not set for Item {0}. Please set an Expense Account for the item in the Items table").format(item.item_code)) else: is_expense_account = frappe.db.get_value("Account", From 115cedb8e9a31e522a7b7f68826348e0354ca774 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 22 May 2020 12:27:19 +0530 Subject: [PATCH 023/129] fix: Remove duplicate leave ledger entry (#21864) --- .../remove_duplicate_leave_ledger_entries.py | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py index 6353304d7aa..438f0f4ad95 100644 --- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -6,6 +6,7 @@ import frappe def execute(): """Delete duplicate leave ledger entries of type allocation created.""" + frappe.reload_doc('hr', 'doctype', 'leave_ledger_entry') if not frappe.db.a_row_exists("Leave Ledger Entry"): return @@ -15,27 +16,16 @@ def execute(): def get_duplicate_records(): """Fetch all but one duplicate records from the list of expired leave allocation.""" return frappe.db.sql_list(""" - WITH duplicate_records AS - (SELECT - name, transaction_name, is_carry_forward, - ROW_NUMBER() over(partition by transaction_name order by creation)as row - FROM `tabLeave Ledger Entry` l - WHERE (EXISTS - (SELECT name - FROM `tabLeave Ledger Entry` - WHERE - transaction_name = l.transaction_name - AND transaction_type = 'Leave Allocation' - AND name <> l.name - AND employee = l.employee - AND docstatus = 1 - AND leave_type = l.leave_type - AND is_carry_forward=l.is_carry_forward - AND to_date = l.to_date - AND from_date = l.from_date - AND is_expired = 1 - ))) - SELECT name FROM duplicate_records WHERE row > 1 + SELECT name + FROM `tabLeave Ledger Entry` + WHERE + transaction_type = 'Leave Allocation' + AND docstatus = 1 + AND is_expired = 1 + GROUP BY + employee, transaction_name, leave_type, is_carry_forward, from_date, to_date + HAVING + count(name) > 1 """) def delete_duplicate_ledger_entries(duplicate_records_list): From 5f82999397db79367b45efef72d1dceeba63886e Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 22 May 2020 12:29:19 +0530 Subject: [PATCH 024/129] fix: Item tax template not getting mapped from source to traget doc (#21863) --- erpnext/public/js/controllers/transaction.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 4b40c8d4c75..801b6fce4a6 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1614,8 +1614,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if(!r.exc) { $.each(me.frm.doc.items || [], function(i, item) { if(item.item_code && r.message.hasOwnProperty(item.item_code)) { - item.item_tax_template = r.message[item.item_code].item_tax_template; - item.item_tax_rate = r.message[item.item_code].item_tax_rate; + if (!item.item_tax_template) { + item.item_tax_template = r.message[item.item_code].item_tax_template; + item.item_tax_rate = r.message[item.item_code].item_tax_rate; + } me.add_taxes_from_item_tax_template(item.item_tax_rate); } else { item.item_tax_template = ""; From 589c5335d1068c4080029a509f09e69993f520bb Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 22 May 2020 12:41:07 +0530 Subject: [PATCH 025/129] fix: allow on submit for the delivery date field not working for sales order --- .../selling/doctype/sales_order_item/sales_order_item.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index c95f236ebba..722a1adc2fa 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -150,6 +150,7 @@ "width": "300px" }, { + "allow_on_submit": 1, "columns": 2, "depends_on": "eval: !parent.skip_delivery_note", "fieldname": "delivery_date", @@ -757,8 +758,7 @@ ], "idx": 1, "istable": 1, - "links": [], - "modified": "2020-03-05 14:20:28.085117", + "modified": "2020-05-22 12:40:01.696076", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", From 1287f16acd59965822f9aa160908d8b984e2d63b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 22 May 2020 13:13:04 +0530 Subject: [PATCH 026/129] fix: Remove duplicate leave ledger entry (#21867) --- .../remove_duplicate_leave_ledger_entries.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py index 438f0f4ad95..24286dcebf9 100644 --- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -15,8 +15,8 @@ def execute(): def get_duplicate_records(): """Fetch all but one duplicate records from the list of expired leave allocation.""" - return frappe.db.sql_list(""" - SELECT name + return frappe.db.sql(""" + SELECT name, employee, transaction_name, leave_type, is_carry_forward, from_date, to_date FROM `tabLeave Ledger Entry` WHERE transaction_type = 'Leave Allocation' @@ -26,9 +26,21 @@ def get_duplicate_records(): employee, transaction_name, leave_type, is_carry_forward, from_date, to_date HAVING count(name) > 1 + ORDER BY + creation """) def delete_duplicate_ledger_entries(duplicate_records_list): """Delete duplicate leave ledger entries.""" if not duplicate_records_list: return - frappe.db.sql('''DELETE FROM `tabLeave Ledger Entry` WHERE name in %s''', ((tuple(duplicate_records_list)), )) \ No newline at end of file + for d in duplicate_records_list: + frappe.db.sql(''' + DELETE FROM `tabLeave Ledger Entry` + WHERE name != %s + AND employee = %s + AND transaction_name = %s + AND leave_type = %s + AND is_carry_forward = %s + AND from_date = %s + AND to_date = %s + ''', tuple(d)) \ No newline at end of file From 5d8dafc2ec48b2c9ff3815be38f8033861571467 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Fri, 22 May 2020 14:18:08 +0530 Subject: [PATCH 027/129] fix(patch): rerun remove_duplicate_leave_ledger_entries (#21875) --- erpnext/patches.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 78e9bf5e313..74f729678d0 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -662,7 +662,7 @@ erpnext.patches.v12_0.set_updated_purpose_in_pick_list erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 -erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries +erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22 execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_serial_no_status #2020-05-21 From b4b6b694af15100bebca5879f21b143e1b5bc984 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 8 May 2020 15:33:35 +0530 Subject: [PATCH 028/129] fix: local variable 'lft' referenced before assignment --- erpnext/stock/doctype/warehouse/warehouse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index 6ed6044f329..47a10c68cd3 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -177,7 +177,7 @@ def convert_to_group_or_ledger(): return frappe.get_doc("Warehouse", args.docname).convert_to_group_or_ledger() def get_child_warehouses(warehouse): - lft, rgt = frappe.get_cached_value("Warehouse", warehouse, [lft, rgt]) + lft, rgt = frappe.get_cached_value("Warehouse", warehouse, ["lft", "rgt"]) return frappe.db.sql_list("""select name from `tabWarehouse` where lft >= %s and rgt =< %s""", (lft, rgt)) From 18b3c4802b6108c63d27dab3f8618e13f8973268 Mon Sep 17 00:00:00 2001 From: Vishal Dhayagude Date: Mon, 25 May 2020 15:13:09 +0530 Subject: [PATCH 029/129] fix: pound, gram to Ounce converion factor added (#21710) * fix: pound, grams to Ounce converion factor added * fix: patch date updated for UOM Conversion Factor * fix: grams move to gram * fix: duplicate entry of uom converion factor and patch added * fix: typo fix * fix: minor changes * Update patches.txt Co-authored-by: Marica --- erpnext/patches.txt | 1 + .../patches/v12_0/update_uom_conversion_factor.py | 11 +++++++++++ .../setup_wizard/data/uom_conversion_data.json | 14 ++++++++++++++ .../setup_wizard/operations/install_fixtures.py | 15 ++++++++------- 4 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 erpnext/patches/v12_0/update_uom_conversion_factor.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 74f729678d0..40d0e97e304 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -667,3 +667,4 @@ execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom +erpnext.patches.v12_0.update_uom_conversion_factor diff --git a/erpnext/patches/v12_0/update_uom_conversion_factor.py b/erpnext/patches/v12_0/update_uom_conversion_factor.py new file mode 100644 index 00000000000..b5a20aa6fd9 --- /dev/null +++ b/erpnext/patches/v12_0/update_uom_conversion_factor.py @@ -0,0 +1,11 @@ +from __future__ import unicode_literals +import frappe, json + +def execute(): + from erpnext.setup.setup_wizard.operations.install_fixtures import add_uom_data + + frappe.reload_doc("setup", "doctype", "UOM Conversion Factor") + frappe.reload_doc("setup", "doctype", "UOM") + frappe.reload_doc("stock", "doctype", "UOM Category") + + add_uom_data() \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/data/uom_conversion_data.json b/erpnext/setup/setup_wizard/data/uom_conversion_data.json index 174ecd5903e..27a917d9db8 100644 --- a/erpnext/setup/setup_wizard/data/uom_conversion_data.json +++ b/erpnext/setup/setup_wizard/data/uom_conversion_data.json @@ -1571,5 +1571,19 @@ "to_uom": "Parts Per Million", "abbr": "ppm", "value": "10000" + }, + { + "category": "Mass", + "from_uom": "Pound", + "to_uom": "Ounce", + "abbr": "oz", + "value": "16" + }, + { + "category": "Mass", + "from_uom": "Gram", + "to_uom": "Ounce", + "abbr": "oz", + "value": "0.035274" } ] \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index ebd7b509396..85e070a97f7 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -335,13 +335,14 @@ def add_uom_data(): "category_name": _(d.get("category")) }).insert(ignore_permissions=True) - uom_conversion = frappe.get_doc({ - "doctype": "UOM Conversion Factor", - "category": _(d.get("category")), - "from_uom": _(d.get("from_uom")), - "to_uom": _(d.get("to_uom")), - "value": d.get("value") - }).insert(ignore_permissions=True) + if not frappe.db.exists("UOM Conversion Factor", {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))}): + uom_conversion = frappe.get_doc({ + "doctype": "UOM Conversion Factor", + "category": _(d.get("category")), + "from_uom": _(d.get("from_uom")), + "to_uom": _(d.get("to_uom")), + "value": d.get("value") + }).insert(ignore_permissions=True) def add_market_segments(): records = [ From b9d5c71405f51df5c3e1a3fefff6349d366df062 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 25 May 2020 15:25:42 +0530 Subject: [PATCH 030/129] fix: filter batches based on item and warehouse in Pick List (#21778) Co-authored-by: Marica --- erpnext/stock/doctype/pick_list/pick_list.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index d46b98b461b..3a5ef769805 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -31,10 +31,16 @@ frappe.ui.form.on('Pick List', { }; }); frm.set_query('item_code', 'locations', () => { + return erpnext.queries.item({ "is_stock_item": 1 }); + }); + frm.set_query('batch_no', 'locations', (frm, cdt, cdn) => { + const row = locals[cdt][cdn]; return { + query: 'erpnext.controllers.queries.get_batch_no', filters: { - is_stock_item: 1 - } + item_code: row.item_code, + warehouse: row.warehouse + }, }; }); }, From 410196974e6cf89e769bee96ba033f81ac7bb274 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 25 May 2020 16:38:22 +0530 Subject: [PATCH 031/129] fix: comparison operator --- erpnext/stock/doctype/warehouse/warehouse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index 47a10c68cd3..312488984cd 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -180,7 +180,7 @@ def get_child_warehouses(warehouse): lft, rgt = frappe.get_cached_value("Warehouse", warehouse, ["lft", "rgt"]) return frappe.db.sql_list("""select name from `tabWarehouse` - where lft >= %s and rgt =< %s""", (lft, rgt)) + where lft >= %s and rgt <= %s""", (lft, rgt)) def get_warehouses_based_on_account(account, company=None): warehouses = [] From 4e70ecb97d9932d9e40bb7401d77f1c47026445f Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 25 May 2020 18:26:42 +0530 Subject: [PATCH 032/129] fix: Item Price and Add to Cart not showing on Website (#21905) * fix: Item Price and Add to Cart not showing on Website * fix: Use None as default argument --- erpnext/shopping_cart/cart.py | 19 ++++++++++++------- erpnext/shopping_cart/product_info.py | 8 +++++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 8e644f4144d..a33f4a12242 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -319,7 +319,7 @@ def apply_cart_settings(party=None, quotation=None): def set_price_list_and_rate(quotation, cart_settings): """set price list based on billing territory""" - _set_price_list(quotation, cart_settings) + _set_price_list(cart_settings, quotation) # reset values quotation.price_list_currency = quotation.currency = \ @@ -334,23 +334,28 @@ def set_price_list_and_rate(quotation, cart_settings): # set it in cookies for using in product page frappe.local.cookie_manager.set_cookie("selling_price_list", quotation.selling_price_list) -def _set_price_list(quotation, cart_settings): +def _set_price_list(cart_settings, quotation=None): """Set price list based on customer or shopping cart default""" from erpnext.accounts.party import get_default_price_list # check if customer price list exists selling_price_list = None - if quotation.party_name: - selling_price_list = frappe.db.get_value('Customer', quotation.party_name, 'default_price_list') + if quotation and quotation.get("party_name"): + selling_price_list = frappe.db.get_value('Customer', quotation.get("party_name"), 'default_price_list') # else check for territory based price list if not selling_price_list: selling_price_list = cart_settings.price_list - if not selling_price_list and quotation.party_name: - selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.party_name)) + party_name = quotation.get("party_name") if quotation else get_party().get("name") - quotation.selling_price_list = selling_price_list + if not selling_price_list and party_name: + selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) + + if quotation: + quotation.selling_price_list = selling_price_list + + return selling_price_list def set_taxes(quotation, cart_settings): """set taxes based on billing territory""" diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index 21ee335125b..7c08f5b5b24 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from erpnext.shopping_cart.cart import _get_cart_quotation +from erpnext.shopping_cart.cart import _get_cart_quotation, _set_price_list from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings \ import get_shopping_cart_settings, show_quantity_in_website from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status @@ -21,9 +21,11 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): if not skip_quotation_creation: cart_quotation = _get_cart_quotation() + selling_price_list = cart_quotation.get("selling_price_list") if cart_quotation else _set_price_list(cart_settings, None) + price = get_price( item_code, - cart_quotation.selling_price_list, + selling_price_list, cart_settings.default_customer_group, cart_settings.company ) @@ -42,7 +44,7 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): if product_info["price"]: if frappe.session.user != "Guest": - item = cart_quotation.get({"item_code": item_code}) + item = cart_quotation.get({"item_code": item_code}) if cart_quotation else None if item: product_info["qty"] = item[0].qty From bbec37f71300904a1d9b664398a9bceff71298ea Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 25 May 2020 18:39:26 +0530 Subject: [PATCH 033/129] fix: Validation for dates (#21887) --- erpnext/hr/doctype/upload_attendance/upload_attendance.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py index 1707e3578bf..bea1fef8a8d 100644 --- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py +++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py @@ -20,6 +20,9 @@ def get_template(): args = frappe.local.form_dict + if getdate(args.from_date) > getdate(args.to_date): + frappe.throw(_("To Date should be greater than From Date")) + w = UnicodeWriter() w = add_header(w) From 8ae7bb2bc4b3a4292a0b4a837b7704c15664d140 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 25 May 2020 19:20:54 +0530 Subject: [PATCH 034/129] fix: fetch depreciation amount only if depr entry is made (#21894) --- .../asset_depreciations_and_balances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index d7efbad240d..80bccafd28e 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -111,7 +111,7 @@ def get_assets(filters): 0 end), 0) as depreciation_amount_during_the_period from `tabAsset` a, `tabDepreciation Schedule` ds - where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != '' group by a.asset_category union SELECT a.asset_category, From 277f5273a47057f8bd8a45fddae1eb0c275a07bd Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 25 May 2020 21:46:04 +0530 Subject: [PATCH 035/129] chore: Add Import Supplier Invoice to Menu --- erpnext/config/buying.py | 21 +++++++- .../import_supplier_invoice.json | 48 +++++++++++++++++-- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index 1d4054786e6..29589fbcd48 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -1,8 +1,9 @@ from __future__ import unicode_literals +import frappe from frappe import _ def get_data(): - return [ + config = [ { "label": _("Purchasing"), "icon": "fa fa-star", @@ -243,3 +244,21 @@ def get_data(): }, ] + + regional = { + "label": _("Regional"), + "items": [ + { + "type": "doctype", + "name": "Import Supplier Invoice", + "description": _("Import Italian Supplier Invoice."), + "onboard": 1, + } + ] + } + + countries = frappe.get_all("Company", fields="country") + countries = [country["country"] for country in countries] + if "Italy" in countries: + config.append(regional) + return config \ No newline at end of file diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json index 59e955c23f4..6b45841b465 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json @@ -1,5 +1,4 @@ { - "actions": [], "creation": "2019-10-15 12:33:21.845329", "doctype": "DocType", "editable_grid": 1, @@ -92,13 +91,54 @@ "label": "Upload XML Invoices" } ], - "links": [], - "modified": "2019-12-10 16:37:26.793398", + "modified": "2020-05-25 21:32:49.064579", "modified_by": "Administrator", "module": "Regional", "name": "Import Supplier Invoice", "owner": "Administrator", - "permissions": [], + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase User" + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Auditor" + }, + { + "permlevel": 1, + "read": 1, + "role": "Accounts Manager", + "write": 1 + } + ], "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 From 7177514b0db8f8860f9169cdb1cb7a1999e14c19 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 26 May 2020 17:59:24 +0530 Subject: [PATCH 036/129] fix: adding dashboard in course and assessment plan (#21889) (#21919) * Adding dashboards * adding dashboard in course and assessment plan (cherry picked from commit 0a147d5c766a69ee98543fd12f1add57c8792e44) Co-authored-by: Anupam Kumar --- .../assessment_plan_dashboard.py | 17 +++++++++++++ .../doctype/course/course_dashboard.py | 25 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py create mode 100644 erpnext/education/doctype/course/course_dashboard.py diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py new file mode 100644 index 00000000000..c36dfb11b54 --- /dev/null +++ b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py @@ -0,0 +1,17 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'assessment_plan', + 'non_standard_fieldnames': { + }, + 'transactions': [ + { + 'label': _('Assessment'), + 'items': ['Assessment Result'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/course/course_dashboard.py b/erpnext/education/doctype/course/course_dashboard.py new file mode 100644 index 00000000000..752af29a9db --- /dev/null +++ b/erpnext/education/doctype/course/course_dashboard.py @@ -0,0 +1,25 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'course', + 'non_standard_fieldnames': { + }, + 'transactions': [ + { + 'label': _('Course'), + 'items': ['Course Enrollment', 'Course Schedule'] + }, + { + 'label': _('Student'), + 'items': ['Student Group'] + }, + { + 'label': _('Assessment'), + 'items': ['Assessment Plan'] + }, + ] + } \ No newline at end of file From a3ec869c58397915882b4ff1ee583406dc7e8f9a Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 26 May 2020 18:00:27 +0530 Subject: [PATCH 037/129] fix: showing Wrong balance on allocation boundary dates (#21908) --- .../employee_leave_balance.py | 10 +++++----- .../employee_leave_balance_summary.py | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index d20018eef34..d5050e73d46 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import flt +from frappe.utils import flt, add_days from erpnext.hr.doctype.leave_application.leave_application \ import get_leave_balance_on, get_leaves_for_period @@ -26,8 +26,8 @@ def get_columns(leave_types): for leave_type in leave_types: columns.append(_(leave_type) + " " + _("Opening") + ":Float:160") columns.append(_(leave_type) + " " + _("Allocated") + ":Float:160") - columns.append(_(leave_type) + " " + _("Expired") + ":Float:160") columns.append(_(leave_type) + " " + _("Taken") + ":Float:160") + columns.append(_(leave_type) + " " + _("Expired") + ":Float:160") columns.append(_(leave_type) + " " + _("Balance") + ":Float:160") return columns @@ -84,7 +84,7 @@ def calculate_leaves_details(filters, leave_type, employee): # removing expired leaves leaves_taken = leaves_deducted - remove_expired_leave(ledger_entries) - opening = get_leave_balance_on(employee.name, leave_type, filters.from_date) + opening = get_leave_balance_on(employee.name, leave_type, add_days(filters.from_date, -1)) new_allocation , expired_allocation = get_allocated_and_expired_leaves(ledger_entries, filters.from_date, filters.to_date) @@ -94,7 +94,7 @@ def calculate_leaves_details(filters, leave_type, employee): #Formula for calculating closing balance closing = max(opening + new_allocation - (leaves_taken + expired_leaves), 0) - return [opening, new_allocation, expired_leaves, leaves_taken, closing] + return [opening, new_allocation, leaves_taken, expired_leaves, closing] def remove_expired_leave(records): @@ -113,7 +113,7 @@ def get_allocated_and_expired_leaves(records, from_date, to_date): expired_leaves = 0 for record in records: - if record.to_date <= getdate(to_date) and record.leaves>0: + if record.to_date < getdate(to_date) and record.leaves>0: expired_leaves += record.leaves if record.from_date >= getdate(from_date) and record.leaves>0: diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py index 53f8f41915e..21646425bb6 100644 --- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py +++ b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py @@ -45,16 +45,16 @@ def get_columns(): 'fieldtype': 'Float', 'fieldname': 'new_allocation', 'width': 120, - }, { - 'label': _('Expired Leaves'), - 'fieldtype': 'Float', - 'fieldname': 'expired_leaves', - 'width': 120, }, { 'label': _('Leaves Taken'), 'fieldtype': 'float', 'fieldname': 'leaves_taken', 'width': 120, + }, { + 'label': _('Expired Leaves'), + 'fieldtype': 'Float', + 'fieldname': 'expired_leaves', + 'width': 120, }, { 'label': _('Closing Balance'), 'fieldtype': 'float', @@ -96,8 +96,8 @@ def get_data(filters): leave_details = calculate_leaves_details(filters, leave_type, employee) row.opening_balance = flt(leave_details[0]) row.new_allocation = flt(leave_details[1]) - row.expired_leaves = flt(leave_details[2]) - row.leaves_taken = flt(leave_details[3]) + row.leaves_taken = flt(leave_details[2]) + row.expired_leaves = flt(leave_details[3]) row.closing_balance = flt(leave_details[4]) data.append(row) From b09417873405bbbab0aec1173a487f5383c24658 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 26 May 2020 18:02:08 +0530 Subject: [PATCH 038/129] fix(UAE): Incorrect VAT rate display in Sales Invoice (#21883) --- erpnext/regional/united_arab_emirates/utils.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index a01c6ceec36..772bbf5914b 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -1,6 +1,8 @@ from __future__ import unicode_literals +import frappe from frappe.utils import flt from erpnext.controllers.taxes_and_totals import get_itemised_tax +from six import iteritems def update_itemised_tax_data(doc): if not doc.taxes: return @@ -9,7 +11,14 @@ def update_itemised_tax_data(doc): for row in doc.items: tax_rate = 0.0 - if itemised_tax.get(row.item_code): + item_tax_rate = frappe.parse_json(row.item_tax_rate) + + # First check if tax rate is present + # If not then look up in item_wise_tax_detail + if item_tax_rate: + for account, rate in iteritems(item_tax_rate): + tax_rate += rate + elif itemised_tax.get(row.item_code): tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()]) row.tax_rate = flt(tax_rate, row.precision("tax_rate")) From 9ad46f7487d56f96d9088acf98e189935583723b Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 26 May 2020 18:08:08 +0530 Subject: [PATCH 039/129] fix: Added column Expired Leave (#21859) --- .../doctype/leave_application/leave_application.py | 11 ++++++++++- .../leave_application_dashboard.html | 12 +++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 6ef4a303d87..1622fb38eec 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -428,14 +428,23 @@ def get_leave_details(employee, date): leave_allocation = {} for d in allocation_records: allocation = allocation_records.get(d, frappe._dict()) + + total_allocated_leaves = frappe.db.get_value('Leave Allocation', { + 'from_date': ('<=', date), + 'to_date': ('>=', date), + 'leave_type': allocation.leave_type, + }, 'SUM(total_leaves_allocated)') or 0 + remaining_leaves = get_leave_balance_on(employee, d, date, to_date = allocation.to_date, consider_all_leaves_in_the_allocation_period=True) + end_date = allocation.to_date leaves_taken = get_leaves_for_period(employee, d, allocation.from_date, end_date) * -1 leaves_pending = get_pending_leaves_for_period(employee, d, allocation.from_date, end_date) leave_allocation[d] = { - "total_leaves": allocation.total_leaves_allocated, + "total_leaves": total_allocated_leaves, + "expired_leaves": total_allocated_leaves - (remaining_leaves + leaves_taken), "leaves_taken": leaves_taken, "pending_leaves": leaves_pending, "remaining_leaves": remaining_leaves} diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html index 2385b6ac1c4..295f3b43419 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html +++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html @@ -4,11 +4,12 @@ - - - - - + + + + + + @@ -17,6 +18,7 @@ + From c9014b1ba9445538e24e2667a0bb2ab6f4d4d265 Mon Sep 17 00:00:00 2001 From: Andrew McLeod Date: Tue, 26 May 2020 13:39:00 +0100 Subject: [PATCH 040/129] fix: website_list_for_contact, fix changed frappe.db.exist keyword argument (#21827) --- erpnext/controllers/website_list_for_contact.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py index ed379389d7e..ecf041efd17 100644 --- a/erpnext/controllers/website_list_for_contact.py +++ b/erpnext/controllers/website_list_for_contact.py @@ -155,7 +155,7 @@ def has_website_permission(doc, ptype, user, verbose=False): return frappe.db.exists(doctype, get_customer_filter(doc, customers)) elif suppliers: fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier' - return frappe.db.exists(doctype, filters={ + return frappe.db.exists(doctype, { 'name': doc.name, fieldname: ["in", suppliers] }) From 61257d829ec3f263c2edb6b460fd810f25672ebc Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 26 May 2020 18:11:39 +0530 Subject: [PATCH 041/129] fix: throw error if no serial numbers are found in Pick List (#21914) Co-authored-by: Marica --- erpnext/stock/doctype/pick_list/pick_list.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index c79025c1fb7..29c6c2ccbaa 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -24,6 +24,9 @@ class PickList(Document): for item in self.locations: if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'): continue + if not item.serial_no: + frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}".format( + frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse)))) if len(item.serial_no.split('\n')) == item.picked_qty: continue frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity') From f912d780235801d4db315836a3e9e0ef439852d6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 26 May 2020 18:21:31 +0530 Subject: [PATCH 042/129] fix: check for warehouse in the woocommerce settings (#21925) (#21936) (cherry picked from commit ddc170521f896567a1e2d875628432a6ab0c0abe) Co-authored-by: Mangesh-Khairnar --- .../erpnext_integrations/connectors/woocommerce_connection.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 44f87e0462e..1b0c9f60b6e 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -182,7 +182,8 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l company_abbr = frappe.db.get_value('Company', woocommerce_settings.company, 'abbr') default_warehouse = _("Stores - {0}", sys_lang).format(company_abbr) - if not frappe.db.exists("Warehouse", default_warehouse): + if not frappe.db.exists("Warehouse", default_warehouse) \ + and not woocommerce_settings.warehouse: frappe.throw(_("Please set Warehouse in Woocommerce Settings")) for item in order.get("line_items"): From 3c0e22c98ccfdf48f6c9025647f252fd78305c82 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 26 May 2020 18:37:25 +0530 Subject: [PATCH 043/129] fix: shopify error message on failure of sales order creation (#21924) (#21937) (cherry picked from commit 7402451b9695f97937d534ae649c4029491cd36a) Co-authored-by: Mangesh-Khairnar --- erpnext/erpnext_integrations/connectors/shopify_connection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index 3be08a2757a..3d92d55faaf 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -95,10 +95,10 @@ def create_sales_order(shopify_order, shopify_settings, company=None): items = get_order_items(shopify_order.get("line_items"), shopify_settings) if not items: - message = 'Following items are exists in order but relevant record not found in Product master' + message = 'Following items exists in the shopify order but relevant records were not found in the shopify Product master' message += "\n" + ", ".join(product_not_exists) - make_shopify_log(status="Error", exception=e, rollback=True) + make_shopify_log(status="Error", exception=message, rollback=True) return '' From 9720406086e7943df7b8312b9c428154cf25bc6a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 26 May 2020 19:23:45 +0530 Subject: [PATCH 044/129] fix: Do not add filters in report on accounting dimension creation if it already exists --- .../accounts_payable/accounts_payable.js | 9 +-------- .../accounts_payable_summary.js | 9 +-------- .../accounts_receivable/accounts_receivable.js | 9 +-------- .../accounts_receivable_summary.js | 9 +-------- .../report/balance_sheet/balance_sheet.js | 2 ++ erpnext/accounts/report/cash_flow/cash_flow.js | 2 ++ .../report/general_ledger/general_ledger.js | 9 +-------- .../profit_and_loss_statement.js | 2 ++ .../purchase_register/purchase_register.js | 9 +-------- .../report/sales_register/sales_register.js | 9 +-------- .../report/trial_balance/trial_balance.js | 9 +-------- erpnext/public/js/financial_statements.js | 11 +---------- erpnext/public/js/utils.js | 18 ++++++++++++++++++ 13 files changed, 33 insertions(+), 74 deletions(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index df700ec9d3c..2d2c95e79e6 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -135,12 +135,5 @@ frappe.query_reports["Accounts Payable"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Payable', 9); 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 4a9f1b0dc44..6fc0427da15 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -104,12 +104,5 @@ frappe.query_reports["Accounts Payable Summary"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Payable Summary', 9); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 5d0154f597e..a49cc321495 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -199,12 +199,5 @@ frappe.query_reports["Accounts Receivable"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Receivable', 9); 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 d54824b6855..d6f318b0eda 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -122,11 +122,4 @@ frappe.query_reports["Accounts Receivable Summary"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Receivable Summary', 9); diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index c4c24c0bab3..4a4ad4d71cb 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -4,6 +4,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Balance Sheet', 10); + frappe.query_reports["Balance Sheet"]["filters"].push({ "fieldname": "accumulated_values", "label": __("Accumulated Values"), diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index 89244c3781a..4e03b886a46 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -5,6 +5,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Cash Flow"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Cash Flow', 10); + // The last item in the array is the definition for Presentation Currency // filter. It won't be used in cash flow for now so we pop it. Please take // of this if you are working here. diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index ac49d373d47..88ba006d527 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -160,12 +160,5 @@ frappe.query_reports["General Ledger"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('General Ledger', 15) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index baa0bda7005..568a7900216 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -6,6 +6,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Profit and Loss Statement"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Profit and Loss Statement', 10); + frappe.query_reports["Profit and Loss Statement"]["filters"].push( { "fieldname": "project", diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js index b2b95b2b81b..f34ea571639 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.js +++ b/erpnext/accounts/report/purchase_register/purchase_register.js @@ -56,11 +56,4 @@ frappe.query_reports["Purchase Register"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); \ No newline at end of file +erpnext.utils.add_dimensions('Purchase Register', 7); \ No newline at end of file diff --git a/erpnext/accounts/report/sales_register/sales_register.js b/erpnext/accounts/report/sales_register/sales_register.js index 9dee656d4a9..85bbceab827 100644 --- a/erpnext/accounts/report/sales_register/sales_register.js +++ b/erpnext/accounts/report/sales_register/sales_register.js @@ -68,12 +68,5 @@ frappe.query_reports["Sales Register"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Sales Register', 7); diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 07752e1e626..9c0854c5d3a 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -102,14 +102,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "initial_depth": 3 } - erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); - }); + erpnext.utils.add_dimensions('Trial Balance', 6); }); diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index dead309a5d0..d5f83d60298 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -62,7 +62,7 @@ erpnext.financial_statements = { } }; -function get_filters(){ +function get_filters() { let filters = [ { "fieldname":"company", @@ -129,15 +129,6 @@ function get_filters(){ } ] - erpnext.dimension_filters.forEach((dimension) => { - filters.push({ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); - }); - return filters; } diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 224f4f5525d..cf9e83436ec 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -145,6 +145,7 @@ $.extend(erpnext.utils, { }, get_party_name: function(party_type) { + console.log("********"); var dict = {'Customer': 'customer_name', 'Supplier': 'supplier_name', 'Employee': 'employee_name', 'Member': 'member_name'}; return dict[party_type]; @@ -191,6 +192,23 @@ $.extend(erpnext.utils, { }) }, + add_dimensions: function(report_name, index) { + let filters = frappe.query_reports[report_name].filters; + + erpnext.dimension_filters.forEach((dimension) => { + let found = filters.some(el => el.fieldname === dimension['fieldname']); + + if (!found) { + filters.splice(index, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + } + }); + }, + make_subscription: function(doctype, docname) { frappe.call({ method: "frappe.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat", From bb2bcc8a30609a441ea794876098634bda66a810 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 26 May 2020 19:26:47 +0530 Subject: [PATCH 045/129] fix: Remove console statement --- erpnext/public/js/utils.js | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index cf9e83436ec..fb7471150cc 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -145,7 +145,6 @@ $.extend(erpnext.utils, { }, get_party_name: function(party_type) { - console.log("********"); var dict = {'Customer': 'customer_name', 'Supplier': 'supplier_name', 'Employee': 'employee_name', 'Member': 'member_name'}; return dict[party_type]; From aee9a61e0d7ca856fa14ca57c8d07d91cb6e23a4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 27 May 2020 12:46:18 +0530 Subject: [PATCH 046/129] fix: Post Dated unallocated amount not considered in Advance Amount in AR/AP summary (#21838) * fix: Post Dated unallocateed amount not considered in Advance Amount in AR/AP summary report * fix: Add future payment filter in AR/AP summary * fix: Show unallocated future payments only till current creation date * fix: Remove extra query * fix: Remove debug * fix: Condition --- erpnext/accounts/party.py | 8 ++++++-- .../accounts_receivable/accounts_receivable.py | 13 ++++++++++--- .../accounts_receivable_summary.js | 7 ++++++- .../accounts_receivable_summary.py | 2 +- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index b5dfbde5a04..b0b64c8b56f 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -605,10 +605,14 @@ def get_party_shipping_address(doctype, name): else: return '' -def get_partywise_advanced_payment_amount(party_type, posting_date = None, company=None): +def get_partywise_advanced_payment_amount(party_type, posting_date = None, future_payment=0, company=None): cond = "1=1" if posting_date: - cond = "posting_date <= '{0}'".format(posting_date) + if future_payment: + cond = "posting_date <= '{0}' OR DATE(creation) <= '{0}' """.format(posting_date) + else: + cond = "posting_date <= '{0}'".format(posting_date) + if company: cond += "and company = '{0}'".format(company) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index e9c286fcf0d..0fe62143e2d 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -559,6 +559,14 @@ class ReceivablePayableReport(object): conditions, values = self.prepare_conditions() order_by = self.get_order_by_condition() + if self.filters.show_future_payments: + values.insert(2, self.filters.report_date) + + date_condition = """AND (posting_date <= %s + OR (against_voucher IS NULL AND DATE(creation) <= %s))""" + else: + date_condition = "AND posting_date <=%s" + if self.filters.get(scrub(self.party_type)): select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit" else: @@ -574,9 +582,8 @@ class ReceivablePayableReport(object): docstatus < 2 and party_type=%s and (party is not null and party != '') - and posting_date <= %s - {1} {2}""" - .format(select_fields, conditions, order_by), values, as_dict=True) + {1} {2} {3}""" + .format(select_fields, date_condition, conditions, order_by), values, as_dict=True) def get_sales_invoices_or_customers_based_on_sales_person(self): if self.filters.get("sales_person"): 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 d6f318b0eda..ed46cf2d9f1 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -111,7 +111,12 @@ frappe.query_reports["Accounts Receivable Summary"] = { "fieldname":"based_on_payment_terms", "label": __("Based On Payment Terms"), "fieldtype": "Check", - } + }, + { + "fieldname":"show_future_payments", + "label": __("Show Future Payments"), + "fieldtype": "Check", + }, ], onload: function(report) { diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py index aa6b42e89d0..657b3e8f204 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -33,7 +33,7 @@ class AccountsReceivableSummary(ReceivablePayableReport): self.get_party_total(args) party_advance_amount = get_partywise_advanced_payment_amount(self.party_type, - self.filters.report_date, self.filters.company) or {} + self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {} for party, party_dict in iteritems(self.party_total): if party_dict.outstanding == 0: From b4a3e5f70be8b7c12f2bdd41534df95d975d2fc2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 27 May 2020 20:30:41 +0530 Subject: [PATCH 047/129] fix: Total for ageing column 121-Above (#21973) --- .../accounts/report/accounts_receivable/accounts_receivable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 0fe62143e2d..4015b056c58 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -534,7 +534,7 @@ class ReceivablePayableReport(object): def get_ageing_data(self, entry_date, row): # [0-30, 30-60, 60-90, 90-120, 120-above] - row.range1 = row.range2 = row.range3 = row.range4 = range5 = 0.0 + row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0 if not (self.age_as_on and entry_date): return From d9e6900e0759f86f668f8879fa62e6b2e199545b Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 27 May 2020 21:53:01 +0530 Subject: [PATCH 048/129] fix: Added permission via regional setup and patch --- erpnext/patches.txt | 1 + ...ian_import_supplier_invoice_permissions.py | 12 +++++ .../import_supplier_invoice.json | 44 +------------------ erpnext/regional/italy/setup.py | 20 +++++++++ 4 files changed, 34 insertions(+), 43 deletions(-) create mode 100644 erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 74f729678d0..10c875888e1 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -667,3 +667,4 @@ execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom +erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions \ No newline at end of file diff --git a/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py new file mode 100644 index 00000000000..a6011c4dace --- /dev/null +++ b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py @@ -0,0 +1,12 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.regional.italy.setup import add_permissions + +def execute(): + countries = frappe.get_all("Company", fields="country") + countries = [country["country"] for country in countries] + if "Italy" in countries: + add_permissions() \ No newline at end of file diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json index 6b45841b465..c1680c4b492 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json @@ -96,49 +96,7 @@ "module": "Regional", "name": "Import Supplier Invoice", "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "share": 1, - "write": 1 - }, - { - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase User" - }, - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "share": 1, - "write": 1 - }, - { - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Auditor" - }, - { - "permlevel": 1, - "read": 1, - "role": "Accounts Manager", - "write": 1 - } - ], + "permissions": [], "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 diff --git a/erpnext/regional/italy/setup.py b/erpnext/regional/italy/setup.py index 2d0ad66b0a0..6ab73413df2 100644 --- a/erpnext/regional/italy/setup.py +++ b/erpnext/regional/italy/setup.py @@ -7,11 +7,13 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_fields +from frappe.permissions import add_permission, update_permission_property from erpnext.regional.italy import fiscal_regimes, tax_exemption_reasons, mode_of_payment_codes, vat_collectability_options def setup(company=None, patch=True): make_custom_fields() setup_report() + add_permissions() def make_custom_fields(update=True): invoice_item_fields = [ @@ -200,3 +202,21 @@ def setup_report(): dict(role='Accounts Manager') ] )).insert() + +def add_permissions(): + doctype = 'Import Supplier Invoice' + add_permission(doctype, 'All', 0) + + for role in ('Accounts Manager', 'Accounts User','Purchase User', 'Auditor'): + add_permission(doctype, role, 0) + update_permission_property(doctype, role, 0, 'print', 1) + update_permission_property(doctype, role, 0, 'report', 1) + + if role in ('Accounts Manager', 'Accounts User'): + update_permission_property(doctype, role, 0, 'write', 1) + update_permission_property(doctype, role, 0, 'create', 1) + + update_permission_property(doctype, 'Accounts Manager', 0, 'delete', 1) + add_permission(doctype, 'Accounts Manager', 1) + update_permission_property(doctype, 'Accounts Manager', 1, 'write', 1) + update_permission_property(doctype, 'Accounts Manager', 1, 'create', 1) \ No newline at end of file From a23f92900547412736db7eec4797cc7db5880dcb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 27 May 2020 22:44:50 +0530 Subject: [PATCH 049/129] fix: TDS computation summary report --- .../tax_withholding_category/tax_withholding_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index dd6b4fdc603..30bac5efa68 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -180,7 +180,7 @@ def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=No if company: condition += "and company =%s" % (company) if from_date and to_date: - condition += "and posting_date between %s and %s" % (company, from_date, to_date) + condition += "and posting_date between %s and %s" % (from_date, to_date) ## Appending the same supplier again if length of suppliers list is 1 ## since tuple of single element list contains None, For example ('Test Supplier 1', ) From 64078da47b680027a745ef03f41f8b430f9289d0 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 28 May 2020 11:10:32 +0530 Subject: [PATCH 050/129] fix: change description for field (#21995) (#21996) grammatical fixes for description of Default Customer (cherry picked from commit f7beeff2d8633538372a9bf992d9249869428d1f) Co-authored-by: Michelle Alva <50285544+michellealva@users.noreply.github.com> --- .../doctype/shopify_settings/shopify_settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json index 8f1b746e5b6..d1e5f40d6c1 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json @@ -133,7 +133,7 @@ "label": "Customer Settings" }, { - "description": "If Shopify not contains a customer in Order, then while syncing Orders, the system will consider default customer for order", + "description": "If Shopify does not have a customer in the order, then while syncing the orders, the system will consider the default customer for the order", "fieldname": "default_customer", "fieldtype": "Link", "label": "Default Customer", @@ -277,4 +277,4 @@ ], "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} From d65000a3614ca281b5f7983f8f6195e5f2bdfe58 Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Thu, 28 May 2020 12:52:58 +0530 Subject: [PATCH 051/129] fix: change modified time changed modified time to sync the changes (cherry picked from commit 8653419c4cd5241a09f9bb34bdd269ad8fcdc0d1) --- .../doctype/shopify_settings/shopify_settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json index d1e5f40d6c1..5339c99155b 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json @@ -258,7 +258,7 @@ } ], "issingle": 1, - "modified": "2019-09-13 12:32:11.384757", + "modified": "2020-05-28 12:32:11.384757", "modified_by": "umair@erpnext.com", "module": "ERPNext Integrations", "name": "Shopify Settings", From 0fcb79f3610f214e0b584a1e52ad60f4e68e3f17 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 29 May 2020 10:17:18 +0530 Subject: [PATCH 052/129] fix: make transaction date of the oldest transaction as the last integration date (#22017) * fix: make transaction date of the oldest transaction as the last integration date * fix: only save end date when transactions are returned --- .../doctype/plaid_settings/plaid_settings.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index c175eaaa251..81fb9843f61 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -124,10 +124,12 @@ def add_account_subtype(account_subtype): @frappe.whitelist() def sync_transactions(bank, bank_account): + ''' Sync transactions based on the last integration date as the start date, after sync is completed + add the transaction date of the oldest transaction as the last integration date ''' - last_sync_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date") - if last_sync_date: - start_date = formatdate(last_sync_date, "YYYY-MM-dd") + last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date") + if last_transaction_date: + start_date = formatdate(last_transaction_date, "YYYY-MM-dd") else: start_date = formatdate(add_months(today(), -12), "YYYY-MM-dd") end_date = formatdate(today(), "YYYY-MM-dd") @@ -139,12 +141,14 @@ def sync_transactions(bank, bank_account): for transaction in reversed(transactions): result += new_bank_transaction(transaction) - frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( - len(result), bank_account, start_date, end_date)) + if result: + last_transaction_date = frappe.db.get_value('Bank Transaction', result.pop(), 'date') - frappe.db.set_value("Bank Account", bank_account, "last_integration_date", getdate(end_date)) + frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( + len(result), bank_account, start_date, end_date)) + + frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date) - return result except Exception: frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error")) From 840225d47b1d8f25a7843110975e2719114f8f08 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 5 May 2020 16:10:47 +0530 Subject: [PATCH 053/129] fix: cannot make payment entry against shareholder (#21597) (cherry picked from commit d46696fa034e41a33008bdb232529d4162f5956a) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index ab7aa15e3dd..eae801368ca 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -227,6 +227,8 @@ class PaymentEntry(AccountsController): valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry") elif self.party_type == "Employee": valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance") + elif self.party_type == "Shareholder": + valid_reference_doctypes = ("Journal Entry") for d in self.get("references"): if not d.allocated_amount: From 5b695d6e9feea51bf9100f97907122f0c868131b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 29 May 2020 21:26:16 +0530 Subject: [PATCH 054/129] fix: transaction date not found in sales invoice (bp #22001) (#22029) * fix: transaction date not found in sales invoice (cherry picked from commit 603cc3d05e8729f9133ef30b228db53eb8377e15) * chore: tests (cherry picked from commit dd39ba00140a348af397c899d93576a0f8d6ea6c) Co-authored-by: Saqib Ansari --- .../doctype/pricing_rule/test_pricing_rule.py | 44 +++++++++++++++++++ .../accounts/doctype/pricing_rule/utils.py | 3 +- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 2da71dfd0e0..2bf0b725635 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -385,6 +385,50 @@ class TestPricingRule(unittest.TestCase): so.load_from_db() self.assertEqual(so.items[1].is_free_item, 1) self.assertEqual(so.items[1].item_code, "_Test Item 2") + + def test_cumulative_pricing_rule(self): + frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule') + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Cumulative Pricing Rule", + "apply_on": "Item Code", + "currency": "USD", + "items": [{ + "item_code": "_Test Item", + }], + "is_cumulative": 1, + "selling": 1, + "applicable_for": "Customer", + "customer": "_Test Customer", + "rate_or_discount": "Discount Percentage", + "rate": 0, + "min_amt": 0, + "max_amt": 10000, + "discount_percentage": 17.5, + "price_or_product_discount": "Price", + "company": "_Test Company", + "valid_from": frappe.utils.nowdate(), + "valid_upto": frappe.utils.nowdate() + } + frappe.get_doc(test_record.copy()).insert() + + args = frappe._dict({ + "item_code": "_Test Item", + "company": "_Test Company", + "price_list": "_Test Price List", + "currency": "_Test Currency", + "doctype": "Sales Invoice", + "conversion_rate": 1, + "price_list_currency": "_Test Currency", + "plc_conversion_rate": 1, + "order_type": "Sales", + "customer": "_Test Customer", + "name": None, + "transaction_date": frappe.utils.nowdate() + }) + details = get_item_details(args) + + self.assertTrue(details) def make_pricing_rule(**args): args = frappe._dict(args) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index cb05481df5a..53115f92d01 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -366,8 +366,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]): sum_qty, sum_amt = [0, 0] doctype = doc.get('parenttype') or doc.doctype - date_field = ('transaction_date' - if doc.get('transaction_date') else 'posting_date') + date_field = 'transaction_date' if frappe.get_meta(doctype).has_field('transaction_date') else 'posting_date' child_doctype = '{0} Item'.format(doctype) apply_on = frappe.scrub(pr_doc.get('apply_on')) From 592965a4d9ed9069a587a41f1f2034dfe5e27708 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 29 May 2020 21:26:29 +0530 Subject: [PATCH 055/129] fix: disposed asset creates inconsistencies in asset depr report (#22021) (#22030) (cherry picked from commit 3c460364b5345ae823e150d532ee625b64475971) Co-authored-by: Saqib --- .../asset_depreciations_and_balances.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 80bccafd28e..5001ad9f12f 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -93,7 +93,7 @@ def get_assets(filters): sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period from (SELECT a.asset_category, - ifnull(sum(case when ds.schedule_date < %(from_date)s then + ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then ds.depreciation_amount else 0 @@ -115,9 +115,7 @@ def get_assets(filters): group by a.asset_category union SELECT a.asset_category, - ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 - and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) - then + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then 0 else a.opening_accumulated_depreciation From 0407f018ddfece439932b799a606ef20248474d2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 29 May 2020 21:26:40 +0530 Subject: [PATCH 056/129] fix: cannot assign same task to other asset maintenance (#22024) (#22032) (cherry picked from commit cd8b5d1e4ce8ae809b6ad36b0ccdb89ee75ca4ce) Co-authored-by: Saqib --- .../asset_maintenance/asset_maintenance.json | 643 ++---------- .../asset_maintenance/asset_maintenance.py | 6 +- .../asset_maintenance_log.json | 967 ++++-------------- .../asset_maintenance_task.py | 3 +- 4 files changed, 295 insertions(+), 1324 deletions(-) diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json index efb2de3f266..c0c2566fe23 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json @@ -1,559 +1,140 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:asset_name", - "beta": 0, - "creation": "2017-10-19 16:50:22.879545", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "field:asset_name", + "creation": "2017-10-19 16:50:22.879545", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset_name", + "asset_category", + "company", + "column_break_3", + "item_code", + "item_name", + "section_break_6", + "maintenance_team", + "column_break_9", + "maintenance_manager", + "maintenance_manager_name", + "section_break_8", + "asset_maintenance_tasks" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_name", - "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": "Asset Name", - "length": 0, - "no_copy": 0, - "options": "Asset", - "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 - }, + "fieldname": "asset_name", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Asset Name", + "options": "Asset", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.asset_category", - "fieldname": "asset_category", - "fieldtype": "Read Only", - "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": "Asset Category", - "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 - }, + "fieldname": "asset_category", + "fieldtype": "Read Only", + "label": "Asset Category" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.item_code", - "fieldname": "item_code", - "fieldtype": "Read Only", - "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": "Item Code", - "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 - }, + "fieldname": "item_code", + "fieldtype": "Read Only", + "label": "Item Code" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.item_name", - "fieldname": "item_name", - "fieldtype": "Read Only", - "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": "Item Name", - "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 - }, + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, { - "allow_bulk_edit": 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 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 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, - "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 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "select_serial_no", - "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": "Select Serial No", - "length": 0, - "no_copy": 0, - "options": "Serial No", - "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": "section_break_6", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "serial_no", - "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": "Serial No", - "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 - }, + "fieldname": "maintenance_team", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Maintenance Team", + "options": "Asset Maintenance Team", + "reqd": 1 + }, { - "allow_bulk_edit": 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 - }, + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintenance_team", - "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": "Maintenance Team", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance Team", - "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_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 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "maintenance_team.maintenance_manager", - "fieldname": "maintenance_manager", - "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": "Maintenance Manager", - "length": 0, - "no_copy": 0, - "options": "", - "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 - }, + "fieldname": "maintenance_manager", + "fieldtype": "Data", + "hidden": 1, + "label": "Maintenance Manager", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "maintenance_team.maintenance_manager_name", - "fieldname": "maintenance_manager_name", - "fieldtype": "Read Only", - "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": "Maintenance Manager Name", - "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 - }, + "fieldname": "maintenance_manager_name", + "fieldtype": "Read Only", + "label": "Maintenance Manager Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_8", - "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, - "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": "section_break_8", + "fieldtype": "Section Break", + "label": "Tasks" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_maintenance_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": "Maintenance Tasks", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance 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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "asset_maintenance_tasks", + "fieldtype": "Table", + "label": "Maintenance Tasks", + "options": "Asset Maintenance Task", + "reqd": 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": 0, - "max_attachments": 0, - "modified": "2018-05-22 17:20:54.711885", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-05-28 20:28:32.993823", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance", + "owner": "Administrator", "permissions": [ { - "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": "Quality Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, "write": 1 - }, + }, { - "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": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 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 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index ecb55dde9ae..ba63dd661d7 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -39,7 +39,7 @@ class AssetMaintenance(Document): @frappe.whitelist() def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, next_due_date): - team_member = frappe.get_doc('User', assign_to_member).email + team_member = frappe.db.get_value('User', assign_to_member, "email") args = { 'doctype' : 'Asset Maintenance', 'assign_to' : team_member, @@ -78,7 +78,7 @@ def calculate_next_due_date(periodicity, start_date = None, end_date = None, las def update_maintenance_log(asset_maintenance, item_code, item_name, task): asset_maintenance_log = frappe.get_value("Asset Maintenance Log", {"asset_maintenance": asset_maintenance, - "task": task.maintenance_task, "maintenance_status": ('in',['Planned','Overdue'])}) + "task": task.name, "maintenance_status": ('in',['Planned','Overdue'])}) if not asset_maintenance_log: asset_maintenance_log = frappe.get_doc({ @@ -87,7 +87,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task): "asset_name": asset_maintenance, "item_code": item_code, "item_name": item_name, - "task": task.maintenance_task, + "task": task.name, "has_certificate": task.certificate_required, "description": task.description, "assign_to_name": task.assign_to_name, diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json index 42aecdf44ac..7395bec1e62 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json @@ -1,819 +1,210 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2017-10-23 16:58:44.424309", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "naming_series:", + "creation": "2017-10-23 16:58:44.424309", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset_maintenance", + "naming_series", + "asset_name", + "column_break_2", + "item_code", + "item_name", + "section_break_5", + "task", + "task_name", + "maintenance_type", + "periodicity", + "assign_to_name", + "column_break_6", + "due_date", + "completion_date", + "maintenance_status", + "section_break_12", + "has_certificate", + "certificate_attachement", + "section_break_6", + "description", + "column_break_9", + "actions_performed", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_maintenance", - "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": "Asset Maintenance", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance", - "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": "asset_maintenance", + "fieldtype": "Link", + "label": "Asset Maintenance", + "options": "Asset Maintenance" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "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": "Series", - "length": 0, - "no_copy": 0, - "options": "ACC-AML-.YYYY.-", - "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 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "ACC-AML-.YYYY.-", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.asset_name", - "fieldname": "asset_name", - "fieldtype": "Read Only", - "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": "Asset Name", - "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 - }, + "fetch_from": "asset_maintenance.asset_name", + "fieldname": "asset_name", + "fieldtype": "Read Only", + "label": "Asset Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "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 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.item_code", - "fieldname": "item_code", - "fieldtype": "Read Only", - "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": "Item Code", - "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 - }, + "fetch_from": "asset_maintenance.item_code", + "fieldname": "item_code", + "fieldtype": "Read Only", + "label": "Item Code" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.item_name", - "fieldname": "item_name", - "fieldtype": "Read Only", - "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": "Item Name", - "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 - }, + "fetch_from": "asset_maintenance.item_name", + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, { - "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 - }, + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "task", - "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": "Task", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance 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 - }, + "fieldname": "task", + "fieldtype": "Link", + "label": "Task", + "options": "Asset Maintenance Task" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.maintenance_type", - "fieldname": "maintenance_type", - "fieldtype": "Read Only", - "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": "Maintenance Type", - "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 - }, + "fetch_from": "task.maintenance_type", + "fieldname": "maintenance_type", + "fieldtype": "Read Only", + "label": "Maintenance Type" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.periodicity", - "fieldname": "periodicity", - "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": "Periodicity", - "length": 0, - "no_copy": 0, - "options": "", - "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 - }, + "fetch_from": "task.periodicity", + "fieldname": "periodicity", + "fieldtype": "Data", + "label": "Periodicity", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.assign_to_name", - "fieldname": "assign_to_name", - "fieldtype": "Read Only", - "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": "Assign To", - "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 - }, + "fetch_from": "task.assign_to_name", + "fieldname": "assign_to_name", + "fieldtype": "Read Only", + "label": "Assign To" + }, { - "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 - }, + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.next_due_date", - "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": 1, - "in_standard_filter": 0, - "label": "Due Date", - "length": 0, - "no_copy": 0, - "options": "", - "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 - }, + "fetch_from": "task.next_due_date", + "fieldname": "due_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Due Date", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "completion_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": "Completion 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 - }, + "fieldname": "completion_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Completion Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintenance_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": "Maintenance Status", - "length": 0, - "no_copy": 0, - "options": "Planned\nCompleted\nCancelled\nOverdue", - "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 - }, + "fieldname": "maintenance_status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Maintenance Status", + "options": "Planned\nCompleted\nCancelled\nOverdue", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_12", - "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 - }, + "fieldname": "section_break_12", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.certificate_required", - "fieldname": "has_certificate", - "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": "Has Certificate ", - "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 - }, + "default": "0", + "fetch_from": "task.certificate_required", + "fieldname": "has_certificate", + "fieldtype": "Check", + "label": "Has Certificate " + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.has_certificate", - "fieldname": "certificate_attachement", - "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": "Certificate", - "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 - }, + "depends_on": "eval:doc.has_certificate", + "fieldname": "certificate_attachement", + "fieldtype": "Attach", + "label": "Certificate" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_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 - }, + "fieldname": "section_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.description", - "fieldname": "description", - "fieldtype": "Read Only", - "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, - "options": "", - "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 - }, + "fetch_from": "task.description", + "fieldname": "description", + "fieldtype": "Read Only", + "label": "Description", + "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": "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 - }, + "fieldname": "column_break_9", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "actions_performed", - "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": "Actions performed", - "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_on_submit": 1, + "fieldname": "actions_performed", + "fieldtype": "Text Editor", + "label": "Actions performed" + }, { - "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": "Asset Maintenance Log", - "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": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Asset Maintenance Log", + "print_hide": 1, + "read_only": 1 + }, + { + "fetch_from": "task.maintenance_task", + "fieldname": "task_name", + "fieldtype": "Data", + "in_preview": 1, + "label": "Task Name", + "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": "2018-08-21 14:44:51.457835", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance Log", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-05-28 20:51:48.238397", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance Log", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "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", - "title_field": "", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py index d98cc310b3b..2a5666d5064 100644 --- a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py +++ b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py @@ -7,5 +7,4 @@ import frappe from frappe.model.document import Document class AssetMaintenanceTask(Document): - def autoname(self): - self.name = self.maintenance_task + pass From f214789e4b868e64358953e2b9806380aeddfd2c Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 29 May 2020 21:27:31 +0530 Subject: [PATCH 057/129] fix: routing operations table is blank on pull of operations in BOM (#22040) --- erpnext/manufacturing/doctype/bom/bom.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index d11077270aa..4f40fb71fc1 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -102,7 +102,13 @@ class BOM(WebsiteGenerator): self.set("operations", []) for d in frappe.get_all("BOM Operation", fields = ["*"], filters = {'parenttype': 'Routing', 'parent': self.routing}): - child = self.append('operations', d) + child = self.append('operations', { + "operation": d.operation, + "workstation": d.workstation, + "description": d.description, + "time_in_mins": d.time_in_mins, + "batch_size": d.batch_size + }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) def validate_rm_item(self, item): From e7e3af1684911b244c93eb3eb26f06a941ecd925 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 11 May 2020 11:44:45 +0530 Subject: [PATCH 058/129] fix: '<' not supported between instances of 'str' and 'int' --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 66145c650be..e48a2211e32 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -178,7 +178,7 @@ class StockEntry(StockController): stock_items = self.get_stock_items() serialized_items = self.get_serialized_items() for item in self.get("items"): - if item.qty and item.qty < 0: + if flt(item.qty) and flt(item.qty) < 0: frappe.throw(_("Row {0}: The item {1}, quantity must be positive number") .format(item.idx, frappe.bold(item.item_code))) From ffbc8122716198ad9a100c3dd7ce6fd660b85cf2 Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Sat, 30 May 2020 15:00:56 +0530 Subject: [PATCH 059/129] fix(ewb): remove checksum validation for TRANSIN (cherry picked from commit ca46bedfcb874ac5e03d44a00b996d31db336d85) --- erpnext/regional/india/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index d22aa1eebb8..abcc953277a 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -600,8 +600,9 @@ def get_transport_details(data, doc): data.transDocDate = frappe.utils.formatdate(doc.lr_date, 'dd/mm/yyyy') if doc.gst_transporter_id: - validate_gstin_check_digit(doc.gst_transporter_id, label='GST Transporter ID') - data.transporterId = doc.gst_transporter_id + if doc.gst_transporter_id[0:2] != "88": + validate_gstin_check_digit(doc.gst_transporter_id, label='GST Transporter ID') + data.transporterId = doc.gst_transporter_id return data From 144facf2033342ce4f5606a83c74f0514c0c7329 Mon Sep 17 00:00:00 2001 From: marination Date: Sun, 31 May 2020 20:11:40 +0530 Subject: [PATCH 060/129] fix: Procurement Tracker Data Consistency --- .../procurement_tracker.py | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index 39668795cba..88a865f0f85 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.utils import flt def execute(filters=None): columns = get_columns(filters) @@ -54,15 +55,16 @@ def get_columns(filters): "width": 140 }, { - "label": _("Description"), - "fieldname": "description", - "fieldtype": "Data", - "width": 200 + "label": _("Item"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 150 }, { "label": _("Quantity"), "fieldname": "quantity", - "fieldtype": "Int", + "fieldtype": "Float", "width": 140 }, { @@ -118,7 +120,7 @@ def get_columns(filters): }, { "label": _("Purchase Order Amount(Company Currency)"), - "fieldname": "purchase_order_amt_usd", + "fieldname": "purchase_order_amt_in_company_currency", "fieldtype": "Float", "width": 140 }, @@ -175,17 +177,17 @@ def get_data(filters): "requesting_site": po.warehouse, "requestor": po.owner, "material_request_no": po.material_request, - "description": po.description, - "quantity": po.qty, + "item_code": po.item_code, + "quantity": flt(po.qty), "unit_of_measurement": po.stock_uom, "status": po.status, "purchase_order_date": po.transaction_date, "purchase_order": po.parent, "supplier": po.supplier, - "estimated_cost": mr_record.get('amount'), - "actual_cost": pi_records.get(po.name), - "purchase_order_amt": po.amount, - "purchase_order_amt_in_company_currency": po.base_amount, + "estimated_cost": flt(mr_record.get('amount')), + "actual_cost": flt(pi_records.get(po.name)), + "purchase_order_amt": flt(po.amount), + "purchase_order_amt_in_company_currency": flt(po.base_amount), "expected_delivery_date": po.schedule_date, "actual_delivery_date": pr_records.get(po.name) } @@ -198,9 +200,14 @@ def get_mapped_mr_details(conditions): SELECT par.transaction_date, par.per_ordered, + par.owner, child.name, child.parent, - child.amount + child.amount, + child.qty, + child.item_code, + child.uom, + par.status FROM `tabMaterial Request` par, `tabMaterial Request Item` child WHERE par.per_ordered>=0 @@ -217,7 +224,15 @@ def get_mapped_mr_details(conditions): procurement_record_details = dict( material_request_date=record.transaction_date, material_request_no=record.parent, - estimated_cost=record.amount + requestor=record.owner, + item_code=record.item_code, + estimated_cost=flt(record.amount), + quantity=flt(record.qty), + unit_of_measurement=record.uom, + status=record.status, + actual_cost=0, + purchase_order_amt=0, + purchase_order_amt_in_company_currency=0 ) procurement_record_against_mr.append(procurement_record_details) return mr_records, procurement_record_against_mr @@ -259,7 +274,7 @@ def get_po_entries(conditions): child.warehouse, child.material_request, child.material_request_item, - child.description, + child.item_code, child.stock_uom, child.qty, child.amount, From f42d656cd316b15e4a835d4b3c855cf49d73416b Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 1 Jun 2020 11:56:33 +0530 Subject: [PATCH 061/129] fix: Misleading Error message for Item Attribute. --- erpnext/controllers/item_variant.py | 15 +++++++++++---- .../doctype/item_attribute/item_attribute.py | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 29f8dd57026..50b17abbe6d 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -70,7 +70,7 @@ def validate_item_variant_attributes(item, args=None): else: attributes_list = attribute_values.get(attribute.lower(), []) - validate_item_attribute_value(attributes_list, attribute, value, item.name) + validate_item_attribute_value(attributes_list, attribute, value, item.name, from_variant=True) def validate_is_incremental(numeric_attribute, attribute, value, item): from_range = numeric_attribute.from_range @@ -93,13 +93,20 @@ def validate_is_incremental(numeric_attribute, attribute, value, item): .format(attribute, from_range, to_range, increment, item), InvalidItemAttributeValueError, title=_('Invalid Attribute')) -def validate_item_attribute_value(attributes_list, attribute, attribute_value, item): +def validate_item_attribute_value(attributes_list, attribute, attribute_value, item, from_variant=True): allow_rename_attribute_value = frappe.db.get_single_value('Item Variant Settings', 'allow_rename_attribute_value') if allow_rename_attribute_value: pass elif attribute_value not in attributes_list: - frappe.throw(_("The value {0} is already assigned to an exisiting Item {2}.").format( - attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Rename Not Allowed')) + if from_variant: + frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format( + frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value")) + else: + msg = _("The value {0} is already assigned to an exisiting Item {1}.").format( + frappe.bold(attribute_value), frappe.bold(item)) + msg += "
" + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value")) + + frappe.throw(msg, InvalidItemAttributeValueError, title=_('Edit Not Allowed')) def get_attribute_values(item): if not frappe.flags.attribute_values: diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.py b/erpnext/stock/doctype/item_attribute/item_attribute.py index 71b998fb954..2f75bbd97c0 100644 --- a/erpnext/stock/doctype/item_attribute/item_attribute.py +++ b/erpnext/stock/doctype/item_attribute/item_attribute.py @@ -34,7 +34,7 @@ class ItemAttribute(Document): if self.numeric_values: validate_is_incremental(self, self.name, item.value, item.name) else: - validate_item_attribute_value(attributes_list, self.name, item.value, item.name) + validate_item_attribute_value(attributes_list, self.name, item.value, item.name, from_variant=False) def validate_numeric(self): if self.numeric_values: From 5c448de2ae8764280d493dac79e1ca23059b0dd0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 24 Apr 2020 04:07:14 +0530 Subject: [PATCH 062/129] feat: Add Failed error logs fix: set defaults (cherry picked from commit defeb737471efab0100df5682777572ed0c4e2ee) --- .../tally_migration/tally_migration.json | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json index dc6f093ac9d..005c8a98c6b 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json @@ -28,14 +28,17 @@ "vouchers", "accounts_section", "default_warehouse", - "round_off_account", + "default_round_off_account", "column_break_21", "default_cost_center", "day_book_section", "day_book_data", "column_break_27", "is_day_book_data_processed", - "is_day_book_data_imported" + "is_day_book_data_imported", + "import_log_section", + "failed_import_log", + "failed_import_preview" ], "fields": [ { @@ -57,6 +60,7 @@ "fieldname": "tally_creditors_account", "fieldtype": "Data", "label": "Tally Creditors Account", + "read_only_depends_on": "eval:doc.is_master_data_processed==1", "reqd": 1 }, { @@ -69,6 +73,7 @@ "fieldname": "tally_debtors_account", "fieldtype": "Data", "label": "Tally Debtors Account", + "read_only_depends_on": "eval:doc.is_master_data_processed==1", "reqd": 1 }, { @@ -92,7 +97,7 @@ "fieldname": "erpnext_company", "fieldtype": "Data", "label": "ERPNext Company", - "read_only_depends_on": "eval:doc.is_master_data_processed == 1" + "read_only_depends_on": "eval:doc.is_master_data_processed==1" }, { "fieldname": "processed_files_section", @@ -136,6 +141,7 @@ }, { "depends_on": "is_master_data_imported", + "description": "The accounts are set by the system automatically but do confirm these defaults", "fieldname": "accounts_section", "fieldtype": "Section Break", "label": "Accounts" @@ -146,12 +152,6 @@ "label": "Default Warehouse", "options": "Warehouse" }, - { - "fieldname": "round_off_account", - "fieldtype": "Link", - "label": "Round Off Account", - "options": "Account" - }, { "fieldname": "column_break_21", "fieldtype": "Column Break" @@ -212,11 +212,36 @@ "fieldname": "default_uom", "fieldtype": "Link", "label": "Default UOM", - "options": "UOM" + "options": "UOM", + "read_only_depends_on": "eval:doc.is_master_data_imported==1" + }, + { + "default": "[]", + "fieldname": "failed_import_log", + "fieldtype": "Code", + "hidden": 1, + "label": "Failed Import Log", + "options": "JSON" + }, + { + "fieldname": "failed_import_preview", + "fieldtype": "HTML", + "label": "Failed Import Log" + }, + { + "fieldname": "import_log_section", + "fieldtype": "Section Break", + "label": "Import Log" + }, + { + "fieldname": "default_round_off_account", + "fieldtype": "Link", + "label": "Default Round Off Account", + "options": "Account" } ], "links": [], - "modified": "2020-04-16 13:03:28.894919", + "modified": "2020-04-22 16:01:13.718842", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Tally Migration", From 61381963b764467a724ae8da83692b824afd38b4 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 27 Apr 2020 11:34:00 +0530 Subject: [PATCH 063/129] feat: Error logging via doc fix: Handle duplicate company and COA feat: Error logging via doc feat: fetch defaults for accounts (cherry picked from commit 2de4a5a7c884080db1ac03acf40262ca887f8913) --- .../tally_migration/tally_migration.py | 99 ++++++++++++------- 1 file changed, 65 insertions(+), 34 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 13474e19ee1..32d11681fb0 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import json import re +import sys import traceback import zipfile from decimal import Decimal @@ -15,13 +16,14 @@ from bs4 import BeautifulSoup as bs import frappe from erpnext import encode_company_abbr from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts +from erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer import unset_existing_data + from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.model.document import Document from frappe.model.naming import getseries, revert_series_if_last from frappe.utils.data import format_datetime - PRIMARY_ACCOUNT = "Primary" VOUCHER_CHUNK_SIZE = 500 @@ -65,9 +67,17 @@ class TallyMigration(Document): "attached_to_name": self.name, "content": json.dumps(value), "is_private": True - }).insert() + }) + try: + f.insert() + except frappe.DuplicateEntryError: + pass setattr(self, key, f.file_url) + def set_account_defaults(self): + self.default_cost_center, self.default_round_off_account = frappe.db.get_value("Company", self.erpnext_company, ["cost_center", "round_off_account"]) + self.default_warehouse = frappe.db.get_value("Stock Settings", "Stock Settings", "default_warehouse") + def _process_master_data(self): def get_company_name(collection): return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string.strip() @@ -84,7 +94,11 @@ class TallyMigration(Document): children, parents = get_children_and_parent_dict(accounts) group_set = [acc[1] for acc in accounts if acc[2]] children, customers, suppliers = remove_parties(parents, children, group_set) - coa = traverse({}, children, roots, roots, group_set) + + try: + coa = traverse({}, children, roots, roots, group_set) + except RecursionError: + self.log() for account in coa: coa[account]["root_type"] = root_type_map[account] @@ -242,12 +256,18 @@ class TallyMigration(Document): def create_company_and_coa(coa_file_url): coa_file = frappe.get_doc("File", {"file_url": coa_file_url}) frappe.local.flags.ignore_chart_of_accounts = True - company = frappe.get_doc({ - "doctype": "Company", - "company_name": self.erpnext_company, - "default_currency": "INR", - "enable_perpetual_inventory": 0, - }).insert() + + try: + company = frappe.get_doc({ + "doctype": "Company", + "company_name": self.erpnext_company, + "default_currency": "INR", + "enable_perpetual_inventory": 0, + }).insert() + except frappe.DuplicateEntryError: + company = frappe.get_doc("Company", self.erpnext_company) + unset_existing_data(self.erpnext_company) + frappe.local.flags.ignore_chart_of_accounts = False create_charts(company.name, custom_chart=json.loads(coa_file.get_content())) company.create_default_warehouses() @@ -256,36 +276,35 @@ class TallyMigration(Document): parties_file = frappe.get_doc("File", {"file_url": parties_file_url}) for party in json.loads(parties_file.get_content()): try: - frappe.get_doc(party).insert() + party_doc = frappe.get_doc(party) + party_doc.insert() except: - self.log(party) + self.log(party_doc) addresses_file = frappe.get_doc("File", {"file_url": addresses_file_url}) for address in json.loads(addresses_file.get_content()): try: - frappe.get_doc(address).insert(ignore_mandatory=True) + address_doc = frappe.get_doc(address) + address_doc.insert(ignore_mandatory=True) except: - try: - gstin = address.pop("gstin", None) - frappe.get_doc(address).insert(ignore_mandatory=True) - self.log({"address": address, "message": "Invalid GSTIN: {}. Address was created without GSTIN".format(gstin)}) - except: - self.log(address) + self.log(address_doc) def create_items_uoms(items_file_url, uoms_file_url): uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url}) for uom in json.loads(uoms_file.get_content()): if not frappe.db.exists(uom): try: - frappe.get_doc(uom).insert() + uom_doc = frappe.get_doc(uom) + uom_doc.insert() except: - self.log(uom) + self.log(uom_doc) items_file = frappe.get_doc("File", {"file_url": items_file_url}) for item in json.loads(items_file.get_content()): try: - frappe.get_doc(item).insert() + item_doc = frappe.get_doc(item) + item_doc.insert() except: - self.log(item) + self.log(item_doc) try: self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4) @@ -299,6 +318,7 @@ class TallyMigration(Document): self.publish("Import Master Data", _("Done"), 4, 4) + self.set_account_defaults() self.is_master_data_imported = 1 except: @@ -468,13 +488,13 @@ class TallyMigration(Document): oldest_year = new_year def create_custom_fields(doctypes): + df = { + "fieldtype": "Data", + "fieldname": "tally_guid", + "read_only": 1, + "label": "Tally GUID" + } for doctype in doctypes: - df = { - "fieldtype": "Data", - "fieldname": "tally_guid", - "read_only": 1, - "label": "Tally GUID" - } create_custom_field(doctype, df) def create_price_list(): @@ -521,11 +541,12 @@ class TallyMigration(Document): for index, voucher in enumerate(chunk, start=start): try: - doc = frappe.get_doc(voucher).insert() - doc.submit() + voucher_doc = frappe.get_doc(voucher) + voucher_doc.insert() + voucher_doc.submit() self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total) except: - self.log(voucher) + self.log(voucher_doc) if is_last: self.status = "" @@ -551,9 +572,19 @@ class TallyMigration(Document): frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600) def log(self, data=None): - data = data or self.status - message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) - return frappe.log_error(title="Tally Migration Error", message=message) + if isinstance(data, frappe.model.document.Document): + if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError: + failed_import_log = json.loads(self.failed_import_log) + failed_import_log.append({ + "doc": data.as_dict(), + "exc": traceback.format_exc() + }) + self.failed_import_log = json.dumps(failed_import_log) + self.save() + else: + data = data or self.status + message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) + return frappe.log_error(title="Tally Migration Error", message=message) def set_status(self, status=""): self.status = status From 2ab6c8fa60937dbc940b612a6d8cab5e3b0dfcd9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 23 Apr 2020 14:07:14 +0530 Subject: [PATCH 064/129] fix: set default round off acount (cherry picked from commit 38c677689dd54d91e0255700d94a68b0e92b3c1e) --- .../doctype/tally_migration/tally_migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 32d11681fb0..5a5759160b5 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -510,7 +510,7 @@ class TallyMigration(Document): try: frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable") frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable") - frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account) + frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.default_round_off_account) vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers}) vouchers = json.loads(vouchers_file.get_content()) From 0a2bbfaf243b992701efc5f149829c504b672f7e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 27 Apr 2020 13:12:38 +0530 Subject: [PATCH 065/129] feat: error log handling fix: toggle required accounts (cherry picked from commit 82e7a9de2856a27ed497d7559fe775524a1c54e0) --- .../tally_migration/tally_migration.js | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index d84c8234efa..d06fe537ad1 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -1,7 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Tally Migration', { +frappe.ui.form.on("Tally Migration", { onload: function (frm) { let reload_status = true; frappe.realtime.on("tally_migration_progress_update", function (data) { @@ -36,6 +36,10 @@ frappe.ui.form.on('Tally Migration', { }); }, refresh: function (frm) { + frm.trigger("show_import_log"); + ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { + frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) + }) if (frm.doc.master_data && !frm.doc.is_master_data_imported) { if (frm.doc.is_master_data_processed) { if (frm.doc.status != "Importing Master Data") { @@ -71,5 +75,101 @@ frappe.ui.form.on('Tally Migration', { frm.reload_doc(); } ); + }, + show_import_log(frm) { + let index = 0; + let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); + let logs = import_log.slice(0, 20); + let hidden_logs = import_log.slice(20); + + frm.toggle_display("import_log_section", logs.length > 0); + + + const getError = (traceback) => { + let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 + let error_line = traceback.substr(exc_error_idx) + let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; + + return error_line.slice(split_str_idx).trim(); + } + + const cleanDoc = (obj) => { + let temp = obj; + $.each(temp, function(key, value){ + if (value === "" || value === null){ + delete obj[key]; + } else if (Object.prototype.toString.call(value) === '[object Object]') { + cleanDoc(value); + } else if ($.isArray(value)) { + $.each(value, function (k,v) { cleanDoc(v); }); + } + }); + return temp; + }; + + let rows = logs + .map(({ doc, exc }) => { + let id = frappe.dom.get_unique_id(); + let traceback = exc; + + let error_message = getError(traceback); + index++; + + let html = ` + +
+
+
${traceback}
+
+
`; + + let show_doc = ` + +
+
+
${JSON.stringify(cleanDoc(doc), null, 1)}
+
+
`; + + let create_button = ` + ` + + return `
+ + + + + `; + }) + .join(""); + + frm.get_field("failed_import_preview").$wrapper.html(` +
{{ __("Leave Type") }}{{ __("Total Allocated Leaves") }}{{ __("Used Leaves") }}{{ __("Pending Leaves") }}{{ __("Available Leaves") }}{{ __("Leave Type") }}{{ __("Total Allocated Leaves") }}{{ __("Expired Leaves") }}{{ __("Used Leaves") }}{{ __("Pending Leaves") }}{{ __("Available Leaves") }}
{%= key %} {%= value["total_leaves"] %} {%= value["expired_leaves"] %} {%= value["leaves_taken"] %} {%= value["pending_leaves"] %} {%= value["remaining_leaves"] %}
${index} +
${doc.doctype}
+
+
${error_message}
+
${html}
+
${show_doc}
+
+
${create_button}
+
+ + + + + + + ${rows} + + + +
${__("#")}${__("DocType")}${__("Error Message")}${__("Create")}
And ${hidden_logs.length} more others
+ `); } }); From 7ee6e8360583e3f1130f58565bd1a491c7bffea0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 27 Apr 2020 13:42:00 +0530 Subject: [PATCH 066/129] perf: reduce failed_error_log size (cherry picked from commit 0805307514a7c9cafa6af9d6b5e0a490d42c5800) --- .../doctype/tally_migration/tally_migration.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 5a5759160b5..46382c1e8f5 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -575,11 +575,14 @@ class TallyMigration(Document): if isinstance(data, frappe.model.document.Document): if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError: failed_import_log = json.loads(self.failed_import_log) + doc = data.as_dict() + doc_fields = { x.fieldname for x in frappe.get_doc("DocType", doc.doctype).fields } + stripped_doc = { k: v for k, v in doc.items() if k in doc_fields } failed_import_log.append({ - "doc": data.as_dict(), + "doc": stripped_doc, "exc": traceback.format_exc() }) - self.failed_import_log = json.dumps(failed_import_log) + self.failed_import_log = json.dumps(failed_import_log, separators=(',', ':')) self.save() else: data = data or self.status From 2f5208b08d84567e23fab112258158ed4891f02d Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 15:51:40 +0530 Subject: [PATCH 067/129] feat: order failed import log by creation, handle rollbacks and commits (cherry picked from commit 1b5eea691cf166562efb1de019599da1d365aede) --- .../doctype/tally_migration/tally_migration.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 46382c1e8f5..cbc6019c6b4 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -29,6 +29,11 @@ VOUCHER_CHUNK_SIZE = 500 class TallyMigration(Document): + def validate(self): + failed_import_log = json.loads(self.failed_import_log) + sorted_failed_import_log = sorted(failed_import_log, key=lambda row: row["doc"]["creation"]) + self.failed_import_log = json.dumps(sorted_failed_import_log) + def autoname(self): if not self.name: self.name = "Tally Migration on " + format_datetime(self.creation) @@ -320,9 +325,11 @@ class TallyMigration(Document): self.set_account_defaults() self.is_master_data_imported = 1 + frappe.db.commit() except: self.publish("Import Master Data", _("Process Failed"), -1, 5) + frappe.db.rollback() self.log() finally: @@ -343,7 +350,9 @@ class TallyMigration(Document): processed_voucher = function(voucher) if processed_voucher: vouchers.append(processed_voucher) + frappe.db.commit() except: + frappe.db.rollback() self.log(voucher) return vouchers @@ -545,7 +554,9 @@ class TallyMigration(Document): voucher_doc.insert() voucher_doc.submit() self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total) + frappe.db.commit() except: + frappe.db.rollback() self.log(voucher_doc) if is_last: @@ -576,14 +587,14 @@ class TallyMigration(Document): if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError: failed_import_log = json.loads(self.failed_import_log) doc = data.as_dict() - doc_fields = { x.fieldname for x in frappe.get_doc("DocType", doc.doctype).fields } - stripped_doc = { k: v for k, v in doc.items() if k in doc_fields } failed_import_log.append({ - "doc": stripped_doc, + "doc": doc, "exc": traceback.format_exc() }) self.failed_import_log = json.dumps(failed_import_log, separators=(',', ':')) self.save() + frappe.db.commit() + else: data = data or self.status message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) From 28a6e1deeca70c78aa4b087cd3575e1c8edfcb38 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 16:01:59 +0530 Subject: [PATCH 068/129] feat: maintain logs of errors successfully fixed (cherry picked from commit 640636bbcd6f84c12c5ed0e74a3e257acb731f52) --- .../tally_migration/tally_migration.json | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json index 005c8a98c6b..417d9437926 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json @@ -38,7 +38,9 @@ "is_day_book_data_imported", "import_log_section", "failed_import_log", - "failed_import_preview" + "fixed_errors_log", + "failed_import_preview", + "fixed_error_log_preview" ], "fields": [ { @@ -220,7 +222,6 @@ "fieldname": "failed_import_log", "fieldtype": "Code", "hidden": 1, - "label": "Failed Import Log", "options": "JSON" }, { @@ -238,10 +239,22 @@ "fieldtype": "Link", "label": "Default Round Off Account", "options": "Account" + }, + { + "default": "[]", + "fieldname": "fixed_errors_log", + "fieldtype": "Code", + "hidden": 1, + "options": "JSON" + }, + { + "fieldname": "fixed_error_log_preview", + "fieldtype": "HTML", + "label": "Fixed Error Log" } ], "links": [], - "modified": "2020-04-22 16:01:13.718842", + "modified": "2020-04-28 00:29:18.039826", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Tally Migration", From 6950c10c6fc394f66d49a68eb4fc919fb18ea5ca Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 16:02:50 +0530 Subject: [PATCH 069/129] feat: handle resolve and unresolved errors restructured and reused components (cherry picked from commit 2e1b531f4fce10bdaf74218fa7a0d260d67c1d7a) --- .../tally_migration/tally_migration.js | 299 +++++++++++++----- 1 file changed, 217 insertions(+), 82 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index d06fe537ad1..9235bab0683 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -35,8 +35,10 @@ frappe.ui.form.on("Tally Migration", { } }); }, + refresh: function (frm) { - frm.trigger("show_import_log"); + frm.trigger("show_logs_preview"); + ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) }) @@ -63,6 +65,7 @@ frappe.ui.form.on("Tally Migration", { } } }, + add_button: function (frm, label, method) { frm.add_custom_button( label, @@ -76,94 +79,30 @@ frappe.ui.form.on("Tally Migration", { } ); }, - show_import_log(frm) { - let index = 0; - let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); - let logs = import_log.slice(0, 20); - let hidden_logs = import_log.slice(20); - frm.toggle_display("import_log_section", logs.length > 0); - - - const getError = (traceback) => { - let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 - let error_line = traceback.substr(exc_error_idx) - let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; - - return error_line.slice(split_str_idx).trim(); + render_html_table(frm, shown_logs, hidden_logs, field) { + if (shown_logs && shown_logs.length > 0) { + frm.toggle_display(field, true); + } else { + frm.toggle_display(field, false); + return } + let rows = get_html_rows(shown_logs, field); + let rows_head; - const cleanDoc = (obj) => { - let temp = obj; - $.each(temp, function(key, value){ - if (value === "" || value === null){ - delete obj[key]; - } else if (Object.prototype.toString.call(value) === '[object Object]') { - cleanDoc(value); - } else if ($.isArray(value)) { - $.each(value, function (k,v) { cleanDoc(v); }); - } - }); - return temp; - }; - - let rows = logs - .map(({ doc, exc }) => { - let id = frappe.dom.get_unique_id(); - let traceback = exc; - - let error_message = getError(traceback); - index++; - - let html = ` - -
-
-
${traceback}
-
-
`; - - let show_doc = ` - -
-
-
${JSON.stringify(cleanDoc(doc), null, 1)}
-
-
`; - - let create_button = ` - ` - - return ` - ${index} - -
${doc.doctype}
- - -
${error_message}
-
${html}
-
${show_doc}
- - -
${create_button}
- - `; - }) - .join(""); - - frm.get_field("failed_import_preview").$wrapper.html(` + if (field === "fixed_error_log_preview") { + rows_head = `${__("Meta Data")} + ${__("Unresolve")}` + } else { + rows_head = `${__("Error Message")} + ${__("Create")}` + } + frm.get_field(field).$wrapper.html(` - - + ${rows_head} ${rows} @@ -171,5 +110,201 @@ frappe.ui.form.on("Tally Migration", {
${__("#")} ${__("DocType")}${__("Error Message")}${__("Create")}
`); + }, + + show_error_summary() { + let summary = import_log.reduce((summary, row) => { + if (row.doc) { + if (summary[row.doc.doctype]) { + summary[row.doc.doctype] += 1; + } else { + summary[row.doc.doctype] = 1; + } + } + return summary + }, {}); + console.table(summary); + }, + + show_logs_preview(frm) { + let empty = "[]"; + let import_log = frm.doc.failed_import_log || empty; + let completed_log = frm.doc.fixed_errors_log || empty; + let render_section = !(import_log === completed_log && import_log === empty); + + frm.toggle_display("import_log_section", render_section); + + if (render_section) { + frm.trigger("show_errored_import_log"); + frm.trigger("show_fixed_errors_log"); + } + }, + + show_errored_import_log(frm) { + let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); + + let logs = import_log.slice(0, 20); + let hidden_logs = import_log.slice(20); + + frm.events.render_html_table(frm, logs, hidden_logs, "failed_import_preview"); + }, + + show_fixed_errors_log(frm) { + let completed_log = JSON.parse(frm.doc.fixed_errors_log || "[]"); + let logs = completed_log.slice(0, 20); + let hidden_logs = completed_log.slice(20); + + frm.events.render_html_table(frm, logs, hidden_logs, "fixed_error_log_preview"); } }); + +const getError = (traceback) => { + /* Extracts the Error Message from the Python Traceback or Solved error */ + let is_multiline = traceback.trim().indexOf("\n") != -1; + let message; + + if (is_multiline) { + let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 + let error_line = traceback.substr(exc_error_idx) + let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; + message = error_line.slice(split_str_idx).trim(); + } else { + message = traceback; + } + + return message +} + +const cleanDoc = (obj) => { + /* Strips all null and empty values of your JSON object */ + let temp = obj; + $.each(temp, function(key, value){ + if (value === "" || value === null){ + delete obj[key]; + } else if (Object.prototype.toString.call(value) === '[object Object]') { + cleanDoc(value); + } else if ($.isArray(value)) { + $.each(value, function (k,v) { cleanDoc(v); }); + } + }); + return temp; +} + +window.unresolve = (document) => { + let frm = cur_frm; + let failed_log = JSON.parse(frm.doc.failed_import_log); + let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + + let modified_fixed_log = fixed_log.filter(row => { + if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + return row + } + }); + + failed_log.push({ doc: document, exc: `Marked unresolved on ${Date()}` }); + + frm.doc.failed_import_log = JSON.stringify(failed_log); + frm.doc.fixed_errors_log = JSON.stringify(modified_fixed_log); + + // frm.trigger('show_logs_preview') + frm.dirty(); + frm.save(); +} + +window.create_new_doc = (doctype, document) => { + let frm = cur_frm; + let failed_log = JSON.parse(frm.doc.failed_import_log); + let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + + let modified_failed_log = failed_log.filter(row => { + if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + return row + } + }); + fixed_log.push({ doc: document, exc: `Solved on ${Date()}` }); + + frm.doc.failed_import_log = JSON.stringify(modified_failed_log); + frm.doc.fixed_errors_log = JSON.stringify(fixed_log); + + // frm.trigger('show_logs_preview') + frm.dirty(); + frm.save(); + frappe.new_doc(doctype, document); +} + +const get_html_rows = (logs, field) => { + let index = 0; + let rows = logs + .map(({ doc, exc }) => { + let id = frappe.dom.get_unique_id(); + let traceback = exc; + + let error_message = getError(traceback); + index++; + + let show_traceback = ` + +
+
+
${traceback}
+
+
`; + + let show_doc = ` + +
+
+
${JSON.stringify(cleanDoc(doc), null, 1)}
+
+
`; + + let create_button = ` + ` + + let mark_as_unresolved = ` + ` + + if (field === "fixed_error_log_preview") { + return ` + ${index} + +
${doc.doctype}
+ + +
${error_message}
+
${show_traceback}
+
${show_doc}
+ + +
${mark_as_unresolved}
+ + `; + } else { + return ` + ${index} + +
${doc.doctype}
+ + +
${error_message}
+
${show_traceback}
+
${show_doc}
+ + +
${create_button}
+ + `; + } + }) + .join(""); + + return rows +} \ No newline at end of file From d3d511d31a4b5b5e57aa0de7c59a32bd28a41675 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 16:32:15 +0530 Subject: [PATCH 070/129] fix: run summary logger style: indent fixes (cherry picked from commit 27b7172fa4b888feaf47cf2875405079e83196ef) --- .../doctype/tally_migration/tally_migration.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 9235bab0683..bf8ae8c1c6c 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -133,7 +133,7 @@ frappe.ui.form.on("Tally Migration", { let render_section = !(import_log === completed_log && import_log === empty); frm.toggle_display("import_log_section", render_section); - + frm.trigger("show_error_summary"); if (render_section) { frm.trigger("show_errored_import_log"); frm.trigger("show_fixed_errors_log"); @@ -302,9 +302,8 @@ const get_html_rows = (logs, field) => {
${create_button}
`; - } - }) - .join(""); + } + }).join(""); return rows } \ No newline at end of file From 18d432a843a4a1974737cb2bb1040f7ab9a85a0b Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 29 Apr 2020 04:01:24 +0530 Subject: [PATCH 071/129] fix: table cleanups, updated summary block (cherry picked from commit 0d61b35b0bc26ce509801b747bdc2d1d349b7d02) --- .../tally_migration/tally_migration.js | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index bf8ae8c1c6c..9cc3c98a569 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -88,32 +88,38 @@ frappe.ui.form.on("Tally Migration", { return } let rows = get_html_rows(shown_logs, field); - let rows_head; + let rows_head, table_caption; + + let table_footer = (hidden_logs && (hidden_logs.length > 0)) ? ` + And ${hidden_logs.length} more others + `: ""; if (field === "fixed_error_log_preview") { rows_head = `${__("Meta Data")} ${__("Unresolve")}` + table_caption = "Resolved Issues" } else { rows_head = `${__("Error Message")} ${__("Create")}` + table_caption = "Error Log" } + frm.get_field(field).$wrapper.html(` + ${rows_head} ${rows} - - - + ${table_footer}
${table_caption}
${__("#")} ${__("DocType")}
And ${hidden_logs.length} more others
`); }, - show_error_summary() { - let summary = import_log.reduce((summary, row) => { + show_error_summary(frm) { + let summary = JSON.parse(frm.doc.failed_import_log).reduce((summary, row) => { if (row.doc) { if (summary[row.doc.doctype]) { summary[row.doc.doctype] += 1; @@ -280,7 +286,6 @@ const get_html_rows = (logs, field) => {
${error_message}
-
${show_traceback}
${show_doc}
@@ -302,8 +307,8 @@ const get_html_rows = (logs, field) => {
${create_button}
`; - } - }).join(""); + } + }).join(""); return rows } \ No newline at end of file From adb864828aed8c6f49a0a16f406ea07cf643f8a4 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 29 Apr 2020 10:25:18 +0530 Subject: [PATCH 072/129] perf: reduce JS memory heap and maintain namespaces (cherry picked from commit 6b850b7e71e30c2513ae8f50152eb67788604eb0) --- .../tally_migration/tally_migration.js | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 9cc3c98a569..0cb4995ac95 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -1,6 +1,8 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +frappe.provide("erpnext.tally_migration"); + frappe.ui.form.on("Tally Migration", { onload: function (frm) { let reload_status = true; @@ -38,10 +40,13 @@ frappe.ui.form.on("Tally Migration", { refresh: function (frm) { frm.trigger("show_logs_preview"); + erpnext.tally_migration.failed_import_log = JSON.parse(frm.doc.failed_import_log); + erpnext.tally_migration.fixed_errors_log = JSON.parse(frm.doc.fixed_errors_log); ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) }) + if (frm.doc.master_data && !frm.doc.is_master_data_imported) { if (frm.doc.is_master_data_processed) { if (frm.doc.status != "Importing Master Data") { @@ -53,6 +58,7 @@ frappe.ui.form.on("Tally Migration", { } } } + if (frm.doc.day_book_data && !frm.doc.is_day_book_data_imported) { if (frm.doc.is_day_book_data_processed) { if (frm.doc.status != "Importing Day Book Data") { @@ -87,7 +93,7 @@ frappe.ui.form.on("Tally Migration", { frm.toggle_display(field, false); return } - let rows = get_html_rows(shown_logs, field); + let rows = erpnext.tally_migration.get_html_rows(shown_logs, field); let rows_head, table_caption; let table_footer = (hidden_logs && (hidden_logs.length > 0)) ? ` @@ -119,7 +125,7 @@ frappe.ui.form.on("Tally Migration", { }, show_error_summary(frm) { - let summary = JSON.parse(frm.doc.failed_import_log).reduce((summary, row) => { + let summary = erpnext.tally_migration.failed_import_log.reduce((summary, row) => { if (row.doc) { if (summary[row.doc.doctype]) { summary[row.doc.doctype] += 1; @@ -147,8 +153,7 @@ frappe.ui.form.on("Tally Migration", { }, show_errored_import_log(frm) { - let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); - + let import_log = erpnext.tally_migration.failed_import_log; let logs = import_log.slice(0, 20); let hidden_logs = import_log.slice(20); @@ -156,7 +161,7 @@ frappe.ui.form.on("Tally Migration", { }, show_fixed_errors_log(frm) { - let completed_log = JSON.parse(frm.doc.fixed_errors_log || "[]"); + let completed_log = erpnext.tally_migration.fixed_errors_log; let logs = completed_log.slice(0, 20); let hidden_logs = completed_log.slice(20); @@ -164,7 +169,7 @@ frappe.ui.form.on("Tally Migration", { } }); -const getError = (traceback) => { +erpnext.tally_migration.getError = (traceback) => { /* Extracts the Error Message from the Python Traceback or Solved error */ let is_multiline = traceback.trim().indexOf("\n") != -1; let message; @@ -181,28 +186,28 @@ const getError = (traceback) => { return message } -const cleanDoc = (obj) => { +erpnext.tally_migration.cleanDoc = (obj) => { /* Strips all null and empty values of your JSON object */ let temp = obj; $.each(temp, function(key, value){ if (value === "" || value === null){ delete obj[key]; } else if (Object.prototype.toString.call(value) === '[object Object]') { - cleanDoc(value); + erpnext.tally_migration.cleanDoc(value); } else if ($.isArray(value)) { - $.each(value, function (k,v) { cleanDoc(v); }); + $.each(value, function (k,v) { erpnext.tally_migration.cleanDoc(v); }); } }); return temp; } -window.unresolve = (document) => { +erpnext.tally_migration.unresolve = (document) => { let frm = cur_frm; - let failed_log = JSON.parse(frm.doc.failed_import_log); - let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + let failed_log = erpnext.tally_migration.failed_import_log; + let fixed_log = erpnext.tally_migration.fixed_errors_log; let modified_fixed_log = fixed_log.filter(row => { - if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) { return row } }); @@ -217,13 +222,13 @@ window.unresolve = (document) => { frm.save(); } -window.create_new_doc = (doctype, document) => { +erpnext.tally_migration.resolve = (document) => { let frm = cur_frm; - let failed_log = JSON.parse(frm.doc.failed_import_log); - let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + let failed_log = erpnext.tally_migration.failed_import_log; + let fixed_log = erpnext.tally_migration.fixed_errors_log; let modified_failed_log = failed_log.filter(row => { - if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) { return row } }); @@ -235,17 +240,21 @@ window.create_new_doc = (doctype, document) => { // frm.trigger('show_logs_preview') frm.dirty(); frm.save(); +} + +erpnext.tally_migration.create_new_doc = (doctype, document) => { + erpnext.tally_migration.resolve(document); frappe.new_doc(doctype, document); } -const get_html_rows = (logs, field) => { +erpnext.tally_migration.get_html_rows = (logs, field) => { let index = 0; let rows = logs .map(({ doc, exc }) => { let id = frappe.dom.get_unique_id(); let traceback = exc; - let error_message = getError(traceback); + let error_message = erpnext.tally_migration.getError(traceback); index++; let show_traceback = ` @@ -264,17 +273,17 @@ const get_html_rows = (logs, field) => {
-
${JSON.stringify(cleanDoc(doc), null, 1)}
+
${JSON.stringify(erpnext.tally_migration.cleanDoc(doc), null, 1)}
`; let create_button = ` - ` let mark_as_unresolved = ` - ` From 0a7afd77e26d6b79bf284e7acf12b2581d949de2 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 29 Apr 2020 11:21:16 +0530 Subject: [PATCH 073/129] fix: toggle read only for accounts post daybook data processing style: code comments and updates (cherry picked from commit f4926a8205184e21641a1770b9096a8804f2cd64) --- .../doctype/tally_migration/tally_migration.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 0cb4995ac95..e94cca54c52 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -45,6 +45,7 @@ frappe.ui.form.on("Tally Migration", { ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) + frm.toggle_enable(account, frm.doc.is_day_book_data_processed != 1) }) if (frm.doc.master_data && !frm.doc.is_master_data_imported) { @@ -145,8 +146,8 @@ frappe.ui.form.on("Tally Migration", { let render_section = !(import_log === completed_log && import_log === empty); frm.toggle_display("import_log_section", render_section); - frm.trigger("show_error_summary"); if (render_section) { + frm.trigger("show_error_summary"); frm.trigger("show_errored_import_log"); frm.trigger("show_fixed_errors_log"); } @@ -202,6 +203,7 @@ erpnext.tally_migration.cleanDoc = (obj) => { } erpnext.tally_migration.unresolve = (document) => { + /* Mark document migration as unresolved ie. move to failed error log */ let frm = cur_frm; let failed_log = erpnext.tally_migration.failed_import_log; let fixed_log = erpnext.tally_migration.fixed_errors_log; @@ -217,12 +219,12 @@ erpnext.tally_migration.unresolve = (document) => { frm.doc.failed_import_log = JSON.stringify(failed_log); frm.doc.fixed_errors_log = JSON.stringify(modified_fixed_log); - // frm.trigger('show_logs_preview') frm.dirty(); frm.save(); } erpnext.tally_migration.resolve = (document) => { + /* Mark document migration as resolved ie. move to fixed error log */ let frm = cur_frm; let failed_log = erpnext.tally_migration.failed_import_log; let fixed_log = erpnext.tally_migration.fixed_errors_log; @@ -237,12 +239,12 @@ erpnext.tally_migration.resolve = (document) => { frm.doc.failed_import_log = JSON.stringify(modified_failed_log); frm.doc.fixed_errors_log = JSON.stringify(fixed_log); - // frm.trigger('show_logs_preview') frm.dirty(); frm.save(); } erpnext.tally_migration.create_new_doc = (doctype, document) => { + /* Mark as resolved and create new document */ erpnext.tally_migration.resolve(document); frappe.new_doc(doctype, document); } From d50cceaafa6942df70134a4535dc0353c1a1065a Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 30 Apr 2020 04:07:14 +0530 Subject: [PATCH 074/129] feat: prompt that company and COA will be overwritten (cherry picked from commit 51305a028be5f7a766dd099f3d93fbea0c28d93d) --- .../doctype/tally_migration/tally_migration.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index e94cca54c52..d9811b58dde 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -73,6 +73,16 @@ frappe.ui.form.on("Tally Migration", { } }, + erpnext_company: function (frm) { + frappe.db.exists("Company", frm.doc.erpnext_company).then(exists => { + if (exists) { + frappe.msgprint( + __("Company {0} already exists. Continuing will overwrite the Company and Chart of Accounts", [frm.doc.erpnext_company]), + ); + } + }); + }, + add_button: function (frm, label, method) { frm.add_custom_button( label, From 8ea9b0a4158b5bb4fc8ab200418b8716f6e58a60 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 1 May 2020 14:39:11 +0530 Subject: [PATCH 075/129] feat: maintain tally voucher numbers feat: create seperate customer and supplier entries for same party if case exists (cherry picked from commit af612ddb6de0c59381d36f1a1212188401990f05) --- .../tally_migration/tally_migration.py | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index cbc6019c6b4..393c5d4b02b 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -145,14 +145,18 @@ class TallyMigration(Document): def remove_parties(parents, children, group_set): customers, suppliers = set(), set() for account in parents: + found = False if self.tally_creditors_account in parents[account]: - children.pop(account, None) + found = True if account not in group_set: suppliers.add(account) - elif self.tally_debtors_account in parents[account]: - children.pop(account, None) + if self.tally_debtors_account in parents[account]: + found = True if account not in group_set: customers.add(account) + if found: + children.pop(account, None) + return children, customers, suppliers def traverse(tree, children, accounts, roots, group_set): @@ -170,6 +174,7 @@ class TallyMigration(Document): parties, addresses = [], [] for account in collection.find_all("LEDGER"): party_type = None + links = [] if account.NAME.string.strip() in customers: party_type = "Customer" parties.append({ @@ -180,7 +185,9 @@ class TallyMigration(Document): "territory": "All Territories", "customer_type": "Individual", }) - elif account.NAME.string.strip() in suppliers: + links.append({"link_doctype": party_type, "link_name": account["NAME"]}) + + if account.NAME.string.strip() in suppliers: party_type = "Supplier" parties.append({ "doctype": party_type, @@ -189,6 +196,8 @@ class TallyMigration(Document): "supplier_group": "All Supplier Groups", "supplier_type": "Individual", }) + links.append({"link_doctype": party_type, "link_name": account["NAME"]}) + if party_type: address = "\n".join([a.string.strip() for a in account.find_all("ADDRESS")]) addresses.append({ @@ -202,7 +211,7 @@ class TallyMigration(Document): "mobile": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, "phone": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, "gstin": account.PARTYGSTIN.string.strip() if account.PARTYGSTIN else None, - "links": [{"link_doctype": party_type, "link_name": account["NAME"]}], + "links": links }) return parties, addresses @@ -378,6 +387,7 @@ class TallyMigration(Document): journal_entry = { "doctype": "Journal Entry", "tally_guid": voucher.GUID.string.strip(), + "tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "", "posting_date": voucher.DATE.string.strip(), "company": self.erpnext_company, "accounts": accounts, @@ -406,6 +416,7 @@ class TallyMigration(Document): "doctype": doctype, party_field: voucher.PARTYNAME.string.strip(), "tally_guid": voucher.GUID.string.strip(), + "tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "", "posting_date": voucher.DATE.string.strip(), "due_date": voucher.DATE.string.strip(), "items": get_voucher_items(voucher, doctype), @@ -497,14 +508,21 @@ class TallyMigration(Document): oldest_year = new_year def create_custom_fields(doctypes): - df = { + tally_guid_df = { "fieldtype": "Data", "fieldname": "tally_guid", "read_only": 1, "label": "Tally GUID" } - for doctype in doctypes: - create_custom_field(doctype, df) + tally_voucher_no_df = { + "fieldtype": "Data", + "fieldname": "tally_voucher_no", + "read_only": 1, + "label": "Tally Voucher Number" + } + for df in [tally_guid_df, tally_voucher_no_df]: + for doctype in doctypes: + create_custom_field(doctype, df) def create_price_list(): frappe.get_doc({ From 43a19da27aed60abc2844823ab41e274702ae6c2 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Sat, 2 May 2020 18:58:33 +0530 Subject: [PATCH 076/129] fix: un-using buggy JS frappe.new_doc (cherry picked from commit 0c353a64a44fb5567e1de315ca0fef0092c0aa68) --- .../tally_migration/tally_migration.js | 20 ++++++++++++++++--- .../tally_migration/tally_migration.py | 10 ++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index d9811b58dde..fd16d1e84aa 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -253,10 +253,24 @@ erpnext.tally_migration.resolve = (document) => { frm.save(); } -erpnext.tally_migration.create_new_doc = (doctype, document) => { +erpnext.tally_migration.create_new_doc = (document) => { /* Mark as resolved and create new document */ erpnext.tally_migration.resolve(document); - frappe.new_doc(doctype, document); + return frappe.call({ + type: "POST", + method: 'erpnext.erpnext_integrations.doctype.tally_migration.tally_migration.new_doc', + args: { + document + }, + freeze: true, + callback: function(r) { + if(!r.exc) { + frappe.model.sync(r.message); + frappe.get_doc(r.message.doctype, r.message.name).__run_link_triggers = true; + frappe.set_route("Form", r.message.doctype, r.message.name); + } + } + }); } erpnext.tally_migration.get_html_rows = (logs, field) => { @@ -290,7 +304,7 @@ erpnext.tally_migration.get_html_rows = (logs, field) => { `; let create_button = ` - ` diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 393c5d4b02b..d9c5852a6ed 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -28,6 +28,16 @@ PRIMARY_ACCOUNT = "Primary" VOUCHER_CHUNK_SIZE = 500 +@frappe.whitelist() +def new_doc(document): + document = json.loads(document) + doctype = document.pop("doctype") + document.pop("name", None) + doc = frappe.new_doc(doctype) + doc.update(document) + + return doc + class TallyMigration(Document): def validate(self): failed_import_log = json.loads(self.failed_import_log) From 2e825a108faaea413cb66525a342936fa715ec83 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Sat, 9 May 2020 12:05:33 +0530 Subject: [PATCH 077/129] chore: verbose error message for coa recursion (cherry picked from commit cb1376b036edb7d3161c331cdf4f7451a2d97a3e) --- .../doctype/tally_migration/tally_migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index d9c5852a6ed..462685f5e71 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -113,7 +113,7 @@ class TallyMigration(Document): try: coa = traverse({}, children, roots, roots, group_set) except RecursionError: - self.log() + self.log(_("Error occured while parsing Chart of Accounts: Please make sure that no two accounts have the same name")) for account in coa: coa[account]["root_type"] = root_type_map[account] From 99f5ece31cfeee9aa0fe867888327e7a250ba3af Mon Sep 17 00:00:00 2001 From: Myuddin khatri Date: Wed, 3 Jun 2020 11:14:49 +0530 Subject: [PATCH 078/129] fix(stock): able to create purchase invoice from purchase receipt dashboard able to create purchase invoice from purchase receipt dashboard --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index d5914f9b28d..89ca1bef856 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -25,7 +25,7 @@ frappe.ui.form.on("Purchase Receipt", { frm.custom_make_buttons = { 'Stock Entry': 'Return', - 'Purchase Invoice': 'Invoice' + 'Purchase Invoice': 'Purchase Invoice' }; frm.set_query("expense_account", "items", function() { From 4e3caa8ece68ab34cb0a2b1a0780a193a66fee17 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 3 Jun 2020 14:15:16 +0530 Subject: [PATCH 079/129] fix: cannot create existing assets --- erpnext/assets/doctype/asset/asset.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index d56549f7d90..ad7edd1ead2 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -126,6 +126,8 @@ class Asset(AccountsController): frappe.throw(_("Available-for-use Date should be after purchase date")) def validate_gross_and_purchase_amount(self): + if self.is_existing_asset: return + if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount: frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\ Please do not book expense of multiple assets against one single Asset.") From 1a1ce9a7438133d98b9569a58f3ad11a3fa4d19a Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 3 Jun 2020 14:32:07 +0530 Subject: [PATCH 080/129] fix: Apply shipping rule without address too --- erpnext/accounts/doctype/shipping_rule/shipping_rule.py | 4 +++- erpnext/public/js/controllers/transaction.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py index b2638c78279..d32a348741e 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py @@ -45,7 +45,9 @@ class ShippingRule(Document): shipping_amount = 0.0 by_value = False - self.validate_countries(doc) + if doc.get_shipping_address(): + # validate country only if there is address + self.validate_countries(doc) if self.calculate_based_on == 'Net Total': value = doc.base_net_total diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 801b6fce4a6..6e131272dcb 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -879,7 +879,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ shipping_rule: function() { var me = this; - if(this.frm.doc.shipping_rule && this.frm.doc.shipping_address) { + if(this.frm.doc.shipping_rule) { return this.frm.call({ doc: this.frm.doc, method: "apply_shipping_rule", From 89dd0bad28d17c40a3d5a537fda812300cfb91ef Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 3 Jun 2020 17:13:58 +0530 Subject: [PATCH 081/129] fix: '>=' not supported between instances of 'str' and 'int' --- erpnext/stock/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 7f32b8d8bb8..95ecb7fecd7 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -230,12 +230,12 @@ def get_valuation_method(item_code): def get_fifo_rate(previous_stock_queue, qty): """get FIFO (average) Rate from Queue""" - if qty >= 0: + if flt(qty) >= 0: total = sum(f[0] for f in previous_stock_queue) return sum(flt(f[0]) * flt(f[1]) for f in previous_stock_queue) / flt(total) if total else 0.0 else: available_qty_for_outgoing, outgoing_cost = 0, 0 - qty_to_pop = abs(qty) + qty_to_pop = abs(flt(qty)) while qty_to_pop and previous_stock_queue: batch = previous_stock_queue[0] if 0 < batch[0] <= qty_to_pop: From c84961be85c68f368a272abf2b32fc5f38cadffb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 3 Jun 2020 21:51:21 +0530 Subject: [PATCH 082/129] fix: Error when no data is present in AR/AP reeport (cherry picked from commit 7135a75e5e28a5329b709174688761f78c307dff) --- .../report/accounts_receivable/accounts_receivable.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 4015b056c58..724e8b74437 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -169,9 +169,11 @@ class ReceivablePayableReport(object): def append_subtotal_row(self, party): sub_total_row = self.total_row_map.get(party) - self.data.append(sub_total_row) - self.data.append({}) - self.update_sub_total_row(sub_total_row, 'Total') + + if sub_total_row: + self.data.append(sub_total_row) + self.data.append({}) + self.update_sub_total_row(sub_total_row, 'Total') def get_voucher_balance(self, gle): if self.filters.get("sales_person"): @@ -232,7 +234,8 @@ class ReceivablePayableReport(object): if self.filters.get('group_by_party'): self.append_subtotal_row(self.previous_party) - self.data.append(self.total_row_map.get('Total')) + if self.data: + self.data.append(self.total_row_map.get('Total')) def append_row(self, row): self.allocate_future_payments(row) From 58a43be4f5acbf9e28f43d51391c242ccce8a67a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 4 Jun 2020 14:28:34 +0530 Subject: [PATCH 083/129] fix: import supplier invoice not working --- .../doctype/import_supplier_invoice/import_supplier_invoice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py index 203ea8c865a..e6b6f4d5da6 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py @@ -64,7 +64,8 @@ class ImportSupplierInvoice(Document): "buying_price_list": self.default_buying_price_list } - if not invoices_args.get("invoice_no", ''): return + if not invoices_args.get("bill_no", ''): + frappe.throw(_("Numero has not set in the XML file")) supp_dict = get_supplier_details(file_content) invoices_args["destination_code"] = get_destination_code_from_file(file_content) From 1ae2376a8337ada99c22d8b9ca517ffab54801cd Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 4 Jun 2020 14:46:50 +0530 Subject: [PATCH 084/129] fix: routing operations not added sequentially in the BOM --- erpnext/manufacturing/doctype/bom/bom.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 4f40fb71fc1..41881efec2d 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -101,13 +101,14 @@ class BOM(WebsiteGenerator): if self.routing: self.set("operations", []) for d in frappe.get_all("BOM Operation", fields = ["*"], - filters = {'parenttype': 'Routing', 'parent': self.routing}): + filters = {'parenttype': 'Routing', 'parent': self.routing}, order_by="idx"): child = self.append('operations', { "operation": d.operation, "workstation": d.workstation, "description": d.description, "time_in_mins": d.time_in_mins, - "batch_size": d.batch_size + "batch_size": d.batch_size, + "idx": d.idx }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) From efdb55a083d2cea29b423f1773e2fdede3b5caf6 Mon Sep 17 00:00:00 2001 From: P-Froggy <60393001+P-Froggy@users.noreply.github.com> Date: Fri, 5 Jun 2020 07:26:30 +0200 Subject: [PATCH 085/129] fix: Wrong Ordered-Status Indicator for Material Request Items (#22117) The indicator displaying if a material request item has been ordered or not (green/orange) used the wrong quantity field for determining the status. The qty field is not in stock uom while the ordered_qty field is. Now, the stock_qty field is used for correct comparison with ordered_qty. --- erpnext/stock/doctype/material_request/material_request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 6110ea822cf..b113781def3 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -17,7 +17,7 @@ frappe.ui.form.on('Material Request', { // formatter for material request item frm.set_indicator_formatter('item_code', - function(doc) { return (doc.qty<=doc.ordered_qty) ? "green" : "orange"; }); + function(doc) { return (doc.stock_qty<=doc.ordered_qty) ? "green" : "orange"; }); }, From 6732c9ab140dd43ccc5d0133e61974cccc34e9bc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 7 Jun 2020 18:59:06 +0530 Subject: [PATCH 086/129] fix: fetch tax lines within the shipping lines (#22138) (#22139) (cherry picked from commit 303112816771639a7808663170dac497b4f8e2d1) Co-authored-by: Mangesh-Khairnar --- .../connectors/shopify_connection.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index 3d92d55faaf..215f12c2c2f 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -241,14 +241,17 @@ def get_order_taxes(shopify_order, shopify_settings): return taxes def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings): + """Shipping lines represents the shipping details, + each such shipping detail consists of a list of tax_lines""" for shipping_charge in shipping_lines: - taxes.append({ - "charge_type": _("Actual"), - "account_head": get_tax_account_head(shipping_charge), - "description": shipping_charge["title"], - "tax_amount": shipping_charge["price"], - "cost_center": shopify_settings.cost_center - }) + for tax in shipping_charge.get("tax_lines"): + taxes.append({ + "charge_type": _("Actual"), + "account_head": get_tax_account_head(tax), + "description": tax["title"], + "tax_amount": tax["price"], + "cost_center": shopify_settings.cost_center + }) return taxes From 189663f576c764e421c5b9fa439e1105f2d2efdd Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 8 Jun 2020 13:02:00 +0530 Subject: [PATCH 087/129] fix: Finished Product Valuation at Repack --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- .../stock_entry_detail/stock_entry_detail.json | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index e48a2211e32..abc4fd0b377 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -497,7 +497,7 @@ class StockEntry(StockController): if raw_material_cost and self.purpose == "Manufacture": d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate")) d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount")) - elif self.purpose == "Repack" and total_fg_qty: + elif self.purpose == "Repack" and total_fg_qty and not d.set_basic_rate_manually: d.basic_rate = flt(raw_material_cost) / flt(total_fg_qty) d.basic_amount = d.basic_rate * d.qty diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index a848c80cf2c..9992d10febe 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -23,6 +23,7 @@ "image", "image_view", "quantity_and_rate", + "set_basic_rate_manually", "qty", "basic_rate", "basic_amount", @@ -490,12 +491,21 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:parent.purpose===\"Repack\" && doc.t_warehouse", + "fieldname": "set_basic_rate_manually", + "fieldtype": "Check", + "label": "Set Basic Rate Manually", + "show_days": 1, + "show_seconds": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-19 12:34:09.836295", + "modified": "2020-06-08 12:57:03.172887", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", From 5befddca39c643eb4db436cd8cd7b5fac4ec16a8 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Fri, 8 May 2020 18:01:18 +0800 Subject: [PATCH 088/129] fix: Tax Withholding Category Description default This commit fixes a bug that happens when a Purchase Invoice uses a Tax Withholding Category without a category_name. The category_name is used as the tax description which is a required field. The bug was fixed by using the Tax Withholding Category's name as the description if the category_name is empty. --- .../tax_withholding_category/tax_withholding_category.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 30bac5efa68..d4144c91044 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -58,7 +58,7 @@ def get_tax_withholding_details(tax_withholding_category, fiscal_year, company): "rate": tax_rate_detail.tax_withholding_rate, "threshold": tax_rate_detail.single_threshold, "cumulative_threshold": tax_rate_detail.cumulative_threshold, - "description": tax_withholding.category_name + "description": tax_withholding.category_name.strip() if tax_withholding.category_name.strip() else tax_withholding_category }) def get_tax_withholding_rates(tax_withholding, fiscal_year): @@ -225,4 +225,4 @@ def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount, certificate_limit > deducted_amount): valid = True - return valid \ No newline at end of file + return valid From 65fb3f2c48def516fdd33a26c747b59408ec344c Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sat, 16 May 2020 18:07:03 +0530 Subject: [PATCH 089/129] fix: Remove strip --- .../tax_withholding_category/tax_withholding_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index d4144c91044..b539fff74e9 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -58,7 +58,7 @@ def get_tax_withholding_details(tax_withholding_category, fiscal_year, company): "rate": tax_rate_detail.tax_withholding_rate, "threshold": tax_rate_detail.single_threshold, "cumulative_threshold": tax_rate_detail.cumulative_threshold, - "description": tax_withholding.category_name.strip() if tax_withholding.category_name.strip() else tax_withholding_category + "description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category }) def get_tax_withholding_rates(tax_withholding, fiscal_year): From dfd410ded29751092c35ec0c0069fbe5b90802c6 Mon Sep 17 00:00:00 2001 From: Poranut Chollavorn Date: Tue, 9 Jun 2020 03:16:49 +0000 Subject: [PATCH 090/129] fix(pricing_rule): missing income account --- erpnext/accounts/doctype/pricing_rule/utils.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 53115f92d01..9876246c47b 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -13,7 +13,10 @@ from six import string_types import frappe from erpnext.setup.doctype.item_group.item_group import get_child_item_groups from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses -from erpnext.stock.get_item_details import get_conversion_factor +from erpnext.stock.get_item_details import get_conversion_factor, get_default_income_account +from erpnext.stock.doctype.item.item import get_item_defaults +from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults +from erpnext.setup.doctype.brand.brand import get_brand_defaults from frappe import _, throw from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today @@ -480,6 +483,14 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): if item_details.get("parenttype") == 'Sales Order': item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today() + company = args.get('company') or doc.company + item_details.free_item_data['income_account'] = get_default_income_account( + args=args, + item=get_item_defaults(free_item, company), + item_group=get_item_group_defaults(free_item, company), + brand=get_brand_defaults(free_item, company), + ) + def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False): if pricing_rule_args.get('item_code'): items = [d.item_code for d in doc.items From 9831c5beca7b0c2a4000210a582136157f1dbb41 Mon Sep 17 00:00:00 2001 From: Poranut Chollavorn Date: Tue, 9 Jun 2020 15:06:44 +0000 Subject: [PATCH 091/129] fix(loyalty): loyalty point entry use wrong tier --- .../doctype/loyalty_program/loyalty_program.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py index 563165b2cc8..6a82572a8d8 100644 --- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py @@ -41,10 +41,16 @@ def get_loyalty_program_details_with_points(customer, loyalty_program=None, expi loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program) lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry)) - tier_spent_level = sorted([d.as_dict() for d in loyalty_program.collection_rules], - key=lambda rule:rule.min_spent, reverse=True) + # sort collection rule, first item on list will be lowest min_spent + tier_spent_level = sorted( + [d.as_dict() for d in loyalty_program.collection_rules], + key=lambda rule: rule.min_spent, reverse=False, + ) + + # looping and apply tier from highest min_spent for i, d in enumerate(tier_spent_level): - if i==0 or (lp_details.total_spent+current_transaction_amount) <= d.min_spent: + # if cumulative spend more than min_spent then continue to next tier + if (lp_details.total_spent + current_transaction_amount) >= d.min_spent: lp_details.tier_name = d.tier_name lp_details.collection_factor = d.collection_factor else: From 5a5361815fc7c7152da81f47a038fda9aa7334a6 Mon Sep 17 00:00:00 2001 From: Poranut Chollavorn Date: Tue, 9 Jun 2020 15:16:28 +0000 Subject: [PATCH 092/129] docs(loyalty): fix wrong comment --- erpnext/accounts/doctype/loyalty_program/loyalty_program.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py index 6a82572a8d8..d2d852229e2 100644 --- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py @@ -47,7 +47,7 @@ def get_loyalty_program_details_with_points(customer, loyalty_program=None, expi key=lambda rule: rule.min_spent, reverse=False, ) - # looping and apply tier from highest min_spent + # looping and apply tier from lowest min_spent for i, d in enumerate(tier_spent_level): # if cumulative spend more than min_spent then continue to next tier if (lp_details.total_spent + current_transaction_amount) >= d.min_spent: From 703cee6abfc37e1c15e5a743966d428a22e275de Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 10 Jun 2020 12:51:29 +0530 Subject: [PATCH 093/129] fix: update program course (#22166) (#22173) (cherry picked from commit 934e30ecac59dcd6aa5dcb06700c37a51209689c) Co-authored-by: Anupam Kumar --- .../program_course/program_course.json | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/erpnext/education/doctype/program_course/program_course.json b/erpnext/education/doctype/program_course/program_course.json index a24e88a8611..940358e4e9b 100644 --- a/erpnext/education/doctype/program_course/program_course.json +++ b/erpnext/education/doctype/program_course/program_course.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2015-09-07 14:37:01.886859", "doctype": "DocType", "editable_grid": 1, @@ -16,26 +17,33 @@ "in_list_view": 1, "label": "Course", "options": "Course", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, - { + { + "fetch_from": "course.course_name", "fieldname": "course_name", "fieldtype": "Data", "in_list_view": 1, "label": "Course Name", - "fetch_from": "course.course_name", - "read_only":1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "required", "fieldtype": "Check", "in_list_view": 1, - "label": "Mandatory" + "label": "Mandatory", + "show_days": 1, + "show_seconds": 1 } ], "istable": 1, - "modified": "2019-06-12 12:42:12.845972", + "links": [], + "modified": "2020-06-09 18:56:10.213241", "modified_by": "Administrator", "module": "Education", "name": "Program Course", @@ -45,4 +53,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file From c848b9775739b893e23d4367cdfaf63a9b2a60fb Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 10 Jun 2020 17:04:40 +0530 Subject: [PATCH 094/129] fix: Cannot read property 'has_batch_no' of undefined --- 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 6e131272dcb..0d3bbd658f7 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -539,7 +539,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if (show_batch_dialog) return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) .then((r) => { - if(r.message.has_batch_no || r.message.has_serial_no) { + if(r.message && + (r.message.has_batch_no || r.message.has_serial_no)) { frappe.flags.hide_serial_batch_dialog = false; } }); From 8710866886249eae43b2ac2c18fab2f4f8b36ca3 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 10 Jun 2020 18:22:59 +0530 Subject: [PATCH 095/129] fix: Prioritize Default Customer Price List in Portal --- erpnext/shopping_cart/cart.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index a33f4a12242..e5b0abb1ed8 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -337,21 +337,17 @@ def set_price_list_and_rate(quotation, cart_settings): def _set_price_list(cart_settings, quotation=None): """Set price list based on customer or shopping cart default""" from erpnext.accounts.party import get_default_price_list - - # check if customer price list exists + party_name = quotation.get("party_name") if quotation else get_party().get("name") selling_price_list = None - if quotation and quotation.get("party_name"): - selling_price_list = frappe.db.get_value('Customer', quotation.get("party_name"), 'default_price_list') - # else check for territory based price list + # check if default customer price list exists + if party_name: + selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) + + # check default price list in shopping cart if not selling_price_list: selling_price_list = cart_settings.price_list - party_name = quotation.get("party_name") if quotation else get_party().get("name") - - if not selling_price_list and party_name: - selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) - if quotation: quotation.selling_price_list = selling_price_list From 8a2c71935936cee537e242f5195c8567888fbd6a Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 10 Jun 2020 19:11:20 +0530 Subject: [PATCH 096/129] fix: only auto-set serial nos and batches if allowed in Stock Settings (v12) (#21779) * fix: only auto-set serial nos and batches if allowed in Stock Settings * fix: bug with setting disabled batch no in Pick List * Remove auto_set_batch_no Co-authored-by: Marica --- erpnext/stock/doctype/pick_list/pick_list.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 29c6c2ccbaa..63a59b87104 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -119,11 +119,13 @@ def get_items_with_location_and_quantity(item_doc, item_location_map): if item_location.serial_no: serial_nos = '\n'.join(item_location.serial_no[0: cint(stock_qty)]) + auto_set_serial_no = frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo") + locations.append(frappe._dict({ 'qty': qty, 'stock_qty': stock_qty, 'warehouse': item_location.warehouse, - 'serial_no': serial_nos, + 'serial_no': serial_nos if auto_set_serial_no else item_doc.serial_no, 'batch_no': item_location.batch_no })) @@ -206,6 +208,7 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re sle.batch_no = batch.name and sle.`item_code`=%(item_code)s and sle.`company` = %(company)s + and batch.disabled = 0 and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s {warehouse_condition} GROUP BY @@ -471,4 +474,4 @@ def update_common_item_properties(item, location): item.material_request = location.material_request item.serial_no = location.serial_no item.batch_no = location.batch_no - item.material_request_item = location.material_request_item \ No newline at end of file + item.material_request_item = location.material_request_item From 755b8490e7b2963e910b9522a964269c664e102b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 10 Jun 2020 22:15:27 +0530 Subject: [PATCH 097/129] fix: Update payment schedule based on payment terms (cherry picked from commit a0fd97f2ac1598ce7e3457aa4b30b19720906537) --- .../doctype/payment_entry/payment_entry.py | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index eae801368ca..a2e1a4a1612 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -318,7 +318,7 @@ class PaymentEntry(AccountsController): invoice_payment_amount_map.setdefault(key, 0.0) invoice_payment_amount_map[key] += reference.allocated_amount - if not invoice_paid_amount_map.get(reference.reference_name): + if not invoice_paid_amount_map.get(key): payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name}, fields=['paid_amount', 'payment_amount', 'payment_term']) for term in payment_schedule: @@ -331,12 +331,14 @@ class PaymentEntry(AccountsController): frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) else: - outstanding = invoice_paid_amount_map.get(key)['outstanding'] + outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding')) + if amount > outstanding: frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0])) - frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s - WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) + if amount and outstanding: + frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s + WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) def set_status(self): if self.docstatus == 2: @@ -1090,17 +1092,20 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount): references = [] for payment_term in payment_schedule: - references.append({ - 'reference_doctype': dt, - 'reference_name': dn, - 'bill_no': doc.get('bill_no'), - 'due_date': doc.get('due_date'), - 'total_amount': grand_total, - 'outstanding_amount': outstanding_amount, - 'payment_term': payment_term.payment_term, - 'allocated_amount': flt(payment_term.payment_amount - payment_term.paid_amount, + payment_term_outstanding = flt(payment_term.payment_amount - payment_term.paid_amount, payment_term.precision('payment_amount')) - }) + + if payment_term_outstanding: + references.append({ + 'reference_doctype': dt, + 'reference_name': dn, + 'bill_no': doc.get('bill_no'), + 'due_date': doc.get('due_date'), + 'total_amount': grand_total, + 'outstanding_amount': outstanding_amount, + 'payment_term': payment_term.payment_term, + 'allocated_amount': payment_term_outstanding + }) return references From 821606f00188d8207a9e14f00ef0909dfc3da350 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 11 Jun 2020 13:19:53 +0530 Subject: [PATCH 098/129] fix: Handle unavailable Variants in Website --- erpnext/portal/product_configurator/utils.py | 3 +++ erpnext/shopping_cart/cart.py | 4 +++- erpnext/templates/generators/item/item_configure.js | 9 ++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 327a128068f..220bae888a7 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -1,5 +1,6 @@ import frappe import numpy as np +from frappe.utils import cint from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager def get_field_filter_data(): @@ -265,6 +266,8 @@ def get_next_attribute_and_values(item_code, selected_attributes): else: product_info = None + product_info["allow_items_not_in_stock"] = cint(frappe.db.get_singles_value("Shopping Cart Settings", "allow_items_not_in_stock")) + return { 'next_attribute': next_attribute, 'valid_options_for_attributes': valid_options_for_attributes, diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index e5b0abb1ed8..0fded2ec6f7 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -78,8 +78,10 @@ def place_order(): if is_stock_item: item_stock = get_qty_in_stock(item.item_code, "website_warehouse") + if not cint(item_stock.in_stock): + throw(_("{1} Not in Stock").format(item.item_code)) if item.qty > item_stock.stock_qty[0][0]: - throw(_("Only {0} in stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code)) + throw(_("Only {0} in Stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code)) sales_order.flags.ignore_permissions = True sales_order.insert() diff --git a/erpnext/templates/generators/item/item_configure.js b/erpnext/templates/generators/item/item_configure.js index 5fd901169f0..163c955c566 100644 --- a/erpnext/templates/generators/item/item_configure.js +++ b/erpnext/templates/generators/item/item_configure.js @@ -193,14 +193,17 @@ class ItemConfigure { filtered_items_count === 1 ? filtered_items[0] : ''; + // Allow Add to Cart if adding out of stock items enabled in Shopping Cart else check stock. + const in_stock = product_info.allow_items_not_in_stock ? 1 : product_info.in_stock; + const add_to_cart = `${__('Add to cart')}`; + const product_action = in_stock ? add_to_cart : `${__('Not in Stock')}`; + const item_add_to_cart = one_item ? ` `: ''; From a965773c97c613f3b60436209169f96f12571263 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 11 Jun 2020 16:39:03 +0530 Subject: [PATCH 099/129] fix: Don't prompt for Quality Inspection on Return Documents. --- erpnext/controllers/stock_controller.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index d4b0a8d8466..29e0085dc07 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -19,7 +19,8 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass class StockController(AccountsController): def validate(self): super(StockController, self).validate() - self.validate_inspection() + if not self.is_return: + self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() From 5ee33bdbe954ec8e15f486825defb1243c0fd529 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 11 Jun 2020 19:54:44 +0530 Subject: [PATCH 100/129] fix: Check for Company before rendering tree in Account Tree --- erpnext/accounts/doctype/account/account_tree.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index f62d07668de..28b090bdadb 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -14,6 +14,9 @@ frappe.treeview_settings["Account"] = { on_change: function() { var me = frappe.treeview_settings['Account'].treeview; var company = me.page.fields_dict.company.get_value(); + if (!company) { + frappe.throw(__("Please set a Company")); + } frappe.call({ method: "erpnext.accounts.doctype.account.account.get_root_company", args: { From d6cdc3378a519b750b9c0a355badf9752d7ca4af Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Fri, 12 Jun 2020 10:30:32 +0530 Subject: [PATCH 101/129] fix: add filter for cost center in expense table Added filter for cost center in expense table (company and group_node filter) --- erpnext/hr/doctype/expense_claim/expense_claim.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 3194007f231..88bd689d9de 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -154,6 +154,14 @@ frappe.ui.form.on("Expense Claim", { } }; }); + frm.set_query("cost_center", "expenses", function() { + return { + filters: { + "company": frm.doc.company, + "is_group": 0 + } + }; + }); frm.set_query("account_head", "taxes", function(doc) { return { filters: [ From 8ce5e04777af8ae7f2eb8d547f4a5005d2601a75 Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 12 Jun 2020 12:32:12 +0530 Subject: [PATCH 102/129] Update stock_controller.py --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 29e0085dc07..b238600881d 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -19,7 +19,7 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass class StockController(AccountsController): def validate(self): super(StockController, self).validate() - if not self.is_return: + if not self.get('is_return'): self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() From 37b614af0e5737fc9f85e449250da2c89eb9967d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 29 May 2020 21:55:38 +0530 Subject: [PATCH 103/129] fix: submitted sales order can be updated with no permission --- erpnext/controllers/accounts_controller.py | 46 ++++++++++++++----- erpnext/public/js/utils.js | 7 +++ .../doctype/sales_order/test_sales_order.py | 16 +++++++ 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e5073917b8d..fd74ee8d38a 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1134,8 +1134,8 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.item_name = item.item_name child_item.description = item.description child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date + child_item.conversion_factor = flt(d.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") @@ -1154,8 +1154,8 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.item_name = item.item_name child_item.description = item.description child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date + child_item.conversion_factor = flt(d.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation return child_item @@ -1187,6 +1187,26 @@ def check_and_delete_children(parent, data): @frappe.whitelist() def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"): + def check_permissions(doc, perm_type='create'): + try: + doc.check_permission(perm_type) + except: + action = "add" if perm_type == 'create' else "update" + frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions")) + + def get_new_child_item(): + if parent_doctype == "Sales Order": + return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + if parent_doctype == "Purchase Order": + return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + + def validate_quantity(child_item, d): + if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): + frappe.throw(_("Cannot set quantity less than delivered quantity")) + + if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty): + frappe.throw(_("Cannot set quantity less than received quantity")) + data = json.loads(trans_items) sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation'] @@ -1198,20 +1218,19 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil new_child_flag = False if not d.get("docname"): new_child_flag = True - if parent_doctype == "Sales Order": - child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) - if parent_doctype == "Purchase Order": - child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + check_permissions(parent, 'create') + child_item = get_new_child_item() else: + check_permissions(parent, 'write') child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) - if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")): + + rate_unchanged = flt(child_item.get("rate")) == flt(d.get("rate")) + qty_unchanged = flt(child_item.get("qty")) == flt(d.get("qty")) + conversion_factor_unchanged = flt(child_item.get("conversion_factor")) == flt(d.get("conversion_factor")) + if rate_unchanged and qty_unchanged and conversion_factor_changed: continue - if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): - frappe.throw(_("Cannot set quantity less than delivered quantity")) - - if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty): - frappe.throw(_("Cannot set quantity less than received quantity")) + validate_quantity(child_item, d) child_item.qty = flt(d.get("qty")) precision = child_item.precision("rate") or 2 @@ -1221,6 +1240,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil .format(child_item.idx, child_item.item_code)) else: child_item.rate = flt(d.get("rate")) + + if d.get("conversion_factor"): + child_item.conversion_factor = flt(d.get('conversion_factor')) if flt(child_item.price_list_rate): if flt(child_item.rate) > flt(child_item.price_list_rate): diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index fb7471150cc..e0e90f84785 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -489,6 +489,12 @@ erpnext.utils.update_child_items = function(opts) { in_list_view: 1, label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date") }) + fields.splice(3, 0, { + fieldtype: 'Float', + fieldname: "conversion_factor", + in_list_view: 1, + label: __("UOM Conversion Factor") + }) } const dialog = new frappe.ui.Dialog({ @@ -536,6 +542,7 @@ erpnext.utils.update_child_items = function(opts) { "item_code": d.item_code, "delivery_date": d.delivery_date, "schedule_date": d.schedule_date, + "conversion_factor": d.conversion_factor, "qty": d.qty, "rate": d.rate, }); diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 9735c70830d..05e083483d3 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -399,6 +399,22 @@ class TestSalesOrder(unittest.TestCase): trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 2, 'docname': so.items[0].name}]) self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + def test_update_child_qty_rate_perm(self): + so = make_sales_order(item_code= "_Test Item", qty=4) + + user = 'test@example.com' + test_user = frappe.get_doc('User', user) + test_user.add_roles("Accounts User") + frappe.set_user(user) + + # update qty + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + + # add new item + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + def test_warehouse_user(self): frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com") frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com") From 39e125b8a267bd3e85ebc4c01e108fb4ca1c3674 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 29 May 2020 21:59:01 +0530 Subject: [PATCH 104/129] fix: label --- erpnext/public/js/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index e0e90f84785..fe7cebf7d71 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -493,7 +493,7 @@ erpnext.utils.update_child_items = function(opts) { fieldtype: 'Float', fieldname: "conversion_factor", in_list_view: 1, - label: __("UOM Conversion Factor") + label: __("Conversion Factor") }) } From 9ee4841520bfdf36f5400cfabdfe2460a316cdb9 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 29 May 2020 22:21:50 +0530 Subject: [PATCH 105/129] fix: cannot update delivery date --- erpnext/controllers/accounts_controller.py | 24 ++++++++++++++++++---- erpnext/public/js/utils.js | 3 ++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index fd74ee8d38a..e567ee25bbf 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1224,10 +1224,20 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil check_permissions(parent, 'write') child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) - rate_unchanged = flt(child_item.get("rate")) == flt(d.get("rate")) - qty_unchanged = flt(child_item.get("qty")) == flt(d.get("qty")) - conversion_factor_unchanged = flt(child_item.get("conversion_factor")) == flt(d.get("conversion_factor")) - if rate_unchanged and qty_unchanged and conversion_factor_changed: + prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate")) + prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty")) + prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor")) + + if parent_doctype == 'Sales Order': + prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date") + elif parent_doctype == 'Purchase Order': + prev_date, new_date = child_item.get("schedule_date") == d.get("schedule_date") + + rate_unchanged = prev_rate == new_rate + qty_unchanged = prev_qty == prev_qty + conversion_factor_unchanged = prev_con_fac == new_con_fac + date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc + if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged: continue validate_quantity(child_item, d) @@ -1244,6 +1254,12 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if d.get("conversion_factor"): child_item.conversion_factor = flt(d.get('conversion_factor')) + if d.get("delivery_date") and parent_doctype == 'Sales Order': + child_item.delivery_date = d.get('delivery_date') + + if d.get("schedule_date") and parent_doctype == 'Purchase Order': + child_item.schedule_date = d.get('schedule_date') + if flt(child_item.price_list_rate): if flt(child_item.rate) > flt(child_item.price_list_rate): # if rate is greater than price_list_rate, set margin diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index fe7cebf7d71..9b1c94e5ba0 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -487,7 +487,8 @@ erpnext.utils.update_child_items = function(opts) { fieldtype: 'Date', fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date", in_list_view: 1, - label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date") + label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date"), + reqd: 1 }) fields.splice(3, 0, { fieldtype: 'Float', From 6a9f36829252b51721885052b5f909e48db6b483 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 29 May 2020 22:44:59 +0530 Subject: [PATCH 106/129] fix: unexpected removal of print hide field --- erpnext/selling/doctype/sales_order_item/sales_order_item.json | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 722a1adc2fa..909f609a6f1 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -210,6 +210,7 @@ "fieldtype": "Link", "label": "UOM", "options": "UOM", + "print_hide": 0, "reqd": 1 }, { From f6054a2de89fca1465f82521bcaa8627e659f931 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 12 Jun 2020 15:49:53 +0530 Subject: [PATCH 107/129] fix: Added PO test and conversion factor fix - Don't change conversion factor if stock uom and uom is the same - Added PO test - Added Accounts User basic role in PO - Minor fixes, wrong variables --- .../purchase_order/purchase_order.json | 8 +++++++- .../purchase_order/test_purchase_order.py | 20 +++++++++++++++++-- erpnext/controllers/accounts_controller.py | 15 ++++++++------ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 0ccdb25c32f..2018d068edf 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1056,7 +1056,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-04-17 13:04:28.185197", + "modified": "2020-06-12 14:08:11.777120", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", @@ -1100,6 +1100,12 @@ "read": 1, "role": "Purchase Manager", "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "Accounts User" } ], "search_fields": "status, transaction_date, supplier,grand_total", diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 2b6367b08d1..4a6e8642f8c 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -116,7 +116,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(po.get("items")[0].amount, 1400) self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3) - + def test_add_new_item_in_update_child_qty_rate(self): po = create_purchase_order(do_not_save=1) po.items[0].qty = 4 @@ -142,7 +142,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEquals(len(po.get('items')), 2) self.assertEqual(po.status, 'To Receive and Bill') - + def test_remove_item_in_update_child_qty_rate(self): po = create_purchase_order(do_not_save=1) po.items[0].qty = 4 @@ -183,6 +183,22 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEquals(len(po.get('items')), 1) self.assertEqual(po.status, 'To Receive and Bill') + def test_update_child_qty_rate_perm(self): + po = create_purchase_order(item_code= "_Test Item", qty=4) + + user = 'test@example.com' + test_user = frappe.get_doc('User', user) + test_user.add_roles("Accounts User") + frappe.set_user(user) + + # update qty + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.items[0].name}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) + + # add new item + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) + def test_update_qty(self): po = create_purchase_order() diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e567ee25bbf..df0715eae85 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1134,7 +1134,7 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.item_name = item.item_name child_item.description = item.description child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date - child_item.conversion_factor = flt(d.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 + child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: @@ -1154,7 +1154,7 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.item_name = item.item_name child_item.description = item.description child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date - child_item.conversion_factor = flt(d.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 + child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation @@ -1234,7 +1234,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil prev_date, new_date = child_item.get("schedule_date") == d.get("schedule_date") rate_unchanged = prev_rate == new_rate - qty_unchanged = prev_qty == prev_qty + qty_unchanged = prev_qty == new_qty conversion_factor_unchanged = prev_con_fac == new_con_fac date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged: @@ -1250,13 +1250,16 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil .format(child_item.idx, child_item.item_code)) else: child_item.rate = flt(d.get("rate")) - + if d.get("conversion_factor"): - child_item.conversion_factor = flt(d.get('conversion_factor')) + if child_item.stock_uom == child_item.uom: + child_item.conversion_factor = 1 + else: + child_item.conversion_factor = flt(d.get('conversion_factor')) if d.get("delivery_date") and parent_doctype == 'Sales Order': child_item.delivery_date = d.get('delivery_date') - + if d.get("schedule_date") and parent_doctype == 'Purchase Order': child_item.schedule_date = d.get('schedule_date') From 9319153ab365296d5ec9a5a014218deb665468ad Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Fri, 12 Jun 2020 17:04:25 +0530 Subject: [PATCH 108/129] fix: Child is shown in Parent process if added from tree view --- .../doctype/quality_procedure/quality_procedure.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index b4de03e1e4f..3ce5ea015f5 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -10,7 +10,7 @@ from frappe import _ class QualityProcedure(NestedSet): nsm_parent_field = 'parent_quality_procedure' - def before_save(self): + def on_save(self): for process in self.processes: if process.procedure: doc = frappe.get_doc("Quality Procedure", process.procedure) @@ -23,6 +23,11 @@ class QualityProcedure(NestedSet): def after_insert(self): self.set_parent() + #if Child is Added through Tree View. + if self.parent_quality_procedure: + parent_quality_procedure = frappe.get_doc("Quality Procedure", self.parent_quality_procedure) + parent_quality_procedure.append("processes", {"procedure": self.name}) + parent_quality_procedure.save() def on_trash(self): if self.parent_quality_procedure: From 486be1049c1febcaac7d4814c8f79c8a66ac80bc Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Sat, 13 Jun 2020 01:18:29 +0530 Subject: [PATCH 109/129] fix(HR): Rename due_advance_amount field in Employee Advance for v12 (#22216) * added patch for moving data * changed fieldname and references for easier debugging * style: Added EOF * reload doc for patch --- .../employee_advance/employee_advance.js | 4 +-- .../employee_advance/employee_advance.json | 26 ++++++++++++------- .../employee_advance/employee_advance.py | 2 +- erpnext/patches.txt | 2 ++ ...ve_due_advance_amount_to_pending_amount.py | 9 +++++++ 5 files changed, 30 insertions(+), 13 deletions(-) create mode 100644 erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js index 389660387b7..70bf20114a4 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.js +++ b/erpnext/hr/doctype/employee_advance/employee_advance.js @@ -113,13 +113,13 @@ frappe.ui.form.on('Employee Advance', { employee: function (frm) { if (frm.doc.employee) { return frappe.call({ - method: "erpnext.hr.doctype.employee_advance.employee_advance.get_due_advance_amount", + method: "erpnext.hr.doctype.employee_advance.employee_advance.get_pending_amount", args: { "employee": frm.doc.employee, "posting_date": frm.doc.posting_date }, callback: function(r) { - frm.set_value("due_advance_amount",r.message); + frm.set_value("pending_amount",r.message); } }); } diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json index d233a2bb936..435d160f33a 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.json +++ b/erpnext/hr/doctype/employee_advance/employee_advance.json @@ -18,7 +18,7 @@ "column_break_11", "advance_amount", "paid_amount", - "due_advance_amount", + "pending_amount", "claimed_amount", "return_amount", "section_break_7", @@ -101,14 +101,6 @@ "options": "Company:company:default_currency", "read_only": 1 }, - { - "depends_on": "eval:cur_frm.doc.employee", - "fieldname": "due_advance_amount", - "fieldtype": "Currency", - "label": "Due Advance Amount", - "options": "Company:company:default_currency", - "read_only": 1 - }, { "fieldname": "claimed_amount", "fieldtype": "Currency", @@ -169,11 +161,25 @@ "label": "Returned Amount", "options": "Company:company:default_currency", "read_only": 1 + }, + { + "default": "0", + "fieldname": "repay_unclaimed_amount_from_salary", + "fieldtype": "Check", + "label": "Repay unclaimed amount from salary" + }, + { + "depends_on": "eval:cur_frm.doc.employee", + "fieldname": "pending_amount", + "fieldtype": "Currency", + "label": "Pending Amount", + "options": "Company:company:default_currency", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2019-12-15 19:04:07.044505", + "modified": "2020-06-12 12:42:39.833818", "modified_by": "Administrator", "module": "HR", "name": "Employee Advance", diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index f0663aefa83..30a2a896dc6 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -95,7 +95,7 @@ class EmployeeAdvance(Document): frappe.db.set_value("Employee Advance", self.name, "status", self.status) @frappe.whitelist() -def get_due_advance_amount(employee, posting_date): +def get_pending_amount(employee, posting_date): employee_due_amount = frappe.get_all("Employee Advance", \ filters = {"employee":employee, "docstatus":1, "posting_date":("<=", posting_date)}, \ fields = ["advance_amount", "paid_amount"]) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index fb83fadb71d..34faea36eb2 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -669,3 +669,5 @@ erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom erpnext.patches.v12_0.update_uom_conversion_factor erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions +execute:frappe.reload_doc("HR", "doctype", "Employee Advance") +erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount diff --git a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py new file mode 100644 index 00000000000..f1ffaf9d2d4 --- /dev/null +++ b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py @@ -0,0 +1,9 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + ''' Move from due_advance_amount to pending_amount ''' + frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') From 1222fb074fccd4676ac70cdb307b05cd48ea70bf Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 13 Jun 2020 19:16:37 +0530 Subject: [PATCH 110/129] fix: Data not appearing properly for some fiscal_year in financial statemets --- erpnext/accounts/report/financial_statements.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 48d1cde4550..7e96b9e237b 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -50,9 +50,8 @@ def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_v to_date = add_months(start_date, months_to_add) start_date = to_date - if to_date == get_first_day(to_date): - # if to_date is the first day, get the last day of previous month - to_date = add_days(to_date, -1) + # Subtract one day from to_date, as it may be first day in next fiscal year or month + to_date = add_days(to_date, -1) if to_date <= year_end_date: # the normal case From 5b11d18d8686456a905f4c83f4d33d95bc6eeb9e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 14 Jun 2020 14:06:12 +0530 Subject: [PATCH 111/129] fix: item none not found while making sales invoice using opening invoice creation tool --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index fa175fc816e..ce26a258fb6 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -552,6 +552,8 @@ class SalesInvoice(SellingController): continue for d in self.get('items'): + if not d.item_code: continue + is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item') if (d.item_code and is_stock_item ==1 and not d.get(key.lower().replace(' ', '_')) and not self.get(value[1])): msgprint(_("{0} is mandatory for Item {1}").format(key, d.item_code), raise_exception=1) @@ -574,14 +576,14 @@ class SalesInvoice(SellingController): def validate_item_code(self): for d in self.get('items'): - if not d.item_code: + if not d.item_code and self.is_opening == "No": msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True) def validate_warehouse(self): super(SalesInvoice, self).validate_warehouse() for d in self.get_item_list(): - if not d.warehouse and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): + if not d.warehouse and d.item_code and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code)) def validate_delivery_note(self): From 0a31e2ea924a0a3587fb39786787116aadfa8442 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 15 Jun 2020 12:00:16 +0530 Subject: [PATCH 112/129] fix: Fetch cart setting from argument --- erpnext/portal/product_configurator/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 220bae888a7..69ff1dcccaa 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -266,7 +266,7 @@ def get_next_attribute_and_values(item_code, selected_attributes): else: product_info = None - product_info["allow_items_not_in_stock"] = cint(frappe.db.get_singles_value("Shopping Cart Settings", "allow_items_not_in_stock")) + product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock) return { 'next_attribute': next_attribute, From 713cbcea44839f2f777f75fa3387dd76dfb5f7c6 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 15 Jun 2020 11:32:42 +0530 Subject: [PATCH 113/129] fix: Travis --- erpnext/buying/doctype/purchase_order/test_purchase_order.py | 1 + erpnext/controllers/accounts_controller.py | 4 ++-- erpnext/selling/doctype/sales_order/test_sales_order.py | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 4a6e8642f8c..075db6e46ba 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -198,6 +198,7 @@ class TestPurchaseOrder(unittest.TestCase): # add new item trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) + frappe.set_user("Administrator") def test_update_qty(self): po = create_purchase_order() diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index df0715eae85..a4bed51f479 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1178,7 +1178,7 @@ def check_and_delete_children(parent, data): if parent.doctype == "Purchase Order" and flt(d.received_qty): frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been received").format(d.idx, d.item_code)) - + if flt(d.billed_amt): frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been billed.").format(d.idx, d.item_code)) @@ -1231,7 +1231,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if parent_doctype == 'Sales Order': prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date") elif parent_doctype == 'Purchase Order': - prev_date, new_date = child_item.get("schedule_date") == d.get("schedule_date") + prev_date, new_date = child_item.get("schedule_date"), d.get("schedule_date") rate_unchanged = prev_rate == new_rate qty_unchanged = prev_qty == new_qty diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 05e083483d3..6a6d7c96a22 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -334,7 +334,7 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(so.get("items")[-1].qty, 7) self.assertEqual(so.get("items")[-1].amount, 1400) self.assertEqual(so.status, 'To Deliver and Bill') - + def test_remove_item_in_update_child_qty_rate(self): so = make_sales_order(**{ "item_list": [{ @@ -372,7 +372,7 @@ class TestSalesOrder(unittest.TestCase): "docname": so.get("items")[0].name }]) update_child_qty_rate('Sales Order', trans_item, so.name) - + so.reload() self.assertEqual(len(so.get("items")), 1) self.assertEqual(so.status, 'To Deliver and Bill') @@ -414,6 +414,7 @@ class TestSalesOrder(unittest.TestCase): # add new item trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + frappe.set_user("Administrator") def test_warehouse_user(self): frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com") From 84f44588d8f3d053304cfc7fcf65aa197b3c298d Mon Sep 17 00:00:00 2001 From: P-Froggy <60393001+P-Froggy@users.noreply.github.com> Date: Mon, 15 Jun 2020 10:10:54 +0200 Subject: [PATCH 114/129] fix: Validation of Purchase Order against Material Request missing (#22224) Validation of Purchase Order and Purchase Order Item against the linked Material Request Item was missing. This way, it was possible to exchange items while making a purchase order and still fulfill the material request, even though the items did not match. Co-authored-by: Marica --- erpnext/buying/doctype/purchase_order/purchase_order.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index f62df20ae1a..c7efb8a1a17 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -71,6 +71,15 @@ class PurchaseOrder(BuyingController): "compare_fields": [["project", "="], ["item_code", "="], ["uom", "="], ["conversion_factor", "="]], "is_child_table": True + }, + "Material Request": { + "ref_dn_field": "material_request", + "compare_fields": [["company", "="]], + }, + "Material Request Item": { + "ref_dn_field": "material_request_item", + "compare_fields": [["project", "="], ["item_code", "="]], + "is_child_table": True } }) From b819d8cdb6383a8e0c980efb93496b7b2bf7efc3 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 15 Jun 2020 14:40:39 +0530 Subject: [PATCH 115/129] fix: Wrong key sent to gte_valuation_rate --- erpnext/controllers/buying_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 70f9033f431..0973f165232 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -340,7 +340,7 @@ class BuyingController(StockController): }) if not rm.rate: - rm.rate = get_valuation_rate(raw_material_data.item_code, self.supplier_warehouse, + rm.rate = get_valuation_rate(raw_material_data.rm_item_code, self.supplier_warehouse, self.doctype, self.name, currency=self.company_currency, company=self.company) rm.amount = qty * flt(rm.rate) From 956368e457c39e5269b37fb380a6a85eeeafbed7 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 17 Jun 2020 09:37:41 +0530 Subject: [PATCH 116/129] refactor: hide company currency fields in the routing (#22267) (cherry picked from commit fd3ff6be18b0bd5accc88aea22e3adfe48e62aff) --- erpnext/manufacturing/doctype/bom/bom.py | 1 + .../doctype/bom_operation/bom_operation.json | 8 +++++--- erpnext/manufacturing/doctype/routing/routing.js | 1 - 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 41881efec2d..067a6f7674d 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -108,6 +108,7 @@ class BOM(WebsiteGenerator): "description": d.description, "time_in_mins": d.time_in_mins, "batch_size": d.batch_size, + "operating_cost": d.operating_cost, "idx": d.idx }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json index 3ca851d783b..0350e2cb374 100644 --- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json +++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json @@ -78,6 +78,7 @@ "read_only": 1 }, { + "depends_on": "eval:parent.doctype == 'BOM'", "fieldname": "base_hour_rate", "fieldtype": "Currency", "label": "Base Hour Rate(Company Currency)", @@ -87,6 +88,7 @@ }, { "default": "5", + "depends_on": "eval:parent.doctype == 'BOM'", "fieldname": "base_operating_cost", "fieldtype": "Currency", "label": "Operating Cost(Company Currency)", @@ -108,12 +110,12 @@ ], "idx": 1, "istable": 1, - "modified": "2019-07-16 22:35:55.374037", - "modified_by": "govindsmenokee@gmail.com", + "modified": "2020-06-16 17:01:11.128420", + "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/routing/routing.js b/erpnext/manufacturing/doctype/routing/routing.js index 6cfd0bae5b5..d7589fa3907 100644 --- a/erpnext/manufacturing/doctype/routing/routing.js +++ b/erpnext/manufacturing/doctype/routing/routing.js @@ -44,7 +44,6 @@ frappe.ui.form.on('BOM Operation', { name: d.workstation }, callback: function (data) { - frappe.model.set_value(d.doctype, d.name, "base_hour_rate", data.message.hour_rate); frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate); frm.events.calculate_operating_cost(frm, d); } From 9c55efa8c6ef278bb0d976d9d126c28ba9291328 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 17 Jun 2020 09:36:23 +0530 Subject: [PATCH 117/129] fix: pos, serial no popup comin two times (#22268) (cherry picked from commit 03f688acd9705eefb4a07bfe6b0eec456261d88f) --- erpnext/public/js/controllers/transaction.js | 3 +-- erpnext/selling/sales_common.js | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 0d3bbd658f7..445d683788e 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -4,7 +4,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ setup: function() { this._super(); - frappe.flags.hide_serial_batch_dialog = true; frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); var has_margin_field = frappe.meta.has_field(cdt, 'margin_type'); @@ -539,7 +538,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if (show_batch_dialog) return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) .then((r) => { - if(r.message && + if(r.message && !frappe.flags.hide_serial_batch_dialog && (r.message.has_batch_no || r.message.has_serial_no)) { frappe.flags.hide_serial_batch_dialog = false; } diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 4a7dd5ad9b4..99c4e62fdbe 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -229,7 +229,10 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ var me = this; var item = frappe.get_doc(cdt, cdn); - if (item.serial_no && item.qty === item.serial_no.split(`\n`).length) { + let serial_no_count = item.serial_no + ? item.serial_no.split(`\n`).filter(d => d).length : 0; + + if (item.serial_no && item.qty === serial_no_count) { return; } From 5e68fdbaee00ce61aa90f9b671dd9ddaf324c626 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 15 Jun 2020 16:57:20 +0530 Subject: [PATCH 118/129] fix: travis (cherry picked from commit e4834d2a3738f2be1d24b33d3b3ef2bb9d789a72) --- .../loyalty_program/test_loyalty_program.py | 1 + .../test_procurement_tracker.py | 19 +++++++++++-------- ...ve_due_advance_amount_to_pending_amount.py | 4 +++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py index 341884c1901..20fbde19da1 100644 --- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py @@ -68,6 +68,7 @@ class TestLoyaltyProgram(unittest.TestCase): lpe = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_original.name, 'customer': si_original.customer}) + customer.load_from_db() self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program) self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier) self.assertEqual(lpe.loyalty_points, earned_points) diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index bebf0ccec56..c7204a1f341 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -15,7 +15,7 @@ class TestProcurementTracker(unittest.TestCase): def test_result_for_procurement_tracker(self): filters = { 'company': '_Test Procurement Company', - 'cost_center': '_Test Cost Center - _TC' + 'cost_center': 'Main - _TPC' } expected_data = self.generate_expected_data() report = execute(filters) @@ -33,24 +33,27 @@ class TestProcurementTracker(unittest.TestCase): country="Pakistan" )).insert() warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company") - mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse) + mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse, cost_center="Main - _TPC") po = make_purchase_order(mr.name) po.supplier = "_Test Supplier" - po.get("items")[0].cost_center = "_Test Cost Center - _TC" + po.get("items")[0].cost_center = "Main - _TPC" po.submit() pr = make_purchase_receipt(po.name) + pr.get("items")[0].cost_center = "Main - _TPC" pr.submit() frappe.db.commit() date_obj = datetime.date(datetime.now()) + po.load_from_db() + expected_data = { "material_request_date": date_obj, - "cost_center": "_Test Cost Center - _TC", + "cost_center": "Main - _TPC", "project": None, "requesting_site": "_Test Procurement Warehouse - _TPC", "requestor": "Administrator", "material_request_no": mr.name, - "description": '_Test Item 1', + "item_code": '_Test Item', "quantity": 10.0, "unit_of_measurement": "_Test UOM", "status": "To Bill", @@ -58,9 +61,9 @@ class TestProcurementTracker(unittest.TestCase): "purchase_order": po.name, "supplier": "_Test Supplier", "estimated_cost": 0.0, - "actual_cost": None, - "purchase_order_amt": 5000.0, - "purchase_order_amt_in_company_currency": 300000.0, + "actual_cost": 0.0, + "purchase_order_amt": po.net_total, + "purchase_order_amt_in_company_currency": po.base_net_total, "expected_delivery_date": date_obj, "actual_delivery_date": date_obj } diff --git a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py index f1ffaf9d2d4..6013eaa29c6 100644 --- a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py +++ b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py @@ -6,4 +6,6 @@ import frappe def execute(): ''' Move from due_advance_amount to pending_amount ''' - frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') + + if frappe.db.has_column("Employee Advance", "due_advance_amount"): + frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') From 7be4ba93518254e01bd7f4dbd71005b99cdfa638 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 17 Jun 2020 13:46:21 +0530 Subject: [PATCH 119/129] fix: Message Formatting (cherry picked from commit 17ec1d6b28ba60e082b966abc4b9a055f5e67159) --- erpnext/controllers/stock_controller.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index b238600881d..ff6ac420208 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -225,7 +225,9 @@ class StockController(AccountsController): def check_expense_account(self, item): if not item.get("expense_account"): - frappe.throw(_("Expense Account not set for Item {0}. Please set an Expense Account for the item in the Items table").format(item.item_code)) + frappe.throw(_("Row #{0}: Expense Account not set for Item {1}. Please set an Expense \ + Account in the Items table").format(item.idx, frappe.bold(item.item_code)), + title=_("Expense Account Missing")) else: is_expense_account = frappe.db.get_value("Account", From e256a7439e56d673c89ded9fe4e0c1e8eaa0369b Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 17 Jun 2020 19:06:56 +0530 Subject: [PATCH 120/129] fix: Typo --- erpnext/controllers/item_variant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 50b17abbe6d..1f95e004244 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -102,7 +102,7 @@ def validate_item_attribute_value(attributes_list, attribute, attribute_value, i frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format( frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value")) else: - msg = _("The value {0} is already assigned to an exisiting Item {1}.").format( + msg = _("The value {0} is already assigned to an existing Item {1}.").format( frappe.bold(attribute_value), frappe.bold(item)) msg += "
" + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value")) From 6a0bd556d7d68dc48cc59ac34cd7c08050fb21d0 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 18 Jun 2020 12:48:52 +0530 Subject: [PATCH 121/129] fix: update shopify api version (#22297) --- .../shopify_settings/shopify_settings.py | 24 +++++++++++++------ .../doctype/shopify_settings/sync_product.py | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py index 64c3b2d2730..25ffd281099 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py @@ -8,6 +8,7 @@ import json from frappe import _ from frappe.model.document import Document from frappe.utils import get_request_session +from requests.exceptions import HTTPError from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from erpnext.erpnext_integrations.utils import get_webhook_address from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log @@ -29,19 +30,24 @@ class ShopifySettings(Document): webhooks = ["orders/create", "orders/paid", "orders/fulfilled"] # url = get_shopify_url('admin/webhooks.json', self) created_webhooks = [d.method for d in self.webhooks] - url = get_shopify_url('admin/api/2019-04/webhooks.json', self) + url = get_shopify_url('admin/api/2020-04/webhooks.json', self) for method in webhooks: session = get_request_session() try: - d = session.post(url, data=json.dumps({ + res = session.post(url, data=json.dumps({ "webhook": { "topic": method, "address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'), "format": "json" } }), headers=get_header(self)) - d.raise_for_status() - self.update_webhook_table(method, d.json()) + res.raise_for_status() + self.update_webhook_table(method, res.json()) + + except HTTPError as e: + error_message = res.json().get('errors', e) + make_shopify_log(status="Warning", exception=error_message, rollback=True) + except Exception as e: make_shopify_log(status="Warning", exception=e, rollback=True) @@ -50,13 +56,18 @@ class ShopifySettings(Document): deleted_webhooks = [] for d in self.webhooks: - url = get_shopify_url('admin/api/2019-04/webhooks/{0}.json'.format(d.webhook_id), self) + url = get_shopify_url('admin/api/2020-04/webhooks/{0}.json'.format(d.webhook_id), self) try: res = session.delete(url, headers=get_header(self)) res.raise_for_status() deleted_webhooks.append(d) + + except HTTPError as e: + error_message = res.json().get('errors', e) + make_shopify_log(status="Warning", exception=error_message, rollback=True) + except Exception as e: - frappe.log_error(message=frappe.get_traceback(), title=e) + frappe.log_error(message=e, title='Shopify Webhooks Issue') for d in deleted_webhooks: self.remove(d) @@ -125,4 +136,3 @@ def setup_custom_fields(): } create_custom_fields(custom_fields) - diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py index bde101123db..f9f0bb3cecc 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py @@ -8,7 +8,7 @@ from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings impo shopify_variants_attr_list = ["option1", "option2", "option3"] def sync_item_from_shopify(shopify_settings, item): - url = get_shopify_url("admin/api/2019-04/products/{0}.json".format(item.get("product_id")), shopify_settings) + url = get_shopify_url("admin/api/2020-04/products/{0}.json".format(item.get("product_id")), shopify_settings) session = get_request_session() try: From 82abcc94fae6ce969b5b5ca28bba953d2d1c6621 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 18 Jun 2020 13:59:19 +0530 Subject: [PATCH 122/129] fix: incorrect variable used while adding items in the submitted sales order --- erpnext/controllers/accounts_controller.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a4bed51f479..3e6c7dc788f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1194,11 +1194,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil action = "add" if perm_type == 'create' else "update" frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions")) - def get_new_child_item(): + def get_new_child_item(item_row): if parent_doctype == "Sales Order": - return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row) if parent_doctype == "Purchase Order": - return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row) def validate_quantity(child_item, d): if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): @@ -1219,7 +1219,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if not d.get("docname"): new_child_flag = True check_permissions(parent, 'create') - child_item = get_new_child_item() + child_item = get_new_child_item(d) else: check_permissions(parent, 'write') child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) From 2417d9492f1994e98cb2f75378ac5e8ff20154f8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 15:28:39 +0530 Subject: [PATCH 123/129] fix(HR): typo in error message in Employee Balance Report (#22306) (#22313) * fix: typo in date error message * fix: error message cleanup in Leave Balance report (cherry picked from commit af6a0f3a9d091713fdc90574acb5275b0d89c061) Co-authored-by: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com> --- .../report/employee_leave_balance/employee_leave_balance.py | 4 ++-- .../employee_leave_balance_summary.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index d5050e73d46..0a6386f103f 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -49,7 +49,7 @@ def get_data(filters, leave_types): conditions = get_conditions(filters) if filters.to_date <= filters.from_date: - frappe.throw(_("From date can not be greater than than To date")) + frappe.throw(_("'From Date should be less than To Date")) active_employees = frappe.get_all("Employee", filters=conditions, @@ -160,4 +160,4 @@ def get_department_leave_approver_map(department=None): for k, v in approver_list: approvers.setdefault(k, []).append(v) - return approvers \ No newline at end of file + return approvers diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py index 21646425bb6..70510492be9 100644 --- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py +++ b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py @@ -10,7 +10,7 @@ from erpnext.hr.report.employee_leave_balance.employee_leave_balance import calc def execute(filters=None): if filters.to_date <= filters.from_date: - frappe.throw(_('From date can not be greater than than To date')) + frappe.throw(_('From Date should be less than To Date')) columns = get_columns() data = get_data(filters) From 62882ea65b9b7a37b759dd202d718d70b547a5b4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 15:30:04 +0530 Subject: [PATCH 124/129] fix: asset maintenance fixes (#21277) (#22300) * fix: asset maintenance fixes * fix: tests (cherry picked from commit 9c494d3e72d95c6b95f379a0d00545a225f35c33) Co-authored-by: Saqib --- .../asset_maintenance/asset_maintenance.js | 39 ------------------- .../asset_maintenance/asset_maintenance.py | 7 ++-- .../test_asset_maintenance.py | 6 ++- 3 files changed, 7 insertions(+), 45 deletions(-) diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js index 3c135d466c1..001fc26ffe7 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js @@ -24,26 +24,6 @@ frappe.ui.form.on('Asset Maintenance', { return indicator; } ); - - frm.set_query('select_serial_no', function(doc){ - return { - asset: frm.doc.asset_name - } - }) - }, - - select_serial_no: (frm) => { - let serial_nos = frm.doc.serial_no || frm.doc.select_serial_no; - if (serial_nos) { - serial_nos = serial_nos.split('\n'); - serial_nos.push(frm.doc.select_serial_no); - - const unique_sn = serial_nos.filter(function(elem, index, self) { - return index === self.indexOf(elem); - }); - - frm.set_value("serial_no", unique_sn.join('\n')); - } }, refresh: (frm) => { @@ -93,25 +73,6 @@ frappe.ui.form.on('Asset Maintenance Task', { }, end_date: (frm, cdt, cdn) => { get_next_due_date(frm, cdt, cdn); - }, - assign_to: (frm, cdt, cdn) => { - var d = locals[cdt][cdn]; - if (frm.doc.__islocal) { - frappe.model.set_value(cdt, cdn, "assign_to", ""); - frappe.model.set_value(cdt, cdn, "assign_to_name", ""); - frappe.throw(__("Please save before assigning task.")); - } - if (d.assign_to) { - return frappe.call({ - method: 'erpnext.assets.doctype.asset_maintenance.asset_maintenance.assign_tasks', - args: { - asset_maintenance_name: frm.doc.name, - assign_to_member: d.assign_to, - maintenance_task: d.maintenance_task, - next_due_date: d.next_due_date - } - }); - } } }); diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index ba63dd661d7..d6adde6a371 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -16,12 +16,11 @@ class AssetMaintenance(Document): throw(_("Start date should be less than end date for task {0}").format(task.maintenance_task)) if getdate(task.next_due_date) < getdate(nowdate()): task.maintenance_status = "Overdue" + if not task.assign_to and self.docstatus == 0: + throw(_("Row #{}: Please asign task to a member.").format(task.idx)) def on_update(self): for task in self.get('asset_maintenance_tasks'): - if not task.assign_to: - task.db_set("assign_to", self.maintenance_manager) - task.db_set("assign_to_name", self.maintenance_manager_name) assign_tasks(self.name, task.assign_to, task.maintenance_task, task.next_due_date) self.sync_maintenance_tasks() @@ -108,7 +107,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task): @frappe.whitelist() def get_team_members(doctype, txt, searchfield, start, page_len, filters): - return frappe.db.get_values('Maintenance Team Member', {'parent':filters.get("maintenance_team")}) + return frappe.db.get_values('Maintenance Team Member', { 'parent': filters.get("maintenance_team") }) @frappe.whitelist() def get_maintenance_log(asset_name): diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py index 6c2fd67a9af..392fbdd2af7 100644 --- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py @@ -125,13 +125,15 @@ def get_maintenance_tasks(): "start_date": nowdate(), "periodicity": "Monthly", "maintenance_type": "Preventive Maintenance", - "maintenance_status": "Planned" + "maintenance_status": "Planned", + "assign_to": "marcus@abc.com" }, {"maintenance_task": "Check Gears", "start_date": nowdate(), "periodicity": "Yearly", "maintenance_type": "Calibration", - "maintenance_status": "Planned" + "maintenance_status": "Planned", + "assign_to": "thalia@abc.com" } ] From 5bd479e8ee5e9b2aba6ff6b9d61bc09f54bafece Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 17:04:45 +0530 Subject: [PATCH 125/129] refactor: handle exceptions when updating addresses (#22307) (#22316) * refactor: handle exceptions when updating addresses * refactor: fold common statements in a loop (cherry picked from commit 34d2bfbb3e6ce28de2690812191b8885b4f8f6ab) Co-authored-by: Shivam Mishra --- .../connectors/woocommerce_connection.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 1b0c9f60b6e..6dedaa8c530 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -73,10 +73,16 @@ def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name if customer_exists: frappe.rename_doc("Customer", old_name, customer_name) - billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"}) - shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"}) - rename_address(billing_address, customer) - rename_address(shipping_address, customer) + for address_type in ("Billing", "Shipping",): + try: + address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": address_type}) + rename_address(address, customer) + except ( + frappe.DoesNotExistError, + frappe.DuplicateEntryError, + frappe.ValidationError, + ): + pass else: create_address(raw_billing_data, customer, "Billing") create_address(raw_shipping_data, customer, "Shipping") From f53ee9026b98ca4ef777b76781a5e7b42a159445 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 18 Jun 2020 18:13:51 +0530 Subject: [PATCH 126/129] chore: Added Change log --- erpnext/change_log/v12/v12_10_0.md | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 erpnext/change_log/v12/v12_10_0.md diff --git a/erpnext/change_log/v12/v12_10_0.md b/erpnext/change_log/v12/v12_10_0.md new file mode 100644 index 00000000000..702af1fd27f --- /dev/null +++ b/erpnext/change_log/v12/v12_10_0.md @@ -0,0 +1,54 @@ +## ERPNext v12.9.0 Release Note + +### Enhancements + +- Create Shipping Address and Contact in ERPNext from the woocommerece platform ([#21654](https://github.com/frappe/erpnext/pull/21654)) +- Dashboard in Course and Assessment Plan ([#21889](https://github.com/frappe/erpnext/pull/21889)) +- Added conversion factor for pound, gram to Ounce ([#21710](https://github.com/frappe/erpnext/pull/21710)) +- Allowed renaming for Sales Stage ([#21800](https://github.com/frappe/erpnext/pull/21800)) +- Add filter for cost center in expense table ([#22209](https://github.com/frappe/erpnext/pull/22209)) +- Project filter in Trial Balance Report ([#21815](https://github.com/frappe/erpnext/pull/21815)) +- Added column Expired Leave in Leave Application dashboard ([#21859](https://github.com/frappe/erpnext/pull/21859)) +- Auto set serial nos and batches only if allowed in Stock Settings ([#21779](https://github.com/frappe/erpnext/pull/21779)) +- Filter batches based on selected item and warehouse in Pick List ([#21778](https://github.com/frappe/erpnext/pull/21778)) +- Errored documents handling while migrating data from Tally ([#22079](https://github.com/frappe/erpnext/pull/22079)) + +### Fixes + +- Routing operations table is blank on pull of operations in BOM ([#22040](https://github.com/frappe/erpnext/pull/22040)) +- Cannot make payment entry against shareholder ([#21597](https://github.com/frappe/erpnext/pull/21597)) +- Do not add filters in report on accounting dimension creation if it already exists ([#21941](https://github.com/frappe/erpnext/pull/21941)) +- Post Dated unallocated amount not considered in Advance Amount in AR/AP summary ([#21838](https://github.com/frappe/erpnext/pull/21838)) +- Check for Company before rendering tree in Account Tree ([#22204](https://github.com/frappe/erpnext/pull/22204)) +- Loyalty point entry use wrong tier ([#22168](https://github.com/frappe/erpnext/pull/22168)) +- Added Inactive serial no status ([#21849](https://github.com/frappe/erpnext/pull/21849)) +- Routing operations not added sequentially in the BOM ([#22110](https://github.com/frappe/erpnext/pull/22110)) +- TDS computation summary report ([#21987](https://github.com/frappe/erpnext/pull/21987)) +- Validate Payment Gateway only if it exists in Payment Request. ([#21806](https://github.com/frappe/erpnext/pull/21806)) +- Finished Product Valuation at Repack ([#22148](https://github.com/frappe/erpnext/pull/22148)) +- Wrong Ordered-Status Indicator for Material Request Items ([#22117](https://github.com/frappe/erpnext/pull/22117)) +- In-state Invoice not appearing in GSTR-1 report (India) ([#21787](https://github.com/frappe/erpnext/pull/21787)) +- Tax amount in GSTR-1 JSON (India) ([#21791](https://github.com/frappe/erpnext/pull/21791)) +- Fetch depreciation amount only if depreciation entry is made ([#21894](https://github.com/frappe/erpnext/pull/21894)) +- Throw error if no serial numbers are found in Pick List ([#21914](https://github.com/frappe/erpnext/pull/21914)) +- Shopify error message on failure of sales order creation ([#21924](https://github.com/frappe/erpnext/pull/21924)) +- Don't prompt for Quality Inspection on Return Documents. ([#22200](https://github.com/frappe/erpnext/pull/22200)) +- Item tax template not getting mapped from source to target doc ([#21863](https://github.com/frappe/erpnext/pull/21863)) +- Create purchase invoice from purchase receipt dashboard ([#22087](https://github.com/frappe/erpnext/pull/22087)) +- Procurement Tracker Data Consistency ([#22062](https://github.com/frappe/erpnext/pull/22062)) +- Cannot assign same task to other asset maintenance ([#22024](https://github.com/frappe/erpnext/pull/22024)) +- Incorrect VAT rate display in Sales Invoice (UAE) ([#21883](https://github.com/frappe/erpnext/pull/21883)) +- Missing income account when getting free product ([#22158](https://github.com/frappe/erpnext/pull/22158)) +- Item tax template not applied if valid from is blank ([#21819](https://github.com/frappe/erpnext/pull/21819)) +- Tax ID is not fetched when creating Sales Order from Quotation ([#21786](https://github.com/frappe/erpnext/pull/21786)) +- Disposed asset creates inconsistencies in asset depreciation report ([#22021](https://github.com/frappe/erpnext/pull/22021)) +- Submitted sales order can be updated with proper permission ([#22218](https://github.com/frappe/erpnext/pull/22218)) +- Import supplier invoice not working ([#22108](https://github.com/frappe/erpnext/pull/22108)) +- Showing Wrong balance on allocation boundary dates ([#21908](https://github.com/frappe/erpnext/pull/21908)) +- Fetch customer into Delivery Note from Pick List ([#21825](https://github.com/frappe/erpnext/pull/21825)) +- Apply shipping rule without address ([#22093](https://github.com/frappe/erpnext/pull/22093)) +- Prioritize Default Customer Price List in Portal ([#22183](https://github.com/frappe/erpnext/pull/22183)) +- Supplier Invoice No not fetched in Import Supplier Invoice ([#21829](https://github.com/frappe/erpnext/pull/21829)) +- Item Price and Add to Cart not showing on Website ([#21905](https://github.com/frappe/erpnext/pull/21905)) +- Make transaction date of the oldest transaction as the last integration date ([#22017](https://github.com/frappe/erpnext/pull/22017)) +- Misleading Error message for Item Attribute ([#22068](https://github.com/frappe/erpnext/pull/22068)) \ No newline at end of file From fc488a2b464f1ae84683578536a46d094fcf0161 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 18 Jun 2020 18:19:49 +0530 Subject: [PATCH 127/129] fix: Do not copy Item Tax template from SO to PO (#22326) --- erpnext/selling/doctype/sales_order/sales_order.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 05e4aa892b0..ffb66354fa0 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -868,7 +868,8 @@ def make_purchase_order(source_name, for_supplier=None, selected_items=[], targe ], "field_no_map": [ "rate", - "price_list_rate" + "price_list_rate", + "item_tax_template" ], "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == supplier and doc.item_code in selected_items From a9dbff0eaeb48e60358dcc6552bf91450fe68c15 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 18 Jun 2020 20:03:00 +0530 Subject: [PATCH 128/129] chore: correct version in release note --- erpnext/change_log/v12/v12_10_0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/change_log/v12/v12_10_0.md b/erpnext/change_log/v12/v12_10_0.md index 702af1fd27f..07aa885ec82 100644 --- a/erpnext/change_log/v12/v12_10_0.md +++ b/erpnext/change_log/v12/v12_10_0.md @@ -1,4 +1,4 @@ -## ERPNext v12.9.0 Release Note +## ERPNext v12.10.0 Release Note ### Enhancements @@ -51,4 +51,4 @@ - Supplier Invoice No not fetched in Import Supplier Invoice ([#21829](https://github.com/frappe/erpnext/pull/21829)) - Item Price and Add to Cart not showing on Website ([#21905](https://github.com/frappe/erpnext/pull/21905)) - Make transaction date of the oldest transaction as the last integration date ([#22017](https://github.com/frappe/erpnext/pull/22017)) -- Misleading Error message for Item Attribute ([#22068](https://github.com/frappe/erpnext/pull/22068)) \ No newline at end of file +- Misleading Error message for Item Attribute ([#22068](https://github.com/frappe/erpnext/pull/22068)) From aa27a0e85c0f5a79c20cac1a64967dc90da18ea9 Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Thu, 18 Jun 2020 20:26:18 +0550 Subject: [PATCH 129/129] bumped to version 12.10.0 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 701217759c2..a46d4ad58ff 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.9.4' +__version__ = '12.10.0' def get_default_company(user=None): '''Get default company for user'''