From acb2eec7edc189c97eba87380175ea308f1fc956 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 29 Nov 2021 15:05:06 +0530 Subject: [PATCH 01/34] fix: incorrect discount amount set when item is replaced (#28556) (cherry picked from commit c0cc72ec1d839db4bb510efb22ed17e2e4033e8a) --- erpnext/stock/get_item_details.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 1bbedc8ae85..5dfabfc6d6d 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -300,7 +300,7 @@ def get_basic_details(args, item, overwrite_warehouse=True): "warehouse": warehouse, "income_account": get_default_income_account(args, item_defaults, item_group_defaults, brand_defaults), "expense_account": expense_account or get_default_expense_account(args, item_defaults, item_group_defaults, brand_defaults) , - "discount_account": None or get_default_discount_account(args, item_defaults), + "discount_account": get_default_discount_account(args, item_defaults), "cost_center": get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults), 'has_serial_no': item.has_serial_no, 'has_batch_no': item.has_batch_no, @@ -318,6 +318,7 @@ def get_basic_details(args, item, overwrite_warehouse=True): "net_rate": 0.0, "net_amount": 0.0, "discount_percentage": 0.0, + "discount_amount": 0.0, "supplier": get_default_supplier(args, item_defaults, item_group_defaults, brand_defaults), "update_stock": args.get("update_stock") if args.get('doctype') in ['Sales Invoice', 'Purchase Invoice'] else 0, "delivered_by_supplier": item.delivered_by_supplier if args.get("doctype") in ["Sales Order", "Sales Invoice"] else 0, From 35f41592743d93ef352038287d22c812e0104270 Mon Sep 17 00:00:00 2001 From: Ahmad Date: Wed, 24 Nov 2021 02:54:43 +0100 Subject: [PATCH 02/34] refactor(KSA VAT): QR Code as per ZATKA specification (cherry picked from commit d1746caa02c84d22793b9a21c49582f372eca1a9) --- erpnext/regional/saudi_arabia/utils.py | 85 ++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/erpnext/regional/saudi_arabia/utils.py b/erpnext/regional/saudi_arabia/utils.py index a2f634ee22c..558c7ee1d79 100644 --- a/erpnext/regional/saudi_arabia/utils.py +++ b/erpnext/regional/saudi_arabia/utils.py @@ -1,7 +1,10 @@ import io import os +from base64 import b64encode import frappe +from frappe import _ +from frappe.utils.data import add_to_date, get_time, getdate from pyqrcode import create as qr_create from erpnext import get_region @@ -28,24 +31,80 @@ def create_qr_code(doc, method): for field in meta.get_image_fields(): if field.fieldname == 'qr_code': - from urllib.parse import urlencode + ''' TLV conversion for + 1. Seller's Name + 2. VAT Number + 3. Time Stamp + 4. Invoice Amount + 5. VAT Amount + ''' + tlv_array = [] + # Sellers Name - # Creating public url to print format - default_print_format = frappe.db.get_value('Property Setter', dict(property='default_print_format', doc_type=doc.doctype), "value") + '''TODO: Fix arabic conversion''' + # seller_name = frappe.db.get_value( + # 'Company', + # doc.company, + # 'company_name_in_arabic') - # System Language - language = frappe.get_system_settings('language') + # if not seller_name: + # frappe.throw(_('Arabic name missing for {} in the company document'.format(doc.company))) - params = urlencode({ - 'format': default_print_format or 'Standard', - '_lang': language, - 'key': doc.get_signature() - }) + seller_name = doc.company + tag = bytes([1]).hex() + length = bytes([len(seller_name)]).hex() + value = seller_name.encode('utf-8').hex() + tlv_array.append(''.join([tag, length, value])) + + # VAT Number + tax_id = frappe.db.get_value('Company', doc.company, 'tax_id') + if not tax_id: + frappe.throw(_('Tax ID missing for {} in the company document'.format(doc.company))) + + # tax_id = '310122393500003' # + tag = bytes([2]).hex() + length = bytes([len(tax_id)]).hex() + value = tax_id.encode('utf-8').hex() + tlv_array.append(''.join([tag, length, value])) + + # Time Stamp + posting_date = getdate(doc.posting_date) + time = get_time(doc.posting_time) + seconds = time.hour * 60 * 60 + time.minute * 60 + time.second + time_stamp = add_to_date(posting_date, seconds=seconds) + time_stamp = time_stamp.strftime('%Y-%m-%dT%H:%M:%SZ') + + # time_stamp = '2022-04-25T15:30:00Z' # + tag = bytes([3]).hex() + length = bytes([len(time_stamp)]).hex() + value = time_stamp.encode('utf-8').hex() + tlv_array.append(''.join([tag, length, value])) + + # Invoice Amount + invoice_amount = str(doc.total) + # invoice_amount = '1000.00' # + tag = bytes([4]).hex() + length = bytes([len(invoice_amount)]).hex() + value = invoice_amount.encode('utf-8').hex() + tlv_array.append(''.join([tag, length, value])) + + # VAT Amount + vat_amount = str(doc.total_taxes_and_charges) + + # vat_amount = '150.00' # + tag = bytes([5]).hex() + length = bytes([len(vat_amount)]).hex() + value = vat_amount.encode('utf-8').hex() + tlv_array.append(''.join([tag, length, value])) + + # Joining bytes into one + tlv_buff = ''.join(tlv_array) + + # base64 conversion for QR Code + base64_string = b64encode(bytes.fromhex(tlv_buff)).decode() - # creating qr code for the url - url = f"{ frappe.utils.get_url() }/{ doc.doctype }/{ doc.name }?{ params }" qr_image = io.BytesIO() - url = qr_create(url, error='L') + url = qr_create(base64_string, error='L') url.png(qr_image, scale=2, quiet_zone=1) # making file From ca0a6c04fb43e42efeba09e92cf841ebc8d6d018 Mon Sep 17 00:00:00 2001 From: Ahmad Date: Wed, 24 Nov 2021 03:03:17 +0100 Subject: [PATCH 03/34] refactor: comments removed (cherry picked from commit de784d8bfe31008adf735ec065627ea4aea2cbd8) --- erpnext/regional/saudi_arabia/utils.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/regional/saudi_arabia/utils.py b/erpnext/regional/saudi_arabia/utils.py index 558c7ee1d79..c0f4e31b236 100644 --- a/erpnext/regional/saudi_arabia/utils.py +++ b/erpnext/regional/saudi_arabia/utils.py @@ -61,7 +61,6 @@ def create_qr_code(doc, method): if not tax_id: frappe.throw(_('Tax ID missing for {} in the company document'.format(doc.company))) - # tax_id = '310122393500003' # tag = bytes([2]).hex() length = bytes([len(tax_id)]).hex() value = tax_id.encode('utf-8').hex() @@ -74,7 +73,6 @@ def create_qr_code(doc, method): time_stamp = add_to_date(posting_date, seconds=seconds) time_stamp = time_stamp.strftime('%Y-%m-%dT%H:%M:%SZ') - # time_stamp = '2022-04-25T15:30:00Z' # tag = bytes([3]).hex() length = bytes([len(time_stamp)]).hex() value = time_stamp.encode('utf-8').hex() @@ -82,7 +80,6 @@ def create_qr_code(doc, method): # Invoice Amount invoice_amount = str(doc.total) - # invoice_amount = '1000.00' # tag = bytes([4]).hex() length = bytes([len(invoice_amount)]).hex() value = invoice_amount.encode('utf-8').hex() @@ -91,7 +88,6 @@ def create_qr_code(doc, method): # VAT Amount vat_amount = str(doc.total_taxes_and_charges) - # vat_amount = '150.00' # tag = bytes([5]).hex() length = bytes([len(vat_amount)]).hex() value = vat_amount.encode('utf-8').hex() From 7fed860654a7f2700c3c0649614ebd7aed4bda2e Mon Sep 17 00:00:00 2001 From: Ahmad Date: Sat, 27 Nov 2021 14:28:13 +0100 Subject: [PATCH 04/34] fix: KSA VAT QR Code arabic conversion (cherry picked from commit 31b9b84fdf9dfefc2c629fc208bbe5330df43556) --- erpnext/regional/saudi_arabia/utils.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/erpnext/regional/saudi_arabia/utils.py b/erpnext/regional/saudi_arabia/utils.py index c0f4e31b236..516b87c75d6 100644 --- a/erpnext/regional/saudi_arabia/utils.py +++ b/erpnext/regional/saudi_arabia/utils.py @@ -41,18 +41,16 @@ def create_qr_code(doc, method): tlv_array = [] # Sellers Name - '''TODO: Fix arabic conversion''' - # seller_name = frappe.db.get_value( - # 'Company', - # doc.company, - # 'company_name_in_arabic') + seller_name = frappe.db.get_value( + 'Company', + doc.company, + 'company_name_in_arabic') - # if not seller_name: - # frappe.throw(_('Arabic name missing for {} in the company document'.format(doc.company))) + if not seller_name: + frappe.throw(_('Arabic name missing for {} in the company document'.format(doc.company))) - seller_name = doc.company tag = bytes([1]).hex() - length = bytes([len(seller_name)]).hex() + length = bytes([len(seller_name.encode('utf-8'))]).hex() value = seller_name.encode('utf-8').hex() tlv_array.append(''.join([tag, length, value])) From 63d29b5d01c762649176b74a7e4d619f73202644 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 29 Nov 2021 13:43:56 +0530 Subject: [PATCH 05/34] fix: Translations (cherry picked from commit f3f7ed6f0d7d6e758da5eee77a22f48c6fd9bec3) --- erpnext/regional/saudi_arabia/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/saudi_arabia/utils.py b/erpnext/regional/saudi_arabia/utils.py index 516b87c75d6..1051315cbef 100644 --- a/erpnext/regional/saudi_arabia/utils.py +++ b/erpnext/regional/saudi_arabia/utils.py @@ -47,7 +47,7 @@ def create_qr_code(doc, method): 'company_name_in_arabic') if not seller_name: - frappe.throw(_('Arabic name missing for {} in the company document'.format(doc.company))) + frappe.throw(_('Arabic name missing for {} in the company document').format(doc.company)) tag = bytes([1]).hex() length = bytes([len(seller_name.encode('utf-8'))]).hex() @@ -57,7 +57,7 @@ def create_qr_code(doc, method): # VAT Number tax_id = frappe.db.get_value('Company', doc.company, 'tax_id') if not tax_id: - frappe.throw(_('Tax ID missing for {} in the company document'.format(doc.company))) + frappe.throw(_('Tax ID missing for {} in the company document').format(doc.company)) tag = bytes([2]).hex() length = bytes([len(tax_id)]).hex() From eeaed9d730ba94f727924f069b37dcc6cec53dc5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 29 Nov 2021 16:18:35 +0530 Subject: [PATCH 06/34] fix: KSA print format for invoices not having item codes (cherry picked from commit af6fc2977098d51c990ee8a63fc6315122166be8) --- .../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 681f72fd309..8e9a72897df 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\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 {% set tax_amount = frappe.utils.flt(data_object[item.item_code][1]/doc.conversion_rate, row.precision('tax_amount')) %}\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_value(tax_amount, currency=doc.currency) }}\n {% set total.amount = total.amount + tax_amount %}\n {%- endif -%}\n
\n
{{ frappe.format_value(frappe.utils.flt(total.amount, doc.precision('total_taxes_and_charges')), currency=doc.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 {% set key = item.item_code or item.item_name %}\n {% set tax_amount = frappe.utils.flt(data_object[key][1]/doc.conversion_rate, row.precision('tax_amount')) %}\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 or item.item_name }}{{ item.get_formatted(\"rate\") }}{{ item.qty }}{{ item.get_formatted(\"amount\") }}\n
\n {%- if(data_object[key][0])-%}\n {{ frappe.format(data_object[key][0], {'fieldtype': 'Percent'}) }}\n {%- endif -%}\n \n {%- if(data_object[key][1])-%}\n {{ frappe.format_value(tax_amount, currency=doc.currency) }}\n {% set total.amount = total.amount + tax_amount %}\n {%- endif -%}\n
\n
{{ frappe.format_value(frappe.utils.flt(total.amount, doc.precision('total_taxes_and_charges')), currency=doc.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-11-22 10:40:24.716932", + "modified": "2021-11-29 13:47:37.870818", "modified_by": "Administrator", "module": "Regional", "name": "KSA VAT Invoice", From 1733c1c513806905a10f4e328080cac4dcf2de2b Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 29 Nov 2021 16:38:34 +0530 Subject: [PATCH 07/34] fix: changed fieldtype from int to float for the field Batch Size in the work order (cherry picked from commit 7fac9b8e9ccec5d310c701eb1b25a81f7c662745) --- .../doctype/work_order_operation/work_order_operation.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json index 647c14b33d9..4e1a464cb05 100644 --- a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json +++ b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json @@ -178,8 +178,9 @@ }, { "fieldname": "batch_size", - "fieldtype": "Int", - "label": "Batch Size" + "fieldtype": "Float", + "label": "Batch Size", + "read_only": 1 }, { "fieldname": "sequence_id", @@ -200,7 +201,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-11-24 04:52:54.295168", + "modified": "2021-11-29 16:37:18.824489", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order Operation", From f32ed1bd4136d7d89357ea84b2b251d402751dbf Mon Sep 17 00:00:00 2001 From: Mohammed Redah Date: Fri, 19 Nov 2021 15:13:23 +0300 Subject: [PATCH 08/34] fix:Change QR Code Triggerr event This fixes the bug if the user changes the date after insertion it will show the wrong values (cherry picked from commit 9d319c2205c475f4f886bc0bd7c32721993ba06a) --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 1d4e6a1590b..0d413d5a888 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -256,10 +256,10 @@ doc_events = { "validate": "erpnext.regional.india.utils.validate_tax_category" }, "Sales Invoice": { - "after_insert": "erpnext.regional.saudi_arabia.utils.create_qr_code", "on_submit": [ "erpnext.regional.create_transaction_log", "erpnext.regional.italy.utils.sales_invoice_on_submit", + "erpnext.regional.saudi_arabia.utils.create_qr_code", "erpnext.erpnext_integrations.taxjar_integration.create_transaction" ], "on_cancel": [ From db5930742dbadbd1dd8f6362a79fcc485d2b1c9a Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 29 Nov 2021 18:25:47 +0530 Subject: [PATCH 09/34] fix: Move trigger from on trash to on cancel (cherry picked from commit 4382040fb64e333b33fefb2e97ad96ba3e5d52a3) --- erpnext/hooks.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 0d413d5a888..9309929e99e 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -264,12 +264,10 @@ doc_events = { ], "on_cancel": [ "erpnext.regional.italy.utils.sales_invoice_on_cancel", - "erpnext.erpnext_integrations.taxjar_integration.delete_transaction" - ], - "on_trash": [ - "erpnext.regional.check_deletion_permission", + "erpnext.erpnext_integrations.taxjar_integration.delete_transaction", "erpnext.regional.saudi_arabia.utils.delete_qr_code_file" ], + "on_trash": "erpnext.regional.check_deletion_permission", "validate": [ "erpnext.regional.india.utils.validate_document_name", "erpnext.regional.india.utils.update_taxable_values" From 4b9d1fa0deb1c131eadbdc7f926f30e698be0a8a Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 11 Nov 2021 18:40:38 +0530 Subject: [PATCH 10/34] feat: Add Serial No field (cherry picked from commit 1aed8c4b2fca6000b036773539a75f0c697e83a8) --- .../asset_repair_consumed_item.json | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json index 528f0ec986a..f63add12356 100644 --- a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json +++ b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json @@ -5,19 +5,13 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "item", + "item_code", "valuation_rate", "consumed_quantity", - "total_value" + "total_value", + "serial_no" ], "fields": [ - { - "fieldname": "item", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Item", - "options": "Item" - }, { "fetch_from": "item.valuation_rate", "fieldname": "valuation_rate", @@ -38,12 +32,24 @@ "in_list_view": 1, "label": "Total Value", "read_only": 1 + }, + { + "fieldname": "serial_no", + "fieldtype": "Small Text", + "label": "Serial No" + }, + { + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item", + "options": "Item" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-05-12 03:19:55.006300", + "modified": "2021-11-11 18:23:00.492483", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair Consumed Item", From 246f02d58542d14aecde8afe184ab6ea5677c273 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 11 Nov 2021 18:41:16 +0530 Subject: [PATCH 11/34] fix: Rename item to item_code (cherry picked from commit abb535540a0910bb6193ad0985e28116123e38e7) --- erpnext/assets/doctype/asset_repair/asset_repair.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index d780c18ad02..1697905563b 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -118,7 +118,7 @@ class AssetRepair(AccountsController): for stock_item in self.get('stock_items'): stock_entry.append('items', { "s_warehouse": self.warehouse, - "item_code": stock_item.item, + "item_code": stock_item.item_code, "qty": stock_item.consumed_quantity, "basic_rate": stock_item.valuation_rate }) From ebd89e1c3dfd6ba66ca845f3103bf8acf1bb41d5 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 11 Nov 2021 18:42:25 +0530 Subject: [PATCH 12/34] feat: Add 'Add Serial No' button in the Stock Items table (cherry picked from commit 4668bb4be0320580f10608cdc74d23fc4a775a88) --- erpnext/assets/doctype/asset_repair/asset_repair.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index 18a56d33e6d..d554d52a718 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -60,6 +60,10 @@ frappe.ui.form.on('Asset Repair', { if (frm.doc.repair_status == "Completed") { frm.set_value('completion_date', frappe.datetime.now_datetime()); } + }, + + stock_items_on_form_rendered() { + erpnext.setup_serial_or_batch_no(); } }); From b38819aa3f75e35f5f6b9de1d4f31e22f79e62e1 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 11 Nov 2021 18:48:23 +0530 Subject: [PATCH 13/34] fix: Add serial no to Stock Entry doc to decrease quantity for Stock Items consumed during repair (cherry picked from commit 1393f97ad52902cc57a87d2a700ce3c169a5707c) --- erpnext/assets/doctype/asset_repair/asset_repair.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 1697905563b..36848e9f15c 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -120,7 +120,8 @@ class AssetRepair(AccountsController): "s_warehouse": self.warehouse, "item_code": stock_item.item_code, "qty": stock_item.consumed_quantity, - "basic_rate": stock_item.valuation_rate + "basic_rate": stock_item.valuation_rate, + "serial_no": stock_item.serial_no }) stock_entry.insert() From 7dbf6867d5e581215453954cdf2c85991ec67b33 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 25 Nov 2021 18:53:48 +0530 Subject: [PATCH 14/34] fix: Replace 'item' with 'item_code' in tests (cherry picked from commit c9e79ef1f232ac517277d4c3fb6bf9d8ace58abf) --- erpnext/assets/doctype/asset_repair/test_asset_repair.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index 81b4f6c4499..ed5ae2cb4dc 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -70,7 +70,7 @@ class TestAssetRepair(unittest.TestCase): self.assertEqual(stock_entry.stock_entry_type, "Material Issue") self.assertEqual(stock_entry.items[0].s_warehouse, asset_repair.warehouse) - self.assertEqual(stock_entry.items[0].item_code, asset_repair.stock_items[0].item) + self.assertEqual(stock_entry.items[0].item_code, asset_repair.stock_items[0].item_code) self.assertEqual(stock_entry.items[0].qty, asset_repair.stock_items[0].consumed_quantity) def test_increase_in_asset_value_due_to_stock_consumption(self): @@ -139,7 +139,7 @@ def create_asset_repair(**args): asset_repair.stock_consumption = 1 asset_repair.warehouse = create_warehouse("Test Warehouse", company = asset.company) asset_repair.append("stock_items", { - "item": args.item or args.item_code or "_Test Item", + "item_code": args.item_code or "_Test Item", "valuation_rate": args.rate if args.get("rate") is not None else 100, "consumed_quantity": args.qty or 1 }) @@ -158,7 +158,7 @@ def create_asset_repair(**args): }) stock_entry.append('items', { "t_warehouse": asset_repair.warehouse, - "item_code": asset_repair.stock_items[0].item, + "item_code": asset_repair.stock_items[0].item_code, "qty": asset_repair.stock_items[0].consumed_quantity }) stock_entry.submit() From a1f47d8ae04a94573f9d9c863e110fae6c223395 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 25 Nov 2021 19:01:22 +0530 Subject: [PATCH 15/34] fix: Create stock item (cherry picked from commit eea80b6c01df479fbbc6cbc13edc4a7e3fadc2c4) --- erpnext/assets/doctype/asset_repair/test_asset_repair.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index ed5ae2cb4dc..7efb03083e4 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -11,12 +11,14 @@ from erpnext.assets.doctype.asset.test_asset import ( create_asset_data, set_depreciation_settings_in_company, ) +from erpnext.stock.doctype.item.test_item import create_item class TestAssetRepair(unittest.TestCase): def setUp(self): set_depreciation_settings_in_company() create_asset_data() + create_item("_Test Stock Item") frappe.db.sql("delete from `tabTax Rule`") def test_update_status(self): @@ -139,7 +141,7 @@ def create_asset_repair(**args): asset_repair.stock_consumption = 1 asset_repair.warehouse = create_warehouse("Test Warehouse", company = asset.company) asset_repair.append("stock_items", { - "item_code": args.item_code or "_Test Item", + "item_code": args.item_code or "_Test Stock Item", "valuation_rate": args.rate if args.get("rate") is not None else 100, "consumed_quantity": args.qty or 1 }) From 45f948f57574958533e7cfb545f93d147cdbe65a Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 25 Nov 2021 19:02:01 +0530 Subject: [PATCH 16/34] fix: Create setUpClass (cherry picked from commit efac7b0904ff5cc24fdc525c399ada8509f07882) --- erpnext/assets/doctype/asset_repair/test_asset_repair.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index 7efb03083e4..0ef2d800bb9 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -15,7 +15,8 @@ from erpnext.stock.doctype.item.test_item import create_item class TestAssetRepair(unittest.TestCase): - def setUp(self): + @classmethod + def setUpClass(cls): set_depreciation_settings_in_company() create_asset_data() create_item("_Test Stock Item") From 53eb7bd1c3a0943de8620d73494ea27438fbe112 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 25 Nov 2021 22:10:24 +0530 Subject: [PATCH 17/34] fix: Add test for consumption of serialized Assets (cherry picked from commit 6b75d1439f053a1d07ef54a3fdf55964aefa39c4) --- .../doctype/asset_repair/test_asset_repair.py | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index 0ef2d800bb9..34c7282ab26 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -5,6 +5,7 @@ import unittest import frappe from frappe.utils import flt, nowdate +from traceback import print_exc from erpnext.assets.doctype.asset.test_asset import ( create_asset, @@ -76,6 +77,25 @@ class TestAssetRepair(unittest.TestCase): self.assertEqual(stock_entry.items[0].item_code, asset_repair.stock_items[0].item_code) self.assertEqual(stock_entry.items[0].qty, asset_repair.stock_items[0].consumed_quantity) + def test_serialized_item_consumption(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item + from erpnext.stock.doctype.serial_no.serial_no import SerialNoRequiredError + + stock_entry = make_serialized_item() + serial_nos = stock_entry.get("items")[0].serial_no + serial_no = serial_nos.split("\n")[0] + + # should not raise any error + create_asset_repair(stock_consumption = 1, item_code = stock_entry.get("items")[0].item_code, + warehouse = "_Test Warehouse - _TC", serial_no = serial_no, submit = 1) + + # should raise error + asset_repair = create_asset_repair(stock_consumption = 1, warehouse = "_Test Warehouse - _TC", + item_code = stock_entry.get("items")[0].item_code) + + asset_repair.repair_status = "Completed" + self.assertRaises(SerialNoRequiredError, asset_repair.submit) + def test_increase_in_asset_value_due_to_stock_consumption(self): asset = create_asset(calculate_depreciation = 1, submit=1) initial_asset_value = get_asset_value(asset) @@ -140,11 +160,12 @@ def create_asset_repair(**args): if args.stock_consumption: asset_repair.stock_consumption = 1 - asset_repair.warehouse = create_warehouse("Test Warehouse", company = asset.company) + asset_repair.warehouse = args.warehouse or create_warehouse("Test Warehouse", company = asset.company) asset_repair.append("stock_items", { "item_code": args.item_code or "_Test Stock Item", "valuation_rate": args.rate if args.get("rate") is not None else 100, - "consumed_quantity": args.qty or 1 + "consumed_quantity": args.qty or 1, + "serial_no": args.serial_no }) asset_repair.insert(ignore_if_duplicate=True) From 1c643059dfc43425629bf4144c74e8b8cab471ed Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 25 Nov 2021 22:17:54 +0530 Subject: [PATCH 18/34] fix: Remove unused import (cherry picked from commit b28f137ee8d580a480bca0ed772283b62ae3c5b0) --- erpnext/assets/doctype/asset_repair/test_asset_repair.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index 34c7282ab26..e54329c10bd 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -5,7 +5,6 @@ import unittest import frappe from frappe.utils import flt, nowdate -from traceback import print_exc from erpnext.assets.doctype.asset.test_asset import ( create_asset, From 944b995179e4f6b6d8c6a32c5451e356d705e4db Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 25 Nov 2021 22:18:57 +0530 Subject: [PATCH 19/34] fix: Change order of import statements (cherry picked from commit c94f1ed39a40b019604a3476782f27a4ad4dab30) --- erpnext/assets/doctype/asset_repair/test_asset_repair.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index e54329c10bd..7c0d05748e1 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -77,8 +77,8 @@ class TestAssetRepair(unittest.TestCase): self.assertEqual(stock_entry.items[0].qty, asset_repair.stock_items[0].consumed_quantity) def test_serialized_item_consumption(self): - from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item from erpnext.stock.doctype.serial_no.serial_no import SerialNoRequiredError + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item stock_entry = make_serialized_item() serial_nos = stock_entry.get("items")[0].serial_no From 20f9b621e409e3ddaca9c13bcf956b103f1c1406 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 20:46:47 +0530 Subject: [PATCH 20/34] feat(pos): Total item qty field to POS screen (backport #28331) --- erpnext/public/scss/point-of-sale.scss | 5 +++++ .../page/point_of_sale/pos_item_cart.js | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 1677e9b3de4..7a3854cc611 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -495,6 +495,11 @@ font-size: var(--text-md); } + > .item-qty-total-container { + @extend .net-total-container; + padding: 5px 0px 0px 0px; + } + > .taxes-container { display: none; flex-direction: column; diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index a5b2d500414..4920584d95e 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -100,6 +100,10 @@ erpnext.PointOfSale.ItemCart = class { `
${this.get_discount_icon()} ${__('Add Discount')}
+
+
${__('Total Items')}
+
0.00
+
${__("Net Total")}
0.00
@@ -142,6 +146,7 @@ erpnext.PointOfSale.ItemCart = class { this.$numpad_section.prepend( `
+
` @@ -470,6 +475,7 @@ erpnext.PointOfSale.ItemCart = class { if (!frm) frm = this.events.get_frm(); this.render_net_total(frm.doc.net_total); + this.render_total_item_qty(frm.doc.items); const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? frm.doc.grand_total : frm.doc.rounded_total; this.render_grand_total(grand_total); @@ -487,6 +493,21 @@ erpnext.PointOfSale.ItemCart = class { ); } + render_total_item_qty(items) { + var total_item_qty = 0; + items.map((item) => { + total_item_qty = total_item_qty + item.qty; + }); + + this.$totals_section.find('.item-qty-total-container').html( + `
${__('Total Quantity')}
${total_item_qty}
` + ); + + this.$numpad_section.find('.numpad-item-qty-total').html( + `
${__('Total Quantity')}: ${total_item_qty}
` + ); + } + render_grand_total(value) { const currency = this.events.get_frm().doc.currency; this.$totals_section.find('.grand-total-container').html( From b6ffe333b973d4062f14c16adb5972a89c56f67f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 20:47:56 +0530 Subject: [PATCH 21/34] fix(regional): hsn_wise as false returns item_code (#28619) --- erpnext/regional/india/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index f8a31657314..be84aaf3bd5 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -571,17 +571,17 @@ def get_item_list(data, doc, hsn_wise=False): } item_data_attrs = ['sgstRate', 'cgstRate', 'igstRate', 'cessRate', 'cessNonAdvol'] hsn_wise_charges, hsn_taxable_amount = get_itemised_tax_breakup_data(doc, account_wise=True, hsn_wise=hsn_wise) - for hsn_code, taxable_amount in hsn_taxable_amount.items(): + for item_or_hsn, taxable_amount in hsn_taxable_amount.items(): item_data = frappe._dict() - if not hsn_code: + if not item_or_hsn: frappe.throw(_('GST HSN Code does not exist for one or more items')) - item_data.hsnCode = int(hsn_code) + item_data.hsnCode = int(item_or_hsn) if hsn_wise else item_or_hsn item_data.taxableAmount = taxable_amount item_data.qtyUnit = "" for attr in item_data_attrs: item_data[attr] = 0 - for account, tax_detail in hsn_wise_charges.get(hsn_code, {}).items(): + for account, tax_detail in hsn_wise_charges.get(item_or_hsn, {}).items(): account_type = gst_accounts.get(account, '') for tax_acc, attrs in tax_map.items(): if account_type == tax_acc: From c69fe4efb4a9efd3239ddb13923c8264ca70f27c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 30 Nov 2021 13:48:22 +0530 Subject: [PATCH 22/34] fix(ux): remove attachment limit from item doctype (backport #28632) fix(ux): remove attachment limit from item doctype (backport #28632) --- erpnext/selling/doctype/quotation/quotation.json | 3 +-- erpnext/stock/doctype/item/item.json | 3 +-- .../doctype/stock_reconciliation/stock_reconciliation.json | 6 ++++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 43a44900fcb..16a5e0b756d 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -952,8 +952,7 @@ "idx": 82, "is_submittable": 1, "links": [], - "max_attachments": 1, - "modified": "2021-08-27 20:10:07.864951", + "modified": "2021-11-30 01:33:21.106073", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index e360cca124e..38dda2d0643 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -949,8 +949,7 @@ "image_field": "image", "index_web_pages_for_search": 1, "links": [], - "max_attachments": 1, - "modified": "2021-09-10 12:23:07.277077", + "modified": "2021-11-30 01:33:06.572442", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json index b7d1497319f..3402972bb89 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "naming_series:", "creation": "2013-03-28 10:35:31", "description": "This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.", @@ -153,11 +154,12 @@ "icon": "fa fa-upload-alt", "idx": 1, "is_submittable": 1, - "max_attachments": 1, - "modified": "2020-04-08 17:02:47.196206", + "links": [], + "modified": "2021-11-30 01:33:51.437194", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reconciliation", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { From 16ce9dfa1201ccef16364afe7d2da7c917ce8d59 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 30 Nov 2021 14:26:43 +0530 Subject: [PATCH 23/34] fix: Employee Transfer and Project Profitability test cases (#28633) (#28639) * fix: Employee Transfer testcases * fix: Project Profitability test case (cherry picked from commit d0f4f03b668ed08e6685d46e95ee9aca4eea5b13) Co-authored-by: Rucha Mahabal --- .../test_employee_transfer.py | 72 +++++++++---------- .../test_project_profitability.py | 19 ++--- 2 files changed, 43 insertions(+), 48 deletions(-) diff --git a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py index 287dfba35b3..64eee402fec 100644 --- a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py +++ b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py @@ -2,7 +2,6 @@ # See license.txt import unittest -from datetime import date import frappe from frappe.utils import add_days, getdate @@ -12,16 +11,14 @@ from erpnext.hr.doctype.employee.test_employee import make_employee class TestEmployeeTransfer(unittest.TestCase): def setUp(self): - make_employee("employee2@transfers.com") - make_employee("employee3@transfers.com") create_company() - create_employee() - create_employee_transfer() def tearDown(self): frappe.db.rollback() def test_submit_before_transfer_date(self): + make_employee("employee2@transfers.com") + transfer_obj = frappe.get_doc({ "doctype": "Employee Transfer", "employee": frappe.get_value("Employee", {"user_id":"employee2@transfers.com"}, "name"), @@ -43,6 +40,8 @@ class TestEmployeeTransfer(unittest.TestCase): self.assertEqual(transfer.docstatus, 1) def test_new_employee_creation(self): + make_employee("employee3@transfers.com") + transfer = frappe.get_doc({ "doctype": "Employee Transfer", "employee": frappe.get_value("Employee", {"user_id":"employee3@transfers.com"}, "name"), @@ -63,60 +62,51 @@ class TestEmployeeTransfer(unittest.TestCase): self.assertEqual(frappe.get_value("Employee", transfer.employee, "status"), "Left") def test_employee_history(self): - name = frappe.get_value("Employee", {"first_name": "John", "company": "Test Company"}, "name") - doc = frappe.get_doc("Employee",name) + employee = make_employee("employee4@transfers.com", + company="Test Company", + date_of_birth=getdate("30-09-1980"), + date_of_joining=getdate("01-10-2021"), + department="Accounts - TC", + designation="Accountant" + ) + transfer = create_employee_transfer(employee) + count = 0 department = ["Accounts - TC", "Management - TC"] designation = ["Accountant", "Manager"] - dt = [getdate("01-10-2021"), date.today()] + dt = [getdate("01-10-2021"), getdate()] - for data in doc.internal_work_history: + employee = frappe.get_doc("Employee", employee) + for data in employee.internal_work_history: self.assertEqual(data.department, department[count]) self.assertEqual(data.designation, designation[count]) self.assertEqual(data.from_date, dt[count]) count = count + 1 - data = frappe.db.get_list("Employee Transfer", filters={"employee":name}, fields=["*"]) - doc = frappe.get_doc("Employee Transfer", data[0]["name"]) - doc.cancel() - employee_doc = frappe.get_doc("Employee",name) + transfer.cancel() + employee.reload() - for data in employee_doc.internal_work_history: + for data in employee.internal_work_history: self.assertEqual(data.designation, designation[0]) self.assertEqual(data.department, department[0]) self.assertEqual(data.from_date, dt[0]) -def create_employee(): - doc = frappe.get_doc({ - "doctype": "Employee", - "first_name": "John", - "company": "Test Company", - "gender": "Male", - "date_of_birth": getdate("30-09-1980"), - "date_of_joining": getdate("01-10-2021"), - "department": "Accounts - TC", - "designation": "Accountant" - }) - - doc.save() def create_company(): - exists = frappe.db.exists("Company", "Test Company") - if not exists: - doc = frappe.get_doc({ - "doctype": "Company", - "company_name": "Test Company", - "default_currency": "INR", - "country": "India" - }) + if not frappe.db.exists("Company", "Test Company"): + frappe.get_doc({ + "doctype": "Company", + "company_name": "Test Company", + "default_currency": "INR", + "country": "India" + }).insert() - doc.save() -def create_employee_transfer(): +def create_employee_transfer(employee): doc = frappe.get_doc({ "doctype": "Employee Transfer", - "employee": frappe.get_value("Employee", {"first_name": "John", "company": "Test Company"}, "name"), - "transfer_date": date.today(), + "employee": employee, + "transfer_date": getdate(), "transfer_details": [ { "property": "Designation", @@ -134,4 +124,6 @@ def create_employee_transfer(): }) doc.save() - doc.submit() \ No newline at end of file + doc.submit() + + return doc \ No newline at end of file diff --git a/erpnext/projects/report/project_profitability/test_project_profitability.py b/erpnext/projects/report/project_profitability/test_project_profitability.py index 31fd361fce5..b2b18099e6c 100644 --- a/erpnext/projects/report/project_profitability/test_project_profitability.py +++ b/erpnext/projects/report/project_profitability/test_project_profitability.py @@ -2,7 +2,7 @@ import unittest import frappe -from frappe.utils import add_days, getdate, nowdate +from frappe.utils import add_days, getdate from erpnext.hr.doctype.employee.test_employee import make_employee from erpnext.projects.doctype.timesheet.test_timesheet import ( @@ -14,21 +14,26 @@ from erpnext.projects.report.project_profitability.project_profitability import class TestProjectProfitability(unittest.TestCase): - def setUp(self): + frappe.db.sql('delete from `tabTimesheet`') emp = make_employee('test_employee_9@salary.com', company='_Test Company') + if not frappe.db.exists('Salary Component', 'Timesheet Component'): frappe.get_doc({'doctype': 'Salary Component', 'salary_component': 'Timesheet Component'}).insert() + make_salary_structure_for_timesheet(emp, company='_Test Company') - self.timesheet = make_timesheet(emp, simulate = True, is_billable=1) + date = getdate() + + self.timesheet = make_timesheet(emp, is_billable=1) self.salary_slip = make_salary_slip(self.timesheet.name) - holidays = self.salary_slip.get_holidays_for_employee(nowdate(), nowdate()) + + holidays = self.salary_slip.get_holidays_for_employee(date, date) if holidays: frappe.db.set_value('Payroll Settings', None, 'include_holidays_in_total_working_days', 1) self.salary_slip.submit() self.sales_invoice = make_sales_invoice(self.timesheet.name, '_Test Item', '_Test Customer') - self.sales_invoice.due_date = nowdate() + self.sales_invoice.due_date = date self.sales_invoice.submit() frappe.db.set_value('HR Settings', None, 'standard_working_hours', 8) @@ -64,6 +69,4 @@ class TestProjectProfitability(unittest.TestCase): self.assertEqual(fractional_cost, row.fractional_cost) def tearDown(self): - frappe.get_doc("Sales Invoice", self.sales_invoice.name).cancel() - frappe.get_doc("Salary Slip", self.salary_slip.name).cancel() - frappe.get_doc("Timesheet", self.timesheet.name).cancel() + frappe.db.rollback() From 9975da755b0753b4f29eaa3826c775cbf952282d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 30 Nov 2021 14:46:59 +0530 Subject: [PATCH 24/34] fix: allow creating Shift Assignment for same day (#28613) (#28629) (cherry picked from commit 4458b2481351e6784492686e2e6ccf02ee5c8bc5) Co-authored-by: Rucha Mahabal --- erpnext/hr/doctype/shift_assignment/shift_assignment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py index 4e829a3dbd3..517730281fc 100644 --- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py +++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py @@ -19,8 +19,8 @@ class ShiftAssignment(Document): validate_active_employee(self.employee) self.validate_overlapping_dates() - if self.end_date and self.end_date <= self.start_date: - frappe.throw(_("End Date must not be lesser than Start Date")) + if self.end_date: + self.validate_from_to_dates('start_date', 'end_date') def validate_overlapping_dates(self): if not self.name: From d711a3883b20365f9aff04c05c509f07bfc56256 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 30 Nov 2021 18:00:54 +0530 Subject: [PATCH 25/34] chore: remove duplicate code (bp #28646) [skip ci] (cherry picked from commit 0854c183aaa6e443d30be72f8532954986a4d3e5) Co-authored-by: Ankush Menat --- erpnext/hr/doctype/employee/employee.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 79e8f6140aa..88e5ca9d4c5 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -96,15 +96,8 @@ class Employee(NestedSet): 'user': self.user_id }) - if employee_user_permission_exists: return - - employee_user_permission_exists = frappe.db.exists('User Permission', { - 'allow': 'Employee', - 'for_value': self.name, - 'user': self.user_id - }) - - if employee_user_permission_exists: return + if employee_user_permission_exists: + return add_user_permission("Employee", self.name, self.user_id) set_user_permission_if_allowed("Company", self.company, self.user_id) From f3916c02a8185ebb2085f28607ba5ca0dffc8d28 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 30 Nov 2021 13:24:23 +0530 Subject: [PATCH 26/34] feat: Show Zero Values filter in consolidated financial statement (cherry picked from commit 9610086d0c084808455e38a4f950b8b9f68f90bc) --- .../consolidated_financial_statement.js | 5 +++++ .../consolidated_financial_statement.py | 12 +++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index e24a5f99184..d3e836afd10 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -92,6 +92,11 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Include Default Book Entries"), "fieldtype": "Check", "default": 1 + }, + { + "fieldname": "show_zero_values", + "label": __("Show zero values"), + "fieldtype": "Check" } ], "formatter": function(value, row, column, data, default_formatter) { diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index c71bc17ca79..01799d58041 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -22,7 +22,11 @@ from erpnext.accounts.report.cash_flow.cash_flow import ( get_cash_flow_accounts, ) from erpnext.accounts.report.cash_flow.cash_flow import get_report_summary as get_cash_flow_summary -from erpnext.accounts.report.financial_statements import get_fiscal_year_data, sort_accounts +from erpnext.accounts.report.financial_statements import ( + filter_out_zero_value_rows, + get_fiscal_year_data, + sort_accounts, +) from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import ( get_chart_data as get_pl_chart_data, ) @@ -265,7 +269,7 @@ def get_columns(companies, filters): return columns def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, ignore_closing_entries=False): - accounts, accounts_by_name = get_account_heads(root_type, + accounts, accounts_by_name, parent_children_map = get_account_heads(root_type, companies, filters) if not accounts: return [] @@ -294,6 +298,8 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i out = prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency, filters) + out = filter_out_zero_value_rows(out, parent_children_map, show_zero_values=filters.get("show_zero_values")) + if out: add_total_row(out, root_type, balance_must_be, companies, company_currency) @@ -370,7 +376,7 @@ def get_account_heads(root_type, companies, filters): accounts, accounts_by_name, parent_children_map = filter_accounts(accounts) - return accounts, accounts_by_name + return accounts, accounts_by_name, parent_children_map def update_parent_account_names(accounts): """Update parent_account_name in accounts list. From 08b7c856b2ee94d1f8ac2c019c556eef4d7dd7da Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 30 Nov 2021 18:36:56 +0530 Subject: [PATCH 27/34] fix: Unable to search project by project name in Sales Invoice (#28648) --- .../doctype/sales_invoice/sales_invoice.js | 9 --------- erpnext/controllers/queries.py | 14 +++++++++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 118f7945e70..8a6d3cd5935 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -517,15 +517,6 @@ cur_frm.fields_dict.write_off_cost_center.get_query = function(doc) { } } -// project name -//-------------------------- -cur_frm.fields_dict['project'].get_query = function(doc, cdt, cdn) { - return{ - query: "erpnext.controllers.queries.get_project_name", - filters: {'customer': doc.customer} - } -} - // Income Account in Details Table // -------------------------------- cur_frm.set_query("income_account", "items", function(doc) { diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index fe68e659323..ef5ee36a758 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -539,6 +539,10 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters) dimension_filters = get_dimension_filter_map() dimension_filters = dimension_filters.get((filters.get('dimension'),filters.get('account'))) query_filters = [] + or_filters = [] + fields = ['name'] + + searchfields = frappe.get_meta(doctype).get_search_fields() meta = frappe.get_meta(doctype) if meta.is_tree: @@ -550,8 +554,9 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters) if meta.has_field('company'): query_filters.append(['company', '=', filters.get('company')]) - if txt: - query_filters.append([searchfield, 'LIKE', "%%%s%%" % txt]) + for field in searchfields: + or_filters.append([field, 'LIKE', "%%%s%%" % txt]) + fields.append(field) if dimension_filters: if dimension_filters['allow_or_restrict'] == 'Allow': @@ -566,10 +571,9 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters) query_filters.append(['name', query_selector, dimensions]) - output = frappe.get_list(doctype, filters=query_filters) - result = [d.name for d in output] + output = frappe.get_list(doctype, fields=fields, filters=query_filters, or_filters=or_filters, as_list=1) - return [(d,) for d in set(result)] + return [tuple(d) for d in set(output)] @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs From f599c375ed7ce4a23a2cb894fb3690e300598cdd Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 11:23:40 +0530 Subject: [PATCH 28/34] chore: add timeout to GHA workflows (#26714) (#28661) (cherry picked from commit 0bb60b37df4517c81cf684042bc8d42ea2a67364) Co-authored-by: Ankush --- .github/workflows/backport.yml | 1 + .github/workflows/docs-checker.yml | 1 + .github/workflows/patch.yml | 1 + .github/workflows/server-tests.yml | 1 + .github/workflows/ui-tests.yml | 1 + 5 files changed, 5 insertions(+) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 1d180f251e1..bd622275d6d 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -8,6 +8,7 @@ on: jobs: main: runs-on: ubuntu-latest + timeout-minutes: 60 steps: - name: Checkout Actions uses: actions/checkout@v2 diff --git a/.github/workflows/docs-checker.yml b/.github/workflows/docs-checker.yml index cdf676dd674..db46c5621b2 100644 --- a/.github/workflows/docs-checker.yml +++ b/.github/workflows/docs-checker.yml @@ -6,6 +6,7 @@ on: jobs: build: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: 'Setup Environment' diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml index eaab24b9081..8bb44555206 100644 --- a/.github/workflows/patch.yml +++ b/.github/workflows/patch.yml @@ -11,6 +11,7 @@ on: jobs: test: runs-on: ubuntu-18.04 + timeout-minutes: 60 name: Patch Test diff --git a/.github/workflows/server-tests.yml b/.github/workflows/server-tests.yml index a008b638c3f..6d7324d623b 100644 --- a/.github/workflows/server-tests.yml +++ b/.github/workflows/server-tests.yml @@ -15,6 +15,7 @@ on: jobs: test: runs-on: ubuntu-18.04 + timeout-minutes: 60 strategy: fail-fast: false diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 90b5f28e9b7..5459e86123d 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -9,6 +9,7 @@ on: jobs: test: runs-on: ubuntu-18.04 + timeout-minutes: 60 strategy: fail-fast: false From d2a0a554a6b276f3107701eefbd29bcf00762cc1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 15:18:01 +0530 Subject: [PATCH 29/34] fix(POS Profile): replace `cur_frm` with `frm` (#28664) --- .../doctype/pos_profile/pos_profile.js | 177 +++++++++--------- 1 file changed, 91 insertions(+), 86 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js index efdeb1a5e81..813d20dbf93 100755 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.js +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js @@ -3,22 +3,20 @@ {% include "erpnext/public/js/controllers/accounts.js" %} -frappe.ui.form.on("POS Profile", "onload", function(frm) { - frm.set_query("selling_price_list", function() { - return { filters: { selling: 1 } }; - }); - - frm.set_query("tc_name", function() { - return { filters: { selling: 1 } }; - }); - - erpnext.queries.setup_queries(frm, "Warehouse", function() { - return erpnext.queries.warehouse(frm.doc); - }); -}); - frappe.ui.form.on('POS Profile', { setup: function(frm) { + frm.set_query("selling_price_list", function() { + return { filters: { selling: 1 } }; + }); + + frm.set_query("tc_name", function() { + return { filters: { selling: 1 } }; + }); + + erpnext.queries.setup_queries(frm, "Warehouse", function() { + return erpnext.queries.warehouse(frm.doc); + }); + frm.set_query("print_format", function() { return { filters: [ @@ -27,10 +25,16 @@ frappe.ui.form.on('POS Profile', { }; }); - frm.set_query("account_for_change_amount", function() { + frm.set_query("account_for_change_amount", function(doc) { + if (!doc.company) { + frappe.throw(__('Please set Company')); + } + return { filters: { - account_type: ['in', ["Cash", "Bank"]] + account_type: ['in', ["Cash", "Bank"]], + is_group: 0, + company: doc.company } }; }); @@ -45,7 +49,7 @@ frappe.ui.form.on('POS Profile', { }); frm.set_query('company_address', function(doc) { - if(!doc.company) { + if (!doc.company) { frappe.throw(__('Please set Company')); } @@ -58,11 +62,79 @@ frappe.ui.form.on('POS Profile', { }; }); + frm.set_query('income_account', function(doc) { + if (!doc.company) { + frappe.throw(__('Please set Company')); + } + + return { + filters: { + 'is_group': 0, + 'company': doc.company, + 'account_type': "Income Account" + } + }; + }); + + frm.set_query('cost_center', function(doc) { + if (!doc.company) { + frappe.throw(__('Please set Company')); + } + + return { + filters: { + 'company': doc.company, + 'is_group': 0 + } + }; + }); + + frm.set_query('expense_account', function(doc) { + if (!doc.company) { + frappe.throw(__('Please set Company')); + } + + return { + filters: { + "report_type": "Profit and Loss", + "company": doc.company, + "is_group": 0 + } + }; + }); + + frm.set_query("select_print_heading", function() { + return { + filters: [ + ['Print Heading', 'docstatus', '!=', 2] + ] + }; + }); + + frm.set_query("write_off_account", function(doc) { + return { + filters: { + 'report_type': 'Profit and Loss', + 'is_group': 0, + 'company': doc.company + } + }; + }); + + frm.set_query("write_off_cost_center", function(doc) { + return { + filters: { + 'is_group': 0, + 'company': doc.company + } + }; + }); + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, refresh: function(frm) { - if(frm.doc.company) { + if (frm.doc.company) { frm.trigger("toggle_display_account_head"); } }, @@ -76,71 +148,4 @@ frappe.ui.form.on('POS Profile', { frm.toggle_display('expense_account', erpnext.is_perpetual_inventory_enabled(frm.doc.company)); } -}) - -// Income Account -// -------------------------------- -cur_frm.fields_dict['income_account'].get_query = function(doc,cdt,cdn) { - return{ - filters:{ - 'is_group': 0, - 'company': doc.company, - 'account_type': "Income Account" - } - }; -}; - - -// Cost Center -// ----------------------------- -cur_frm.fields_dict['cost_center'].get_query = function(doc,cdt,cdn) { - return{ - filters:{ - 'company': doc.company, - 'is_group': 0 - } - }; -}; - - -// Expense Account -// ----------------------------- -cur_frm.fields_dict["expense_account"].get_query = function(doc) { - return { - filters: { - "report_type": "Profit and Loss", - "company": doc.company, - "is_group": 0 - } - }; -}; - -// ------------------ Get Print Heading ------------------------------------ -cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn) { - return{ - filters:[ - ['Print Heading', 'docstatus', '!=', 2] - ] - }; -}; - -cur_frm.fields_dict.write_off_account.get_query = function(doc) { - return{ - filters:{ - 'report_type': 'Profit and Loss', - 'is_group': 0, - 'company': doc.company - } - }; -}; - -// Write off cost center -// ----------------------- -cur_frm.fields_dict.write_off_cost_center.get_query = function(doc) { - return{ - filters:{ - 'is_group': 0, - 'company': doc.company - } - }; -}; +}); From 742ede7ba486ca04cab6c80e14ffce06d3b7fdfe Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 17:53:53 +0530 Subject: [PATCH 30/34] test: dynamic fiscal year creation in tests (#28667) (#28670) (cherry picked from commit fdffa037b5aa7fe368caa44c365e679401263c96) Co-authored-by: Ankush Menat --- .../doctype/fiscal_year/test_fiscal_year.py | 28 +++++++- .../doctype/fiscal_year/test_records.json | 69 ------------------- 2 files changed, 27 insertions(+), 70 deletions(-) delete mode 100644 erpnext/accounts/doctype/fiscal_year/test_records.json diff --git a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py index bc8c6abeff3..69e13a407de 100644 --- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py @@ -5,10 +5,10 @@ import unittest import frappe +from frappe.utils import now_datetime from erpnext.accounts.doctype.fiscal_year.fiscal_year import FiscalYearIncorrectDate -test_records = frappe.get_test_records('Fiscal Year') test_ignore = ["Company"] class TestFiscalYear(unittest.TestCase): @@ -25,3 +25,29 @@ class TestFiscalYear(unittest.TestCase): }) self.assertRaises(FiscalYearIncorrectDate, fy.insert) + + +def test_record_generator(): + test_records = [ + { + "doctype": "Fiscal Year", + "year": "_Test Short Fiscal Year 2011", + "is_short_year": 1, + "year_end_date": "2011-04-01", + "year_start_date": "2011-12-31" + } + ] + + start = 2012 + end = now_datetime().year + 5 + for year in range(start, end): + test_records.append({ + "doctype": "Fiscal Year", + "year": f"_Test Fiscal Year {year}", + "year_start_date": f"{year}-01-01", + "year_end_date": f"{year}-12-31" + }) + + return test_records + +test_records = test_record_generator() diff --git a/erpnext/accounts/doctype/fiscal_year/test_records.json b/erpnext/accounts/doctype/fiscal_year/test_records.json deleted file mode 100644 index 44052535cbd..00000000000 --- a/erpnext/accounts/doctype/fiscal_year/test_records.json +++ /dev/null @@ -1,69 +0,0 @@ -[ - { - "doctype": "Fiscal Year", - "year": "_Test Short Fiscal Year 2011", - "is_short_year": 1, - "year_end_date": "2011-04-01", - "year_start_date": "2011-12-31" - }, - { - "doctype": "Fiscal Year", - "year": "_Test Fiscal Year 2012", - "year_end_date": "2012-12-31", - "year_start_date": "2012-01-01" - }, - { - "doctype": "Fiscal Year", - "year": "_Test Fiscal Year 2013", - "year_end_date": "2013-12-31", - "year_start_date": "2013-01-01" - }, - { - "doctype": "Fiscal Year", - "year": "_Test Fiscal Year 2014", - "year_end_date": "2014-12-31", - "year_start_date": "2014-01-01" - }, - { - "doctype": "Fiscal Year", - "year": "_Test Fiscal Year 2015", - "year_end_date": "2015-12-31", - "year_start_date": "2015-01-01" - }, - { - "doctype": "Fiscal Year", - "year": "_Test Fiscal Year 2016", - "year_end_date": "2016-12-31", - "year_start_date": "2016-01-01" - }, - { - "doctype": "Fiscal Year", - "year": "_Test Fiscal Year 2017", - "year_end_date": "2017-12-31", - "year_start_date": "2017-01-01" - }, - { - "doctype": "Fiscal Year", - "year": "_Test Fiscal Year 2018", - "year_end_date": "2018-12-31", - "year_start_date": "2018-01-01" - }, - { - "doctype": "Fiscal Year", - "year": "_Test Fiscal Year 2019", - "year_end_date": "2019-12-31", - "year_start_date": "2019-01-01" - }, - { - "doctype": "Fiscal Year", - "year": "_Test Fiscal Year 2020", - "year_end_date": "2020-12-31", - "year_start_date": "2020-01-01" - }, - { - "doctype": "Fiscal Year", - "year": "_Test Fiscal Year 2021", - "year_end_date": "2021-12-31", - "year_start_date": "2021-01-01" - } -] From b6d752201aae3c232f16e1b205ed06276622e207 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 2 Dec 2021 12:13:34 +0530 Subject: [PATCH 31/34] fix: actual tax conversion in case of multicurrency invoices (#28686) --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 555f8d04cc2..62607c6b0d1 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1094,7 +1094,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ $.each(this.frm.doc.taxes || [], function(i, d) { if(d.charge_type == "Actual") { frappe.model.set_value(d.doctype, d.name, "tax_amount", - flt(d.tax_amount) / flt(exchange_rate)); + flt(d.base_tax_amount) / flt(exchange_rate)); } }); }, From e7982a9c9e991f01f8521fbd6e01d966f72ba4d2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 2 Dec 2021 12:13:50 +0530 Subject: [PATCH 32/34] fix: Make buttons translatable (#28685) --- erpnext/assets/doctype/asset/asset.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index da5778ea3d5..c2b1bbcf142 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -80,20 +80,20 @@ frappe.ui.form.on('Asset', { if (frm.doc.docstatus==1) { if (in_list(["Submitted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) { - frm.add_custom_button("Transfer Asset", function() { + frm.add_custom_button(__("Transfer Asset"), function() { erpnext.asset.transfer_asset(frm); }, __("Manage")); - frm.add_custom_button("Scrap Asset", function() { + frm.add_custom_button(__("Scrap Asset"), function() { erpnext.asset.scrap_asset(frm); }, __("Manage")); - frm.add_custom_button("Sell Asset", function() { + frm.add_custom_button(__("Sell Asset"), function() { frm.trigger("make_sales_invoice"); }, __("Manage")); } else if (frm.doc.status=='Scrapped') { - frm.add_custom_button("Restore Asset", function() { + frm.add_custom_button(__("Restore Asset"), function() { erpnext.asset.restore_asset(frm); }, __("Manage")); } @@ -121,7 +121,7 @@ frappe.ui.form.on('Asset', { } if (frm.doc.purchase_receipt || !frm.doc.is_existing_asset) { - frm.add_custom_button("View General Ledger", function() { + frm.add_custom_button(__("View General Ledger"), function() { frappe.route_options = { "voucher_no": frm.doc.name, "from_date": frm.doc.available_for_use_date, From 0325ed1277e3883215f4ba9d8f7bf1df45b43204 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 2 Dec 2021 14:58:17 +0530 Subject: [PATCH 33/34] fix: dont requeue repost immediately and clear progress (#28684) (#28689) (cherry picked from commit a37c99a23d83e9c68cd0404a64dba12b2c86ce41) Co-authored-by: Ankush Menat --- .../repost_item_valuation/repost_item_valuation.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 291b749ddf2..b3fc1258590 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -54,9 +54,11 @@ class RepostItemValuation(Document): @frappe.whitelist() def restart_reposting(self): - self.set_status('Queued') - frappe.enqueue(repost, timeout=1800, queue='long', - job_name='repost_sle', now=True, doc=self) + self.set_status('Queued', write=False) + self.current_index = 0 + self.distinct_item_and_warehouse = None + self.items_to_be_repost = None + self.db_update() def deduplicate_similar_repost(self): """ Deduplicate similar reposts based on item-warehouse-posting combination.""" From 080843850832992e445aa7d7899a7a4c2be8ad7e Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 2 Dec 2021 15:00:42 +0530 Subject: [PATCH 34/34] fix: remove change_abbr method (#28691) --- erpnext/setup/doctype/company/company.js | 37 ---------------------- erpnext/setup/doctype/company/company.json | 9 +----- 2 files changed, 1 insertion(+), 45 deletions(-) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 8f83d3cd73a..95ca3867ee7 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -204,43 +204,6 @@ erpnext.company.set_chart_of_accounts_options = function(doc) { } } -cur_frm.cscript.change_abbr = function() { - var dialog = new frappe.ui.Dialog({ - title: "Replace Abbr", - fields: [ - {"fieldtype": "Data", "label": "New Abbreviation", "fieldname": "new_abbr", - "reqd": 1 }, - {"fieldtype": "Button", "label": "Update", "fieldname": "update"}, - ] - }); - - dialog.fields_dict.update.$input.click(function() { - var args = dialog.get_values(); - if(!args) return; - frappe.show_alert(__("Update in progress. It might take a while.")); - return frappe.call({ - method: "erpnext.setup.doctype.company.company.enqueue_replace_abbr", - args: { - "company": cur_frm.doc.name, - "old": cur_frm.doc.abbr, - "new": args.new_abbr - }, - callback: function(r) { - if(r.exc) { - frappe.msgprint(__("There were errors.")); - return; - } else { - cur_frm.set_value("abbr", args.new_abbr); - } - dialog.hide(); - cur_frm.refresh(); - }, - btn: this - }) - }); - dialog.show(); -} - erpnext.company.setup_queries = function(frm) { $.each([ ["default_bank_account", {"account_type": "Bank"}], diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 98e46d42b0c..dae64e4ad65 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -12,7 +12,6 @@ "details", "company_name", "abbr", - "change_abbr", "is_group", "cb0", "domain", @@ -128,12 +127,6 @@ "reqd": 1, "set_only_once": 1 }, - { - "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")", - "fieldname": "change_abbr", - "fieldtype": "Button", - "label": "Change Abbreviation" - }, { "bold": 1, "default": "0", @@ -749,7 +742,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2021-05-12 16:51:08.187233", + "modified": "2021-12-02 14:52:08.187233", "modified_by": "Administrator", "module": "Setup", "name": "Company",