From 232396494bd36081df275311b7449acc79c61e5c Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Mon, 4 May 2020 19:20:55 +0530 Subject: [PATCH 1/4] fix(patch): reload GSTR 3B report --- erpnext/patches/v11_0/add_permissions_in_gst_settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py index d7936110ed..83b2a4cc09 100644 --- a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py +++ b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py @@ -7,4 +7,5 @@ def execute(): return frappe.reload_doc("regional", "doctype", "lower_deduction_certificate") - add_permissions() \ No newline at end of file + frappe.reload_doc("regional", "doctype", "gstr_3b_report") + add_permissions() From 8e08698c158a3be19fdc9da1bba2c578a3d82196 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Tue, 5 May 2020 12:06:58 +0530 Subject: [PATCH 2/4] test: Fix wrong test for search function (#21588) --- erpnext/tests/test_search.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/tests/test_search.py b/erpnext/tests/test_search.py index b9665dbd76..566495f1ec 100644 --- a/erpnext/tests/test_search.py +++ b/erpnext/tests/test_search.py @@ -4,11 +4,13 @@ import frappe from frappe.contacts.address_and_contact import filter_dynamic_link_doctypes class TestSearch(unittest.TestCase): - #Search for the word "cond", part of the word "conduire" (Lead) in french. + # Search for the word "cond", part of the word "conduire" (Lead) in french. def test_contact_search_in_foreign_language(self): frappe.local.lang = 'fr' - output = filter_dynamic_link_doctypes("DocType", "prospect", "name", 0, 20, {'fieldtype': 'HTML', 'fieldname': 'contact_html'}) - + output = filter_dynamic_link_doctypes("DocType", "cond", "name", 0, 20, { + 'fieldtype': 'HTML', + 'fieldname': 'contact_html' + }) result = [['found' for x in y if x=="Lead"] for y in output] self.assertTrue(['found'] in result) From 5210761e570f1c9feabddea9a7fcd2c4bc7415bf Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 5 May 2020 12:12:33 +0530 Subject: [PATCH 3/4] fix: heatmap not working for customer and supplier (#21578) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Co-authored-by: Nabin Hait --- erpnext/accounts/party.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 6e5b33f07d..528fb4e113 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -162,7 +162,7 @@ def get_default_price_list(party): def set_price_list(party_details, party, party_type, given_price_list, pos=None): # price list price_list = get_permitted_documents('Price List') - + # if there is only one permitted document based on user permissions, set it if price_list and len(price_list) == 1: price_list = price_list[0] @@ -465,23 +465,25 @@ def get_timeline_data(doctype, name): from frappe.desk.form.load import get_communication_data out = {} - fields = 'date(creation), count(name)' + fields = 'creation, count(*)' after = add_years(None, -1).strftime('%Y-%m-%d') - group_by='group by date(creation)' + group_by='group by Date(creation)' - data = get_communication_data(doctype, name, after=after, group_by='group by date(creation)', - fields='date(C.creation) as creation, count(C.name)',as_dict=False) + data = get_communication_data(doctype, name, after=after, group_by='group by creation', + fields='C.creation as creation, count(C.name)',as_dict=False) # fetch and append data from Activity Log data += frappe.db.sql("""select {fields} from `tabActivity Log` - where (reference_doctype="{doctype}" and reference_name="{name}") - or (timeline_doctype in ("{doctype}") and timeline_name="{name}") - or (reference_doctype in ("Quotation", "Opportunity") and timeline_name="{name}") + where (reference_doctype=%(doctype)s and reference_name=%(name)s) + or (timeline_doctype in (%(doctype)s) and timeline_name=%(name)s) + or (reference_doctype in ("Quotation", "Opportunity") and timeline_name=%(name)s) and status!='Success' and creation > {after} {group_by} order by creation desc - """.format(doctype=frappe.db.escape(doctype), name=frappe.db.escape(name), fields=fields, - group_by=group_by, after=after), as_dict=False) + """.format(fields=fields, group_by=group_by, after=after), { + "doctype": doctype, + "name": name + }, as_dict=False) timeline_items = dict(data) From 5e3d8050aa6c82cbb1862c9bf1ee8b37d9b41f5b Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 5 May 2020 14:37:39 +0530 Subject: [PATCH 4/4] fix: Handle rows with same item code from Purchase Receipt to Invoice. (#20724) * fix: Handle rows with same item code from Purchase Receipt to Invoice. * fix: Added patch, fixed tests, fixed delivery note behaviour * chore: Added comments amd fixed typo * fix: Added patch to patches.txt * fix: Patch fix and simplification, json timestamp updation. Co-authored-by: Nabin Hait --- .../controllers/sales_and_purchase_return.py | 5 +- erpnext/patches.txt | 1 + ...t_purchase_receipt_delivery_note_detail.py | 84 +++++++++++++++++++ .../doctype/delivery_note/delivery_note.py | 11 ++- .../delivery_note/test_delivery_note.py | 5 +- .../delivery_note_item.json | 10 +++ .../purchase_receipt/purchase_receipt.py | 9 +- .../purchase_receipt/test_purchase_receipt.py | 5 +- .../purchase_receipt_item.json | 12 ++- 9 files changed, 127 insertions(+), 15 deletions(-) create mode 100644 erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 81fdbbefc3..90c67f1e52 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -74,7 +74,7 @@ def validate_returned_items(doc): for d in doc.get("items"): if d.item_code and (flt(d.qty) < 0 or flt(d.get('received_qty')) < 0): if d.item_code not in valid_items: - frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}") + frappe.throw(_("Row # {0}: Returned Item {1} does not exist in {2} {3}") .format(d.idx, d.item_code, doc.doctype, doc.return_against)) else: ref = valid_items.get(d.item_code, frappe._dict()) @@ -266,6 +266,8 @@ def make_return_doc(doctype, source_name, target_doc=None): target_doc.purchase_order = source_doc.purchase_order target_doc.purchase_order_item = source_doc.purchase_order_item target_doc.rejected_warehouse = source_doc.rejected_warehouse + target_doc.purchase_receipt_item = source_doc.name + elif doctype == "Purchase Invoice": target_doc.received_qty = -1* source_doc.received_qty target_doc.rejected_qty = -1* source_doc.rejected_qty @@ -282,6 +284,7 @@ def make_return_doc(doctype, source_name, target_doc=None): target_doc.so_detail = source_doc.so_detail target_doc.si_detail = source_doc.si_detail target_doc.expense_account = source_doc.expense_account + target_doc.dn_detail = source_doc.name if default_warehouse_for_sales_return: target_doc.warehouse = default_warehouse_for_sales_return elif doctype == "Sales Invoice": diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 5295399695..8a31cf33d0 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -662,6 +662,7 @@ erpnext.patches.v12_0.create_irs_1099_field_united_states erpnext.patches.v12_0.move_bank_account_swift_number_to_bank erpnext.patches.v12_0.rename_bank_reconciliation erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22 +erpnext.patches.v12_0.set_purchase_receipt_delivery_note_detail erpnext.patches.v12_0.add_permission_in_lower_deduction erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom erpnext.patches.v12_0.rename_account_type_doctype diff --git a/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py b/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py new file mode 100644 index 0000000000..f5bd8c3aa2 --- /dev/null +++ b/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py @@ -0,0 +1,84 @@ +from __future__ import unicode_literals +import frappe +from collections import defaultdict + +def execute(): + def map_rows(doc_row, return_doc_row, detail_field, doctype): + """Map rows after identifying similar ones.""" + + frappe.db.sql(""" UPDATE `tab{doctype} Item` set {detail_field} = '{doc_row_name}' + where name = '{return_doc_row_name}'""" \ + .format(doctype=doctype, + detail_field=detail_field, + doc_row_name=doc_row.get('name'), + return_doc_row_name=return_doc_row.get('name'))) #nosec + + def row_is_mappable(doc_row, return_doc_row, detail_field): + """Checks if two rows are similar enough to be mapped.""" + + if doc_row.item_code == return_doc_row.item_code and not return_doc_row.get(detail_field): + if doc_row.get('batch_no') and return_doc_row.get('batch_no') and doc_row.batch_no == return_doc_row.batch_no: + return True + + elif doc_row.get('serial_no') and return_doc_row.get('serial_no'): + doc_sn = doc_row.serial_no.split('\n') + return_doc_sn = return_doc_row.serial_no.split('\n') + + if set(doc_sn) & set(return_doc_sn): + # if two rows have serial nos in common, map them + return True + + elif doc_row.rate == return_doc_row.rate: + return True + else: + return False + + def make_return_document_map(doctype, return_document_map): + """Returns a map of documents and it's return documents. + Format => { 'document' : ['return_document_1','return_document_2'] }""" + + return_against_documents = frappe.db.sql(""" + SELECT + return_against as document, name as return_document + FROM `tab{doctype}` + WHERE + is_return = 1 and docstatus = 1""".format(doctype=doctype),as_dict=1) #nosec + + for entry in return_against_documents: + return_document_map[entry.document].append(entry.return_document) + + return return_document_map + + def set_document_detail_in_return_document(doctype): + """Map each row of the original document in the return document.""" + mapped = [] + return_document_map = defaultdict(list) + detail_field = "purchase_receipt_item" if doctype=="Purchase Receipt" else "dn_detail" + + child_doc = frappe.scrub("{0} Item".format(doctype)) + frappe.reload_doc("stock", "doctype", child_doc) + + return_document_map = make_return_document_map(doctype, return_document_map) + + #iterate through original documents and its return documents + for docname in return_document_map: + doc_items = frappe.get_doc(doctype, docname).get("items") + for return_doc in return_document_map[docname]: + return_doc_items = frappe.get_doc(doctype, return_doc).get("items") + + #iterate through return document items and original document items for mapping + for return_item in return_doc_items: + for doc_item in doc_items: + if row_is_mappable(doc_item, return_item, detail_field) and doc_item.get('name') not in mapped: + map_rows(doc_item, return_item, detail_field, doctype) + mapped.append(doc_item.get('name')) + break + else: + continue + + set_document_detail_in_return_document("Purchase Receipt") + set_document_detail_in_return_document("Delivery Note") + frappe.db.commit() + + + diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 37f9097937..d04cf785ab 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -388,13 +388,12 @@ def get_invoiced_qty_map(delivery_note): def get_returned_qty_map(delivery_note): """returns a map: {so_detail: returned_qty}""" - returned_qty_map = frappe._dict(frappe.db.sql("""select dn_item.item_code, sum(abs(dn_item.qty)) as qty + returned_qty_map = frappe._dict(frappe.db.sql("""select dn_item.dn_detail, abs(dn_item.qty) as qty from `tabDelivery Note Item` dn_item, `tabDelivery Note` dn where dn.name = dn_item.parent and dn.docstatus = 1 and dn.is_return = 1 and dn.return_against = %s - group by dn_item.item_code """, delivery_note)) return returned_qty_map @@ -413,7 +412,7 @@ def make_sales_invoice(source_name, target_doc=None): target.run_method("set_po_nos") if len(target.get("items")) == 0: - frappe.throw(_("All these items have already been invoiced")) + frappe.throw(_("All these items have already been Invoiced/Returned")) target.run_method("calculate_taxes_and_totals") @@ -438,9 +437,9 @@ def make_sales_invoice(source_name, target_doc=None): pending_qty = item_row.qty - invoiced_qty_map.get(item_row.name, 0) returned_qty = 0 - if returned_qty_map.get(item_row.item_code, 0) > 0: - returned_qty = flt(returned_qty_map.get(item_row.item_code, 0)) - returned_qty_map[item_row.item_code] -= pending_qty + if returned_qty_map.get(item_row.name, 0) > 0: + returned_qty = flt(returned_qty_map.get(item_row.name, 0)) + returned_qty_map[item_row.name] -= pending_qty if returned_qty: if returned_qty >= pending_qty: diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index bf7007abee..a921a56f53 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -612,6 +612,7 @@ class TestDeliveryNote(unittest.TestCase): dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-1, do_not_submit=True) dn1.items[0].against_sales_order = so.name dn1.items[0].so_detail = so.items[0].name + dn1.items[0].dn_detail = dn.items[0].name dn1.submit() si = make_sales_invoice(dn.name) @@ -638,7 +639,9 @@ class TestDeliveryNote(unittest.TestCase): si1.save() si1.submit() - create_delivery_note(is_return=1, return_against=dn.name, qty=-2) + dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-2, do_not_submit=True) + dn1.items[0].dn_detail = dn.items[0].name + dn1.submit() si2 = make_sales_invoice(dn.name) self.assertEquals(si2.items[0].qty, 2) diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 782ac84e57..7ea2de2753 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -67,6 +67,7 @@ "so_detail", "against_sales_invoice", "si_detail", + "dn_detail", "section_break_40", "batch_no", "serial_no", @@ -699,6 +700,15 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "dn_detail", + "fieldtype": "Data", + "hidden": 1, + "label": "Against Delivery Note Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 8dfe1d1030..e6ab8d634d 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -504,7 +504,7 @@ def make_purchase_invoice(source_name, target_doc=None): def set_missing_values(source, target): if len(target.get("items")) == 0: - frappe.throw(_("All items have already been invoiced")) + frappe.throw(_("All items have already been Invoiced/Returned")) doc = frappe.get_doc(target) doc.ignore_pricing_rule = 1 @@ -514,11 +514,11 @@ def make_purchase_invoice(source_name, target_doc=None): def update_item(source_doc, target_doc, source_parent): target_doc.qty, returned_qty = get_pending_qty(source_doc) - returned_qty_map[source_doc.item_code] = returned_qty + returned_qty_map[source_doc.name] = returned_qty def get_pending_qty(item_row): pending_qty = item_row.qty - invoiced_qty_map.get(item_row.name, 0) - returned_qty = flt(returned_qty_map.get(item_row.item_code, 0)) + returned_qty = flt(returned_qty_map.get(item_row.name, 0)) if returned_qty: if returned_qty >= pending_qty: pending_qty = 0 @@ -576,13 +576,12 @@ def get_invoiced_qty_map(purchase_receipt): def get_returned_qty_map(purchase_receipt): """returns a map: {so_detail: returned_qty}""" - returned_qty_map = frappe._dict(frappe.db.sql("""select pr_item.item_code, sum(abs(pr_item.qty)) as qty + returned_qty_map = frappe._dict(frappe.db.sql("""select pr_item.purchase_receipt_item, abs(pr_item.qty) as qty from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr where pr.name = pr_item.parent and pr.docstatus = 1 and pr.is_return = 1 and pr.return_against = %s - group by pr_item.item_code """, purchase_receipt)) return returned_qty_map diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 3d42590e4c..649cfdcaac 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -475,6 +475,7 @@ class TestPurchaseReceipt(unittest.TestCase): pr1 = make_purchase_receipt(is_return=1, return_against=pr.name, qty=-1, do_not_submit=True) pr1.items[0].purchase_order = po.name pr1.items[0].purchase_order_item = po.items[0].name + pr1.items[0].purchase_receipt_item = pr.items[0].name pr1.submit() pi = make_purchase_invoice(pr.name) @@ -498,7 +499,9 @@ class TestPurchaseReceipt(unittest.TestCase): pi1.save() pi1.submit() - make_purchase_receipt(is_return=1, return_against=pr1.name, qty=-2) + pr2 = make_purchase_receipt(is_return=1, return_against=pr1.name, qty=-2, do_not_submit=True) + pr2.items[0].purchase_receipt_item = pr1.items[0].name + pr2.submit() pi2 = make_purchase_invoice(pr1.name) self.assertEquals(pi2.items[0].qty, 2) diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index bc6bce95d6..c1e1f901ba 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -71,6 +71,7 @@ "quality_inspection", "purchase_order_item", "material_request_item", + "purchase_receipt_item", "section_break_45", "allow_zero_valuation_rate", "bom", @@ -820,6 +821,15 @@ "label": "Supplier Warehouse", "options": "Warehouse" }, + { + "fieldname": "purchase_receipt_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Purchase Receipt Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { "collapsible": 1, "fieldname": "image_column", @@ -829,7 +839,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-04-10 19:01:21.154963", + "modified": "2020-04-28 19:01:21.154963", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item",