From 59c31bb124048a0bbf203311ff7e4ce09feadb28 Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Fri, 22 Oct 2021 19:26:31 +0530 Subject: [PATCH 01/60] fix: check if gst_category exist --- erpnext/regional/india/utils.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 0e4128024d..46cf9f2253 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -29,12 +29,13 @@ def validate_gstin_for_india(doc, method): gst_category = [] - if len(doc.links): - link_doctype = doc.links[0].get("link_doctype") - link_name = doc.links[0].get("link_name") + if hasattr(doc, 'gst_category'): + if len(doc.links): + link_doctype = doc.links[0].get("link_doctype") + link_name = doc.links[0].get("link_name") - if link_doctype in ["Customer", "Supplier"]: - gst_category = frappe.db.get_value(link_doctype, {'name': link_name}, ['gst_category']) + if link_doctype in ["Customer", "Supplier"]: + gst_category = frappe.db.get_value(link_doctype, {'name': link_name}, ['gst_category']) doc.gstin = doc.gstin.upper().strip() if not doc.gstin or doc.gstin == 'NA': @@ -76,12 +77,13 @@ def validate_tax_category(doc, method): frappe.throw(_("Intra State tax category for GST State {0} already exists").format(doc.gst_state)) def update_gst_category(doc, method): - for link in doc.links: - if link.link_doctype in ['Customer', 'Supplier']: - if doc.get('gstin'): - frappe.db.sql(""" - UPDATE `tab{0}` SET gst_category = %s WHERE name = %s AND gst_category = 'Unregistered' - """.format(link.link_doctype), ("Registered Regular", link.link_name)) #nosec + if hasattr(doc, 'gst_category'): + for link in doc.links: + if link.link_doctype in ['Customer', 'Supplier']: + if doc.get('gstin'): + frappe.db.sql(""" + UPDATE `tab{0}` SET gst_category = %s WHERE name = %s AND gst_category = 'Unregistered' + """.format(link.link_doctype), ("Registered Regular", link.link_name)) #nosec def set_gst_state_and_state_number(doc): if not doc.gst_state: From 4f538376243647b779b0b35a2017264881d4ed41 Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Fri, 22 Oct 2021 20:43:54 +0530 Subject: [PATCH 02/60] fix: sider --- erpnext/regional/india/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 46cf9f2253..14302cde89 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -83,7 +83,7 @@ def update_gst_category(doc, method): if doc.get('gstin'): frappe.db.sql(""" UPDATE `tab{0}` SET gst_category = %s WHERE name = %s AND gst_category = 'Unregistered' - """.format(link.link_doctype), ("Registered Regular", link.link_name)) #nosec + """.format(link.link_doctype), ("Registered Regular", link.link_name)) #nosec def set_gst_state_and_state_number(doc): if not doc.gst_state: From 6a3bd882b42f62db4404841b4e338ead938b7417 Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Fri, 22 Oct 2021 20:44:49 +0530 Subject: [PATCH 03/60] fix: sider --- erpnext/regional/india/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 14302cde89..936d5539bc 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -82,8 +82,8 @@ def update_gst_category(doc, method): if link.link_doctype in ['Customer', 'Supplier']: if doc.get('gstin'): frappe.db.sql(""" - UPDATE `tab{0}` SET gst_category = %s WHERE name = %s AND gst_category = 'Unregistered' - """.format(link.link_doctype), ("Registered Regular", link.link_name)) #nosec + UPDATE `tab{0}` SET gst_category = %s WHERE name = %s AND gst_category = 'Unregistered' """ + .format(link.link_doctype), ("Registered Regular", link.link_name)) #nosec def set_gst_state_and_state_number(doc): if not doc.gst_state: From 23af036894ef61f590278fa7e84402f0be3ff50d Mon Sep 17 00:00:00 2001 From: Anupam Date: Fri, 29 Oct 2021 17:27:17 +0530 Subject: [PATCH 04/60] feat: provision to close the Work Order --- .../doctype/work_order/work_order.js | 48 +++++++++++-------- .../doctype/work_order/work_order.json | 5 +- .../doctype/work_order/work_order.py | 31 +++++++++++- 3 files changed, 60 insertions(+), 24 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 51c46f63ab..d4235731ae 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -135,24 +135,26 @@ frappe.ui.form.on("Work Order", { frm.set_intro(__("Submit this Work Order for further processing.")); } - if (frm.doc.docstatus===1) { - frm.trigger('show_progress_for_items'); - frm.trigger('show_progress_for_operations'); - } + if (frm.doc.status != "Closed") { + if (frm.doc.docstatus===1) { + frm.trigger('show_progress_for_items'); + frm.trigger('show_progress_for_operations'); + } - if (frm.doc.docstatus === 1 - && frm.doc.operations && frm.doc.operations.length) { + if (frm.doc.docstatus === 1 + && frm.doc.operations && frm.doc.operations.length) { - const not_completed = frm.doc.operations.filter(d => { - if(d.status != 'Completed') { - return true; + const not_completed = frm.doc.operations.filter(d => { + if(d.status != 'Completed') { + return true; + } + }); + + if(not_completed && not_completed.length) { + frm.add_custom_button(__('Create Job Card'), () => { + frm.trigger("make_job_card"); + }).addClass('btn-primary'); } - }); - - if(not_completed && not_completed.length) { - frm.add_custom_button(__('Create Job Card'), () => { - frm.trigger("make_job_card"); - }).addClass('btn-primary'); } } @@ -517,14 +519,19 @@ frappe.ui.form.on("Work Order Operation", { erpnext.work_order = { set_custom_buttons: function(frm) { var doc = frm.doc; - if (doc.docstatus === 1) { + if (doc.docstatus === 1 && doc.status != "Closed") { + console.log("check"); + frm.add_custom_button(__('Close'), function() { + erpnext.work_order.change_work_order_status(frm, "Closed"); + }, __("Status")); + if (doc.status != 'Stopped' && doc.status != 'Completed') { frm.add_custom_button(__('Stop'), function() { - erpnext.work_order.stop_work_order(frm, "Stopped"); + erpnext.work_order.change_work_order_status(frm, "Stopped"); }, __("Status")); } else if (doc.status == 'Stopped') { frm.add_custom_button(__('Re-open'), function() { - erpnext.work_order.stop_work_order(frm, "Resumed"); + erpnext.work_order.change_work_order_status(frm, "Resumed"); }, __("Status")); } @@ -713,9 +720,10 @@ erpnext.work_order = { }); }, - stop_work_order: function(frm, status) { + change_work_order_status: function(frm, status) { + let method_name = status=="Closed" ? "close_work_order" : "stop_unstop"; frappe.call({ - method: "erpnext.manufacturing.doctype.work_order.work_order.stop_unstop", + method: `erpnext.manufacturing.doctype.work_order.work_order.${method_name}`, freeze: true, freeze_message: __("Updating Work Order status"), args: { diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index 7f8e816a22..df7ee53b92 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -99,7 +99,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled", + "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nClosed\nCancelled", "read_only": 1, "reqd": 1, "search_index": 1 @@ -573,7 +573,8 @@ "image_field": "image", "is_submittable": 1, "links": [], - "modified": "2021-10-27 19:21:35.139888", + "migration_hash": "a18118963f4fcdb7f9d326de5f4063ba", + "modified": "2021-10-29 15:12:32.203605", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index f881e1bf16..6901d71ad4 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -175,7 +175,7 @@ class WorkOrder(Document): def update_status(self, status=None): '''Update status of work order if unknown''' - if status != "Stopped": + if status != "Stopped" and status != "Closed": status = self.get_status(status) if status != self.status: @@ -624,7 +624,6 @@ class WorkOrder(Document): def validate_operation_time(self): for d in self.operations: if not d.time_in_mins > 0: - print(self.bom_no, self.production_item) frappe.throw(_("Operation Time must be greater than 0 for Operation {0}").format(d.operation)) def update_required_items(self): @@ -967,6 +966,10 @@ def stop_unstop(work_order, status): frappe.throw(_("Not permitted"), frappe.PermissionError) pro_order = frappe.get_doc("Work Order", work_order) + + if pro_order.status == "Closed": + frappe.throw(_("Closed Work Order can not be stopped or Re-opened")) + pro_order.update_status(status) pro_order.update_planned_qty() frappe.msgprint(_("Work Order has been {0}").format(status)) @@ -1001,6 +1004,30 @@ def make_job_card(work_order, operations): if row.job_card_qty > 0: create_job_card(work_order, row, auto_create=True) +@frappe.whitelist() +def close_work_order(work_order, status): + if not frappe.has_permission("Work Order", "write"): + frappe.throw(_("Not permitted"), frappe.PermissionError) + + work_order = frappe.get_doc("Work Order", work_order) + if work_order.get("operations"): + job_cards = frappe.get_list("Job Card", + filters={ + "work_order": work_order.name, + "status": "Work In Progress" + }, + pluck='name') + + if job_cards: + job_cards = ", ".join(job_cards) + frappe.throw(_("Can not close Work Order. Since {0} Job Cards are in Work In Progress state.").format(job_cards)) + + work_order.update_status(status) + work_order.update_planned_qty() + frappe.msgprint(_("Work Order has been {0}").format(status)) + work_order.notify_update() + return work_order.status + def split_qty_based_on_batch_size(wo_doc, row, qty): if not cint(frappe.db.get_value("Operation", row.operation, "create_job_card_based_on_batch_size")): From bc1e7bc15fd9e21ed6d4f0438ca649944316bfdc Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Fri, 29 Oct 2021 21:32:20 +0530 Subject: [PATCH 05/60] fix: re-writing sql query with ORM methods --- erpnext/regional/india/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 5dc683ce21..753faab904 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -81,9 +81,9 @@ def update_gst_category(doc, method): for link in doc.links: if link.link_doctype in ['Customer', 'Supplier']: if doc.get('gstin'): - frappe.db.sql(""" - UPDATE `tab{0}` SET gst_category = %s WHERE name = %s AND gst_category = 'Unregistered' """ - .format(link.link_doctype), ("Registered Regular", link.link_name)) #nosec + gst_category = frappe.db.get_value(link.link_doctype, link.link_name, 'gst_category') + if gst_category == 'Unregistered': + frappe.db.set_value(link.link_doctype, link.link_name, 'gst_category', 'Registered Regular') def set_gst_state_and_state_number(doc): if not doc.gst_state: From 2a5beec885a40c6dd210ad11280ac46bc1867feb Mon Sep 17 00:00:00 2001 From: ahmadpak Date: Sat, 30 Oct 2021 17:18:45 +0300 Subject: [PATCH 06/60] new (Print Format): KSA VAT Invoice --- .../print_format/ksa_vat_invoice/__init__.py | 0 .../ksa_vat_invoice/ksa_vat_invoice.json | 32 +++++++++++++++++++ erpnext/regional/saudi_arabia/setup.py | 32 ++++++++++++++++--- 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 erpnext/regional/print_format/ksa_vat_invoice/__init__.py create mode 100644 erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json diff --git a/erpnext/regional/print_format/ksa_vat_invoice/__init__.py b/erpnext/regional/print_format/ksa_vat_invoice/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json b/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json new file mode 100644 index 0000000000..edb107adef --- /dev/null +++ b/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json @@ -0,0 +1,32 @@ +{ + "absolute_value": 0, + "align_labels_right": 0, + "creation": "2021-10-29 22:46:26.039023", + "css": ".qr-code{\n float:right;\n}\n\n.invoice-heading {\n margin: 0;\n}\n\n.ksa-invoice-table {\n border: 1px solid #888a8e;\n border-collapse: collapse;\n width: 100%;\n margin: 20px 0;\n color: #888a8e;\n font-size: 16px;\n}\n\n.ksa-invoice-table.two-columns td:nth-child(2) {\n direction: rtl;\n}\n\n.ksa-invoice-table th {\n border: 1px solid #888a8e;\n max-width: 50%;\n background-color: #265e4a !important;\n color: #fff;\n padding: 8px;\n}\n\n.ksa-invoice-table td {\n padding: 5px;\n border: 1px solid #888a8e;\n max-width: 50%;\n}\n\n.ksa-invoice-table thead,\n.ksa-invoice-table tfoot {\n text-transform: uppercase;\n}\n\n.qr-rtl {\n direction: rtl;\n}\n\n.qr-flex{\n display: flex;\n justify-content: space-between;\n}", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Sales Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font_size": 14, + "html": "
\n
\n
\n

TAX INVOICE

\n

\u0641\u0627\u062a\u0648\u0631\u0629 \u0636\u0631\u064a\u0628\u064a\u0629

\n
\n \n \n
\n {% set company = frappe.get_doc(\"Company\", doc.company)%}\n {% if (doc.company_address) %}\n {% set supplier_address_doc = frappe.get_doc('Address', doc.company_address) %}\n {% endif %}\n \n {% if(doc.customer_address) %}\n {% set customer_address = frappe.get_doc('Address', doc.customer_address ) %}\n {% endif %}\n \n {% if(doc.shipping_address_name) %}\n {% set customer_shipping_address = frappe.get_doc('Address', doc.shipping_address_name ) %}\n {% endif %} \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {% if(supplier_address_doc) %}\n \n \n \n \n \n \n \n \n \n \n \n \n {% endif %}\n \n {% if (company.tax_id) %}\n \n \n \n \n \n \n \n \n {% endif %}\n \n \n \n \n \n \n \n \n \n \n \n {% if(customer_address) %}\n \n \n \n \n {% endif %}\n \n {% if(customer_shipping_address) %}\n \n \n \n \n \n \n \n \n \n {% endif %}\n \n \n \n \n \n \n {% if(doc.po_no) %}\n \n \n \n \n {% endif %}\n \n \n \n \n \n \n
{{ company.name }}{{ company.company_name_in_arabic }}
Invoice#: {{doc.name}}\u0631\u0642\u0645 \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.name}}
Invoice Date: {{doc.posting_date}}\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.posting_date}}
Date of Supply:{{doc.posting_date}}\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u062a\u0648\u0631\u064a\u062f: {{doc.posting_date}}
Supplier:\u0627\u0644\u0645\u0648\u0631\u062f:
{{ company.name }}{{ company.company_name_in_arabic }}
{{ supplier_address_doc.address_line1}} {{ supplier_address_doc.address_in_arabic}}
Phone: {{ supplier_address_doc.phone }}\u0647\u0627\u062a\u0641: {{ supplier_address_doc.phone }}
Email: {{ supplier_address_doc.email_id }}\u0628\u0631\u064a\u062f \u0627\u0644\u0643\u062a\u0631\u0648\u0646\u064a: {{ supplier_address_doc.email_id }}
Supplier Tax Identification Number:\u0631\u0642\u0645 \u0627\u0644\u062a\u0639\u0631\u064a\u0641 \u0627\u0644\u0636\u0631\u064a\u0628\u064a \u0644\u0644\u0645\u0648\u0631\u062f:
{{ company.tax_id }}{{ company.tax_id }}
CUSTOMER:\u0639\u0645\u064a\u0644:
{{ doc.customer }} {{ doc.customer_name_in_arabic }}
{{ customer_address.address_line1}} {{ customer_address.address_in_arabic}}
SHIPPING ADDRESS:\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0634\u062d\u0646:
{{ customer_shipping_address.address_line1}} {{ customer_shipping_address.address_in_arabic}}
OTHER INFORMATION\u0645\u0639\u0644\u0648\u0645\u0627\u062a \u0623\u062e\u0631\u0649
Purchase Order Number: {{ doc.po_no }}\u0631\u0642\u0645 \u0623\u0645\u0631 \u0627\u0644\u0634\u0631\u0627\u0621: {{ doc.po_no }}
Payment Due Date: {{ doc.due_date}} \u062a\u0627\u0631\u064a\u062e \u0627\u0633\u062a\u062d\u0642\u0627\u0642 \u0627\u0644\u062f\u0641\u0639: {{ doc.due_date}}
\n\n \n {% set col = namespace(one = 2, two = 1) %}\n {% set length = doc.taxes | length %}\n {% set length = length / 2 | round %}\n {% set col.one = col.one + length %}\n {% set col.two = col.two + length %}\n \n {%- if(doc.taxes | length % 2 > 0 ) -%}\n {% set col.two = col.two + 1 %}\n {% endif %}\n \n \n {% set total = namespace(amount = 0) %}\n \n \n \n \n \n \n \n \n {% for row in doc.taxes %}\n \n {% endfor %}\n \n \n \n \n \n {%- for item in doc.items -%}\n {% set total.amount = item.amount %}\n \n \n \n \n \n {% for row in doc.taxes %}\n {% set data_object = json.loads(row.item_wise_tax_detail) %}\n \n {% endfor %}\n \n \n {%- endfor -%}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Nature of goods or services
\u0637\u0628\u064a\u0639\u0629 \u0627\u0644\u0633\u0644\u0639 \u0623\u0648 \u0627\u0644\u062e\u062f\u0645\u0627\u062a
\n Unit price
\n \u0633\u0639\u0631 \u0627\u0644\u0648\u062d\u062f\u0629\n
\n Quantity
\n \u0627\u0644\u0643\u0645\u064a\u0629\n
\n Taxable Amount
\n \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u062e\u0627\u0636\u0639 \u0644\u0644\u0636\u0631\u064a\u0628\u0629\n
{{row.description}}\n Total
\n \u0627\u0644\u0645\u062c\u0645\u0648\u0639\n
{{ item.item_code }}{{ item.get_formatted(\"rate\") }}{{ item.qty }}{{ item.get_formatted(\"amount\") }}\n
\n {%- if(data_object[item.item_code][0])-%}\n {{ frappe.format(data_object[item.item_code][0], {'fieldtype': 'Percent'}) }}\n {%- endif -%}\n \n {%- if(data_object[item.item_code][1])-%}\n {{ frappe.format(data_object[item.item_code][1], {'fieldtype': 'Currency'}) }}\n {% set total.amount = total.amount + data_object[item.item_code][1] %}\n {%- endif -%}\n
\n
{{ frappe.format(total.amount, {'fieldtype': 'Currency'}) }}
\n {{ doc.get_formatted(\"total\") }}
\n {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n
\n \u0627\u0644\u0625\u062c\u0645\u0627\u0644\u064a \u0628\u0627\u0633\u062a\u062b\u0646\u0627\u0621 \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n
\n \u0625\u062c\u0645\u0627\u0644\u064a \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n
\n Total (Excluding VAT)\n
\n Total VAT\n
\n {{ doc.get_formatted(\"total\") }}
\n {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n
{{ doc.get_formatted(\"grand_total\") }}\n \u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u0645\u0633\u062a\u062d\u0642Total Amount Due{{ doc.get_formatted(\"grand_total\") }}
\n\n\t{%- if doc.terms -%}\n

\n {{doc.terms}}\n

\n\t{%- endif -%}\n
\n", + "idx": 0, + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2021-10-30 17:12:49.496507", + "modified_by": "Administrator", + "module": "Regional", + "name": "KSA VAT Invoice", + "owner": "Administrator", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/erpnext/regional/saudi_arabia/setup.py b/erpnext/regional/saudi_arabia/setup.py index 6113f48d3f..2e139e6eb8 100644 --- a/erpnext/regional/saudi_arabia/setup.py +++ b/erpnext/regional/saudi_arabia/setup.py @@ -13,7 +13,7 @@ def setup(company=None, patch=True): add_print_formats() add_permissions() create_ksa_vat_setting(company) - make_qrcode_field() + make_custom_fields() def add_permissions(): """Add Permissions for KSA VAT Setting.""" @@ -26,12 +26,34 @@ def add_permissions(): """Enable KSA VAT Report""" frappe.db.set_value('Report', 'KSA VAT', 'disabled', 0) -def make_qrcode_field(): - """Created QR code Image file""" - qr_code_field = dict( +def make_custom_fields(): + """Create Custom fields + - QR code Image file + - Company Name in Arabic + - Address in Arabic + """ + qr_code = dict( fieldname='qr_code', label='QR Code', fieldtype='Attach Image', read_only=1, no_copy=1, hidden=1) - create_custom_field('Sales Invoice', qr_code_field) + create_custom_field('Sales Invoice', qr_code) + + company_name_in_arabic = dict( + fieldname='company_name_in_arabic', + label='Company Name In Arabic', + fieldtype='Data', + insert_after='company_name' + ) + + create_custom_field('Company', company_name_in_arabic) + + address_in_arabic = dict( + fieldname='address_in_arabic', + label='Address in Arabic', + fieldtype='Data', + insert_after='address_line2' + ) + + create_custom_field('Address', address_in_arabic) From 5d4c5652aff59d6f051adf34dd60094cce96af07 Mon Sep 17 00:00:00 2001 From: Anupam Date: Sun, 31 Oct 2021 14:20:03 +0530 Subject: [PATCH 07/60] feat: added confirm dialog on closing of workorder --- erpnext/manufacturing/doctype/work_order/work_order.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index d4235731ae..9ab81619f9 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -520,9 +520,12 @@ erpnext.work_order = { set_custom_buttons: function(frm) { var doc = frm.doc; if (doc.docstatus === 1 && doc.status != "Closed") { - console.log("check"); frm.add_custom_button(__('Close'), function() { - erpnext.work_order.change_work_order_status(frm, "Closed"); + frappe.confirm(__("Once the Work Order is Closed. It can't be resumed."), + () => { + erpnext.work_order.change_work_order_status(frm, "Closed"); + } + ); }, __("Status")); if (doc.status != 'Stopped' && doc.status != 'Completed') { From e290fe0721a2db994171e0be5a98d7fce272a3c7 Mon Sep 17 00:00:00 2001 From: Anupam Date: Sun, 31 Oct 2021 14:42:10 +0530 Subject: [PATCH 08/60] fix: sider issue --- erpnext/manufacturing/doctype/work_order/work_order.js | 4 ++-- erpnext/manufacturing/doctype/work_order/work_order.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 9ab81619f9..bfce1b8cbe 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -145,12 +145,12 @@ frappe.ui.form.on("Work Order", { && frm.doc.operations && frm.doc.operations.length) { const not_completed = frm.doc.operations.filter(d => { - if(d.status != 'Completed') { + if (d.status != 'Completed') { return true; } }); - if(not_completed && not_completed.length) { + if (not_completed && not_completed.length) { frm.add_custom_button(__('Create Job Card'), () => { frm.trigger("make_job_card"); }).addClass('btn-primary'); diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 6901d71ad4..af80c4ef0b 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1015,8 +1015,7 @@ def close_work_order(work_order, status): filters={ "work_order": work_order.name, "status": "Work In Progress" - }, - pluck='name') + }, pluck='name') if job_cards: job_cards = ", ".join(job_cards) From 55e97dce8a7cffd12180b36586ee277b3188cda5 Mon Sep 17 00:00:00 2001 From: Anupam Date: Sun, 31 Oct 2021 14:45:36 +0530 Subject: [PATCH 09/60] fix: sider issue --- erpnext/manufacturing/doctype/work_order/work_order.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index af80c4ef0b..117e2a227a 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1012,10 +1012,10 @@ def close_work_order(work_order, status): work_order = frappe.get_doc("Work Order", work_order) if work_order.get("operations"): job_cards = frappe.get_list("Job Card", - filters={ - "work_order": work_order.name, - "status": "Work In Progress" - }, pluck='name') + filters={ + "work_order": work_order.name, + "status": "Work In Progress" + }, pluck='name') if job_cards: job_cards = ", ".join(job_cards) From 264b0df9ffe5074542c28b46c6ead2c3436e27ed Mon Sep 17 00:00:00 2001 From: Anupam Date: Sun, 31 Oct 2021 16:11:11 +0530 Subject: [PATCH 10/60] fix: sider issue --- erpnext/manufacturing/doctype/work_order/work_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 117e2a227a..48703bc621 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1015,7 +1015,7 @@ def close_work_order(work_order, status): filters={ "work_order": work_order.name, "status": "Work In Progress" - }, pluck='name') + }, pluck='name') if job_cards: job_cards = ", ".join(job_cards) From dd3cadd46b28d980154b0f6ccc1204fd5ce4fa66 Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Mon, 1 Nov 2021 11:38:02 +0530 Subject: [PATCH 11/60] fix: update set_value query --- erpnext/regional/india/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 753faab904..99ea53d943 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -81,9 +81,7 @@ def update_gst_category(doc, method): for link in doc.links: if link.link_doctype in ['Customer', 'Supplier']: if doc.get('gstin'): - gst_category = frappe.db.get_value(link.link_doctype, link.link_name, 'gst_category') - if gst_category == 'Unregistered': - frappe.db.set_value(link.link_doctype, link.link_name, 'gst_category', 'Registered Regular') + frappe.db.set_value(link.link_doctype, { 'name': link.link_name, 'gst_category': 'Unregistered' }, 'gst_category', 'Registered Regular') def set_gst_state_and_state_number(doc): if not doc.gst_state: From e2d866d6a2299aecac91ac49e4db8bb568c6d093 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Mon, 1 Nov 2021 15:55:19 +0530 Subject: [PATCH 12/60] fix: regional(India) `totalValue` for ewaybill --- erpnext/regional/india/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 1733220c0a..dcb6481f9b 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -446,7 +446,7 @@ def get_ewb_data(dt, dn): data = get_address_details(data, doc, company_address, billing_address, dispatch_address) data.itemList = [] - data.totalValue = doc.total + data.totalValue = doc.net_total data = get_item_list(data, doc, hsn_wise=True) From 530a0f481e8901d2b525a54c663251a477cc3758 Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 2 Nov 2021 12:39:13 +0530 Subject: [PATCH 13/60] fix: added testcase --- erpnext/manufacturing/doctype/work_order/test_work_order.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 85b5bfb9bf..dc81729681 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -14,6 +14,7 @@ from erpnext.manufacturing.doctype.work_order.work_order import ( StockOverProductionError, make_stock_entry, stop_unstop, + close_work_order, ) from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.stock.doctype.item.test_item import create_item, make_item @@ -800,6 +801,10 @@ class TestWorkOrder(unittest.TestCase): if row.is_scrap_item: self.assertEqual(row.qty, 1) + def test_close_work_order(self): + close_work_order(self.wo_order.name, "Stopped") + self.assertEqual(self.wo_order.status, "Closed") + def update_job_card(job_card): job_card_doc = frappe.get_doc('Job Card', job_card) job_card_doc.set('scrap_items', [ From 59e4fd980c1c411f82df1f588bc2c80e17d8e317 Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 2 Nov 2021 12:46:00 +0530 Subject: [PATCH 14/60] fix: linter issues --- erpnext/manufacturing/doctype/work_order/test_work_order.py | 2 +- erpnext/manufacturing/doctype/work_order/work_order.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index dc81729681..f5851dae36 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -12,9 +12,9 @@ from erpnext.manufacturing.doctype.work_order.work_order import ( ItemHasVariantError, OverProductionError, StockOverProductionError, + close_work_order, make_stock_entry, stop_unstop, - close_work_order, ) from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.stock.doctype.item.test_item import create_item, make_item diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 48703bc621..36dae99a69 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1011,6 +1011,7 @@ def close_work_order(work_order, status): work_order = frappe.get_doc("Work Order", work_order) if work_order.get("operations"): + job_cards = frappe.get_list("Job Card", filters={ "work_order": work_order.name, From e36da4d13749afd0fb0e1523aa6d2ecc33ab730c Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 2 Nov 2021 12:53:00 +0530 Subject: [PATCH 15/60] fix: linter issues --- erpnext/manufacturing/doctype/work_order/work_order.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 36dae99a69..0090f4d04e 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1011,8 +1011,7 @@ def close_work_order(work_order, status): work_order = frappe.get_doc("Work Order", work_order) if work_order.get("operations"): - - job_cards = frappe.get_list("Job Card", + job_cards = frappe.get_list("Job Card", filters={ "work_order": work_order.name, "status": "Work In Progress" From 9c0906f1b51431f67c3bc009a1410e1129a80744 Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 2 Nov 2021 20:43:00 +0530 Subject: [PATCH 16/60] fix: test cases --- erpnext/manufacturing/doctype/work_order/test_work_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index f5851dae36..b6a8700bac 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -802,7 +802,7 @@ class TestWorkOrder(unittest.TestCase): self.assertEqual(row.qty, 1) def test_close_work_order(self): - close_work_order(self.wo_order.name, "Stopped") + close_work_order(self.wo_order.name, "Closed") self.assertEqual(self.wo_order.status, "Closed") def update_job_card(job_card): From f2fbcc81249f0c36a8188bcd09095b48b3e933e1 Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Tue, 2 Nov 2021 23:29:06 +0530 Subject: [PATCH 17/60] fix: sider --- erpnext/regional/india/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 99ea53d943..102e84f917 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -81,7 +81,7 @@ def update_gst_category(doc, method): for link in doc.links: if link.link_doctype in ['Customer', 'Supplier']: if doc.get('gstin'): - frappe.db.set_value(link.link_doctype, { 'name': link.link_name, 'gst_category': 'Unregistered' }, 'gst_category', 'Registered Regular') + frappe.db.set_value(link.link_doctype, {'name': link.link_name, 'gst_category': 'Unregistered'}, 'gst_category', 'Registered Regular') def set_gst_state_and_state_number(doc): if not doc.gst_state: From 9b4c7e479650851228181cdf562647364ba51cf9 Mon Sep 17 00:00:00 2001 From: Anupam Date: Wed, 3 Nov 2021 13:27:50 +0530 Subject: [PATCH 18/60] fix: validate job card --- .../doctype/job_card/job_card.js | 5 +++ .../doctype/job_card/job_card.py | 14 +++++++ .../doctype/work_order/test_work_order.py | 39 ++++++++++++++++++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index 35be38813e..df35028ca7 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -28,6 +28,11 @@ frappe.ui.form.on('Job Card', { frappe.flags.resume_job = 0; let has_items = frm.doc.items && frm.doc.items.length; + if (frm.doc.__onload.work_order_stopped) { + frm.disable_save(); + return + } + if (!frm.doc.__islocal && has_items && frm.doc.docstatus < 2) { let to_request = frm.doc.for_quantity > frm.doc.transferred_qty; let excess_transfer_allowed = frm.doc.__onload.job_card_excess_transfer; diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index e1d79be81c..dd1df20216 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -37,6 +37,7 @@ class JobCard(Document): def onload(self): excess_transfer = frappe.db.get_single_value("Manufacturing Settings", "job_card_excess_transfer") self.set_onload("job_card_excess_transfer", excess_transfer) + self.set_onload("work_order_stopped", self.is_work_order_stopped()) def validate(self): self.validate_time_logs() @@ -45,6 +46,7 @@ class JobCard(Document): self.validate_sequence_id() self.set_sub_operations() self.update_sub_operation_status() + self.validate_work_order() def set_sub_operations(self): if self.operation: @@ -549,6 +551,18 @@ class JobCard(Document): frappe.throw(_("{0}, complete the operation {1} before the operation {2}.") .format(message, bold(row.operation), bold(self.operation)), OperationSequenceError) + def validate_work_order(self): + if self.is_work_order_stopped(): + frappe.throw(_("You can't make any changes to Job Card since Work Order is stopped.")) + + def is_work_order_stopped(self): + if self.work_order: + status = frappe.get_value('Work Order', self.work_order) + + if status == "Closed": + return True + + return False @frappe.whitelist() def make_time_log(args): diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index b6a8700bac..3dbbf31757 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -802,8 +802,43 @@ class TestWorkOrder(unittest.TestCase): self.assertEqual(row.qty, 1) def test_close_work_order(self): - close_work_order(self.wo_order.name, "Closed") - self.assertEqual(self.wo_order.status, "Closed") + items = ['Test FG Item for Closed WO', 'Test RM Item 1 for Closed WO', + 'Test RM Item 2 for Closed WO'] + + company = '_Test Company with perpetual inventory' + for item_code in items: + create_item(item_code = item_code, is_stock_item = 1, + is_purchase_item=1, opening_stock=100, valuation_rate=10, company=company, warehouse='Stores - TCP1') + + item = 'Test FG Item for Closed WO' + raw_materials = ['Test RM Item 1 for Closed WO', 'Test RM Item 2 for Closed WO'] + if not frappe.db.get_value('BOM', {'item': item}): + bom = make_bom(item=item, source_warehouse='Stores - TCP1', raw_materials=raw_materials, do_not_save=True) + bom.with_operations = 1 + bom.append('operations', { + 'operation': '_Test Operation 1', + 'workstation': '_Test Workstation 1', + 'hour_rate': 20, + 'time_in_mins': 60 + }) + + bom.submit() + + wo_order = make_wo_order_test_record(item=item, company=company, planned_start_date=now(), qty=20, skip_transfer=1) + job_cards = frappe.db.get_value('Job Card', {'work_order': wo_order.name}, 'name') + + for jc in job_cards: + job_card_doc = frappe.get_doc('Job Card', jc) + job_card_doc.append('time_logs', { + 'from_time': now(), + 'time_in_mins': 60, + 'completed_qty': job_card_doc.for_quantity + }) + + job_card_doc.submit() + + close_work_order(wo_order, "Closed") + self.assertEqual(wo_order.get('status'), "Closed") def update_job_card(job_card): job_card_doc = frappe.get_doc('Job Card', job_card) From ba47bd02b6999411736aa864303a181addc225f3 Mon Sep 17 00:00:00 2001 From: Anupam Date: Wed, 3 Nov 2021 13:41:22 +0530 Subject: [PATCH 19/60] fix: sider isuue --- erpnext/manufacturing/doctype/job_card/job_card.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index df35028ca7..e3eed92d7e 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -30,7 +30,7 @@ frappe.ui.form.on('Job Card', { if (frm.doc.__onload.work_order_stopped) { frm.disable_save(); - return + return; } if (!frm.doc.__islocal && has_items && frm.doc.docstatus < 2) { From cc15cf6ae2915ba01188a36c427ab19d7fd89e2b Mon Sep 17 00:00:00 2001 From: Anupam Date: Wed, 3 Nov 2021 13:54:46 +0530 Subject: [PATCH 20/60] fix: linter isuue --- erpnext/manufacturing/doctype/work_order/test_work_order.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 3dbbf31757..4f67507755 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -826,7 +826,6 @@ class TestWorkOrder(unittest.TestCase): wo_order = make_wo_order_test_record(item=item, company=company, planned_start_date=now(), qty=20, skip_transfer=1) job_cards = frappe.db.get_value('Job Card', {'work_order': wo_order.name}, 'name') - for jc in job_cards: job_card_doc = frappe.get_doc('Job Card', jc) job_card_doc.append('time_logs', { From 7044ae5e39669a0edf59f3abd19b5b52e1de233b Mon Sep 17 00:00:00 2001 From: Anupam Date: Wed, 3 Nov 2021 17:41:04 +0530 Subject: [PATCH 21/60] fix: testcases: --- erpnext/manufacturing/doctype/work_order/test_work_order.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 4f67507755..c3f3917b5c 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -826,6 +826,7 @@ class TestWorkOrder(unittest.TestCase): wo_order = make_wo_order_test_record(item=item, company=company, planned_start_date=now(), qty=20, skip_transfer=1) job_cards = frappe.db.get_value('Job Card', {'work_order': wo_order.name}, 'name') + self.assertEqual(len(job_cards), len(bom.operations)) for jc in job_cards: job_card_doc = frappe.get_doc('Job Card', jc) job_card_doc.append('time_logs', { From b7a44fe0a3e9756eef5ac9000913b0e7d7b86db0 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 3 Nov 2021 18:17:31 +0530 Subject: [PATCH 22/60] perf: improve financial statement loading time --- erpnext/accounts/report/financial_statements.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 2cb8a6802a..352b1c8392 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -425,8 +425,7 @@ def set_gl_entries_by_account( {additional_conditions} and posting_date <= %(to_date)s and is_cancelled = 0 - {distributed_cost_center_query} - order by account, posting_date""".format( + {distributed_cost_center_query}""".format( additional_conditions=additional_conditions, distributed_cost_center_query=distributed_cost_center_query), gl_filters, as_dict=True) #nosec From c0b889b94d823a67f847c3fe0d97ab0b14a7b490 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 7 Nov 2021 17:42:51 +0530 Subject: [PATCH 23/60] fix: auto update price list rate (#28255) (#28256) * fix: auto update price list rate * fix: hide field when auto insert isn't enabled (cherry picked from commit bb3957eba35a5204a1a01e412dee596532bd525e) Co-authored-by: Sagar Sharma <63660334+s-aga-r@users.noreply.github.com> [skip ci] --- .../stock/doctype/stock_settings/stock_settings.json | 12 ++++++++++-- erpnext/stock/get_item_details.py | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index f75cb56138..33d9a6ce41 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -21,6 +21,7 @@ "mr_qty_allowance", "column_break_12", "auto_insert_price_list_rate_if_missing", + "update_existing_price_list_rate", "allow_negative_stock", "show_barcode_field", "clean_description_html", @@ -290,6 +291,13 @@ "fieldname": "mr_qty_allowance", "fieldtype": "Float", "label": "Over Transfer Allowance" + }, + { + "default": "0", + "depends_on": "auto_insert_price_list_rate_if_missing", + "fieldname": "update_existing_price_list_rate", + "fieldtype": "Check", + "label": "Update Existing Price List Rate" } ], "icon": "icon-cog", @@ -297,7 +305,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-06-28 17:02:26.683002", + "modified": "2021-11-06 19:40:02.183592", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", @@ -317,4 +325,4 @@ "sort_field": "modified", "sort_order": "ASC", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 438d3ebc4d..e00382bec1 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -706,7 +706,7 @@ def insert_item_price(args): {'item_code': args.item_code, 'price_list': args.price_list, 'currency': args.currency}, ['name', 'price_list_rate'], as_dict=1) if item_price and item_price.name: - if item_price.price_list_rate != price_list_rate: + if item_price.price_list_rate != price_list_rate and frappe.db.get_single_value('Stock Settings', 'update_existing_price_list_rate'): frappe.db.set_value('Item Price', item_price.name, "price_list_rate", price_list_rate) frappe.msgprint(_("Item Price updated for {0} in Price List {1}").format(args.item_code, args.price_list), alert=True) From db3193dcab1dc04b67bf04cb3f61b59438371951 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 8 Nov 2021 10:57:20 +0530 Subject: [PATCH 24/60] chore: remove migration_hash (#28263) --- erpnext/crm/doctype/prospect/prospect.json | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/crm/doctype/prospect/prospect.json b/erpnext/crm/doctype/prospect/prospect.json index 0e872ace04..c9554ba31a 100644 --- a/erpnext/crm/doctype/prospect/prospect.json +++ b/erpnext/crm/doctype/prospect/prospect.json @@ -165,7 +165,6 @@ ], "index_web_pages_for_search": 1, "links": [], - "migration_hash": "f39fb8f4e18a0e7fd391f0b4b52d8375", "modified": "2021-11-01 13:10:36.759249", "modified_by": "Administrator", "module": "CRM", From 8df50cf1e0a7f58fbb9af94c2598e91d82f3faf5 Mon Sep 17 00:00:00 2001 From: ahmadpak Date: Mon, 8 Nov 2021 09:29:33 +0300 Subject: [PATCH 25/60] update(Print Format): Sales Invoice - KSA VAT Invoice Customer Identification Number Added --- .../print_format/ksa_vat_invoice/ksa_vat_invoice.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json b/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json index edb107adef..36d653616b 100644 --- a/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json +++ b/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json @@ -10,14 +10,14 @@ "docstatus": 0, "doctype": "Print Format", "font_size": 14, - "html": "
\n
\n
\n

TAX INVOICE

\n

\u0641\u0627\u062a\u0648\u0631\u0629 \u0636\u0631\u064a\u0628\u064a\u0629

\n
\n \n \n
\n {% set company = frappe.get_doc(\"Company\", doc.company)%}\n {% if (doc.company_address) %}\n {% set supplier_address_doc = frappe.get_doc('Address', doc.company_address) %}\n {% endif %}\n \n {% if(doc.customer_address) %}\n {% set customer_address = frappe.get_doc('Address', doc.customer_address ) %}\n {% endif %}\n \n {% if(doc.shipping_address_name) %}\n {% set customer_shipping_address = frappe.get_doc('Address', doc.shipping_address_name ) %}\n {% endif %} \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {% if(supplier_address_doc) %}\n \n \n \n \n \n \n \n \n \n \n \n \n {% endif %}\n \n {% if (company.tax_id) %}\n \n \n \n \n \n \n \n \n {% endif %}\n \n \n \n \n \n \n \n \n \n \n \n {% if(customer_address) %}\n \n \n \n \n {% endif %}\n \n {% if(customer_shipping_address) %}\n \n \n \n \n \n \n \n \n \n {% endif %}\n \n \n \n \n \n \n {% if(doc.po_no) %}\n \n \n \n \n {% endif %}\n \n \n \n \n \n \n
{{ company.name }}{{ company.company_name_in_arabic }}
Invoice#: {{doc.name}}\u0631\u0642\u0645 \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.name}}
Invoice Date: {{doc.posting_date}}\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.posting_date}}
Date of Supply:{{doc.posting_date}}\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u062a\u0648\u0631\u064a\u062f: {{doc.posting_date}}
Supplier:\u0627\u0644\u0645\u0648\u0631\u062f:
{{ company.name }}{{ company.company_name_in_arabic }}
{{ supplier_address_doc.address_line1}} {{ supplier_address_doc.address_in_arabic}}
Phone: {{ supplier_address_doc.phone }}\u0647\u0627\u062a\u0641: {{ supplier_address_doc.phone }}
Email: {{ supplier_address_doc.email_id }}\u0628\u0631\u064a\u062f \u0627\u0644\u0643\u062a\u0631\u0648\u0646\u064a: {{ supplier_address_doc.email_id }}
Supplier Tax Identification Number:\u0631\u0642\u0645 \u0627\u0644\u062a\u0639\u0631\u064a\u0641 \u0627\u0644\u0636\u0631\u064a\u0628\u064a \u0644\u0644\u0645\u0648\u0631\u062f:
{{ company.tax_id }}{{ company.tax_id }}
CUSTOMER:\u0639\u0645\u064a\u0644:
{{ doc.customer }} {{ doc.customer_name_in_arabic }}
{{ customer_address.address_line1}} {{ customer_address.address_in_arabic}}
SHIPPING ADDRESS:\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0634\u062d\u0646:
{{ customer_shipping_address.address_line1}} {{ customer_shipping_address.address_in_arabic}}
OTHER INFORMATION\u0645\u0639\u0644\u0648\u0645\u0627\u062a \u0623\u062e\u0631\u0649
Purchase Order Number: {{ doc.po_no }}\u0631\u0642\u0645 \u0623\u0645\u0631 \u0627\u0644\u0634\u0631\u0627\u0621: {{ doc.po_no }}
Payment Due Date: {{ doc.due_date}} \u062a\u0627\u0631\u064a\u062e \u0627\u0633\u062a\u062d\u0642\u0627\u0642 \u0627\u0644\u062f\u0641\u0639: {{ doc.due_date}}
\n\n \n {% set col = namespace(one = 2, two = 1) %}\n {% set length = doc.taxes | length %}\n {% set length = length / 2 | round %}\n {% set col.one = col.one + length %}\n {% set col.two = col.two + length %}\n \n {%- if(doc.taxes | length % 2 > 0 ) -%}\n {% set col.two = col.two + 1 %}\n {% endif %}\n \n \n {% set total = namespace(amount = 0) %}\n \n \n \n \n \n \n \n \n {% for row in doc.taxes %}\n \n {% endfor %}\n \n \n \n \n \n {%- for item in doc.items -%}\n {% set total.amount = item.amount %}\n \n \n \n \n \n {% for row in doc.taxes %}\n {% set data_object = json.loads(row.item_wise_tax_detail) %}\n \n {% endfor %}\n \n \n {%- endfor -%}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Nature of goods or services
\u0637\u0628\u064a\u0639\u0629 \u0627\u0644\u0633\u0644\u0639 \u0623\u0648 \u0627\u0644\u062e\u062f\u0645\u0627\u062a
\n Unit price
\n \u0633\u0639\u0631 \u0627\u0644\u0648\u062d\u062f\u0629\n
\n Quantity
\n \u0627\u0644\u0643\u0645\u064a\u0629\n
\n Taxable Amount
\n \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u062e\u0627\u0636\u0639 \u0644\u0644\u0636\u0631\u064a\u0628\u0629\n
{{row.description}}\n Total
\n \u0627\u0644\u0645\u062c\u0645\u0648\u0639\n
{{ item.item_code }}{{ item.get_formatted(\"rate\") }}{{ item.qty }}{{ item.get_formatted(\"amount\") }}\n
\n {%- if(data_object[item.item_code][0])-%}\n {{ frappe.format(data_object[item.item_code][0], {'fieldtype': 'Percent'}) }}\n {%- endif -%}\n \n {%- if(data_object[item.item_code][1])-%}\n {{ frappe.format(data_object[item.item_code][1], {'fieldtype': 'Currency'}) }}\n {% set total.amount = total.amount + data_object[item.item_code][1] %}\n {%- endif -%}\n
\n
{{ frappe.format(total.amount, {'fieldtype': 'Currency'}) }}
\n {{ doc.get_formatted(\"total\") }}
\n {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n
\n \u0627\u0644\u0625\u062c\u0645\u0627\u0644\u064a \u0628\u0627\u0633\u062a\u062b\u0646\u0627\u0621 \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n
\n \u0625\u062c\u0645\u0627\u0644\u064a \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n
\n Total (Excluding VAT)\n
\n Total VAT\n
\n {{ doc.get_formatted(\"total\") }}
\n {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n
{{ doc.get_formatted(\"grand_total\") }}\n \u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u0645\u0633\u062a\u062d\u0642Total Amount Due{{ doc.get_formatted(\"grand_total\") }}
\n\n\t{%- if doc.terms -%}\n

\n {{doc.terms}}\n

\n\t{%- endif -%}\n
\n", + "html": "
\n
\n
\n

TAX INVOICE

\n

\u0641\u0627\u062a\u0648\u0631\u0629 \u0636\u0631\u064a\u0628\u064a\u0629

\n
\n \n \n
\n {% set company = frappe.get_doc(\"Company\", doc.company)%}\n {% if (doc.company_address) %}\n {% set supplier_address_doc = frappe.get_doc('Address', doc.company_address) %}\n {% endif %}\n \n {% if(doc.customer_address) %}\n {% set customer_address = frappe.get_doc('Address', doc.customer_address ) %}\n {% endif %}\n \n {% if(doc.shipping_address_name) %}\n {% set customer_shipping_address = frappe.get_doc('Address', doc.shipping_address_name ) %}\n {% endif %} \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\t\t{% if (company.tax_id) %}\n \n \n \n \n \n \n \n \n {% endif %}\n \n \n \n \n \n \n {% if(supplier_address_doc) %}\n \n \n \n \n \n \n \n \n \n \n \n \n {% endif %}\n \n \n \n \n \n \n\t\t{% set customer_tax_id = frappe.db.get_value('Customer', doc.customer, 'tax_id') %}\n\t\t{% if customer_tax_id %}\n \n \n \n \n \n \n \n \n {% endif %}\n \n \n \n \n \n {% if(customer_address) %}\n \n \n \n \n {% endif %}\n \n {% if(customer_shipping_address) %}\n \n \n \n \n \n \n \n \n \n {% endif %}\n \n\t\t{% if(doc.po_no) %}\n \n \n \n \n \n \n \n \n \n {% endif %}\n \n \n \n \n \n \n
{{ company.name }}{{ company.company_name_in_arabic }}
Invoice#: {{doc.name}}\u0631\u0642\u0645 \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.name}}
Invoice Date: {{doc.posting_date}}\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.posting_date}}
Date of Supply:{{doc.posting_date}}\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u062a\u0648\u0631\u064a\u062f: {{doc.posting_date}}
Supplier:\u0627\u0644\u0645\u0648\u0631\u062f:
Supplier Tax Identification Number:\u0631\u0642\u0645 \u0627\u0644\u062a\u0639\u0631\u064a\u0641 \u0627\u0644\u0636\u0631\u064a\u0628\u064a \u0644\u0644\u0645\u0648\u0631\u062f:
{{ company.tax_id }}{{ company.tax_id }}
{{ company.name }}{{ company.company_name_in_arabic }}
{{ supplier_address_doc.address_line1}} {{ supplier_address_doc.address_in_arabic}}
Phone: {{ supplier_address_doc.phone }}\u0647\u0627\u062a\u0641: {{ supplier_address_doc.phone }}
Email: {{ supplier_address_doc.email_id }}\u0628\u0631\u064a\u062f \u0627\u0644\u0643\u062a\u0631\u0648\u0646\u064a: {{ supplier_address_doc.email_id }}
CUSTOMER:\u0639\u0645\u064a\u0644:
Customer Tax Identification Number:\u0631\u0642\u0645 \u0627\u0644\u062a\u0639\u0631\u064a\u0641 \u0627\u0644\u0636\u0631\u064a\u0628\u064a \u0644\u0644\u0639\u0645\u064a\u0644:
{{ customer_tax_id }}{{ customer_tax_id }}
{{ doc.customer }} {{ doc.customer_name_in_arabic }}
{{ customer_address.address_line1}} {{ customer_address.address_in_arabic}}
SHIPPING ADDRESS:\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0634\u062d\u0646:
{{ customer_shipping_address.address_line1}} {{ customer_shipping_address.address_in_arabic}}
OTHER INFORMATION\u0645\u0639\u0644\u0648\u0645\u0627\u062a \u0623\u062e\u0631\u0649
Purchase Order Number: {{ doc.po_no }}\u0631\u0642\u0645 \u0623\u0645\u0631 \u0627\u0644\u0634\u0631\u0627\u0621: {{ doc.po_no }}
Payment Due Date: {{ doc.due_date}} \u062a\u0627\u0631\u064a\u062e \u0627\u0633\u062a\u062d\u0642\u0627\u0642 \u0627\u0644\u062f\u0641\u0639: {{ doc.due_date}}
\n\n \n {% set col = namespace(one = 2, two = 1) %}\n {% set length = doc.taxes | length %}\n {% set length = length / 2 | round %}\n {% set col.one = col.one + length %}\n {% set col.two = col.two + length %}\n \n {%- if(doc.taxes | length % 2 > 0 ) -%}\n {% set col.two = col.two + 1 %}\n {% endif %}\n \n \n {% set total = namespace(amount = 0) %}\n \n \n \n \n \n \n \n \n {% for row in doc.taxes %}\n \n {% endfor %}\n \n \n \n \n \n {%- for item in doc.items -%}\n {% set total.amount = item.amount %}\n \n \n \n \n \n {% for row in doc.taxes %}\n {% set data_object = json.loads(row.item_wise_tax_detail) %}\n \n {% endfor %}\n \n \n {%- endfor -%}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Nature of goods or services
\u0637\u0628\u064a\u0639\u0629 \u0627\u0644\u0633\u0644\u0639 \u0623\u0648 \u0627\u0644\u062e\u062f\u0645\u0627\u062a
\n Unit price
\n \u0633\u0639\u0631 \u0627\u0644\u0648\u062d\u062f\u0629\n
\n Quantity
\n \u0627\u0644\u0643\u0645\u064a\u0629\n
\n Taxable Amount
\n \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u062e\u0627\u0636\u0639 \u0644\u0644\u0636\u0631\u064a\u0628\u0629\n
{{row.description}}\n Total
\n \u0627\u0644\u0645\u062c\u0645\u0648\u0639\n
{{ item.item_code }}{{ item.get_formatted(\"rate\") }}{{ item.qty }}{{ item.get_formatted(\"amount\") }}\n
\n {%- if(data_object[item.item_code][0])-%}\n {{ frappe.format(data_object[item.item_code][0], {'fieldtype': 'Percent'}) }}\n {%- endif -%}\n \n {%- if(data_object[item.item_code][1])-%}\n {{ frappe.format(data_object[item.item_code][1], {'fieldtype': 'Currency'}) }}\n {% set total.amount = total.amount + data_object[item.item_code][1] %}\n {%- endif -%}\n
\n
{{ frappe.format(total.amount, {'fieldtype': 'Currency'}) }}
\n {{ doc.get_formatted(\"total\") }}
\n {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n
\n \u0627\u0644\u0625\u062c\u0645\u0627\u0644\u064a \u0628\u0627\u0633\u062a\u062b\u0646\u0627\u0621 \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n
\n \u0625\u062c\u0645\u0627\u0644\u064a \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n
\n Total (Excluding VAT)\n
\n Total VAT\n
\n {{ doc.get_formatted(\"total\") }}
\n {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n
{{ doc.get_formatted(\"grand_total\") }}\n \u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u0645\u0633\u062a\u062d\u0642Total Amount Due{{ doc.get_formatted(\"grand_total\") }}
\n\n\t{%- if doc.terms -%}\n

\n {{doc.terms}}\n

\n\t{%- endif -%}\n
\n", "idx": 0, "line_breaks": 0, "margin_bottom": 15.0, "margin_left": 15.0, "margin_right": 15.0, "margin_top": 15.0, - "modified": "2021-10-30 17:12:49.496507", + "modified": "2021-11-08 09:19:18.660806", "modified_by": "Administrator", "module": "Regional", "name": "KSA VAT Invoice", From c81d4734c48656b7ca9766a01ce33ed4d2ac00ed Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 8 Nov 2021 17:14:03 +0530 Subject: [PATCH 26/60] fix: KSA VAT setup issues --- erpnext/regional/report/ksa_vat/ksa_vat.js | 1 - erpnext/regional/report/ksa_vat/ksa_vat.py | 4 +- erpnext/regional/saudi_arabia/setup.py | 55 ++++++++++--------- .../wizard/data/ksa_vat_settings.json | 4 +- .../operations/setup_ksa_vat_setting.py | 3 - .../setup_wizard/data/country_wise_tax.json | 4 ++ 6 files changed, 38 insertions(+), 33 deletions(-) diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.js b/erpnext/regional/report/ksa_vat/ksa_vat.js index d46d260ac1..59e72c3e63 100644 --- a/erpnext/regional/report/ksa_vat/ksa_vat.js +++ b/erpnext/regional/report/ksa_vat/ksa_vat.js @@ -49,7 +49,6 @@ frappe.query_reports["KSA VAT"] = { value = $(`${value}`); var $value = $(value).css("font-weight", "bold"); value = $value.wrap("

").parent().html(); - console.log($value) return value } }else{ diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.py b/erpnext/regional/report/ksa_vat/ksa_vat.py index 198b0b2b1b..b41b2b0428 100644 --- a/erpnext/regional/report/ksa_vat/ksa_vat.py +++ b/erpnext/regional/report/ksa_vat/ksa_vat.py @@ -118,14 +118,14 @@ def get_tax_data_for_each_vat_setting(vat_setting, filters, doctype): total_taxable_adjustment_amount = 0 total_tax = 0 # Fetch All Invoices - invoices = frappe.get_list(doctype, + invoices = frappe.get_all(doctype, filters ={ 'docstatus': 1, 'posting_date': ['between', [from_date, to_date]] }, fields =['name', 'is_return']) for invoice in invoices: - invoice_items = frappe.get_list(f'{doctype} Item', + invoice_items = frappe.get_all(f'{doctype} Item', filters ={ 'docstatus': 1, 'parent': invoice.name, diff --git a/erpnext/regional/saudi_arabia/setup.py b/erpnext/regional/saudi_arabia/setup.py index 0c52ee178b..38a089c632 100644 --- a/erpnext/regional/saudi_arabia/setup.py +++ b/erpnext/regional/saudi_arabia/setup.py @@ -5,13 +5,12 @@ import frappe from frappe.permissions import add_permission, update_permission_property from erpnext.regional.united_arab_emirates.setup import make_custom_fields as uae_custom_fields, add_print_formats from erpnext.regional.saudi_arabia.wizard.operations.setup_ksa_vat_setting import create_ksa_vat_setting -from frappe.custom.doctype.custom_field.custom_field import create_custom_field +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields def setup(company=None, patch=True): uae_custom_fields() add_print_formats() add_permissions() - create_ksa_vat_setting(company) make_custom_fields() def add_permissions(): @@ -31,28 +30,34 @@ def make_custom_fields(): - Company Name in Arabic - Address in Arabic """ - qr_code = dict( - fieldname='qr_code', - label='QR Code', - fieldtype='Attach Image', - read_only=1, no_copy=1, hidden=1) + custom_fields = { + 'Sales Invoice': [ + dict( + fieldname='qr_code', + label='QR Code', + fieldtype='Attach Image', + read_only=1, no_copy=1, hidden=1 + ) + ], + 'Address': [ + dict( + fieldname='address_in_arabic', + label='Address in Arabic', + fieldtype='Data', + insert_after='address_line2' + ) + ], + 'Company': [ + dict( + fieldname='company_name_in_arabic', + label='Company Name In Arabic', + fieldtype='Data', + insert_after='company_name' + ) + ] + } - create_custom_field('Sales Invoice', qr_code) + create_custom_fields(custom_fields, update=True) - company_name_in_arabic = dict( - fieldname='company_name_in_arabic', - label='Company Name In Arabic', - fieldtype='Data', - insert_after='company_name' - ) - - create_custom_field('Company', company_name_in_arabic) - - address_in_arabic = dict( - fieldname='address_in_arabic', - label='Address in Arabic', - fieldtype='Data', - insert_after='address_line2' - ) - - create_custom_field('Address', address_in_arabic) +def update_regional_tax_settings(country, company): + create_ksa_vat_setting(company) diff --git a/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json b/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json index 709d65be04..60951a9cec 100644 --- a/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json +++ b/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json @@ -15,7 +15,7 @@ { "title": "Exempted sales", "item_tax_template": "KSA VAT Exempted", - "account": "VAT Zero" + "account": "VAT Exempted" } ] }, @@ -40,7 +40,7 @@ { "title": "Exempted purchases", "item_tax_template": "KSA VAT Exempted", - "account": "VAT Zero" + "account": "VAT Exempted" } ] } diff --git a/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py b/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py index 3c89edd37e..97300dc378 100644 --- a/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py +++ b/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py @@ -3,14 +3,11 @@ import os import frappe -from erpnext.setup.setup_wizard.operations.taxes_setup import setup_taxes_and_charges - def create_ksa_vat_setting(company): """On creation of first company. Creates KSA VAT Setting""" company = frappe.get_doc('Company', company) - setup_taxes_and_charges(company.name, company.country) file_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'ksa_vat_settings.json') with open(file_path, 'r') as json_file: diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json index 8a1338583b..14b79510c1 100644 --- a/erpnext/setup/setup_wizard/data/country_wise_tax.json +++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json @@ -2120,6 +2120,10 @@ "account_name": "VAT 15%", "tax_rate": 15.00 }, + "KSA VAT 5%": { + "account_name": "VAT 5%", + "tax_rate": 5.00 + }, "KSA VAT Zero": { "account_name": "VAT Zero", "tax_rate": 0.00 From 508832e90a3863e0a2030b999a6772d291afd84b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 8 Nov 2021 17:46:24 +0530 Subject: [PATCH 27/60] fix: Add patch to make custom fields --- erpnext/patches.txt | 1 + .../patches/v13_0/create_ksa_vat_custom_fields.py | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 erpnext/patches/v13_0/create_ksa_vat_custom_fields.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2c21ab6c3a..a60b851c5a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -309,3 +309,4 @@ erpnext.patches.v14_0.delete_healthcare_doctypes erpnext.patches.v13_0.update_category_in_ltds_certificate erpnext.patches.v13_0.create_pan_field_for_india #2 erpnext.patches.v14_0.delete_hub_doctypes +erpnext.patches.v13_0.create_ksa_vat_custom_fields diff --git a/erpnext/patches/v13_0/create_ksa_vat_custom_fields.py b/erpnext/patches/v13_0/create_ksa_vat_custom_fields.py new file mode 100644 index 0000000000..f33b4b3ea0 --- /dev/null +++ b/erpnext/patches/v13_0/create_ksa_vat_custom_fields.py @@ -0,0 +1,12 @@ +import frappe + +from erpnext.regional.saudi_arabia.setup import make_custom_fields + + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'Saudi Arabia'}) + if not company: + return + + make_custom_fields() + From 75e9100a5389eacca8f51c4a7ebe268f641cf2b8 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Mon, 8 Nov 2021 09:49:11 +0530 Subject: [PATCH 28/60] fix: Fixed customer address variable, sales invoice item field currency issue (cherry picked from commit 904010ab645fdb16f09072c062269945f7747712) --- .../doctype/taxjar_settings/taxjar_settings.py | 4 ++-- erpnext/erpnext_integrations/taxjar_integration.py | 2 +- erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py index bfc5e6f98d..b9f24b65b3 100644 --- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py +++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py @@ -80,9 +80,9 @@ def make_custom_fields(update=True): dict(fieldname='product_tax_category', fieldtype='Link', insert_after='description', options='Product Tax Category', label='Product Tax Category', fetch_from='item_code.product_tax_category'), dict(fieldname='tax_collectable', fieldtype='Currency', insert_after='net_amount', - label='Tax Collectable', read_only=1), + label='Tax Collectable', read_only=1, options='currency'), dict(fieldname='taxable_amount', fieldtype='Currency', insert_after='tax_collectable', - label='Taxable Amount', read_only=1) + label='Taxable Amount', read_only=1, options='currency') ], 'Item': [ dict(fieldname='product_tax_category', fieldtype='Link', insert_after='item_group', options='Product Tax Category', diff --git a/erpnext/erpnext_integrations/taxjar_integration.py b/erpnext/erpnext_integrations/taxjar_integration.py index 2a7243c243..39aac0c407 100644 --- a/erpnext/erpnext_integrations/taxjar_integration.py +++ b/erpnext/erpnext_integrations/taxjar_integration.py @@ -262,7 +262,7 @@ def get_shipping_address_details(doc): if doc.shipping_address_name: shipping_address = frappe.get_doc("Address", doc.shipping_address_name) elif doc.customer_address: - shipping_address = frappe.get_doc("Address", doc.customer_address_name) + shipping_address = frappe.get_doc("Address", doc.customer_address) else: shipping_address = get_company_address_details(doc) diff --git a/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py index fba9e2c443..1625e4f17e 100644 --- a/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py +++ b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py @@ -21,9 +21,9 @@ def execute(): dict(fieldname='product_tax_category', fieldtype='Link', insert_after='description', options='Product Tax Category', label='Product Tax Category', fetch_from='item_code.product_tax_category'), dict(fieldname='tax_collectable', fieldtype='Currency', insert_after='net_amount', - label='Tax Collectable', read_only=1), + label='Tax Collectable', read_only=1, options='currency'), dict(fieldname='taxable_amount', fieldtype='Currency', insert_after='tax_collectable', - label='Taxable Amount', read_only=1) + label='Taxable Amount', read_only=1, options='currency') ], 'Item': [ dict(fieldname='product_tax_category', fieldtype='Link', insert_after='item_group', options='Product Tax Category', From e51c4ba8a9c96dd28580a8a6e3fcffdb45c7bdb4 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Mon, 8 Nov 2021 15:16:20 +0530 Subject: [PATCH 29/60] fix: Added company field, filtered account heads (cherry picked from commit 902c03cd375c7b1b2f35cff4dfbc5f1ff3e4cd56) --- .../taxjar_settings/taxjar_settings.js | 17 ++++++++++++++ .../taxjar_settings/taxjar_settings.json | 22 ++++++++++++++----- .../taxjar_integration.py | 6 +++++ .../custom_fields_for_taxjar_integration.py | 4 ++++ 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js index d49598932f..a362ba1f0b 100644 --- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js +++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js @@ -7,6 +7,23 @@ frappe.ui.form.on('TaxJar Settings', { frm.toggle_reqd("sandbox_api_key", frm.doc.is_sandbox); }, + on_load: (frm) => { + frm.set_query('shipping_account_head', function() { + return { + filters: { + 'company': frm.doc.current_company + } + }; + }); + frm.set_query('tax_account_head', function() { + return { + filters: { + 'company': frm.doc.current_company + } + }; + }); + }, + refresh: (frm) => { frm.add_custom_button(__('Update Nexus List'), function() { frm.call({ diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json index 2d17f2ed83..1fda8929e9 100644 --- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json +++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json @@ -14,6 +14,8 @@ "cb_keys", "sandbox_api_key", "configuration", + "current_company", + "column_break_10", "tax_account_head", "configuration_cb", "shipping_account_head", @@ -67,10 +69,6 @@ "fieldtype": "Password", "label": "Sandbox API Key" }, - { - "fieldname": "configuration_cb", - "fieldtype": "Column Break" - }, { "default": "0", "depends_on": "taxjar_calculate_tax", @@ -104,11 +102,25 @@ "label": "Nexus", "options": "TaxJar Nexus", "read_only": 1 + }, + { + "fieldname": "current_company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "fieldname": "configuration_cb", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" } ], "issingle": 1, "links": [], - "modified": "2021-10-06 10:59:13.475442", + "modified": "2021-11-08 14:40:15.684224", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "TaxJar Settings", diff --git a/erpnext/erpnext_integrations/taxjar_integration.py b/erpnext/erpnext_integrations/taxjar_integration.py index 39aac0c407..794b281c55 100644 --- a/erpnext/erpnext_integrations/taxjar_integration.py +++ b/erpnext/erpnext_integrations/taxjar_integration.py @@ -19,6 +19,8 @@ SUPPORTED_STATE_CODES = ['AL', 'AK', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DE', 'DC', ' 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MD', 'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'NM', 'NY', 'NC', 'ND', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VT', 'VA', 'WA', 'WV', 'WI', 'WY'] +USA_COMPANIES = frappe.get_all('Company', filters = {'country': 'United States'}, fields=['name'], pluck='name') + def get_client(): @@ -158,6 +160,10 @@ def set_sales_tax(doc, method): if not TAXJAR_CALCULATE_TAX: return + taxjar_settings_company = frappe.db.get_single_value('TaxJar Settings','current_company') + if taxjar_settings_company not in USA_COMPANIES: + return + if not doc.items: return diff --git a/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py index 1625e4f17e..775e4d8cd5 100644 --- a/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py +++ b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py @@ -28,6 +28,10 @@ def execute(): 'Item': [ dict(fieldname='product_tax_category', fieldtype='Link', insert_after='item_group', options='Product Tax Category', label='Product Tax Category') + ], + 'TaxJar Settings': [ + dict(fieldname='current_company', fieldtype='Link', insert_after='configuration', options='Company', + label='Company') ] } create_custom_fields(custom_fields, update=True) From d2915c6bc7330eb245d398dc61bd1d63d13a3b48 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Mon, 8 Nov 2021 17:59:03 +0530 Subject: [PATCH 30/60] fix: fixed company field, updated patch (cherry picked from commit 7f2d304f32fdf580a354c1dbba58189cdd4f35f2) # Conflicts: # erpnext/patches.txt --- .../doctype/taxjar_settings/taxjar_settings.js | 4 ++-- .../doctype/taxjar_settings/taxjar_settings.json | 9 +-------- erpnext/erpnext_integrations/taxjar_integration.py | 6 ++---- erpnext/patches.txt | 4 ++++ .../v13_0/custom_fields_for_taxjar_integration.py | 2 +- 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js index a362ba1f0b..2925db82e3 100644 --- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js +++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js @@ -11,14 +11,14 @@ frappe.ui.form.on('TaxJar Settings', { frm.set_query('shipping_account_head', function() { return { filters: { - 'company': frm.doc.current_company + 'company': frm.doc.company } }; }); frm.set_query('tax_account_head', function() { return { filters: { - 'company': frm.doc.current_company + 'company': frm.doc.company } }; }); diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json index 1fda8929e9..d9ae5b601c 100644 --- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json +++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json @@ -14,7 +14,6 @@ "cb_keys", "sandbox_api_key", "configuration", - "current_company", "column_break_10", "tax_account_head", "configuration_cb", @@ -103,12 +102,6 @@ "options": "TaxJar Nexus", "read_only": 1 }, - { - "fieldname": "current_company", - "fieldtype": "Link", - "label": "Company", - "options": "Company" - }, { "fieldname": "configuration_cb", "fieldtype": "Column Break" @@ -120,7 +113,7 @@ ], "issingle": 1, "links": [], - "modified": "2021-11-08 14:40:15.684224", + "modified": "2021-11-08 17:57:17.323275", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "TaxJar Settings", diff --git a/erpnext/erpnext_integrations/taxjar_integration.py b/erpnext/erpnext_integrations/taxjar_integration.py index 794b281c55..1adc1ddb14 100644 --- a/erpnext/erpnext_integrations/taxjar_integration.py +++ b/erpnext/erpnext_integrations/taxjar_integration.py @@ -6,7 +6,7 @@ from frappe import _ from frappe.contacts.doctype.address.address import get_company_address from frappe.utils import cint, flt -from erpnext import get_default_company +from erpnext import get_default_company, get_region TAX_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "tax_account_head") SHIP_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "shipping_account_head") @@ -19,7 +19,6 @@ SUPPORTED_STATE_CODES = ['AL', 'AK', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DE', 'DC', ' 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MD', 'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'NM', 'NY', 'NC', 'ND', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VT', 'VA', 'WA', 'WV', 'WI', 'WY'] -USA_COMPANIES = frappe.get_all('Company', filters = {'country': 'United States'}, fields=['name'], pluck='name') @@ -160,8 +159,7 @@ def set_sales_tax(doc, method): if not TAXJAR_CALCULATE_TAX: return - taxjar_settings_company = frappe.db.get_single_value('TaxJar Settings','current_company') - if taxjar_settings_company not in USA_COMPANIES: + if get_region(doc.company): return if not doc.items: diff --git a/erpnext/patches.txt b/erpnext/patches.txt index a60b851c5a..0dcd3b73a3 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -283,8 +283,12 @@ erpnext.patches.v13_0.reset_clearance_date_for_intracompany_payment_entries erpnext.patches.v13_0.einvoicing_deprecation_warning execute:frappe.reload_doc("erpnext_integrations", "doctype", "TaxJar Settings") execute:frappe.reload_doc("erpnext_integrations", "doctype", "Product Tax Category") +<<<<<<< HEAD erpnext.patches.v13_0.custom_fields_for_taxjar_integration erpnext.patches.v14_0.delete_einvoicing_doctypes +======= +erpnext.patches.v13_0.custom_fields_for_taxjar_integration #08-11-2021 +>>>>>>> 7f2d304f32 (fix: fixed company field, updated patch) erpnext.patches.v13_0.set_operation_time_based_on_operating_cost erpnext.patches.v13_0.validate_options_for_data_field erpnext.patches.v13_0.create_gst_payment_entry_fields diff --git a/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py index 775e4d8cd5..078c558d88 100644 --- a/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py +++ b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py @@ -30,7 +30,7 @@ def execute(): label='Product Tax Category') ], 'TaxJar Settings': [ - dict(fieldname='current_company', fieldtype='Link', insert_after='configuration', options='Company', + dict(fieldname='company', fieldtype='Link', insert_after='configuration', options='Company', label='Company') ] } From 45fd819729a73be9e334194e64ad00e81b36dabc Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Mon, 8 Nov 2021 18:03:44 +0530 Subject: [PATCH 31/60] fix: company condition fix, added company field (cherry picked from commit 7ad2717accea61883f98d418b4abed47ca3937a8) --- .../doctype/taxjar_settings/taxjar_settings.json | 9 ++++++++- erpnext/erpnext_integrations/taxjar_integration.py | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json index d9ae5b601c..23ccb7e4da 100644 --- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json +++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json @@ -14,6 +14,7 @@ "cb_keys", "sandbox_api_key", "configuration", + "company", "column_break_10", "tax_account_head", "configuration_cb", @@ -109,11 +110,17 @@ { "fieldname": "column_break_10", "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" } ], "issingle": 1, "links": [], - "modified": "2021-11-08 17:57:17.323275", + "modified": "2021-11-08 18:02:29.232090", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "TaxJar Settings", diff --git a/erpnext/erpnext_integrations/taxjar_integration.py b/erpnext/erpnext_integrations/taxjar_integration.py index 1adc1ddb14..a4e21579e3 100644 --- a/erpnext/erpnext_integrations/taxjar_integration.py +++ b/erpnext/erpnext_integrations/taxjar_integration.py @@ -159,7 +159,7 @@ def set_sales_tax(doc, method): if not TAXJAR_CALCULATE_TAX: return - if get_region(doc.company): + if get_region(doc.company) != 'United States': return if not doc.items: From 94d70bca9ed8bfbaf28d70569970bd19cf4e23fe Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 8 Nov 2021 18:48:46 +0530 Subject: [PATCH 32/60] fix: Resolve conflicts --- erpnext/patches.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0dcd3b73a3..1006e2180f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -283,12 +283,8 @@ erpnext.patches.v13_0.reset_clearance_date_for_intracompany_payment_entries erpnext.patches.v13_0.einvoicing_deprecation_warning execute:frappe.reload_doc("erpnext_integrations", "doctype", "TaxJar Settings") execute:frappe.reload_doc("erpnext_integrations", "doctype", "Product Tax Category") -<<<<<<< HEAD -erpnext.patches.v13_0.custom_fields_for_taxjar_integration erpnext.patches.v14_0.delete_einvoicing_doctypes -======= erpnext.patches.v13_0.custom_fields_for_taxjar_integration #08-11-2021 ->>>>>>> 7f2d304f32 (fix: fixed company field, updated patch) erpnext.patches.v13_0.set_operation_time_based_on_operating_cost erpnext.patches.v13_0.validate_options_for_data_field erpnext.patches.v13_0.create_gst_payment_entry_fields From 27709a1c71a9943cb95deb8ef1b563c465489dcb Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 9 Nov 2021 09:56:58 +0530 Subject: [PATCH 33/60] fix: test cases --- .../doctype/work_order/test_work_order.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index c3f3917b5c..f4a88dc459 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -826,19 +826,20 @@ class TestWorkOrder(unittest.TestCase): wo_order = make_wo_order_test_record(item=item, company=company, planned_start_date=now(), qty=20, skip_transfer=1) job_cards = frappe.db.get_value('Job Card', {'work_order': wo_order.name}, 'name') - self.assertEqual(len(job_cards), len(bom.operations)) - for jc in job_cards: - job_card_doc = frappe.get_doc('Job Card', jc) - job_card_doc.append('time_logs', { - 'from_time': now(), - 'time_in_mins': 60, - 'completed_qty': job_card_doc.for_quantity - }) - job_card_doc.submit() + if len(job_cards) == len(bom.operations): + for jc in job_cards: + job_card_doc = frappe.get_doc('Job Card', jc) + job_card_doc.append('time_logs', { + 'from_time': now(), + 'time_in_mins': 60, + 'completed_qty': job_card_doc.for_quantity + }) - close_work_order(wo_order, "Closed") - self.assertEqual(wo_order.get('status'), "Closed") + job_card_doc.submit() + + close_work_order(wo_order, "Closed") + self.assertEqual(wo_order.get('status'), "Closed") def update_job_card(job_card): job_card_doc = frappe.get_doc('Job Card', job_card) From 0e8e7e21c3bf020497be7c599d43650ff5ff0b91 Mon Sep 17 00:00:00 2001 From: Bibin <17405044+bibinqcs@users.noreply.github.com> Date: Tue, 9 Nov 2021 08:32:05 +0400 Subject: [PATCH 34/60] fix: filter only submitted fees in student fee collection report (#28280) * Update student_fee_collection.json Fix: filter and show only submitted fees documents * fix: add total row for the student fee collection --- .../student_fee_collection/student_fee_collection.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/education/report/student_fee_collection/student_fee_collection.json b/erpnext/education/report/student_fee_collection/student_fee_collection.json index 8deb865ebc..c0229a2ee2 100644 --- a/erpnext/education/report/student_fee_collection/student_fee_collection.json +++ b/erpnext/education/report/student_fee_collection/student_fee_collection.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "creation": "2016-06-22 02:58:41.024538", "disable_prepared_report": 0, "disabled": 0, @@ -13,7 +13,7 @@ "name": "Student Fee Collection", "owner": "Administrator", "prepared_report": 0, - "query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(grand_total) - sum(outstanding_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student", + "query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(grand_total) - sum(outstanding_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nWHERE\n docstatus=1 \nGROUP BY\n student", "ref_doctype": "Fees", "report_name": "Student Fee Collection", "report_type": "Query Report", @@ -22,4 +22,4 @@ "role": "Academics User" } ] -} \ No newline at end of file +} From 663a7afe4df81fe3956af4eb18ca1eaa0614b626 Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 9 Nov 2021 10:50:38 +0530 Subject: [PATCH 35/60] fix: get_planned_qty chnages --- erpnext/stock/stock_balance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py index 1cb0f0d0a4..51c20b0250 100644 --- a/erpnext/stock/stock_balance.py +++ b/erpnext/stock/stock_balance.py @@ -161,7 +161,7 @@ def get_ordered_qty(item_code, warehouse): def get_planned_qty(item_code, warehouse): planned_qty = frappe.db.sql(""" select sum(qty - produced_qty) from `tabWork Order` - where production_item = %s and fg_warehouse = %s and status not in ("Stopped", "Completed") + where production_item = %s and fg_warehouse = %s and status not in ("Stopped", "Completed", "Closed") and docstatus=1 and qty > produced_qty""", (item_code, warehouse)) return flt(planned_qty[0][0]) if planned_qty else 0 From 34f5283c1773b79463fcf77a037c6ae258e329dc Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 9 Nov 2021 11:55:33 +0530 Subject: [PATCH 36/60] fix: show full item name in search widget (#28283) --- erpnext/controllers/queries.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 22e02b4a74..76a7d1a95f 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -210,12 +210,15 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals meta = frappe.get_meta("Item", cached=True) searchfields = meta.get_search_fields() - if "description" in searchfields: - searchfields.remove("description") + # these are handled separately + ignored_search_fields = ("item_name", "description") + for ignored_field in ignored_search_fields: + if ignored_field in searchfields: + searchfields.remove(ignored_field) columns = '' extra_searchfields = [field for field in searchfields - if not field in ["name", "item_group", "description"]] + if not field in ["name", "item_group", "description", "item_name"]] if extra_searchfields: columns = ", " + ", ".join(extra_searchfields) @@ -252,10 +255,8 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals if frappe.db.count('Item', cache=True) < 50000: # scan description only if items are less than 50000 description_cond = 'or tabItem.description LIKE %(txt)s' - return frappe.db.sql("""select tabItem.name, - if(length(tabItem.item_name) > 40, - concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name, - tabItem.item_group, + return frappe.db.sql("""select + tabItem.name, tabItem.item_name, tabItem.item_group, if(length(tabItem.description) > 40, \ concat(substr(tabItem.description, 1, 40), "..."), description) as description {columns} From 4a3cef6436287b7d0be69b5e0965c4f989957931 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 9 Nov 2021 14:16:38 +0530 Subject: [PATCH 37/60] fix: update 'current_invoice_end' after processing invoice (#28278) --- erpnext/accounts/doctype/subscription/subscription.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 63b714e68c..1dae87f2a4 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -519,8 +519,6 @@ class Subscription(Document): 2. Change the `Subscription` status to 'Past Due Date' 3. Change the `Subscription` status to 'Cancelled' """ - if getdate() > getdate(self.current_invoice_end) and self.is_prepaid_to_invoice(): - self.update_subscription_period(add_days(self.current_invoice_end, 1)) if not self.is_current_invoice_generated(self.current_invoice_start, self.current_invoice_end) \ and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()): @@ -528,6 +526,9 @@ class Subscription(Document): prorate = frappe.db.get_single_value('Subscription Settings', 'prorate') self.generate_invoice(prorate) + if getdate() > getdate(self.current_invoice_end) and self.is_prepaid_to_invoice(): + self.update_subscription_period(add_days(self.current_invoice_end, 1)) + if self.cancel_at_period_end and getdate() > getdate(self.current_invoice_end): self.cancel_subscription_at_period_end() From 17acb08545a5bab991a5eb2f0f3e720886006bc4 Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Tue, 9 Nov 2021 15:21:51 +0530 Subject: [PATCH 38/60] fix: sum of components in salary register (#28237) * fix: sum of components in salary register * fix: sum of deduction components Co-authored-by: Rucha Mahabal --- .../report/salary_register/salary_register.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/payroll/report/salary_register/salary_register.py b/erpnext/payroll/report/salary_register/salary_register.py index 13ee691d87..78deb22778 100644 --- a/erpnext/payroll/report/salary_register/salary_register.py +++ b/erpnext/payroll/report/salary_register/salary_register.py @@ -134,11 +134,11 @@ def get_ss_earning_map(salary_slips, currency, company_currency): ss_earning_map = {} for d in ss_earnings: - ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, []) + ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0) if currency == company_currency: - ss_earning_map[d.parent][d.salary_component] = flt(d.amount) * flt(d.exchange_rate if d.exchange_rate else 1) + ss_earning_map[d.parent][d.salary_component] += flt(d.amount) * flt(d.exchange_rate if d.exchange_rate else 1) else: - ss_earning_map[d.parent][d.salary_component] = flt(d.amount) + ss_earning_map[d.parent][d.salary_component] += flt(d.amount) return ss_earning_map @@ -149,10 +149,10 @@ def get_ss_ded_map(salary_slips, currency, company_currency): ss_ded_map = {} for d in ss_deductions: - ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, []) + ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0) if currency == company_currency: - ss_ded_map[d.parent][d.salary_component] = flt(d.amount) * flt(d.exchange_rate if d.exchange_rate else 1) + ss_ded_map[d.parent][d.salary_component] += flt(d.amount) * flt(d.exchange_rate if d.exchange_rate else 1) else: - ss_ded_map[d.parent][d.salary_component] = flt(d.amount) + ss_ded_map[d.parent][d.salary_component] += flt(d.amount) return ss_ded_map From da22744e0fd7e19a6536aa653ebd78a83fafbac1 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Tue, 9 Nov 2021 15:49:32 +0530 Subject: [PATCH 39/60] fix: specify fields to be set in Lead (#28288) --- erpnext/shopping_cart/cart.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index a7d90ea201..ebbe233ca3 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -194,7 +194,9 @@ def add_new_address(doc): def create_lead_for_item_inquiry(lead, subject, message): lead = frappe.parse_json(lead) lead_doc = frappe.new_doc('Lead') - lead_doc.update(lead) + for fieldname in ("lead_name", "company_name", "email_id", "phone"): + lead_doc.set(fieldname, lead.get(fieldname)) + lead_doc.set('lead_owner', '') if not frappe.db.exists('Lead Source', 'Product Inquiry'): @@ -202,6 +204,7 @@ def create_lead_for_item_inquiry(lead, subject, message): 'doctype': 'Lead Source', 'source_name' : 'Product Inquiry' }).insert(ignore_permissions=True) + lead_doc.set('source', 'Product Inquiry') try: From 88b5bda34b80fb83e71f94a984184304e5b4c853 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 9 Nov 2021 16:04:52 +0530 Subject: [PATCH 40/60] fix(India setup): setup company independent fixtures for patch --- erpnext/regional/india/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 42ee27171f..316bb6b197 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -12,7 +12,7 @@ from frappe.utils import today def setup(company=None, patch=True): # Company independent fixtures should be called only once at the first company setup - if frappe.db.count('Company', {'country': 'India'}) <=1: + if patch or frappe.db.count('Company', {'country': 'India'}) <=1: setup_company_independent_fixtures(patch=patch) if not patch: From 6907ad8adb2e15239598df8908eec16b56e106f1 Mon Sep 17 00:00:00 2001 From: Wolfram Schmidt Date: Tue, 9 Nov 2021 13:02:57 +0100 Subject: [PATCH 41/60] fix: add Email option to contact email field (#28296) * Update warranty_claim.json Added the Email in option field of Contact Email so you are able to create a notification mapping to this field as reciever. * Update warranty_claim.json --- erpnext/support/doctype/warranty_claim/warranty_claim.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.json b/erpnext/support/doctype/warranty_claim/warranty_claim.json index 88ee4a3beb..45485ca2c2 100644 --- a/erpnext/support/doctype/warranty_claim/warranty_claim.json +++ b/erpnext/support/doctype/warranty_claim/warranty_claim.json @@ -256,6 +256,7 @@ "fieldname": "contact_email", "fieldtype": "Data", "label": "Contact Email", + "options": "Email", "read_only": 1 }, { @@ -361,7 +362,7 @@ ], "icon": "fa fa-bug", "idx": 1, - "modified": "2020-09-18 17:26:09.703215", + "modified": "2021-11-09 17:26:09.703215", "modified_by": "Administrator", "module": "Support", "name": "Warranty Claim", @@ -385,4 +386,4 @@ "sort_order": "DESC", "timeline_field": "customer", "title_field": "customer_name" -} \ No newline at end of file +} From 37799fe3dd5beed8b05ab0ea83139f3e5f200179 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 3 Nov 2021 16:33:28 +0530 Subject: [PATCH 42/60] fix: use completion qty instead of transfer quantity for JC status --- erpnext/manufacturing/doctype/job_card/job_card.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index f0f18cf7dd..5eea032be5 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -504,13 +504,11 @@ class JobCard(Document): self.status = 'Work In Progress' if (self.docstatus == 1 and - (self.for_quantity <= self.transferred_qty or not self.items)): - # consider excess transfer - # completed qty is checked via separate validation + (self.for_quantity <= self.total_completed_qty or not self.items)): self.status = 'Completed' if self.status != 'Completed': - if self.for_quantity == self.transferred_qty: + if self.for_quantity <= self.transferred_qty: self.status = 'Material Transferred' if update_status: From fdfa39c231ed3648d6e8b7cf0cda1934062b49da Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 3 Nov 2021 12:48:57 +0530 Subject: [PATCH 43/60] fix: avoid mutating iterator while iterating over it --- 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 fa2436bd27..46c9576a9c 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1450,7 +1450,7 @@ class StockEntry(StockController): item_dict[item]["qty"] = 0 # delete items with 0 qty - list_of_items = item_dict.keys() + list_of_items = list(item_dict.keys()) for item in list_of_items: if not item_dict[item]["qty"]: del item_dict[item] From ccf84ae88a752d94d459e369fa5c1afaab9e49f1 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 7 Nov 2021 18:06:37 +0530 Subject: [PATCH 44/60] fix: patch to update job card status --- erpnext/patches.txt | 1 + .../patches/v13_0/update_job_card_status.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 erpnext/patches/v13_0/update_job_card_status.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1006e2180f..778cbdf65b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -303,6 +303,7 @@ erpnext.patches.v13_0.set_status_in_maintenance_schedule_table erpnext.patches.v13_0.add_default_interview_notification_templates erpnext.patches.v13_0.enable_scheduler_job_for_item_reposting erpnext.patches.v13_0.requeue_failed_reposts +erpnext.patches.v13_0.update_job_card_status erpnext.patches.v12_0.update_production_plan_status erpnext.patches.v13_0.healthcare_deprecation_warning erpnext.patches.v14_0.delete_healthcare_doctypes diff --git a/erpnext/patches/v13_0/update_job_card_status.py b/erpnext/patches/v13_0/update_job_card_status.py new file mode 100644 index 0000000000..797a3e2ae3 --- /dev/null +++ b/erpnext/patches/v13_0/update_job_card_status.py @@ -0,0 +1,18 @@ +# Copyright (c) 2021, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe + + +def execute(): + + job_card = frappe.qb.DocType("Job Card") + (frappe.qb + .update(job_card) + .set(job_card.status, "Completed") + .where( + (job_card.docstatus == 1) + & (job_card.for_quantity <= job_card.total_completed_qty) + & (job_card.status.isin(["Work In Progress", "Material Transferred"])) + ) + ).run() From 6954dd6329f2af0db88413fe933c5e1a111bcb9b Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Tue, 9 Nov 2021 19:36:27 +0530 Subject: [PATCH 45/60] feat: Competitor Tagging in Opportunity and Quotation (#28050) * feat: Competitor Tagging in Opportunity and Quotation * fix: review changes * fix: linter issue * fix: section label Co-authored-by: Rucha Mahabal --- erpnext/crm/doctype/competitor/__init__.py | 0 erpnext/crm/doctype/competitor/competitor.js | 8 +++ .../crm/doctype/competitor/competitor.json | 68 +++++++++++++++++++ erpnext/crm/doctype/competitor/competitor.py | 9 +++ .../crm/doctype/competitor/test_competitor.py | 9 +++ .../crm/doctype/competitor_detail/__init__.py | 0 .../competitor_detail/competitor_detail.json | 33 +++++++++ .../competitor_detail/competitor_detail.py | 9 +++ .../crm/doctype/opportunity/opportunity.json | 31 +++++++-- .../crm/doctype/opportunity/opportunity.py | 10 ++- .../selling/doctype/quotation/quotation.json | 15 +++- .../selling/doctype/quotation/quotation.py | 5 +- erpnext/selling/sales_common.js | 18 +++-- 13 files changed, 197 insertions(+), 18 deletions(-) create mode 100644 erpnext/crm/doctype/competitor/__init__.py create mode 100644 erpnext/crm/doctype/competitor/competitor.js create mode 100644 erpnext/crm/doctype/competitor/competitor.json create mode 100644 erpnext/crm/doctype/competitor/competitor.py create mode 100644 erpnext/crm/doctype/competitor/test_competitor.py create mode 100644 erpnext/crm/doctype/competitor_detail/__init__.py create mode 100644 erpnext/crm/doctype/competitor_detail/competitor_detail.json create mode 100644 erpnext/crm/doctype/competitor_detail/competitor_detail.py diff --git a/erpnext/crm/doctype/competitor/__init__.py b/erpnext/crm/doctype/competitor/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/crm/doctype/competitor/competitor.js b/erpnext/crm/doctype/competitor/competitor.js new file mode 100644 index 0000000000..a5b617dc74 --- /dev/null +++ b/erpnext/crm/doctype/competitor/competitor.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Competitor', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/crm/doctype/competitor/competitor.json b/erpnext/crm/doctype/competitor/competitor.json new file mode 100644 index 0000000000..280441f16f --- /dev/null +++ b/erpnext/crm/doctype/competitor/competitor.json @@ -0,0 +1,68 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:competitor_name", + "creation": "2021-10-21 10:28:52.071316", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "competitor_name", + "website" + ], + "fields": [ + { + "fieldname": "competitor_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Competitor Name", + "reqd": 1, + "unique": 1 + }, + { + "allow_in_quick_entry": 1, + "fieldname": "website", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Website", + "options": "URL" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-10-21 12:43:59.106807", + "modified_by": "Administrator", + "module": "CRM", + "name": "Competitor", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/crm/doctype/competitor/competitor.py b/erpnext/crm/doctype/competitor/competitor.py new file mode 100644 index 0000000000..a292e46104 --- /dev/null +++ b/erpnext/crm/doctype/competitor/competitor.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class Competitor(Document): + pass diff --git a/erpnext/crm/doctype/competitor/test_competitor.py b/erpnext/crm/doctype/competitor/test_competitor.py new file mode 100644 index 0000000000..f77d7e658d --- /dev/null +++ b/erpnext/crm/doctype/competitor/test_competitor.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + + +class TestCompetitor(unittest.TestCase): + pass diff --git a/erpnext/crm/doctype/competitor_detail/__init__.py b/erpnext/crm/doctype/competitor_detail/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/crm/doctype/competitor_detail/competitor_detail.json b/erpnext/crm/doctype/competitor_detail/competitor_detail.json new file mode 100644 index 0000000000..9512b22a3f --- /dev/null +++ b/erpnext/crm/doctype/competitor_detail/competitor_detail.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2021-10-21 10:34:58.841689", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "competitor" + ], + "fields": [ + { + "fieldname": "competitor", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Competitor", + "options": "Competitor", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-10-21 10:34:58.841689", + "modified_by": "Administrator", + "module": "CRM", + "name": "Competitor Detail", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/crm/doctype/competitor_detail/competitor_detail.py b/erpnext/crm/doctype/competitor_detail/competitor_detail.py new file mode 100644 index 0000000000..0ef75605b7 --- /dev/null +++ b/erpnext/crm/doctype/competitor_detail/competitor_detail.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class CompetitorDetail(Document): + pass diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index dc886b51b4..feb6044d64 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -23,7 +23,6 @@ "status", "converted_by", "sales_stage", - "order_lost_reason", "first_response_time", "expected_closing", "next_contact", @@ -64,7 +63,11 @@ "transaction_date", "language", "amended_from", - "lost_reasons" + "lost_detail_section", + "lost_reasons", + "order_lost_reason", + "column_break_56", + "competitors" ], "fields": [ { @@ -154,10 +157,9 @@ "reqd": 1 }, { - "depends_on": "eval:doc.status===\"Lost\"", "fieldname": "order_lost_reason", "fieldtype": "Small Text", - "label": "Lost Reason", + "label": "Detailed Reason", "no_copy": 1, "read_only": 1 }, @@ -409,6 +411,7 @@ "width": "150px" }, { + "depends_on": "eval:doc.status===\"Lost\"", "fieldname": "lost_reasons", "fieldtype": "Table MultiSelect", "label": "Lost Reasons", @@ -486,15 +489,33 @@ "label": "Grand Total", "options": "currency", "read_only": 1 + }, + { + "fieldname": "lost_detail_section", + "fieldtype": "Section Break", + "label": "Lost Reasons" + }, + { + "fieldname": "column_break_56", + "fieldtype": "Column Break" + }, + { + "fieldname": "competitors", + "fieldtype": "Table MultiSelect", + "label": "Competitors", + "options": "Competitor Detail", + "read_only": 1 } ], "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2021-09-06 10:02:18.609136", + "migration_hash": "d87c646ea2579b6900197fd41e6c5c5a", + "modified": "2021-10-21 11:04:30.151379", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 90eae8dcb9..0bef80a749 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -116,16 +116,20 @@ class Opportunity(TransactionBase): self.party_name = lead_name @frappe.whitelist() - def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None): + def declare_enquiry_lost(self, lost_reasons_list, competitors, detailed_reason=None): if not self.has_active_quotation(): - frappe.db.set(self, 'status', 'Lost') + self.status = 'Lost' + self.lost_reasons = self.competitors = [] if detailed_reason: - frappe.db.set(self, 'order_lost_reason', detailed_reason) + self.order_lost_reason = detailed_reason for reason in lost_reasons_list: self.append('lost_reasons', reason) + for competitor in competitors: + self.append('competitors', competitor) + self.save() else: diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 43a44900fc..ad788e5c8b 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -110,7 +110,8 @@ "enq_det", "supplier_quotation", "opportunity", - "lost_reasons" + "lost_reasons", + "competitors" ], "fields": [ { @@ -946,6 +947,14 @@ "label": "Bundle Items", "options": "fa fa-suitcase", "print_hide": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "competitors", + "fieldtype": "Table MultiSelect", + "label": "Competitors", + "options": "Competitor Detail", + "read_only": 1 } ], "icon": "fa fa-shopping-cart", @@ -953,10 +962,12 @@ "is_submittable": 1, "links": [], "max_attachments": 1, - "modified": "2021-08-27 20:10:07.864951", + "migration_hash": "75a86a19f062c2257bcbc8e6e31c7f1e", + "modified": "2021-10-21 12:58:55.514512", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 31b62d6da5..c4752aebb5 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -68,7 +68,7 @@ class Quotation(SellingController): opp.set_status(status=status, update=True) @frappe.whitelist() - def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None): + def declare_enquiry_lost(self, lost_reasons_list, competitors, detailed_reason=None): if not self.has_sales_order(): get_lost_reasons = frappe.get_list('Quotation Lost Reason', fields = ["name"]) @@ -84,6 +84,9 @@ class Quotation(SellingController): else: frappe.throw(_("Invalid lost reason {0}, please create a new lost reason").format(frappe.bold(reason.get('lost_reason')))) + for competitor in competitors: + self.append('competitors', competitor) + self.update_opportunity('Lost') self.update_lead() self.save() diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index a86e60494e..20504789aa 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -473,6 +473,12 @@ frappe.ui.form.on(cur_frm.doctype, { "options": frm.doctype === 'Opportunity' ? 'Opportunity Lost Reason Detail': 'Quotation Lost Reason Detail', "reqd": 1 }, + { + "fieldtype": "Table MultiSelect", + "label": __("Competitors"), + "fieldname": "competitors", + "options": "Competitor Detail" + }, { "fieldtype": "Text", "label": __("Detailed Reason"), @@ -480,27 +486,25 @@ frappe.ui.form.on(cur_frm.doctype, { }, ], primary_action: function() { - var values = dialog.get_values(); - var reasons = values["lost_reason"]; - var detailed_reason = values["detailed_reason"]; + let values = dialog.get_values(); frm.call({ doc: frm.doc, method: 'declare_enquiry_lost', args: { - 'lost_reasons_list': reasons, - 'detailed_reason': detailed_reason + 'lost_reasons_list': values.lost_reason, + 'competitors': values.competitors, + 'detailed_reason': values.detailed_reason }, callback: function(r) { dialog.hide(); frm.reload_doc(); }, }); - refresh_field("lost_reason"); }, primary_action_label: __('Declare Lost') }); dialog.show(); } -}) +}) \ No newline at end of file From 4d4bfa2333f78b666fc3e6d8821a56d165bfb047 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 10 Nov 2021 00:00:38 +0530 Subject: [PATCH 46/60] fix: commision rate not fetch from sales person --- erpnext/accounts/party.py | 3 +- .../doctype/sales_team/sales_team.json | 321 +++++------------- 2 files changed, 89 insertions(+), 235 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 7ea6ccee5b..85aa9f5080 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -85,7 +85,8 @@ def _get_party_details(party=None, account=None, party_type="Customer", company= if party_type=="Customer": party_details["sales_team"] = [{ "sales_person": d.sales_person, - "allocated_percentage": d.allocated_percentage or None + "allocated_percentage": d.allocated_percentage or None, + "commission_rate": d.commission_rate } for d in party.get("sales_team")] # supplier tax withholding category diff --git a/erpnext/selling/doctype/sales_team/sales_team.json b/erpnext/selling/doctype/sales_team/sales_team.json index 876789135c..cac5b763ff 100644 --- a/erpnext/selling/doctype/sales_team/sales_team.json +++ b/erpnext/selling/doctype/sales_team/sales_team.json @@ -1,247 +1,100 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-04-19 13:30:51", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2013-04-19 13:30:51", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "sales_person", + "contact_no", + "allocated_percentage", + "allocated_amount", + "commission_rate", + "incentives" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_person", - "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": "Sales Person", - "length": 0, - "no_copy": 0, - "oldfieldname": "sales_person", - "oldfieldtype": "Link", - "options": "Sales Person", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "200px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "allow_on_submit": 1, + "fieldname": "sales_person", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Sales Person", + "oldfieldname": "sales_person", + "oldfieldtype": "Link", + "options": "Sales Person", + "print_width": "200px", + "reqd": 1, + "search_index": 1, "width": "200px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_no", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Contact No.", - "length": 0, - "no_copy": 0, - "oldfieldname": "contact_no", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "100px", - "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": "contact_no", + "fieldtype": "Data", + "hidden": 1, + "in_list_view": 1, + "label": "Contact No.", + "oldfieldname": "contact_no", + "oldfieldtype": "Data", + "print_width": "100px", "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "allocated_percentage", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Contribution (%)", - "length": 0, - "no_copy": 0, - "oldfieldname": "allocated_percentage", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "100px", - "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": "allocated_percentage", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Contribution (%)", + "oldfieldname": "allocated_percentage", + "oldfieldtype": "Currency", + "print_width": "100px", "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "allocated_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Contribution to Net Total", - "length": 0, - "no_copy": 0, - "oldfieldname": "allocated_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "120px", - "read_only": 1, - "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": "allocated_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Contribution to Net Total", + "oldfieldname": "allocated_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_width": "120px", + "read_only": 1, "width": "120px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "commission_rate", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Commission Rate", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "sales_person.commission_rate", + "fetch_if_empty": 1, + "fieldname": "commission_rate", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Commission Rate", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "incentives", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Incentives", - "length": 0, - "no_copy": 0, - "oldfieldname": "incentives", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "allow_on_submit": 1, + "fieldname": "incentives", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Incentives", + "oldfieldname": "incentives", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-09-17 13:03:14.755974", - "modified_by": "Administrator", - "module": "Selling", - "name": "Sales Team", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} + ], + "idx": 1, + "istable": 1, + "links": [], + "modified": "2021-11-09 23:55:20.670475", + "modified_by": "Administrator", + "module": "Selling", + "name": "Sales Team", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file From e498389b00a2edadff47454e2cc55465d5d1bd52 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 10 Nov 2021 12:02:07 +0530 Subject: [PATCH 47/60] fix: ignore cancelled entries in incorrect balance qty report --- .../incorrect_balance_qty_after_transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py index a381820ca2..6aa12ac2c9 100644 --- a/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py +++ b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py @@ -46,7 +46,7 @@ def get_incorrect_data(data): return row def get_stock_ledger_entries(report_filters): - filters = {} + filters = {"is_cancelled": 0} fields = ['name', 'voucher_type', 'voucher_no', 'item_code', 'actual_qty', 'posting_date', 'posting_time', 'company', 'warehouse', 'qty_after_transaction', 'batch_no'] From 2f004693c36f3dc34c2ef96889fb17e28f2bd385 Mon Sep 17 00:00:00 2001 From: Anuja Pawar <60467153+Anuja-pawar@users.noreply.github.com> Date: Wed, 10 Nov 2021 12:36:54 +0530 Subject: [PATCH 48/60] fix(Bank Reconciliation): get credit amount for bank account of type liability --- .../bank_reconciliation_tool.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py index 002e312a39..5cbf00b2c6 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -342,7 +342,15 @@ def get_pe_matching_query(amount_condition, account_from_to, transaction): def get_je_matching_query(amount_condition, transaction): # get matching journal entry query - cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit" + + company_account = frappe.get_value("Bank Account", transaction.bank_account, "account") + root_type = frappe.get_value("Account", company_account, "root_type") + + if root_type == "Liability": + cr_or_dr = "debit" if transaction.withdrawal > 0 else "credit" + else: + cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit" + return f""" SELECT From 12e81df2b79a0c7fb7cedb3e467837b60540be04 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 10 Nov 2021 12:36:00 +0530 Subject: [PATCH 49/60] fix: default value for allow neg stock in repost_item_valuation Negative stock can be toggled back after queuing transactions, this causes failure when repost is executed. Now allow_negative_stock stock is set at time of queuing the repost job. This means setting changes done afterwards won't affect already submitted reposts. --- .../doctype/repost_item_valuation/repost_item_valuation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index 86b4b860f8..170aa7f76c 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -31,6 +31,9 @@ class RepostItemValuation(Document): self.voucher_type = None self.voucher_no = None + self.allow_negative_stock = self.allow_negative_stock or \ + cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) + def set_company(self): if self.voucher_type and self.voucher_no: self.company = frappe.get_cached_value(self.voucher_type, self.voucher_no, "company") From 6d05bb527432c7fb70794634c7c51692b25bdbfe Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 10 Nov 2021 13:06:55 +0530 Subject: [PATCH 50/60] fix(pos): get mode of payments query (#28321) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index cd270f505e..59d46fc2e8 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -2029,7 +2029,7 @@ def get_mode_of_payments_info(mode_of_payments, company): mpa.parent = mp.name and mpa.company = %s and mp.enabled = 1 and - mp.name in (%s) + mp.name in %s group by mp.name """, (company, mode_of_payments), as_dict=1) From b728597ef41dc27ddca4531f22109834bb4268ab Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 10 Nov 2021 17:59:07 +0530 Subject: [PATCH 51/60] fix: use hotfix branches for patch tests --- .github/workflows/patch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml index f8abb6c774..97bccf5d56 100644 --- a/.github/workflows/patch.yml +++ b/.github/workflows/patch.yml @@ -93,7 +93,7 @@ jobs: for version in $(seq 12 13) do echo "Updating to v$version" - branch_name="version-$version" + branch_name="version-$version-hotfix" git -C "apps/frappe" fetch --depth 1 upstream $branch_name:$branch_name git -C "apps/erpnext" fetch --depth 1 upstream $branch_name:$branch_name From c96b5492dbe860e40f82497a6972cc1cb29071a5 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 10 Nov 2021 19:04:37 +0530 Subject: [PATCH 52/60] fix: reload doctype forcefully --- erpnext/patches/v13_0/create_pan_field_for_india.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v13_0/create_pan_field_for_india.py b/erpnext/patches/v13_0/create_pan_field_for_india.py index c37651aaa3..6df6e1eb32 100644 --- a/erpnext/patches/v13_0/create_pan_field_for_india.py +++ b/erpnext/patches/v13_0/create_pan_field_for_india.py @@ -5,6 +5,7 @@ from frappe.custom.doctype.custom_field.custom_field import create_custom_fields def execute(): frappe.reload_doc('buying', 'doctype', 'supplier', force=True) frappe.reload_doc('selling', 'doctype', 'customer', force=True) + frappe.reload_doc('core', 'doctype', 'doctype', force=True) custom_fields = { 'Supplier': [ From 0dca97eb9f56a9a45b736c42cbf48b4a3a7ee834 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 10 Nov 2021 20:54:12 +0530 Subject: [PATCH 53/60] fix(India): Sales Invoice with duplicate items not showing correct taxable value --- erpnext/regional/report/gstr_1/gstr_1.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 27cfaf7614..11b684d3f6 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -248,18 +248,17 @@ class Gstr1Report(object): """ % (self.doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1) for d in items: - if d.item_code not in self.invoice_items.get(d.parent, {}): - self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0) - self.invoice_items[d.parent][d.item_code] += d.get('taxable_value', 0) or d.get('base_net_amount', 0) + self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0) + self.invoice_items[d.parent][d.item_code] += d.get('taxable_value', 0) or d.get('base_net_amount', 0) - item_tax_rate = {} + item_tax_rate = {} - if d.item_tax_rate: - item_tax_rate = json.loads(d.item_tax_rate) + if d.item_tax_rate: + item_tax_rate = json.loads(d.item_tax_rate) - for account, rate in item_tax_rate.items(): - tax_rate_dict = self.item_tax_rate.setdefault(d.parent, {}).setdefault(d.item_code, []) - tax_rate_dict.append(rate) + for account, rate in item_tax_rate.items(): + tax_rate_dict = self.item_tax_rate.setdefault(d.parent, {}).setdefault(d.item_code, []) + tax_rate_dict.append(rate) def get_items_based_on_tax_rate(self): self.tax_details = frappe.db.sql(""" From ecbe4b16b8dedacb6a4d5bbdf43fa20570095009 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 11 Nov 2021 13:55:21 +0530 Subject: [PATCH 54/60] perf(minor): general ledger report (#27987) --- .../report/general_ledger/general_ledger.py | 65 ++++++++++++------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 050403d21e..385c8b2b6e 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -6,7 +6,7 @@ from collections import OrderedDict import frappe from frappe import _, _dict -from frappe.utils import cstr, flt, getdate +from frappe.utils import cstr, getdate from erpnext import get_company_currency, get_default_company from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( @@ -17,6 +17,8 @@ from erpnext.accounts.report.financial_statements import get_cost_centers_with_c from erpnext.accounts.report.utils import convert_to_presentation_currency, get_currency from erpnext.accounts.utils import get_account_currency +# to cache translations +TRANSLATIONS = frappe._dict() def execute(filters=None): if not filters: @@ -42,10 +44,20 @@ def execute(filters=None): columns = get_columns(filters) + update_translations() + res = get_result(filters, account_details) return columns, res +def update_translations(): + TRANSLATIONS.update( + dict( + OPENING = _('Opening'), + TOTAL = _('Total'), + CLOSING_TOTAL = _('Closing (Opening + Total)') + ) + ) def validate_filters(filters, account_details): if not filters.get("company"): @@ -351,9 +363,9 @@ def get_totals_dict(): credit_in_account_currency=0.0 ) return _dict( - opening = _get_debit_credit_dict(_('Opening')), - total = _get_debit_credit_dict(_('Total')), - closing = _get_debit_credit_dict(_('Closing (Opening + Total)')) + opening = _get_debit_credit_dict(TRANSLATIONS.OPENING), + total = _get_debit_credit_dict(TRANSLATIONS.TOTAL), + closing = _get_debit_credit_dict(TRANSLATIONS.CLOSING_TOTAL) ) def group_by_field(group_by): @@ -378,22 +390,23 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map): entries = [] consolidated_gle = OrderedDict() group_by = group_by_field(filters.get('group_by')) + group_by_voucher_consolidated = filters.get("group_by") == 'Group by Voucher (Consolidated)' if filters.get('show_net_values_in_party_account'): account_type_map = get_account_type_map(filters.get('company')) def update_value_in_dict(data, key, gle): - data[key].debit += flt(gle.debit) - data[key].credit += flt(gle.credit) + data[key].debit += gle.debit + data[key].credit += gle.credit - data[key].debit_in_account_currency += flt(gle.debit_in_account_currency) - data[key].credit_in_account_currency += flt(gle.credit_in_account_currency) + data[key].debit_in_account_currency += gle.debit_in_account_currency + data[key].credit_in_account_currency += gle.credit_in_account_currency if filters.get('show_net_values_in_party_account') and \ account_type_map.get(data[key].account) in ('Receivable', 'Payable'): - net_value = flt(data[key].debit) - flt(data[key].credit) - net_value_in_account_currency = flt(data[key].debit_in_account_currency) \ - - flt(data[key].credit_in_account_currency) + net_value = data[key].debit - data[key].credit + net_value_in_account_currency = data[key].debit_in_account_currency \ + - data[key].credit_in_account_currency if net_value < 0: dr_or_cr = 'credit' @@ -411,19 +424,29 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map): data[key].against_voucher += ', ' + gle.against_voucher from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) - for gle in gl_entries: - if (gle.posting_date < from_date or - (cstr(gle.is_opening) == "Yes" and not filters.get("show_opening_entries"))): - update_value_in_dict(gle_map[gle.get(group_by)].totals, 'opening', gle) - update_value_in_dict(totals, 'opening', gle) + show_opening_entries = filters.get("show_opening_entries") - update_value_in_dict(gle_map[gle.get(group_by)].totals, 'closing', gle) + for gle in gl_entries: + group_by_value = gle.get(group_by) + + if (gle.posting_date < from_date or (cstr(gle.is_opening) == "Yes" and not show_opening_entries)): + if not group_by_voucher_consolidated: + update_value_in_dict(gle_map[group_by_value].totals, 'opening', gle) + update_value_in_dict(gle_map[group_by_value].totals, 'closing', gle) + + update_value_in_dict(totals, 'opening', gle) update_value_in_dict(totals, 'closing', gle) elif gle.posting_date <= to_date: - if filters.get("group_by") != 'Group by Voucher (Consolidated)': - gle_map[gle.get(group_by)].entries.append(gle) - elif filters.get("group_by") == 'Group by Voucher (Consolidated)': + if not group_by_voucher_consolidated: + update_value_in_dict(gle_map[group_by_value].totals, 'total', gle) + update_value_in_dict(gle_map[group_by_value].totals, 'closing', gle) + update_value_in_dict(totals, 'total', gle) + update_value_in_dict(totals, 'closing', gle) + + gle_map[group_by_value].entries.append(gle) + + elif group_by_voucher_consolidated: keylist = [gle.get("voucher_type"), gle.get("voucher_no"), gle.get("account")] for dim in accounting_dimensions: keylist.append(gle.get(dim)) @@ -435,9 +458,7 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map): update_value_in_dict(consolidated_gle, key, gle) for key, value in consolidated_gle.items(): - update_value_in_dict(gle_map[value.get(group_by)].totals, 'total', value) update_value_in_dict(totals, 'total', value) - update_value_in_dict(gle_map[value.get(group_by)].totals, 'closing', value) update_value_in_dict(totals, 'closing', value) entries.append(value) From 944bf8da71284a4fd72afa4410e421ded63992fa Mon Sep 17 00:00:00 2001 From: Sagar Sharma <63660334+s-aga-r@users.noreply.github.com> Date: Thu, 11 Nov 2021 19:49:41 +0530 Subject: [PATCH 55/60] fix: Unable to edit supplier scorecard criteria name once created (#28348) --- .../supplier_scorecard_criteria.json | 228 +++++------------- 1 file changed, 57 insertions(+), 171 deletions(-) diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json index 2623585aea..3668b2505f 100644 --- a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json +++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json @@ -1,184 +1,70 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:criteria_name", - "beta": 0, - "creation": "2017-05-29 01:32:43.064891", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_rename": 1, + "autoname": "field:criteria_name", + "creation": "2017-05-29 01:32:43.064891", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "criteria_name", + "max_score", + "formula", + "weight" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "criteria_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": "Criteria Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "fieldname": "criteria_name", + "fieldtype": "Data", + "label": "Criteria Name", + "reqd": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "100", - "fieldname": "max_score", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Max Score", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "default": "100", + "fieldname": "max_score", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Max Score", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "formula", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Criteria Formula", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "formula", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Criteria Formula", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "weight", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Criteria Weight", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "weight", + "fieldtype": "Percent", + "label": "Criteria Weight" } - ], - "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": "2019-01-22 10:47:00.000822", - "modified_by": "Administrator", - "module": "Buying", - "name": "Supplier Scorecard Criteria", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2021-11-11 18:34:58.477648", + "modified_by": "Administrator", + "module": "Buying", + "name": "Supplier Scorecard Criteria", + "naming_rule": "By fieldname", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System 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 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file From 88648570d78ebba2894357af6873adb4fc55f728 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 12 Nov 2021 12:39:30 +0530 Subject: [PATCH 56/60] fix: Default party account getting overriden in invoices --- .../doctype/purchase_invoice/purchase_invoice.js | 13 +++++++++++-- .../accounts/doctype/sales_invoice/sales_invoice.js | 13 +++++++++++-- erpnext/accounts/party.py | 9 ++++++--- erpnext/controllers/accounts_controller.py | 4 ++-- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 3526e488e1..1a398aba2e 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -592,8 +592,17 @@ frappe.ui.form.on("Purchase Invoice", { erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); if (frm.doc.company) { - frappe.db.get_value('Company', frm.doc.company, 'default_payable_account', (r) => { - frm.set_value('credit_to', r.default_payable_account); + frappe.call({ + method: + "erpnext.accounts.party.get_party_account", + args: { + party_type: 'Supplier', + party: frm.doc.supplier, + company: frm.doc.company + }, + callback: (response) => { + if (response) frm.set_value("credit_to", response.message); + }, }); } }, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index bee153b7b8..a1f3ee4b06 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -15,8 +15,17 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e let me = this; if (this.frm.doc.company) { - frappe.db.get_value('Company', this.frm.doc.company, 'default_receivable_account', (r) => { - me.frm.set_value('debit_to', r.default_receivable_account); + frappe.call({ + method: + "erpnext.accounts.party.get_party_account", + args: { + party_type: 'Customer', + party: this.frm.doc.customer, + company: this.frm.doc.company + }, + callback: (response) => { + if (response) me.frm.set_value("debit_to", response.message); + }, }); } } diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 2108bc158d..1c9d19d8dc 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -218,7 +218,7 @@ def set_account_and_due_date(party, account, party_type, company, posting_date, return out @frappe.whitelist() -def get_party_account(party_type, party, company=None): +def get_party_account(party_type, party=None, company=None): """Returns the account for the given `party`. Will first search in party (Customer / Supplier) record, if not found, will search in group (Customer Group / Supplier Group), @@ -226,8 +226,11 @@ def get_party_account(party_type, party, company=None): if not company: frappe.throw(_("Please select a Company")) - if not party: - return + if not party and party_type in ['Customer', 'Supplier']: + default_account_name = "default_receivable_account" \ + if party_type=="Customer" else "default_payable_account" + + return frappe.get_cached_value('Company', company, default_account_name) account = frappe.db.get_value("Party Account", {"parenttype": party_type, "parent": party, "company": company}, "account") diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e0551a4ca8..3190feac70 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1402,8 +1402,8 @@ class AccountsController(TransactionBase): grand_total -= self.get("total_advance") base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total")) - if flt(total, self.precision("grand_total")) != flt(grand_total, self.precision("grand_total")) or \ - flt(base_total, self.precision("base_grand_total")) != flt(base_grand_total, self.precision("base_grand_total")): + if flt(total, self.precision("grand_total")) - flt(grand_total, self.precision("grand_total")) > 0.1 or \ + flt(base_total, self.precision("base_grand_total")) - flt(base_grand_total, self.precision("base_grand_total")) > 0.1: frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total")) def is_rounded_total_disabled(self): From a424310581fc7c4306fafd3038e7deaa2c584706 Mon Sep 17 00:00:00 2001 From: Sagar Sharma <63660334+s-aga-r@users.noreply.github.com> Date: Fri, 12 Nov 2021 13:50:30 +0530 Subject: [PATCH 57/60] fix: Collapse Scrap Items in Job Card (#28362) --- erpnext/manufacturing/doctype/job_card/job_card.js | 6 ++++++ erpnext/manufacturing/doctype/job_card/job_card.json | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index e3eed92d7e..f9259fbdf4 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -23,6 +23,12 @@ frappe.ui.form.on('Job Card', { ); }, + onload: function(frm) { + if (frm.doc.scrap_items.length == 0) { + frm.fields_dict['scrap_items_section'].collapse(); + } + }, + refresh: function(frm) { frappe.flags.pause_job = 0; frappe.flags.resume_job = 0; diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json index 7dd38f4673..6528199d82 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.json +++ b/erpnext/manufacturing/doctype/job_card/job_card.json @@ -396,6 +396,7 @@ "options": "Batch" }, { + "collapsible": 1, "fieldname": "scrap_items_section", "fieldtype": "Section Break", "label": "Scrap Items" @@ -411,7 +412,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2021-09-14 00:38:46.873105", + "modified": "2021-11-12 10:15:03.572401", "modified_by": "Administrator", "module": "Manufacturing", "name": "Job Card", From 24b048925bb758cad3cb0fa59a571165320132f8 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 12 Nov 2021 14:08:02 +0530 Subject: [PATCH 58/60] fix(WooCommerce): always expect signature in webhook requests (#28367) --- .../erpnext_integrations/connectors/woocommerce_connection.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index f5f9ce3b16..9409485a9f 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -19,8 +19,7 @@ def verify_request(): ) if frappe.request.data and \ - frappe.get_request_header("X-Wc-Webhook-Signature") and \ - not sig == bytes(frappe.get_request_header("X-Wc-Webhook-Signature").encode()): + not sig == frappe.get_request_header("X-Wc-Webhook-Signature", "").encode(): frappe.throw(_("Unverified Webhook Data")) frappe.set_user(woocommerce_settings.creation_user) From 6d3e9bce5f6bcecd6f4eec555cbdffd8f1df62e3 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 12 Nov 2021 14:24:33 +0530 Subject: [PATCH 59/60] fix(M-Pesa): validate type before executing `get_doc` (#28369) --- .../doctype/mpesa_settings/mpesa_settings.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py index d2b6190a29..e7b4a30e0a 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py @@ -141,6 +141,9 @@ def verify_transaction(**kwargs): transaction_response = frappe._dict(kwargs["Body"]["stkCallback"]) checkout_id = getattr(transaction_response, "CheckoutRequestID", "") + if not isinstance(checkout_id, str): + frappe.throw(_("Invalid Checkout Request ID")) + integration_request = frappe.get_doc("Integration Request", checkout_id) transaction_data = frappe._dict(loads(integration_request.data)) total_paid = 0 # for multiple integration request made against a pos invoice @@ -231,6 +234,9 @@ def process_balance_info(**kwargs): account_balance_response = frappe._dict(kwargs["Result"]) conversation_id = getattr(account_balance_response, "ConversationID", "") + if not isinstance(conversation_id, str): + frappe.throw(_("Invalid Conversation ID")) + request = frappe.get_doc("Integration Request", conversation_id) if request.status == "Completed": From c0f06bc8e382a0543a476f119989f7422a21add0 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 12 Nov 2021 14:45:51 +0530 Subject: [PATCH 60/60] fix: validate hmac unconditionally (#28372) --- erpnext/erpnext_integrations/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py index b52c3fc2a8..d922d875fd 100644 --- a/erpnext/erpnext_integrations/utils.py +++ b/erpnext/erpnext_integrations/utils.py @@ -23,7 +23,6 @@ def validate_webhooks_request(doctype, hmac_key, secret_key='secret'): ) if frappe.request.data and \ - frappe.get_request_header(hmac_key) and \ not sig == bytes(frappe.get_request_header(hmac_key).encode()): frappe.throw(_("Unverified Webhook Data")) frappe.set_user(settings.modified_by)