From 4e92926a525b396173dbc4d6dd476b2ab4874f9b Mon Sep 17 00:00:00 2001
From: Abhinav Raut
Date: Fri, 11 Mar 2022 16:44:21 +0530
Subject: [PATCH 01/35] fix: incorrect payable amount for loan closure - Add
penalty amount to payable amount for loan closure
---
erpnext/loan_management/doctype/loan_repayment/loan_repayment.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 67c2b1ee14d..63dea6f726a 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -609,5 +609,6 @@ def calculate_amounts(against_loan, posting_date, payment_type=''):
amounts['payable_principal_amount'] = amounts['pending_principal_amount']
amounts['interest_amount'] += amounts['unaccrued_interest']
amounts['payable_amount'] = amounts['payable_principal_amount'] + amounts['interest_amount']
+ amounts['payable_amount'] = amounts['penalty_amount']
return amounts
From 8c76a76154d8976760d19d95f421dd2b0ee238bf Mon Sep 17 00:00:00 2001
From: Abhinav Raut
Date: Fri, 11 Mar 2022 16:46:30 +0530
Subject: [PATCH 02/35] fix: incorrect payable amount for loan closure
---
.../loan_management/doctype/loan_repayment/loan_repayment.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 63dea6f726a..f7e75768a82 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -609,6 +609,6 @@ def calculate_amounts(against_loan, posting_date, payment_type=''):
amounts['payable_principal_amount'] = amounts['pending_principal_amount']
amounts['interest_amount'] += amounts['unaccrued_interest']
amounts['payable_amount'] = amounts['payable_principal_amount'] + amounts['interest_amount']
- amounts['payable_amount'] = amounts['penalty_amount']
+ amounts['payable_amount'] += amounts['penalty_amount']
return amounts
From 1b2c6a5b78d4ee2e31817eb78bb1f614b672eda4 Mon Sep 17 00:00:00 2001
From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
Date: Mon, 21 Mar 2022 17:06:23 +0530
Subject: [PATCH 03/35] fix: Code cleanup
---
.../loan_management/doctype/loan_repayment/loan_repayment.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index f7e75768a82..07100001525 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -608,7 +608,6 @@ def calculate_amounts(against_loan, posting_date, payment_type=''):
if payment_type == 'Loan Closure':
amounts['payable_principal_amount'] = amounts['pending_principal_amount']
amounts['interest_amount'] += amounts['unaccrued_interest']
- amounts['payable_amount'] = amounts['payable_principal_amount'] + amounts['interest_amount']
- amounts['payable_amount'] += amounts['penalty_amount']
+ amounts['payable_amount'] = amounts['payable_principal_amount'] + amounts['interest_amount'] + amounts['penalty_amount']
return amounts
From 7e207c8901be2cfd24afc7d9970e4a0ca42bd0b9 Mon Sep 17 00:00:00 2001
From: marination
Date: Thu, 31 Mar 2022 16:29:18 +0530
Subject: [PATCH 04/35] fix: Call Redisearch index creation functions on
enabling redisearch in settings
---
.../e_commerce_settings.json | 12 ++++++-
.../e_commerce_settings.py | 17 ++++++++++
erpnext/e_commerce/redisearch_utils.py | 34 +++++++++----------
erpnext/templates/pages/product_search.py | 10 +++---
4 files changed, 50 insertions(+), 23 deletions(-)
diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json
index d5fb9697f89..62505e61db6 100644
--- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json
+++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json
@@ -47,6 +47,7 @@
"item_search_settings_section",
"redisearch_warning",
"search_index_fields",
+ "is_redisearch_enabled",
"show_categories_in_search_autocomplete",
"is_redisearch_loaded",
"shop_by_category_section",
@@ -303,6 +304,7 @@
},
{
"default": "1",
+ "depends_on": "is_redisearch_enabled",
"fieldname": "show_categories_in_search_autocomplete",
"fieldtype": "Check",
"label": "Show Categories in Search Autocomplete",
@@ -365,12 +367,19 @@
"fieldname": "show_price_in_quotation",
"fieldtype": "Check",
"label": "Show Price in Quotation"
+ },
+ {
+ "default": "0",
+ "fieldname": "is_redisearch_enabled",
+ "fieldtype": "Check",
+ "label": "Enable Redisearch",
+ "read_only_depends_on": "eval:!doc.is_redisearch_loaded"
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-09-02 14:02:44.785824",
+ "modified": "2022-03-31 16:01:46.308663",
"modified_by": "Administrator",
"module": "E-commerce",
"name": "E Commerce Settings",
@@ -389,5 +398,6 @@
],
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py
index b5cd067e38f..2bb4ad69784 100644
--- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py
+++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py
@@ -9,6 +9,7 @@ from frappe.utils import comma_and, flt, unique
from erpnext.e_commerce.redisearch_utils import (
create_website_items_index,
+ define_autocomplete_dictionary,
get_indexable_web_fields,
is_search_module_loaded,
)
@@ -21,6 +22,8 @@ class ShoppingCartSetupError(frappe.ValidationError):
class ECommerceSettings(Document):
def onload(self):
self.get("__onload").quotation_series = frappe.get_meta("Quotation").get_options("naming_series")
+
+ # flag >> if redisearch is installed and loaded
self.is_redisearch_loaded = is_search_module_loaded()
def validate(self):
@@ -34,6 +37,20 @@ class ECommerceSettings(Document):
frappe.clear_document_cache("E Commerce Settings", "E Commerce Settings")
+ self.is_redisearch_enabled_pre_save = frappe.db.get_single_value(
+ "E Commerce Settings", "is_redisearch_enabled"
+ )
+
+ def after_save(self):
+ self.create_redisearch_indexes()
+
+ def create_redisearch_indexes(self):
+ # if redisearch is enabled (value changed) create indexes and dictionary
+ value_changed = self.is_redisearch_enabled != self.is_redisearch_enabled_pre_save
+ if self.is_redisearch_loaded and self.is_redisearch_enabled and value_changed:
+ define_autocomplete_dictionary()
+ create_website_items_index()
+
def validate_field_filters(self):
if not (self.enable_field_filters and self.filter_fields):
return
diff --git a/erpnext/e_commerce/redisearch_utils.py b/erpnext/e_commerce/redisearch_utils.py
index 82829bf1eff..78cc05af38b 100644
--- a/erpnext/e_commerce/redisearch_utils.py
+++ b/erpnext/e_commerce/redisearch_utils.py
@@ -22,6 +22,12 @@ def get_indexable_web_fields():
return [df.fieldname for df in valid_fields]
+def is_redisearch_enabled():
+ "Return True only if redisearch is loaded and enabled."
+ is_redisearch_enabled = frappe.db.get_single_value("E Commerce Settings", "is_redisearch_enabled")
+ return is_search_module_loaded() and is_redisearch_enabled
+
+
def is_search_module_loaded():
try:
cache = frappe.cache()
@@ -35,11 +41,11 @@ def is_search_module_loaded():
return False
-def if_redisearch_loaded(function):
- "Decorator to check if Redisearch is loaded."
+def if_redisearch_enabled(function):
+ "Decorator to check if Redisearch is enabled."
def wrapper(*args, **kwargs):
- if is_search_module_loaded():
+ if is_redisearch_enabled():
func = function(*args, **kwargs)
return func
return
@@ -51,7 +57,7 @@ def make_key(key):
return "{0}|{1}".format(frappe.conf.db_name, key).encode("utf-8")
-@if_redisearch_loaded
+@if_redisearch_enabled
def create_website_items_index():
"Creates Index Definition."
@@ -91,7 +97,7 @@ def to_search_field(field):
return TextField(field)
-@if_redisearch_loaded
+@if_redisearch_enabled
def insert_item_to_index(website_item_doc):
# Insert item to index
key = get_cache_key(website_item_doc.name)
@@ -104,7 +110,7 @@ def insert_item_to_index(website_item_doc):
insert_to_name_ac(website_item_doc.web_item_name, website_item_doc.name)
-@if_redisearch_loaded
+@if_redisearch_enabled
def insert_to_name_ac(web_name, doc_name):
ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=frappe.cache())
ac.add_suggestions(Suggestion(web_name, payload=doc_name))
@@ -120,14 +126,14 @@ def create_web_item_map(website_item_doc):
return web_item
-@if_redisearch_loaded
+@if_redisearch_enabled
def update_index_for_item(website_item_doc):
# Reinsert to Cache
insert_item_to_index(website_item_doc)
define_autocomplete_dictionary()
-@if_redisearch_loaded
+@if_redisearch_enabled
def delete_item_from_index(website_item_doc):
cache = frappe.cache()
key = get_cache_key(website_item_doc.name)
@@ -141,7 +147,7 @@ def delete_item_from_index(website_item_doc):
return True
-@if_redisearch_loaded
+@if_redisearch_enabled
def delete_from_ac_dict(website_item_doc):
"""Removes this items's name from autocomplete dictionary"""
cache = frappe.cache()
@@ -149,7 +155,7 @@ def delete_from_ac_dict(website_item_doc):
name_ac.delete(website_item_doc.web_item_name)
-@if_redisearch_loaded
+@if_redisearch_enabled
def define_autocomplete_dictionary():
"""Creates an autocomplete search dictionary for `name`.
Also creats autocomplete dictionary for `categories` if
@@ -182,7 +188,7 @@ def define_autocomplete_dictionary():
return True
-@if_redisearch_loaded
+@if_redisearch_enabled
def reindex_all_web_items():
items = frappe.get_all("Website Item", fields=get_fields_indexed(), filters={"published": True})
@@ -208,9 +214,3 @@ def get_fields_indexed():
fields_to_index = fields_to_index + mandatory_fields
return fields_to_index
-
-
-# TODO: Remove later
-# # Figure out a way to run this at startup
-define_autocomplete_dictionary()
-create_website_items_index()
diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py
index 77a749ef9eb..ce04068cf64 100644
--- a/erpnext/templates/pages/product_search.py
+++ b/erpnext/templates/pages/product_search.py
@@ -9,7 +9,7 @@ from erpnext.e_commerce.redisearch_utils import (
WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE,
WEBSITE_ITEM_INDEX,
WEBSITE_ITEM_NAME_AUTOCOMPLETE,
- is_search_module_loaded,
+ is_redisearch_enabled,
make_key,
)
from erpnext.e_commerce.shopping_cart.product_info import set_product_info_for_website
@@ -74,8 +74,8 @@ def search(query):
def product_search(query, limit=10, fuzzy_search=True):
search_results = {"from_redisearch": True, "results": []}
- if not is_search_module_loaded():
- # Redisearch module not loaded
+ if not is_redisearch_enabled():
+ # Redisearch module not enabled
search_results["from_redisearch"] = False
search_results["results"] = get_product_data(query, 0, limit)
return search_results
@@ -121,8 +121,8 @@ def convert_to_dict(redis_search_doc):
def get_category_suggestions(query):
search_results = {"results": []}
- if not is_search_module_loaded():
- # Redisearch module not loaded, query db
+ if not is_redisearch_enabled():
+ # Redisearch module not enabled, query db
categories = frappe.db.get_all(
"Item Group",
filters={"name": ["like", "%{0}%".format(query)], "show_in_website": 1},
From d074c93ac73cb771494c3a814a8f39e7902cdd55 Mon Sep 17 00:00:00 2001
From: Sagar Sharma
Date: Thu, 31 Mar 2022 19:57:42 +0530
Subject: [PATCH 05/35] refactor!: change "is_subcontracted" field type from
"Select" to "Check"
---
.../purchase_invoice/purchase_invoice.js | 6 ++---
.../purchase_invoice/purchase_invoice.json | 7 +++--
.../purchase_invoice/test_purchase_invoice.py | 6 ++---
.../purchase_invoice_item.json | 2 +-
.../report/tax_detail/test_tax_detail.json | 2 +-
.../doctype/purchase_order/purchase_order.js | 4 +--
.../purchase_order/purchase_order.json | 7 +++--
.../doctype/purchase_order/purchase_order.py | 8 +++---
.../purchase_order/test_purchase_order.py | 20 +++++++-------
.../doctype/purchase_order/test_records.json | 4 +--
.../purchase_order_item.json | 4 +--
.../supplier_quotation.json | 5 ++--
.../supplier_quotation/test_records.json | 2 +-
.../subcontract_order_summary.js | 2 +-
.../subcontract_order_summary.py | 2 +-
.../subcontracted_item_to_be_received.py | 2 +-
.../test_subcontracted_item_to_be_received.py | 2 +-
...tracted_raw_materials_to_be_transferred.py | 2 +-
...tracted_raw_materials_to_be_transferred.py | 2 +-
erpnext/controllers/accounts_controller.py | 2 +-
erpnext/controllers/buying_controller.py | 10 +++----
erpnext/controllers/subcontracting.py | 2 +-
erpnext/manufacturing/doctype/bom/test_bom.py | 2 +-
.../production_plan/production_plan.py | 2 +-
erpnext/patches.txt | 3 ++-
.../change_is_subcontracted_fieldtype.py | 26 +++++++++++++++++++
erpnext/public/js/controllers/buying.js | 2 +-
erpnext/public/js/controllers/transaction.js | 2 +-
erpnext/public/js/utils.js | 2 +-
erpnext/stock/doctype/bin/bin.py | 4 +--
.../item_alternative/test_item_alternative.py | 2 +-
.../purchase_receipt/purchase_receipt.js | 6 ++---
.../purchase_receipt/purchase_receipt.json | 7 +++--
.../purchase_receipt/test_purchase_receipt.py | 10 +++----
.../purchase_receipt/test_records.json | 2 +-
.../purchase_receipt_item.json | 2 +-
.../stock/doctype/stock_entry/stock_entry.js | 2 +-
.../test_stock_ledger_entry.py | 2 +-
erpnext/stock/get_item_details.py | 8 +++---
erpnext/stock/stock_ledger.py | 2 +-
erpnext/tests/test_subcontracting.py | 26 +++++++++----------
41 files changed, 119 insertions(+), 96 deletions(-)
create mode 100644 erpnext/patches/v14_0/change_is_subcontracted_fieldtype.py
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 1a398aba2ec..0af3f2c513a 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -141,7 +141,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
})
}, __("Get Items From"));
}
- this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes");
+ this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted);
if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) {
frappe.model.with_doc("Supplier", me.frm.doc.supplier, function() {
@@ -569,10 +569,10 @@ frappe.ui.form.on("Purchase Invoice", {
},
is_subcontracted: function(frm) {
- if (frm.doc.is_subcontracted === "Yes") {
+ if (frm.doc.is_subcontracted) {
erpnext.buying.get_default_bom(frm);
}
- frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes");
+ frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted);
},
update_stock: function(frm) {
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index bd0116443ff..e1f01d0b511 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -543,11 +543,10 @@
"fieldtype": "Column Break"
},
{
- "default": "No",
+ "default": "0",
"fieldname": "is_subcontracted",
- "fieldtype": "Select",
+ "fieldtype": "Check",
"label": "Raw Materials Supplied",
- "options": "No\nYes",
"print_hide": 1
},
{
@@ -1366,7 +1365,7 @@
"width": "50px"
},
{
- "depends_on": "eval:doc.update_stock && doc.is_subcontracted==\"Yes\"",
+ "depends_on": "eval:doc.update_stock && doc.is_subcontracted",
"fieldname": "supplier_warehouse",
"fieldtype": "Link",
"label": "Supplier Warehouse",
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 843f66d546b..73390dd6f45 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -901,7 +901,7 @@ class TestPurchaseInvoice(unittest.TestCase):
)
pi = make_purchase_invoice(
- item_code="_Test FG Item", qty=10, rate=500, update_stock=1, is_subcontracted="Yes"
+ item_code="_Test FG Item", qty=10, rate=500, update_stock=1, is_subcontracted=1
)
self.assertEqual(len(pi.get("supplied_items")), 2)
@@ -1611,7 +1611,7 @@ def make_purchase_invoice(**args):
pi.conversion_rate = args.conversion_rate or 1
pi.is_return = args.is_return
pi.return_against = args.return_against
- pi.is_subcontracted = args.is_subcontracted or "No"
+ pi.is_subcontracted = args.is_subcontracted or 0
pi.supplier_warehouse = args.supplier_warehouse or "_Test Warehouse 1 - _TC"
pi.cost_center = args.parent_cost_center
@@ -1674,7 +1674,7 @@ def make_purchase_invoice_against_cost_center(**args):
pi.is_return = args.is_return
pi.is_return = args.is_return
pi.credit_to = args.return_against or "Creditors - _TC"
- pi.is_subcontracted = args.is_subcontracted or "No"
+ pi.is_subcontracted = args.is_subcontracted or 0
if args.supplier_warehouse:
pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index f9b2efd053b..6651195e5f2 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -623,7 +623,7 @@
},
{
"default": "0",
- "depends_on": "eval:parent.is_subcontracted == 'Yes'",
+ "depends_on": "eval:parent.is_subcontracted",
"fieldname": "include_exploded_items",
"fieldtype": "Check",
"label": "Include Exploded Items",
diff --git a/erpnext/accounts/report/tax_detail/test_tax_detail.json b/erpnext/accounts/report/tax_detail/test_tax_detail.json
index 3a4b1754554..e4903167cba 100644
--- a/erpnext/accounts/report/tax_detail/test_tax_detail.json
+++ b/erpnext/accounts/report/tax_detail/test_tax_detail.json
@@ -302,7 +302,7 @@
"is_opening": "No",
"is_paid": 0,
"is_return": 0,
- "is_subcontracted": "No",
+ "is_subcontracted": 0,
"items": [
{
"allow_zero_valuation_rate": 0,
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 2005dac37d7..c9e67987c6b 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -179,7 +179,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e
if (doc.status != "On Hold") {
if(flt(doc.per_received) < 100 && allow_receipt) {
cur_frm.add_custom_button(__('Purchase Receipt'), this.make_purchase_receipt, __('Create'));
- if(doc.is_subcontracted==="Yes" && me.has_unsupplied_items()) {
+ if(doc.is_subcontracted && me.has_unsupplied_items()) {
cur_frm.add_custom_button(__('Material to Supplier'),
function() { me.make_stock_entry(); }, __("Transfer"));
}
@@ -636,7 +636,7 @@ function set_schedule_date(frm) {
frappe.provide("erpnext.buying");
frappe.ui.form.on("Purchase Order", "is_subcontracted", function(frm) {
- if (frm.doc.is_subcontracted === "Yes") {
+ if (frm.doc.is_subcontracted) {
erpnext.buying.get_default_bom(frm);
}
});
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 896208f25e1..e4e00d18888 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -457,16 +457,15 @@
"fieldtype": "Column Break"
},
{
- "default": "No",
+ "default": "0",
"fieldname": "is_subcontracted",
- "fieldtype": "Select",
+ "fieldtype": "Check",
"in_standard_filter": 1,
"label": "Supply Raw Materials",
- "options": "No\nYes",
"print_hide": 1
},
{
- "depends_on": "eval:doc.is_subcontracted==\"Yes\"",
+ "depends_on": "eval:doc.is_subcontracted",
"fieldname": "supplier_warehouse",
"fieldtype": "Link",
"label": "Supplier Warehouse",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 582bd8d1db8..5860c4c8ae2 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -194,7 +194,7 @@ class PurchaseOrder(BuyingController):
)
def validate_bom_for_subcontracting_items(self):
- if self.is_subcontracted == "Yes":
+ if self.is_subcontracted:
for item in self.items:
if not item.bom:
frappe.throw(
@@ -294,7 +294,7 @@ class PurchaseOrder(BuyingController):
self.set_status(update=True, status=status)
self.update_requested_qty()
self.update_ordered_qty()
- if self.is_subcontracted == "Yes":
+ if self.is_subcontracted:
self.update_reserved_qty_for_subcontract()
self.notify_update()
@@ -311,7 +311,7 @@ class PurchaseOrder(BuyingController):
self.update_ordered_qty()
self.validate_budget()
- if self.is_subcontracted == "Yes":
+ if self.is_subcontracted:
self.update_reserved_qty_for_subcontract()
frappe.get_doc("Authorization Control").validate_approving_authority(
@@ -331,7 +331,7 @@ class PurchaseOrder(BuyingController):
if self.has_drop_ship_item():
self.update_delivered_qty_in_sales_order()
- if self.is_subcontracted == "Yes":
+ if self.is_subcontracted:
self.update_reserved_qty_for_subcontract()
self.check_on_hold_or_closed_status()
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index e4fb970c3f7..1a7f2dd5d97 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -390,7 +390,7 @@ class TestPurchaseOrder(FrappeTestCase):
frappe.get_doc("Item Tax Template", "Test Update Items Template - _TC").delete()
def test_update_child_uom_conv_factor_change(self):
- po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
+ po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1)
total_reqd_qty = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")])
trans_item = json.dumps(
@@ -573,7 +573,7 @@ class TestPurchaseOrder(FrappeTestCase):
automatically_fetch_payment_terms(enable=0)
def test_subcontracting(self):
- po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
+ po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1)
self.assertEqual(len(po.get("supplied_items")), 2)
def test_warehouse_company_validation(self):
@@ -617,7 +617,7 @@ class TestPurchaseOrder(FrappeTestCase):
"doctype": "Purchase Order",
"company": "_Test Company",
"supplier": "_Test Supplier",
- "is_subcontracted": "No",
+ "is_subcontracted": 0,
"schedule_date": add_days(nowdate(), 1),
"currency": frappe.get_cached_value("Company", "_Test Company", "default_currency"),
"conversion_factor": 1,
@@ -764,7 +764,7 @@ class TestPurchaseOrder(FrappeTestCase):
)
# Submit PO
- po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
+ po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1)
bin2 = frappe.db.get_value(
"Bin",
@@ -919,7 +919,7 @@ class TestPurchaseOrder(FrappeTestCase):
po = create_purchase_order(
item_code=item_code,
qty=1,
- is_subcontracted="Yes",
+ is_subcontracted=1,
supplier_warehouse="_Test Warehouse 1 - _TC",
include_exploded_items=1,
)
@@ -936,7 +936,7 @@ class TestPurchaseOrder(FrappeTestCase):
po1 = create_purchase_order(
item_code=item_code,
qty=1,
- is_subcontracted="Yes",
+ is_subcontracted=1,
supplier_warehouse="_Test Warehouse 1 - _TC",
include_exploded_items=0,
)
@@ -957,7 +957,7 @@ class TestPurchaseOrder(FrappeTestCase):
po = create_purchase_order(
item_code=item_code,
qty=order_qty,
- is_subcontracted="Yes",
+ is_subcontracted=1,
supplier_warehouse="_Test Warehouse 1 - _TC",
)
@@ -1050,7 +1050,7 @@ class TestPurchaseOrder(FrappeTestCase):
po = create_purchase_order(
item_code=item_code,
qty=order_qty,
- is_subcontracted="Yes",
+ is_subcontracted=1,
supplier_warehouse="_Test Warehouse 1 - _TC",
do_not_save=True,
)
@@ -1283,7 +1283,7 @@ def create_purchase_order(**args):
po.schedule_date = add_days(nowdate(), 1)
po.company = args.company or "_Test Company"
po.supplier = args.supplier or "_Test Supplier"
- po.is_subcontracted = args.is_subcontracted or "No"
+ po.is_subcontracted = args.is_subcontracted or 0
po.currency = args.currency or frappe.get_cached_value("Company", po.company, "default_currency")
po.conversion_factor = args.conversion_factor or 1
po.supplier_warehouse = args.supplier_warehouse or None
@@ -1309,7 +1309,7 @@ def create_purchase_order(**args):
if not args.do_not_save:
po.insert()
if not args.do_not_submit:
- if po.is_subcontracted == "Yes":
+ if po.is_subcontracted:
supp_items = po.get("supplied_items")
for d in supp_items:
if not d.reserve_warehouse:
diff --git a/erpnext/buying/doctype/purchase_order/test_records.json b/erpnext/buying/doctype/purchase_order/test_records.json
index 74b8f1b429b..896050ce43a 100644
--- a/erpnext/buying/doctype/purchase_order/test_records.json
+++ b/erpnext/buying/doctype/purchase_order/test_records.json
@@ -8,7 +8,7 @@
"doctype": "Purchase Order",
"base_grand_total": 5000.0,
"grand_total": 5000.0,
- "is_subcontracted": "Yes",
+ "is_subcontracted": 1,
"naming_series": "_T-Purchase Order-",
"base_net_total": 5000.0,
"items": [
@@ -42,7 +42,7 @@
"doctype": "Purchase Order",
"base_grand_total": 5000.0,
"grand_total": 5000.0,
- "is_subcontracted": "No",
+ "is_subcontracted": 0,
"naming_series": "_T-Purchase Order-",
"base_net_total": 5000.0,
"items": [
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index a18c527644e..f72c5988404 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -572,7 +572,7 @@
"read_only": 1
},
{
- "depends_on": "eval:parent.is_subcontracted == 'Yes'",
+ "depends_on": "eval:parent.is_subcontracted",
"fieldname": "bom",
"fieldtype": "Link",
"label": "BOM",
@@ -581,7 +581,7 @@
},
{
"default": "0",
- "depends_on": "eval:parent.is_subcontracted == 'Yes'",
+ "depends_on": "eval:parent.is_subcontracted",
"fieldname": "include_exploded_items",
"fieldtype": "Check",
"label": "Include Exploded Items",
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 567e41fb61f..8d1939a101b 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -773,11 +773,10 @@
"fieldtype": "Column Break"
},
{
- "default": "No",
+ "default": "0",
"fieldname": "is_subcontracted",
- "fieldtype": "Select",
+ "fieldtype": "Check",
"label": "Is Subcontracted",
- "options": "\nYes\nNo",
"print_hide": 1
},
{
diff --git a/erpnext/buying/doctype/supplier_quotation/test_records.json b/erpnext/buying/doctype/supplier_quotation/test_records.json
index 0f835d2a40a..8acac3210d5 100644
--- a/erpnext/buying/doctype/supplier_quotation/test_records.json
+++ b/erpnext/buying/doctype/supplier_quotation/test_records.json
@@ -7,7 +7,7 @@
"doctype": "Supplier Quotation",
"base_grand_total": 5000.0,
"grand_total": 5000.0,
- "is_subcontracted": "No",
+ "is_subcontracted": 0,
"naming_series": "_T-Supplier Quotation-",
"base_net_total": 5000.0,
"items": [
diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
index 5ba52f1b21e..6889322fb93 100644
--- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
+++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
@@ -35,7 +35,7 @@ frappe.query_reports["Subcontract Order Summary"] = {
return {
filters: {
docstatus: 1,
- is_subcontracted: 'Yes',
+ is_subcontracted: 1,
company: frappe.query_report.get_filter_value('company')
}
}
diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
index 1b2705a7be3..3d666375764 100644
--- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
+++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
@@ -45,7 +45,7 @@ def get_subcontracted_orders(report_filters):
def get_filters(report_filters):
filters = [
["Purchase Order", "docstatus", "=", 1],
- ["Purchase Order", "is_subcontracted", "=", "Yes"],
+ ["Purchase Order", "is_subcontracted", "=", 1],
[
"Purchase Order",
"transaction_date",
diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py
index 004657b6e86..2e90de66efe 100644
--- a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py
+++ b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py
@@ -78,7 +78,7 @@ def get_data(data, filters):
def get_po(filters):
record_filters = [
- ["is_subcontracted", "=", "Yes"],
+ ["is_subcontracted", "=", 1],
["supplier", "=", filters.supplier],
["transaction_date", "<=", filters.to_date],
["transaction_date", ">=", filters.from_date],
diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
index 26e4243eeee..57f8741b5bf 100644
--- a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
+++ b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
@@ -17,7 +17,7 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
class TestSubcontractedItemToBeReceived(FrappeTestCase):
def test_pending_and_received_qty(self):
- po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
+ po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1)
transfer_param = []
make_stock_entry(
item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100
diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py
index 98b18da4acb..6b8a3b140a7 100644
--- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py
+++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py
@@ -72,7 +72,7 @@ def get_po_items_to_supply(filters):
],
filters=[
["Purchase Order", "per_received", "<", "100"],
- ["Purchase Order", "is_subcontracted", "=", "Yes"],
+ ["Purchase Order", "is_subcontracted", "=", 1],
["Purchase Order", "supplier", "=", filters.supplier],
["Purchase Order", "transaction_date", "<=", filters.to_date],
["Purchase Order", "transaction_date", ">=", filters.from_date],
diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
index 401176d5cef..2791a26db78 100644
--- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
+++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
@@ -19,7 +19,7 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
class TestSubcontractedItemToBeTransferred(FrappeTestCase):
def test_pending_and_transferred_qty(self):
po = create_purchase_order(
- item_code="_Test FG Item", is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ item_code="_Test FG Item", is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
# Material Receipt of RMs
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 3a20d3f232f..8a9318e184e 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -2586,7 +2586,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
parent.update_ordered_qty()
parent.update_ordered_and_reserved_qty()
parent.update_receiving_percentage()
- if parent.is_subcontracted == "Yes":
+ if parent.is_subcontracted:
parent.update_reserved_qty_for_subcontract()
parent.create_raw_materials_supplied("supplied_items")
parent.save()
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 47892073f3b..c0fd83ec597 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -167,7 +167,7 @@ class BuyingController(StockController, Subcontracting):
_("Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same").format(item.idx)
)
- if item.get("from_warehouse") and self.get("is_subcontracted") == "Yes":
+ if item.get("from_warehouse") and self.get("is_subcontracted"):
frappe.throw(
_(
"Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor"
@@ -342,7 +342,7 @@ class BuyingController(StockController, Subcontracting):
if not self.is_subcontracted and self.sub_contracted_items:
frappe.throw(_("Please enter 'Is Subcontracted' as Yes or No"))
- if self.is_subcontracted == "Yes":
+ if self.is_subcontracted:
if self.doctype in ["Purchase Receipt", "Purchase Invoice"] and not self.supplier_warehouse:
frappe.throw(_("Supplier Warehouse mandatory for sub-contracted {0}").format(self.doctype))
@@ -363,14 +363,14 @@ class BuyingController(StockController, Subcontracting):
item.bom = None
def create_raw_materials_supplied(self, raw_material_table):
- if self.is_subcontracted == "Yes":
+ if self.is_subcontracted:
self.set_materials_for_subcontracted_items(raw_material_table)
elif self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
for item in self.get("items"):
item.rm_supp_cost = 0.0
- if self.is_subcontracted == "No" and self.get("supplied_items"):
+ if not self.is_subcontracted and self.get("supplied_items"):
self.set("supplied_items", [])
@property
@@ -803,7 +803,7 @@ class BuyingController(StockController, Subcontracting):
if self.doctype == "Material Request":
return
- if hasattr(self, "is_subcontracted") and self.is_subcontracted == "Yes":
+ if hasattr(self, "is_subcontracted") and self.is_subcontracted:
validate_item_type(self, "is_sub_contracted_item", "subcontracted")
else:
validate_item_type(self, "is_purchase_item", "purchase")
diff --git a/erpnext/controllers/subcontracting.py b/erpnext/controllers/subcontracting.py
index 70830882efa..4bce06ff9b0 100644
--- a/erpnext/controllers/subcontracting.py
+++ b/erpnext/controllers/subcontracting.py
@@ -407,7 +407,7 @@ class Subcontracting:
def set_consumed_qty_in_po(self):
# Update consumed qty back in the purchase order
- if self.is_subcontracted != "Yes":
+ if not self.is_subcontracted:
return
self.__get_purchase_orders()
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index 524f45bfc23..62fc0724e03 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -251,7 +251,7 @@ class TestBOM(FrappeTestCase):
self.assertEqual(bom.items[2].rate, 0)
# test in Purchase Order sourced_by_supplier is not added to Supplied Item
po = create_purchase_order(
- item_code=item_code, qty=1, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ item_code=item_code, qty=1, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
bom_items = sorted([d.item_code for d in bom.items if d.sourced_by_supplier != 1])
supplied_items = sorted([d.rm_item_code for d in po.supplied_items])
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 89f9ca6d83a..60b32b84b05 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -501,7 +501,7 @@ class ProductionPlan(Document):
po = frappe.new_doc("Purchase Order")
po.supplier = supplier
po.schedule_date = getdate(po_list[0].schedule_date) if po_list[0].schedule_date else nowdate()
- po.is_subcontracted = "Yes"
+ po.is_subcontracted = 1
for row in po_list:
po_data = {
"item_code": row.production_item,
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 028834a0ec4..0b7dd02e9a2 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -334,6 +334,7 @@ erpnext.patches.v13_0.delete_bank_reconciliation_detail
erpnext.patches.v13_0.enable_provisional_accounting
erpnext.patches.v13_0.non_profit_deprecation_warning
erpnext.patches.v13_0.enable_ksa_vat_docs #1
+erpnext.patches.v14_0.change_is_subcontracted_fieldtype
[post_model_sync]
erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents
@@ -362,4 +363,4 @@ erpnext.patches.v14_0.update_employee_advance_status
erpnext.patches.v13_0.add_cost_center_in_loans
erpnext.patches.v13_0.set_return_against_in_pos_invoice_references
erpnext.patches.v13_0.remove_unknown_links_to_prod_plan_items # 24-03-2022
-erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances
+erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances
\ No newline at end of file
diff --git a/erpnext/patches/v14_0/change_is_subcontracted_fieldtype.py b/erpnext/patches/v14_0/change_is_subcontracted_fieldtype.py
new file mode 100644
index 00000000000..ba919a756a8
--- /dev/null
+++ b/erpnext/patches/v14_0/change_is_subcontracted_fieldtype.py
@@ -0,0 +1,26 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+
+
+def execute():
+ for doctype in ["Purchase Order", "Purchase Receipt", "Purchase Invoice", "Supplier Quotation"]:
+ frappe.db.sql(
+ """
+ UPDATE `tab{doctype}`
+ SET is_subcontracted = 0
+ where is_subcontracted in ('', NULL, 'No')""".format(
+ doctype=doctype
+ )
+ )
+ frappe.db.sql(
+ """
+ UPDATE `tab{doctype}`
+ SET is_subcontracted = 1
+ where is_subcontracted = 'Yes'""".format(
+ doctype=doctype
+ )
+ )
+
+ frappe.reload_doc(frappe.get_meta(doctype).module, "doctype", frappe.scrub(doctype))
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index 54e5daa6bd4..bbf1ff67368 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -81,7 +81,7 @@ erpnext.buying.BuyingController = class BuyingController extends erpnext.Transac
}
this.frm.set_query("item_code", "items", function() {
- if (me.frm.doc.is_subcontracted == "Yes") {
+ if (me.frm.doc.is_subcontracted) {
return{
query: "erpnext.controllers.queries.item_query",
filters:{ 'supplier': me.frm.doc.supplier, 'is_sub_contracted_item': 1 }
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 23c2bd405c1..57cbe91fa09 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -239,7 +239,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
() => set_value('currency', currency),
() => set_value('price_list_currency', currency),
() => set_value('status', 'Draft'),
- () => set_value('is_subcontracted', 'No'),
+ () => set_value('is_subcontracted', 0),
() => {
if(this.frm.doc.company && !this.frm.doc.amended_from) {
this.frm.trigger("company");
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 9339c5d998f..eded16529cc 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -483,7 +483,7 @@ erpnext.utils.update_child_items = function(opts) {
if (frm.doc.doctype == 'Sales Order') {
filters = {"is_sales_item": 1};
} else if (frm.doc.doctype == 'Purchase Order') {
- if (frm.doc.is_subcontracted == "Yes") {
+ if (frm.doc.is_subcontracted) {
filters = {"is_sub_contracted_item": 1};
} else {
filters = {"is_purchase_item": 1};
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 6cb9f7e479a..6ea4525606f 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -54,7 +54,7 @@ class Bin(Document):
(supplied_item.rm_item_code == self.item_code)
& (po.name == supplied_item.parent)
& (po.docstatus == 1)
- & (po.is_subcontracted == "Yes")
+ & (po.is_subcontracted)
& (po.status != "Closed")
& (po.per_received < 100)
& (supplied_item.reserve_warehouse == self.warehouse)
@@ -79,7 +79,7 @@ class Bin(Document):
& (se.name == se_item.parent)
& (po.name == se.purchase_order)
& (po.docstatus == 1)
- & (po.is_subcontracted == "Yes")
+ & (po.is_subcontracted == 1)
& (po.status != "Closed")
& (po.per_received < 100)
)
diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.py b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
index d829b2cbf39..32c58c5ae1d 100644
--- a/erpnext/stock/doctype/item_alternative/test_item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
@@ -41,7 +41,7 @@ class TestItemAlternative(FrappeTestCase):
supplier_warehouse = "Test Supplier Warehouse - _TC"
po = create_purchase_order(
item="Test Finished Goods - A",
- is_subcontracted="Yes",
+ is_subcontracted=1,
qty=5,
rate=3000,
supplier_warehouse=supplier_warehouse,
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 0182ed55a18..51ec598f726 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -200,7 +200,7 @@ erpnext.stock.PurchaseReceiptController = class PurchaseReceiptController extend
cur_frm.add_custom_button(__('Reopen'), this.reopen_purchase_receipt, __("Status"))
}
- this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes");
+ this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted);
}
make_purchase_invoice() {
@@ -298,10 +298,10 @@ cur_frm.fields_dict['items'].grid.get_field('bom').get_query = function(doc, cdt
frappe.provide("erpnext.buying");
frappe.ui.form.on("Purchase Receipt", "is_subcontracted", function(frm) {
- if (frm.doc.is_subcontracted === "Yes") {
+ if (frm.doc.is_subcontracted) {
erpnext.buying.get_default_bom(frm);
}
- frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes");
+ frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted);
});
frappe.ui.form.on('Purchase Receipt Item', {
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index 6d4b4a19bd2..e4e4d15d382 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -437,17 +437,16 @@
"fieldtype": "Column Break"
},
{
- "default": "No",
+ "default": "0",
"fieldname": "is_subcontracted",
- "fieldtype": "Select",
+ "fieldtype": "Check",
"label": "Raw Materials Consumed",
"oldfieldname": "is_subcontracted",
"oldfieldtype": "Select",
- "options": "No\nYes",
"print_hide": 1
},
{
- "depends_on": "eval:doc.is_subcontracted==\"Yes\"",
+ "depends_on": "eval:doc.is_subcontracted",
"fieldname": "supplier_warehouse",
"fieldtype": "Link",
"label": "Supplier Warehouse",
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index a6f82b08dc0..bfbdd562921 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -327,7 +327,7 @@ class TestPurchaseReceipt(FrappeTestCase):
target="_Test Warehouse 1 - _TC",
basic_rate=100,
)
- pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=500, is_subcontracted="Yes")
+ pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=500, is_subcontracted=1)
self.assertEqual(len(pr.get("supplied_items")), 2)
rm_supp_cost = sum(d.amount for d in pr.get("supplied_items"))
@@ -362,7 +362,7 @@ class TestPurchaseReceipt(FrappeTestCase):
item_code="_Test FG Item",
qty=10,
rate=0,
- is_subcontracted="Yes",
+ is_subcontracted=1,
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
supplier_warehouse="Work In Progress - TCP1",
@@ -401,7 +401,7 @@ class TestPurchaseReceipt(FrappeTestCase):
item_code=item_code,
qty=1,
include_exploded_items=0,
- is_subcontracted="Yes",
+ is_subcontracted=1,
supplier_warehouse="_Test Warehouse 1 - _TC",
)
@@ -1122,7 +1122,7 @@ class TestPurchaseReceipt(FrappeTestCase):
po = create_purchase_order(
item_code=item_code,
qty=order_qty,
- is_subcontracted="Yes",
+ is_subcontracted=1,
supplier_warehouse="_Test Warehouse 1 - _TC",
)
@@ -1465,7 +1465,7 @@ def make_purchase_receipt(**args):
pr.set_posting_time = 1
pr.company = args.company or "_Test Company"
pr.supplier = args.supplier or "_Test Supplier"
- pr.is_subcontracted = args.is_subcontracted or "No"
+ pr.is_subcontracted = args.is_subcontracted or 0
pr.supplier_warehouse = args.supplier_warehouse or "_Test Warehouse 1 - _TC"
pr.currency = args.currency or "INR"
pr.is_return = args.is_return
diff --git a/erpnext/stock/doctype/purchase_receipt/test_records.json b/erpnext/stock/doctype/purchase_receipt/test_records.json
index 724e3d729a2..990ad12b30e 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_records.json
+++ b/erpnext/stock/doctype/purchase_receipt/test_records.json
@@ -92,7 +92,7 @@
"currency": "INR",
"doctype": "Purchase Receipt",
"base_grand_total": 5000.0,
- "is_subcontracted": "Yes",
+ "is_subcontracted": 1,
"base_net_total": 5000.0,
"items": [
{
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index e5994b2dd48..03a4201ce5c 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -648,7 +648,7 @@
},
{
"default": "0",
- "depends_on": "eval:parent.is_subcontracted == 'Yes'",
+ "depends_on": "eval:parent.is_subcontracted",
"fieldname": "include_exploded_items",
"fieldtype": "Check",
"label": "Include Exploded Items",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 1aafcee5bf8..a94087821a5 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -793,7 +793,7 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle
return {
"filters": {
"docstatus": 1,
- "is_subcontracted": "Yes",
+ "is_subcontracted": 1,
"company": me.frm.doc.company
}
};
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index 42956a129be..6561362c3af 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -436,7 +436,7 @@ class TestStockLedgerEntry(FrappeTestCase):
item_code=subcontracted_item,
qty=10,
rate=20,
- is_subcontracted="Yes",
+ is_subcontracted=1,
)
self.assertEqual(pr1.items[0].valuation_rate, 120)
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index f72588e034d..b75bc7241d4 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -50,7 +50,7 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru
"transaction_date": None,
"conversion_rate": 1.0,
"buying_price_list": None,
- "is_subcontracted": "Yes" / "No",
+ "is_subcontracted": 0/1,
"ignore_pricing_rule": 0/1
"project": ""
"set_warehouse": ""
@@ -124,7 +124,7 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru
if args.transaction_date and item.lead_time_days:
out.schedule_date = out.lead_time_date = add_days(args.transaction_date, item.lead_time_days)
- if args.get("is_subcontracted") == "Yes":
+ if args.get("is_subcontracted"):
out.bom = args.get("bom") or get_default_bom(args.item_code)
get_gross_profit(out)
@@ -237,7 +237,7 @@ def validate_item_details(args, item):
throw(_("Item {0} is a template, please select one of its variants").format(item.name))
elif args.transaction_type == "buying" and args.doctype != "Material Request":
- if args.get("is_subcontracted") == "Yes" and item.is_sub_contracted_item != 1:
+ if args.get("is_subcontracted") and item.is_sub_contracted_item != 1:
throw(_("Item {0} must be a Sub-contracted Item").format(item.name))
@@ -258,7 +258,7 @@ def get_basic_details(args, item, overwrite_warehouse=True):
"transaction_date": None,
"conversion_rate": 1.0,
"buying_price_list": None,
- "is_subcontracted": "Yes" / "No",
+ "is_subcontracted": 0/1,
"ignore_pricing_rule": 0/1
"project": "",
barcode: "",
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 967b2b2294d..3e0ddab6d3b 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -715,7 +715,7 @@ class update_entries_after(object):
)
# Recalculate subcontracted item's rate in case of subcontracted purchase receipt/invoice
- if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_subcontracted") == "Yes":
+ if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_subcontracted"):
doc = frappe.get_doc(sle.voucher_type, sle.voucher_no)
doc.update_valuation_rate(reset_outgoing_rate=False)
for d in doc.items + doc.supplied_items:
diff --git a/erpnext/tests/test_subcontracting.py b/erpnext/tests/test_subcontracting.py
index 07291e851b5..bf12181c527 100644
--- a/erpnext/tests/test_subcontracting.py
+++ b/erpnext/tests/test_subcontracting.py
@@ -50,7 +50,7 @@ class TestSubcontracting(unittest.TestCase):
itemwise_details = make_stock_in_entry(rm_items=rm_items)
po = create_purchase_order(
- rm_items=items, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
for d in rm_items:
@@ -112,7 +112,7 @@ class TestSubcontracting(unittest.TestCase):
itemwise_details = make_stock_in_entry(rm_items=rm_items)
po = create_purchase_order(
- rm_items=items, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
for d in rm_items:
@@ -175,7 +175,7 @@ class TestSubcontracting(unittest.TestCase):
itemwise_details = make_stock_in_entry(rm_items=rm_items)
po = create_purchase_order(
- rm_items=items, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
for d in rm_items:
@@ -239,7 +239,7 @@ class TestSubcontracting(unittest.TestCase):
itemwise_details = make_stock_in_entry(rm_items=rm_items)
po = create_purchase_order(
- rm_items=items, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
for d in rm_items:
@@ -298,7 +298,7 @@ class TestSubcontracting(unittest.TestCase):
itemwise_details = make_stock_in_entry(rm_items=rm_items)
po = create_purchase_order(
- rm_items=items, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
for d in rm_items:
@@ -363,7 +363,7 @@ class TestSubcontracting(unittest.TestCase):
itemwise_details = make_stock_in_entry(rm_items=rm_items)
po = create_purchase_order(
- rm_items=items, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
for d in rm_items:
@@ -421,7 +421,7 @@ class TestSubcontracting(unittest.TestCase):
itemwise_details = make_stock_in_entry(rm_items=rm_items)
po = create_purchase_order(
- rm_items=items, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
for d in rm_items:
@@ -492,7 +492,7 @@ class TestSubcontracting(unittest.TestCase):
itemwise_details = make_stock_in_entry(rm_items=rm_items)
po = create_purchase_order(
- rm_items=items, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
for d in rm_items:
@@ -529,7 +529,7 @@ class TestSubcontracting(unittest.TestCase):
itemwise_details = make_stock_in_entry(rm_items=rm_items)
po = create_purchase_order(
- rm_items=items, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
for d in rm_items:
@@ -609,7 +609,7 @@ class TestSubcontracting(unittest.TestCase):
itemwise_details = make_stock_in_entry(rm_items=rm_items)
po = create_purchase_order(
- rm_items=items, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
for d in rm_items:
@@ -675,7 +675,7 @@ class TestSubcontracting(unittest.TestCase):
itemwise_details = make_stock_in_entry(rm_items=rm_items)
po = create_purchase_order(
- rm_items=items, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
for d in rm_items:
@@ -751,7 +751,7 @@ class TestSubcontracting(unittest.TestCase):
itemwise_details = make_stock_in_entry(rm_items=rm_items)
po = create_purchase_order(
- rm_items=items, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
for d in rm_items:
@@ -834,7 +834,7 @@ class TestSubcontracting(unittest.TestCase):
itemwise_details = make_stock_in_entry(rm_items=rm_items)
po = create_purchase_order(
- rm_items=items, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC"
+ rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
)
for d in rm_items:
From 0a3cdc2623fc978709069557d3d2be047bec2586 Mon Sep 17 00:00:00 2001
From: Sagar Sharma
Date: Thu, 31 Mar 2022 20:14:42 +0530
Subject: [PATCH 06/35] chore: rename "is_subcontracted" field label to "Is
Subcontracted"
---
erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json | 2 +-
erpnext/buying/doctype/purchase_order/purchase_order.json | 2 +-
erpnext/stock/doctype/purchase_receipt/purchase_receipt.json | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index e1f01d0b511..9f87c5ab54e 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -546,7 +546,7 @@
"default": "0",
"fieldname": "is_subcontracted",
"fieldtype": "Check",
- "label": "Raw Materials Supplied",
+ "label": "Is Subcontracted",
"print_hide": 1
},
{
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index e4e00d18888..9a1f9d19958 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -461,7 +461,7 @@
"fieldname": "is_subcontracted",
"fieldtype": "Check",
"in_standard_filter": 1,
- "label": "Supply Raw Materials",
+ "label": "Is Subcontracted",
"print_hide": 1
},
{
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index e4e4d15d382..6e5f6f5b529 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -440,7 +440,7 @@
"default": "0",
"fieldname": "is_subcontracted",
"fieldtype": "Check",
- "label": "Raw Materials Consumed",
+ "label": "Is Subcontracted",
"oldfieldname": "is_subcontracted",
"oldfieldtype": "Select",
"print_hide": 1
From 01c64915c8da72d2c4d421685886ac14ab25ca13 Mon Sep 17 00:00:00 2001
From: Sagar Sharma
Date: Thu, 31 Mar 2022 22:40:28 +0530
Subject: [PATCH 07/35] chore: remove unwanted validation
---
erpnext/controllers/buying_controller.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index c0fd83ec597..3a69e717bd9 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -339,9 +339,6 @@ class BuyingController(StockController, Subcontracting):
return supplied_items_cost
def validate_for_subcontracting(self):
- if not self.is_subcontracted and self.sub_contracted_items:
- frappe.throw(_("Please enter 'Is Subcontracted' as Yes or No"))
-
if self.is_subcontracted:
if self.doctype in ["Purchase Receipt", "Purchase Invoice"] and not self.supplier_warehouse:
frappe.throw(_("Supplier Warehouse mandatory for sub-contracted {0}").format(self.doctype))
From 07f17453cd2b673311029764135c3e26128905b8 Mon Sep 17 00:00:00 2001
From: marination
Date: Fri, 1 Apr 2022 18:47:01 +0530
Subject: [PATCH 08/35] fix: Use Payload in AutoCompleter (categories in
search) and misc
- Separate Item group and Item autocomplete dict definition
- Add payload along with Item group, containing namke and route
- Pass weightage while defining item group autocomplete dict (auto sort)
- Use payload while getting results for categories in search
- Remove check to show categories, always show
- Search fields mandatory if reidsearch enabled
- Code separation (rough)
---
.../e_commerce_settings.json | 12 +----
erpnext/e_commerce/redisearch_utils.py | 46 +++++++++++++------
erpnext/templates/pages/product_search.py | 8 +++-
3 files changed, 41 insertions(+), 25 deletions(-)
diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json
index 62505e61db6..e6f08f708a8 100644
--- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json
+++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json
@@ -48,7 +48,6 @@
"redisearch_warning",
"search_index_fields",
"is_redisearch_enabled",
- "show_categories_in_search_autocomplete",
"is_redisearch_loaded",
"shop_by_category_section",
"slideshow",
@@ -294,6 +293,7 @@
"fieldname": "search_index_fields",
"fieldtype": "Small Text",
"label": "Search Index Fields",
+ "mandatory_depends_on": "is_redisearch_enabled",
"read_only_depends_on": "eval:!doc.is_redisearch_loaded"
},
{
@@ -302,14 +302,6 @@
"fieldtype": "Section Break",
"label": "Item Search Settings"
},
- {
- "default": "1",
- "depends_on": "is_redisearch_enabled",
- "fieldname": "show_categories_in_search_autocomplete",
- "fieldtype": "Check",
- "label": "Show Categories in Search Autocomplete",
- "read_only_depends_on": "eval:!doc.is_redisearch_loaded"
- },
{
"default": "0",
"fieldname": "is_redisearch_loaded",
@@ -379,7 +371,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2022-03-31 16:01:46.308663",
+ "modified": "2022-04-01 18:35:56.106756",
"modified_by": "Administrator",
"module": "E-commerce",
"name": "E Commerce Settings",
diff --git a/erpnext/e_commerce/redisearch_utils.py b/erpnext/e_commerce/redisearch_utils.py
index 78cc05af38b..32b35db04ce 100644
--- a/erpnext/e_commerce/redisearch_utils.py
+++ b/erpnext/e_commerce/redisearch_utils.py
@@ -157,17 +157,14 @@ def delete_from_ac_dict(website_item_doc):
@if_redisearch_enabled
def define_autocomplete_dictionary():
- """Creates an autocomplete search dictionary for `name`.
- Also creats autocomplete dictionary for `categories` if
- checked in E Commerce Settings"""
+ """
+ Defines/Redefines an autocomplete search dictionary for Website Item Name.
+ Also creats autocomplete dictionary for Published Item Groups.
+ """
cache = frappe.cache()
- name_ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=cache)
- cat_ac = AutoCompleter(make_key(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE), conn=cache)
-
- ac_categories = frappe.db.get_single_value(
- "E Commerce Settings", "show_categories_in_search_autocomplete"
- )
+ item_ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=cache)
+ item_group_ac = AutoCompleter(make_key(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE), conn=cache)
# Delete both autocomplete dicts
try:
@@ -176,16 +173,39 @@ def define_autocomplete_dictionary():
except Exception:
return False
+ create_items_autocomplete_dict(autocompleter=item_ac)
+ create_item_groups_autocomplete_dict(autocompleter=item_group_ac)
+
+
+@if_redisearch_enabled
+def create_items_autocomplete_dict(autocompleter):
+ "Add items as suggestions in Autocompleter."
items = frappe.get_all(
"Website Item", fields=["web_item_name", "item_group"], filters={"published": 1}
)
for item in items:
- name_ac.add_suggestions(Suggestion(item.web_item_name))
- if ac_categories and item.item_group:
- cat_ac.add_suggestions(Suggestion(item.item_group))
+ autocompleter.add_suggestions(Suggestion(item.web_item_name))
- return True
+
+@if_redisearch_enabled
+def create_item_groups_autocomplete_dict(autocompleter):
+ "Add item groups with weightage as suggestions in Autocompleter."
+ published_item_groups = frappe.get_all(
+ "Item Group", fields=["name", "route", "weightage"], filters={"show_in_website": 1}
+ )
+ if not published_item_groups:
+ return
+
+ for item_group in published_item_groups:
+ payload = {"name": item_group, "route": item_group.route}
+ autocompleter.add_suggestions(
+ Suggestion(
+ string=item_group.name,
+ score=item_group.weightage,
+ payload=payload, # additional info that can be retrieved later
+ )
+ )
@if_redisearch_enabled
diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py
index ce04068cf64..94e893e1369 100644
--- a/erpnext/templates/pages/product_search.py
+++ b/erpnext/templates/pages/product_search.py
@@ -1,6 +1,8 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
+import json
+
import frappe
from frappe.utils import cint, cstr
from redisearch import AutoCompleter, Client, Query
@@ -135,8 +137,10 @@ def get_category_suggestions(query):
return search_results
ac = AutoCompleter(make_key(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE), conn=frappe.cache())
- suggestions = ac.get_suggestions(query, num=10)
+ suggestions = ac.get_suggestions(query, num=10, with_payloads=True)
- search_results["results"] = [s.string for s in suggestions]
+ results = [json.loads(s.payload) for s in suggestions]
+
+ search_results["results"] = results
return search_results
From ea036e495854e0a65fc2c7fa2065078745e87987 Mon Sep 17 00:00:00 2001
From: marination
Date: Mon, 4 Apr 2022 11:07:53 +0530
Subject: [PATCH 09/35] fix: Better Exception Handling and vaeiabl naming
- Function to handle RS exceptions (create log and raise error)
- Handle `ResponseError` where it is anticipated
- Misc: Better variables
---
erpnext/e_commerce/redisearch_utils.py | 44 ++++++++++++++++++--------
1 file changed, 30 insertions(+), 14 deletions(-)
diff --git a/erpnext/e_commerce/redisearch_utils.py b/erpnext/e_commerce/redisearch_utils.py
index 32b35db04ce..f9890cca1a8 100644
--- a/erpnext/e_commerce/redisearch_utils.py
+++ b/erpnext/e_commerce/redisearch_utils.py
@@ -1,8 +1,10 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
+from frappe import _
from frappe.utils.redis_wrapper import RedisWrapper
+from redis import ResponseError
from redisearch import AutoCompleter, Client, IndexDefinition, Suggestion, TagField, TextField
WEBSITE_ITEM_INDEX = "website_items_index"
@@ -38,7 +40,7 @@ def is_search_module_loaded():
)
return "search" in parsed_output
except Exception:
- return False
+ return False # handling older redis versions
def if_redisearch_enabled(function):
@@ -64,15 +66,18 @@ def create_website_items_index():
# CREATE index
client = Client(make_key(WEBSITE_ITEM_INDEX), conn=frappe.cache())
- # DROP if already exists
try:
- client.drop_index()
- except Exception:
+ client.drop_index() # drop if already exists
+ except ResponseError:
+ # will most likely raise a ResponseError if index does not exist
+ # ignore and create index
pass
+ except Exception:
+ raise_redisearch_error()
idx_def = IndexDefinition([make_key(WEBSITE_ITEM_KEY_PREFIX)])
- # Based on e-commerce settings
+ # Index fields mentioned in e-commerce settings
idx_fields = frappe.db.get_single_value("E Commerce Settings", "search_index_fields")
idx_fields = idx_fields.split(",") if idx_fields else []
@@ -104,8 +109,8 @@ def insert_item_to_index(website_item_doc):
cache = frappe.cache()
web_item = create_web_item_map(website_item_doc)
- for k, v in web_item.items():
- super(RedisWrapper, cache).hset(make_key(key), k, v)
+ for field, value in web_item.items():
+ super(RedisWrapper, cache).hset(make_key(key), field, value)
insert_to_name_ac(website_item_doc.web_item_name, website_item_doc.name)
@@ -120,8 +125,8 @@ def create_web_item_map(website_item_doc):
fields_to_index = get_fields_indexed()
web_item = {}
- for f in fields_to_index:
- web_item[f] = website_item_doc.get(f) or ""
+ for field in fields_to_index:
+ web_item[field] = website_item_doc.get(field) or ""
return web_item
@@ -141,7 +146,7 @@ def delete_item_from_index(website_item_doc):
try:
cache.delete(key)
except Exception:
- return False
+ raise_redisearch_error()
delete_from_ac_dict(website_item_doc)
return True
@@ -171,7 +176,7 @@ def define_autocomplete_dictionary():
cache.delete(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE))
cache.delete(make_key(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE))
except Exception:
- return False
+ raise_redisearch_error()
create_items_autocomplete_dict(autocompleter=item_ac)
create_item_groups_autocomplete_dict(autocompleter=item_group_ac)
@@ -217,8 +222,8 @@ def reindex_all_web_items():
web_item = create_web_item_map(item)
key = make_key(get_cache_key(item.name))
- for k, v in web_item.items():
- super(RedisWrapper, cache).hset(key, k, v)
+ for field, value in web_item.items():
+ super(RedisWrapper, cache).hset(key, field, value)
def get_cache_key(name):
@@ -234,3 +239,14 @@ def get_fields_indexed():
fields_to_index = fields_to_index + mandatory_fields
return fields_to_index
+
+
+def raise_redisearch_error():
+ "Create an Error Log and raise error."
+ traceback = frappe.get_traceback()
+ log = frappe.log_error(traceback, frappe._("Redisearch Error"))
+ log_link = frappe.utils.get_link_to_form("Error Log", log.name)
+
+ frappe.throw(
+ msg=_("Something went wrong. Check {0}").format(log_link), title=_("Redisearch Error")
+ )
From 97e3a855f7cb3f7efa4068e5480ad57e9158a37e Mon Sep 17 00:00:00 2001
From: marination
Date: Mon, 4 Apr 2022 11:32:49 +0530
Subject: [PATCH 10/35] fix: Convert payload to string before adding to
autocompleter
---
erpnext/e_commerce/redisearch_utils.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/erpnext/e_commerce/redisearch_utils.py b/erpnext/e_commerce/redisearch_utils.py
index f9890cca1a8..95b74e0795c 100644
--- a/erpnext/e_commerce/redisearch_utils.py
+++ b/erpnext/e_commerce/redisearch_utils.py
@@ -1,6 +1,8 @@
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
+import json
+
import frappe
from frappe import _
from frappe.utils.redis_wrapper import RedisWrapper
@@ -203,7 +205,7 @@ def create_item_groups_autocomplete_dict(autocompleter):
return
for item_group in published_item_groups:
- payload = {"name": item_group, "route": item_group.route}
+ payload = json.dumps({"name": item_group, "route": item_group.route})
autocompleter.add_suggestions(
Suggestion(
string=item_group.name,
From 7ef1ccbe8489d38168517758a8badfde2c1dc6fb Mon Sep 17 00:00:00 2001
From: marination
Date: Mon, 4 Apr 2022 12:04:35 +0530
Subject: [PATCH 11/35] fix: Add default score of 1 to Item Group Autocompleter
- If score 0 is inserted into suggestions, RS does not consider that suggestion
---
erpnext/e_commerce/redisearch_utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/e_commerce/redisearch_utils.py b/erpnext/e_commerce/redisearch_utils.py
index 95b74e0795c..b2f97e308c4 100644
--- a/erpnext/e_commerce/redisearch_utils.py
+++ b/erpnext/e_commerce/redisearch_utils.py
@@ -209,7 +209,7 @@ def create_item_groups_autocomplete_dict(autocompleter):
autocompleter.add_suggestions(
Suggestion(
string=item_group.name,
- score=item_group.weightage,
+ score=frappe.utils.flt(item_group.weightage) or 1.0,
payload=payload, # additional info that can be retrieved later
)
)
From 1736ab57ac785b745f37b733691e1f7acb604173 Mon Sep 17 00:00:00 2001
From: Ankush Menat
Date: Mon, 28 Mar 2022 09:37:37 +0530
Subject: [PATCH 12/35] feat: Barcode scanning in Stock Reconciliation
[skip ci]
---
.../stock_reconciliation.js | 37 ++++++++++++++++--
.../stock_reconciliation.json | 39 ++++++++++++++++++-
2 files changed, 71 insertions(+), 5 deletions(-)
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index 4438acf8118..3e8ac1f19f8 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -55,6 +55,25 @@ frappe.ui.form.on("Stock Reconciliation", {
}
},
+ scan_barcode: function(frm) {
+ const barcode_scanner = new erpnext.utils.BarcodeScanner({frm:frm});
+ barcode_scanner.process_scan();
+ },
+
+ scan_mode: function(frm) {
+ if (frm.doc.scan_mode) {
+ frappe.show_alert({
+ message: __("Scan mode enabled, existing quantity will not be fetched."),
+ indicator: "green"
+ });
+ }
+ },
+
+ set_warehouse: function(frm) {
+ let transaction_controller = new erpnext.TransactionController({frm:frm});
+ transaction_controller.autofill_warehouse(frm.doc.items, "warehouse", frm.doc.set_warehouse);
+ },
+
get_items: function(frm) {
let fields = [
{
@@ -148,15 +167,18 @@ frappe.ui.form.on("Stock Reconciliation", {
batch_no: d.batch_no
},
callback: function(r) {
- frappe.model.set_value(cdt, cdn, "qty", r.message.qty);
+ const row = frappe.model.get_doc(cdt, cdn);
+ if (!frm.doc.scan_mode) {
+ frappe.model.set_value(cdt, cdn, "qty", r.message.qty);
+ }
frappe.model.set_value(cdt, cdn, "valuation_rate", r.message.rate);
frappe.model.set_value(cdt, cdn, "current_qty", r.message.qty);
frappe.model.set_value(cdt, cdn, "current_valuation_rate", r.message.rate);
frappe.model.set_value(cdt, cdn, "current_amount", r.message.rate * r.message.qty);
- frappe.model.set_value(cdt, cdn, "amount", r.message.rate * r.message.qty);
+ frappe.model.set_value(cdt, cdn, "amount", row.qty * row.valuation_rate);
frappe.model.set_value(cdt, cdn, "current_serial_no", r.message.serial_nos);
- if (frm.doc.purpose == "Stock Reconciliation") {
+ if (frm.doc.purpose == "Stock Reconciliation" && !frm.doc.scan_mode) {
frappe.model.set_value(cdt, cdn, "serial_no", r.message.serial_nos);
}
}
@@ -239,7 +261,14 @@ frappe.ui.form.on("Stock Reconciliation Item", {
const serial_nos = child.serial_no.trim().split('\n');
frappe.model.set_value(cdt, cdn, "qty", serial_nos.length);
}
- }
+ },
+
+ items_add: function(frm, cdt, cdn) {
+ var item = frappe.get_doc(cdt, cdn);
+ if (!item.warehouse && frm.doc.set_warehouse) {
+ frappe.model.set_value(cdt, cdn, "warehouse", frm.doc.set_warehouse);
+ }
+ },
});
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
index a882a61e5a5..e545b8ea5c3 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
@@ -14,6 +14,12 @@
"posting_date",
"posting_time",
"set_posting_time",
+ "section_break_8",
+ "set_warehouse",
+ "section_break_22",
+ "scan_barcode",
+ "column_break_12",
+ "scan_mode",
"sb9",
"items",
"section_break_9",
@@ -139,13 +145,44 @@
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "section_break_8",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "scan_barcode",
+ "fieldtype": "Data",
+ "label": "Scan Barcode",
+ "options": "Barcode"
+ },
+ {
+ "default": "0",
+ "description": "Disables auto-fetching of existing quantity",
+ "fieldname": "scan_mode",
+ "fieldtype": "Check",
+ "label": "Scan Mode"
+ },
+ {
+ "fieldname": "set_warehouse",
+ "fieldtype": "Link",
+ "label": "Default Warehouse",
+ "options": "Warehouse"
+ },
+ {
+ "fieldname": "section_break_22",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "column_break_12",
+ "fieldtype": "Column Break"
}
],
"icon": "fa fa-upload-alt",
"idx": 1,
"is_submittable": 1,
"links": [],
- "modified": "2022-02-06 14:28:19.043905",
+ "modified": "2022-03-27 08:57:47.161959",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Reconciliation",
From f83a1c19891c76e32757124df209995156158c19 Mon Sep 17 00:00:00 2001
From: Ankush Menat
Date: Sat, 2 Apr 2022 13:22:36 +0530
Subject: [PATCH 13/35] fix: dont reset barcode in scan mode
---
.../doctype/stock_reconciliation/stock_reconciliation.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index 3e8ac1f19f8..05dd105d99d 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -226,7 +226,7 @@ frappe.ui.form.on("Stock Reconciliation Item", {
warehouse: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
- if (child.batch_no) {
+ if (child.batch_no && !frm.doc.scan_mode) {
frappe.model.set_value(child.cdt, child.cdn, "batch_no", "");
}
@@ -235,7 +235,7 @@ frappe.ui.form.on("Stock Reconciliation Item", {
item_code: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
- if (child.batch_no) {
+ if (child.batch_no && !frm.doc.scan_mode) {
frappe.model.set_value(cdt, cdn, "batch_no", "");
}
From deca1aaf063a3a13404ddc7639e14a0a7bf55ff8 Mon Sep 17 00:00:00 2001
From: Ankush Menat
Date: Sat, 2 Apr 2022 13:24:37 +0530
Subject: [PATCH 14/35] fix: dont fetch qty in reco if batch is required
---
.../stock_reconciliation.py | 27 ++++++++++++++-----
1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 07a8566d4a4..5d5a27f1eab 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -1,6 +1,7 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
+from typing import Optional
import frappe
from frappe import _, msgprint
@@ -706,29 +707,43 @@ def get_itemwise_batch(warehouse, posting_date, company, item_code=None):
@frappe.whitelist()
def get_stock_balance_for(
- item_code, warehouse, posting_date, posting_time, batch_no=None, with_valuation_rate=True
+ item_code: str,
+ warehouse: str,
+ posting_date: str,
+ posting_time: str,
+ batch_no: Optional[str] = None,
+ with_valuation_rate: bool = True,
):
frappe.has_permission("Stock Reconciliation", "write", throw=True)
- item_dict = frappe.db.get_value("Item", item_code, ["has_serial_no", "has_batch_no"], as_dict=1)
+ item_dict = frappe.get_cached_value(
+ "Item", item_code, ["has_serial_no", "has_batch_no"], as_dict=1
+ )
if not item_dict:
# In cases of data upload to Items table
msg = _("Item {} does not exist.").format(item_code)
frappe.throw(msg, title=_("Missing"))
- serial_nos = ""
- with_serial_no = True if item_dict.get("has_serial_no") else False
+ serial_nos = None
+ has_serial_no = bool(item_dict.get("has_serial_no"))
+ has_batch_no = bool(item_dict.get("has_batch_no"))
+
+ if not batch_no and has_batch_no:
+ # Not enough information to fetch data
+ return {"qty": 0, "rate": 0, "serial_nos": None}
+
+ # TODO: fetch only selected batch's values
data = get_stock_balance(
item_code,
warehouse,
posting_date,
posting_time,
with_valuation_rate=with_valuation_rate,
- with_serial_no=with_serial_no,
+ with_serial_no=has_serial_no,
)
- if with_serial_no:
+ if has_serial_no:
qty, rate, serial_nos = data
else:
qty, rate = data
From e8118fcdf12bd98ff3b2cea67f6aafe6cc11cc4f Mon Sep 17 00:00:00 2001
From: Ankush Menat
Date: Sat, 2 Apr 2022 13:48:35 +0530
Subject: [PATCH 15/35] fix: doctype layout based on expected tab-order
---
.../stock_reconciliation_item.json | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
index 6bbba051f98..79c2fcc252c 100644
--- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
@@ -16,15 +16,15 @@
"amount",
"allow_zero_valuation_rate",
"serial_no_and_batch_section",
- "serial_no",
- "column_break_11",
"batch_no",
+ "column_break_11",
+ "serial_no",
"section_break_3",
"current_qty",
- "current_serial_no",
+ "current_amount",
"column_break_9",
"current_valuation_rate",
- "current_amount",
+ "current_serial_no",
"section_break_14",
"quantity_difference",
"column_break_16",
@@ -181,7 +181,7 @@
],
"istable": 1,
"links": [],
- "modified": "2021-05-21 12:13:33.041266",
+ "modified": "2022-04-02 04:19:40.380587",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Reconciliation Item",
@@ -190,5 +190,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
From cf5f37e6d8b7192020f469fd6cc189b30cea2a58 Mon Sep 17 00:00:00 2001
From: Ankush Menat
Date: Mon, 4 Apr 2022 12:14:23 +0530
Subject: [PATCH 16/35] refactor: move schema change patch higher up
---
erpnext/patches.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 0b7dd02e9a2..a3bf78b532e 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -4,6 +4,7 @@ erpnext.patches.v11_0.rename_production_order_to_work_order
erpnext.patches.v13_0.add_bin_unique_constraint
erpnext.patches.v11_0.refactor_naming_series
erpnext.patches.v11_0.refactor_autoname_naming
+erpnext.patches.v14_0.change_is_subcontracted_fieldtype
execute:frappe.reload_doc("accounts", "doctype", "POS Payment Method") #2020-05-28
execute:frappe.reload_doc("HR", "doctype", "HR Settings") #2020-01-16 #2020-07-24
erpnext.patches.v4_2.update_requested_and_ordered_qty #2021-03-31
@@ -334,7 +335,6 @@ erpnext.patches.v13_0.delete_bank_reconciliation_detail
erpnext.patches.v13_0.enable_provisional_accounting
erpnext.patches.v13_0.non_profit_deprecation_warning
erpnext.patches.v13_0.enable_ksa_vat_docs #1
-erpnext.patches.v14_0.change_is_subcontracted_fieldtype
[post_model_sync]
erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents
@@ -363,4 +363,4 @@ erpnext.patches.v14_0.update_employee_advance_status
erpnext.patches.v13_0.add_cost_center_in_loans
erpnext.patches.v13_0.set_return_against_in_pos_invoice_references
erpnext.patches.v13_0.remove_unknown_links_to_prod_plan_items # 24-03-2022
-erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances
\ No newline at end of file
+erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances
From 0c26f9a8c8100104cb5ef3923e5cf9e739f3adae Mon Sep 17 00:00:00 2001
From: Saqib Ansari
Date: Mon, 4 Apr 2022 12:28:09 +0530
Subject: [PATCH 17/35] fix(india): cannot generate e-invoice for is_pos
invoices
* If mode of payment > 18 characters, the e-invoice portal throws error
---
erpnext/regional/india/e_invoice/utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index cbdec564ba9..8fd9c1c43d2 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -387,7 +387,7 @@ def update_other_charges(
def get_payment_details(invoice):
payee_name = invoice.company
- mode_of_payment = ", ".join([d.mode_of_payment for d in invoice.payments])
+ mode_of_payment = ""
paid_amount = invoice.base_paid_amount
outstanding_amount = invoice.outstanding_amount
From 3445682563374f247327e1242fddd346f81d6c15 Mon Sep 17 00:00:00 2001
From: marination
Date: Mon, 4 Apr 2022 12:33:25 +0530
Subject: [PATCH 18/35] fix: Payload incorrect data (pass item_group.name)
---
erpnext/e_commerce/redisearch_utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/e_commerce/redisearch_utils.py b/erpnext/e_commerce/redisearch_utils.py
index b2f97e308c4..f2dd796f2c5 100644
--- a/erpnext/e_commerce/redisearch_utils.py
+++ b/erpnext/e_commerce/redisearch_utils.py
@@ -205,7 +205,7 @@ def create_item_groups_autocomplete_dict(autocompleter):
return
for item_group in published_item_groups:
- payload = json.dumps({"name": item_group, "route": item_group.route})
+ payload = json.dumps({"name": item_group.name, "route": item_group.route})
autocompleter.add_suggestions(
Suggestion(
string=item_group.name,
From 397b46a08b4b5b1dde1cd89ccbd0ad25304395bc Mon Sep 17 00:00:00 2001
From: marination
Date: Mon, 4 Apr 2022 13:24:56 +0530
Subject: [PATCH 19/35] chore: Add TODOs(perf) for Redisearch Query
---
erpnext/templates/pages/product_search.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py
index 94e893e1369..3ed056f55e7 100644
--- a/erpnext/templates/pages/product_search.py
+++ b/erpnext/templates/pages/product_search.py
@@ -88,6 +88,8 @@ def product_search(query, limit=10, fuzzy_search=True):
red = frappe.cache()
query = clean_up_query(query)
+ # TODO: Check perf/correctness with Suggestions & Query vs only Query
+ # TODO: Use Levenshtein Distance in Query (max=3)
ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=red)
client = Client(make_key(WEBSITE_ITEM_INDEX), conn=red)
suggestions = ac.get_suggestions(
From a896895a9e76a68ab055ce7871bb9d181d3fac15 Mon Sep 17 00:00:00 2001
From: Ankush Menat
Date: Sat, 2 Apr 2022 16:56:59 +0530
Subject: [PATCH 20/35] fix: bulk fix (~330) missing translations
---
.../accounts/doctype/account/account_tree.js | 2 +-
.../bank_statement_import.js | 2 +-
.../cash_flow_mapping/cash_flow_mapping.py | 5 +-
.../currency_exchange_settings.py | 3 +-
.../mode_of_payment/mode_of_payment.py | 13 ++--
.../doctype/pos_profile/pos_profile.py | 4 +-
.../process_statement_of_accounts.js | 4 +-
.../doctype/sales_invoice/sales_invoice.py | 2 +-
.../report/balance_sheet/balance_sheet.py | 8 +--
.../budget_variance_report.py | 4 +-
.../dimension_wise_accounts_balance_report.py | 2 +-
.../inactive_sales_items.py | 2 +-
.../payment_period_based_on_invoice_date.py | 6 +-
.../doctype/asset_category/asset_category.py | 2 +-
.../doctype/asset_movement/asset_movement.py | 13 ++--
.../doctype/asset_repair/asset_repair.js | 2 +-
.../asset_value_adjustment.py | 2 +-
.../supplier_scorecard/supplier_scorecard.py | 3 +-
.../supplier_scorecard_criteria.py | 2 +-
.../purchase_order_trends.py | 2 +-
.../linkedin_settings/linkedin_settings.js | 2 +-
.../social_media_post/social_media_post.js | 2 +-
.../first_response_time_for_opportunity.py | 3 +-
.../course_scheduling_tool.py | 2 -
.../student_group_creation_tool.py | 6 +-
.../department_approver.py | 2 +-
.../shift_assignment/shift_assignment.py | 4 +-
.../hr/doctype/shift_request/shift_request.py | 4 +-
.../hr/doctype/staffing_plan/staffing_plan.py | 9 +--
.../doctype/loan_repayment/loan_repayment.py | 4 +-
.../maintenance_schedule.py | 2 +-
.../maintenance_visit/maintenance_visit.py | 2 +-
.../report/bom_explorer/bom_explorer.py | 22 ++++---
.../production_plan_summary.py | 17 +++---
.../work_order_stock_report.py | 29 +++++----
.../employee_benefit_claim.py | 6 +-
.../doctype/gratuity_rule/gratuity_rule.js | 2 +-
.../doctype/payroll_entry/payroll_entry.js | 2 +-
.../salary_payments_based_on_payment_mode.py | 6 +-
.../delayed_tasks_summary.py | 24 +++++---
.../doctype/datev_settings/datev_settings.js | 2 +-
.../doctype/sales_order/sales_order.js | 2 +-
.../customer_acquisition_and_loyalty.py | 2 +-
.../customer_credit_balance.py | 2 +-
.../item_wise_sales_history.py | 2 +-
.../quotation_trends/quotation_trends.py | 2 +-
.../sales_order_trends/sales_order_trends.py | 4 +-
erpnext/stock/doctype/item/item.py | 2 +-
.../material_request/material_request.py | 6 +-
erpnext/stock/doctype/pick_list/pick_list.py | 11 ++--
.../stock/doctype/stock_entry/stock_entry.js | 2 +-
.../warehouse_capacity_summary.js | 2 +-
erpnext/stock/reorder_item.py | 3 +-
erpnext/stock/report/bom_search/bom_search.py | 5 +-
.../item_variant_details.py | 2 +-
.../stock_ledger_invariant_check.py | 61 ++++++++++---------
.../first_response_time_for_issues.py | 5 +-
erpnext/utilities/doctype/video/video.js | 2 +-
.../youtube_interactions.py | 2 +-
59 files changed, 179 insertions(+), 173 deletions(-)
diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js
index a3ef38465ec..8ae90ceb383 100644
--- a/erpnext/accounts/doctype/account/account_tree.js
+++ b/erpnext/accounts/doctype/account/account_tree.js
@@ -160,7 +160,7 @@ frappe.treeview_settings["Account"] = {
let root_company = treeview.page.fields_dict.root_company.get_value();
if(root_company) {
- frappe.throw(__("Please add the account to root level Company - ") + root_company);
+ frappe.throw(__("Please add the account to root level Company - {0}"), [root_company]);
} else {
treeview.new_node();
}
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
index 990d6d9c8d4..a964965c26f 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
@@ -200,7 +200,7 @@ frappe.ui.form.on("Bank Statement Import", {
})
.then((result) => {
if (result.length > 0) {
- frm.add_custom_button("Report Error", () => {
+ frm.add_custom_button(__("Report Error"), () => {
let fake_xhr = {
responseText: JSON.stringify({
exc: result[0].error,
diff --git a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py
index 3bce4d51c7a..402469fc1c0 100644
--- a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py
+++ b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py
@@ -3,6 +3,7 @@
import frappe
+from frappe import _
from frappe.model.document import Document
@@ -16,6 +17,6 @@ class CashFlowMapping(Document):
]
if len(checked_fields) > 1:
frappe.throw(
- frappe._("You can only select a maximum of one option from the list of check boxes."),
- title="Error",
+ _("You can only select a maximum of one option from the list of check boxes."),
+ title=_("Error"),
)
diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py
index 04a8e8ea92f..edea37dcfd9 100644
--- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py
+++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py
@@ -68,9 +68,8 @@ class CurrencyExchangeSettings(Document):
str(key.key).format(transaction_date=nowdate(), to_currency="INR", from_currency="USD")
]
except Exception:
- frappe.throw("Invalid result key. Response: " + response.text)
+ frappe.throw(_("Invalid result key. Response:") + " " + response.text)
if not isinstance(value, (int, float)):
frappe.throw(_("Returned exchange rate is neither integer not float."))
self.url = response.url
- frappe.msgprint("Exchange rate of USD to INR is " + str(value))
diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py
index d0373021a69..ed35d1e0945 100644
--- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py
+++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py
@@ -42,12 +42,7 @@ class ModeofPayment(Document):
pos_profiles = list(map(lambda x: x[0], pos_profiles))
if pos_profiles:
- message = (
- "POS Profile "
- + frappe.bold(", ".join(pos_profiles))
- + " contains \
- Mode of Payment "
- + frappe.bold(str(self.name))
- + ". Please remove them to disable this mode."
- )
- frappe.throw(_(message), title="Not Allowed")
+ message = _(
+ "POS Profile {} contains Mode of Payment {}. Please remove them to disable this mode."
+ ).format(frappe.bold(", ".join(pos_profiles)), frappe.bold(str(self.name)))
+ frappe.throw(message, title=_("Not Allowed"))
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py
index 65fd4af3508..e83dc0f11e5 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py
@@ -61,13 +61,13 @@ class POSProfile(Document):
if len(item_groups) != len(set(item_groups)):
frappe.throw(
- _("Duplicate item group found in the item group table"), title="Duplicate Item Group"
+ _("Duplicate item group found in the item group table"), title=_("Duplicate Item Group")
)
if len(customer_groups) != len(set(customer_groups)):
frappe.throw(
_("Duplicate customer group found in the cutomer group table"),
- title="Duplicate Customer Group",
+ title=_("Duplicate Customer Group"),
)
def validate_payment_methods(self):
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js
index 29f2e98e779..7dd77fbb3c7 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js
@@ -8,7 +8,7 @@ frappe.ui.form.on('Process Statement Of Accounts', {
},
refresh: function(frm){
if(!frm.doc.__islocal) {
- frm.add_custom_button('Send Emails',function(){
+ frm.add_custom_button(__('Send Emails'), function(){
frappe.call({
method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails",
args: {
@@ -24,7 +24,7 @@ frappe.ui.form.on('Process Statement Of Accounts', {
}
});
});
- frm.add_custom_button('Download',function(){
+ frm.add_custom_button(__('Download'), function(){
var url = frappe.urllib.get_full_url(
'/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?'
+ 'document_name='+encodeURIComponent(frm.doc.name))
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 7d98c22033d..1efd3dca0d3 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1412,7 +1412,7 @@ class SalesInvoice(SellingController):
)
)
else:
- frappe.throw(_("Select change amount account"), title="Mandatory Field")
+ frappe.throw(_("Select change amount account"), title=_("Mandatory Field"))
def make_write_off_gl_entry(self, gl_entries):
# write off entries, applicable if only pos
diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py
index 7b1e9793266..07552e311c0 100644
--- a/erpnext/accounts/report/balance_sheet/balance_sheet.py
+++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py
@@ -201,17 +201,17 @@ def get_report_summary(
net_provisional_profit_loss += provisional_profit_loss.get(key)
return [
- {"value": net_asset, "label": "Total Asset", "datatype": "Currency", "currency": currency},
+ {"value": net_asset, "label": _("Total Asset"), "datatype": "Currency", "currency": currency},
{
"value": net_liability,
- "label": "Total Liability",
+ "label": _("Total Liability"),
"datatype": "Currency",
"currency": currency,
},
- {"value": net_equity, "label": "Total Equity", "datatype": "Currency", "currency": currency},
+ {"value": net_equity, "label": _("Total Equity"), "datatype": "Currency", "currency": currency},
{
"value": net_provisional_profit_loss,
- "label": "Provisional Profit / Loss (Credit)",
+ "label": _("Provisional Profit / Loss (Credit)"),
"indicator": "Green" if net_provisional_profit_loss > 0 else "Red",
"datatype": "Currency",
"currency": currency,
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
index ca341f4993e..7b774ba740b 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
@@ -97,8 +97,8 @@ def get_columns(filters):
if filters["period"] == "Yearly":
labels = [
_("Budget") + " " + str(year[0]),
- _("Actual ") + " " + str(year[0]),
- _("Variance ") + " " + str(year[0]),
+ _("Actual") + " " + str(year[0]),
+ _("Variance") + " " + str(year[0]),
]
for label in labels:
columns.append(
diff --git a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py
index 8e8465cee9f..ecad9f104fa 100644
--- a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py
+++ b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py
@@ -230,7 +230,7 @@ def get_columns(dimension_list):
columns.append(
{
"fieldname": "total",
- "label": "Total",
+ "label": _("Total"),
"fieldtype": "Currency",
"options": "currency",
"width": 150,
diff --git a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
index 8db72de22f3..1a003993aac 100644
--- a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
+++ b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
@@ -29,7 +29,7 @@ def get_columns():
"options": "Item Group",
"width": 150,
},
- {"fieldname": "item", "fieldtype": "Link", "options": "Item", "label": "Item", "width": 150},
+ {"fieldname": "item", "fieldtype": "Link", "options": "Item", "label": _("Item"), "width": 150},
{"fieldname": "item_name", "fieldtype": "Data", "label": _("Item Name"), "width": 150},
{
"fieldname": "customer",
diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
index 00f5948a1b6..3f178f4715c 100644
--- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
+++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
@@ -115,9 +115,9 @@ def get_columns(filters):
{"fieldname": "credit", "label": _("Credit"), "fieldtype": "Currency", "width": 140},
{"fieldname": "remarks", "label": _("Remarks"), "fieldtype": "Data", "width": 200},
{"fieldname": "age", "label": _("Age"), "fieldtype": "Int", "width": 50},
- {"fieldname": "range1", "label": "0-30", "fieldtype": "Currency", "width": 140},
- {"fieldname": "range2", "label": "30-60", "fieldtype": "Currency", "width": 140},
- {"fieldname": "range3", "label": "60-90", "fieldtype": "Currency", "width": 140},
+ {"fieldname": "range1", "label": _("0-30"), "fieldtype": "Currency", "width": 140},
+ {"fieldname": "range2", "label": _("30-60"), "fieldtype": "Currency", "width": 140},
+ {"fieldname": "range3", "label": _("60-90"), "fieldtype": "Currency", "width": 140},
{"fieldname": "range4", "label": _("90 Above"), "fieldtype": "Currency", "width": 140},
{
"fieldname": "delay_in_payment",
diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py
index 7291daf2b33..a4d2c82845a 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.py
+++ b/erpnext/assets/doctype/asset_category/asset_category.py
@@ -87,7 +87,7 @@ class AssetCategory(Document):
missing_cwip_accounts_for_company.append(get_link_to_form("Company", d.company_name))
if missing_cwip_accounts_for_company:
- msg = _("""To enable Capital Work in Progress Accounting, """)
+ msg = _("""To enable Capital Work in Progress Accounting,""") + " "
msg += _("""you must select Capital Work in Progress Account in accounts table""")
msg += "
"
msg += _("You can also set default CWIP account in Company {}").format(
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py
index e61efadb123..143f215db2e 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.py
@@ -46,10 +46,9 @@ class AssetMovement(Document):
if d.target_location:
frappe.throw(
_(
- "Issuing cannot be done to a location. \
- Please enter employee who has issued Asset {0}"
+ "Issuing cannot be done to a location. Please enter employee who has issued Asset {0}"
).format(d.asset),
- title="Incorrect Movement Purpose",
+ title=_("Incorrect Movement Purpose"),
)
if not d.to_employee:
frappe.throw(_("Employee is required while issuing Asset {0}").format(d.asset))
@@ -58,10 +57,9 @@ class AssetMovement(Document):
if d.to_employee:
frappe.throw(
_(
- "Transferring cannot be done to an Employee. \
- Please enter location where Asset {0} has to be transferred"
+ "Transferring cannot be done to an Employee. Please enter location where Asset {0} has to be transferred"
).format(d.asset),
- title="Incorrect Movement Purpose",
+ title=_("Incorrect Movement Purpose"),
)
if not d.target_location:
frappe.throw(_("Target Location is required while transferring Asset {0}").format(d.asset))
@@ -89,8 +87,7 @@ class AssetMovement(Document):
if d.to_employee and d.target_location:
frappe.throw(
_(
- "Asset {0} cannot be received at a location and \
- given to employee in a single movement"
+ "Asset {0} cannot be received at a location and given to employee in a single movement"
).format(d.asset)
)
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js
index 3fe6b2d0d5d..f5e4e723b44 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.js
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.js
@@ -32,7 +32,7 @@ frappe.ui.form.on('Asset Repair', {
refresh: function(frm) {
if (frm.doc.docstatus) {
- frm.add_custom_button("View General Ledger", function() {
+ frm.add_custom_button(__("View General Ledger"), function() {
frappe.route_options = {
"voucher_no": frm.doc.name
};
diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
index 9953c61a811..20865e8ddc4 100644
--- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
+++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
@@ -37,7 +37,7 @@ class AssetValueAdjustment(Document):
_("Asset Value Adjustment cannot be posted before Asset's purchase date {0}.").format(
formatdate(asset_purchase_date)
),
- title="Incorrect Date",
+ title=_("Incorrect Date"),
)
def set_difference_amount(self):
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
index 992bc805a55..486bf23e909 100644
--- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
@@ -213,7 +213,8 @@ def make_all_scorecards(docname):
end_date = get_scorecard_date(sc.period, start_date)
if scp_count > 0:
frappe.msgprint(
- _("Created {0} scorecards for {1} between: ").format(scp_count, sc.supplier)
+ _("Created {0} scorecards for {1} between:").format(scp_count, sc.supplier)
+ + " "
+ str(first_start_date)
+ " - "
+ str(last_end_date)
diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py
index 130adc97d40..ab7d4879c43 100644
--- a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py
+++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py
@@ -80,6 +80,6 @@ def _get_variables(criteria):
)[0]
my_variables.append(var)
except Exception:
- frappe.throw(_("Unable to find variable: ") + str(match.group(1)), InvalidFormulaVariable)
+ frappe.throw(_("Unable to find variable:") + " " + str(match.group(1)), InvalidFormulaVariable)
return my_variables
diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py
index 11a74491a4c..dbdc62e9ec7 100644
--- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py
+++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py
@@ -48,7 +48,7 @@ def get_chart_data(data, conditions, filters):
"data": {
"labels": labels,
"datasets": [
- {"name": _("{0}").format(filters.get("period")) + _(" Purchase Value"), "values": datapoints}
+ {"name": _(filters.get("period")) + " " + _("Purchase Value"), "values": datapoints}
],
},
"type": "line",
diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js
index 7aa0b777596..d532236b7d2 100644
--- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js
+++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js
@@ -37,7 +37,7 @@ frappe.ui.form.on('LinkedIn Settings', {
let msg,color;
if (days>0){
- msg = __("Your Session will be expire in ") + days + __(" days.");
+ msg = __("Your Session will be expire in {0} days.", [days]);
color = "green";
}
else {
diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.js b/erpnext/crm/doctype/social_media_post/social_media_post.js
index 6874caac71f..d4ac0bad16c 100644
--- a/erpnext/crm/doctype/social_media_post/social_media_post.js
+++ b/erpnext/crm/doctype/social_media_post/social_media_post.js
@@ -86,7 +86,7 @@ frappe.ui.form.on('Social Media Post', {
frm.trigger('add_post_btn');
}
if (frm.doc.post_status !='Deleted') {
- frm.add_custom_button(('Delete Post'), function() {
+ frm.add_custom_button(__('Delete Post'), function() {
frappe.confirm(__('Are you sure want to delete the Post from Social Media platforms?'),
function() {
frappe.call({
diff --git a/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.py b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.py
index 9dae1d50f68..db36581cecd 100644
--- a/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.py
+++ b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.py
@@ -3,11 +3,12 @@
import frappe
+from frappe import _
def execute(filters=None):
columns = [
- {"fieldname": "creation_date", "label": "Date", "fieldtype": "Date", "width": 300},
+ {"fieldname": "creation_date", "label": _("Date"), "fieldtype": "Date", "width": 300},
{
"fieldname": "first_response_time",
"fieldtype": "Duration",
diff --git a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.py b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.py
index 4db6f981fca..b3072c20b6c 100644
--- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.py
+++ b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.py
@@ -41,10 +41,8 @@ class CourseSchedulingTool(Document):
if self.day == calendar.day_name[getdate(date).weekday()]:
course_schedule = self.make_course_schedule(date)
try:
- print("pass")
course_schedule.save()
except OverlapError:
- print("fail")
course_schedules_errors.append(date)
else:
course_schedules.append(course_schedule)
diff --git a/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py b/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py
index 0fb255077f1..bbeb654bfc4 100644
--- a/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py
+++ b/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py
@@ -69,13 +69,13 @@ class StudentGroupCreationTool(Document):
l = len(self.courses)
for d in self.courses:
if not d.student_group_name:
- frappe.throw(_("""Student Group Name is mandatory in row {0}""".format(d.idx)))
+ frappe.throw(_("Student Group Name is mandatory in row {0}").format(d.idx))
if d.group_based_on == "Course" and not d.course:
- frappe.throw(_("""Course is mandatory in row {0}""".format(d.idx)))
+ frappe.throw(_("Course is mandatory in row {0}").format(d.idx))
if d.group_based_on == "Batch" and not d.batch:
- frappe.throw(_("""Batch is mandatory in row {0}""".format(d.idx)))
+ frappe.throw(_("Batch is mandatory in row {0}").format(d.idx))
frappe.publish_realtime(
"student_group_creation_progress", {"progress": [d.idx, l]}, user=frappe.session.user
diff --git a/erpnext/hr/doctype/department_approver/department_approver.py b/erpnext/hr/doctype/department_approver/department_approver.py
index d849900ef2d..87bdddd6ff2 100644
--- a/erpnext/hr/doctype/department_approver/department_approver.py
+++ b/erpnext/hr/doctype/department_approver/department_approver.py
@@ -87,7 +87,7 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters):
field_name, frappe.bold(employee.employee_name)
)
if department_list:
- error_msg += _(" or for Department: {0}").format(frappe.bold(employee_department))
+ error_msg += " " + _("or for Department: {0}").format(frappe.bold(employee_department))
frappe.throw(error_msg, title=_(field_name + " Missing"))
return set(tuple(approver) for approver in approvers)
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
index 5a1248698c2..f6bd15951d3 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
@@ -73,10 +73,10 @@ class ShiftAssignment(Document):
frappe.bold(self.employee), frappe.bold(self.shift_type), frappe.bold(shift_details.name)
)
if shift_details.start_date:
- msg += _(" from {0}").format(getdate(self.start_date).strftime("%d-%m-%Y"))
+ msg += " " + _("from {0}").format(getdate(self.start_date).strftime("%d-%m-%Y"))
title = "Ongoing Shift"
if shift_details.end_date:
- msg += _(" to {0}").format(getdate(self.end_date).strftime("%d-%m-%Y"))
+ msg += " " + _("to {0}").format(getdate(self.end_date).strftime("%d-%m-%Y"))
title = "Active Shift"
if msg:
frappe.throw(msg, title=title)
diff --git a/erpnext/hr/doctype/shift_request/shift_request.py b/erpnext/hr/doctype/shift_request/shift_request.py
index 1e3e8ff6464..b5beef7a99e 100644
--- a/erpnext/hr/doctype/shift_request/shift_request.py
+++ b/erpnext/hr/doctype/shift_request/shift_request.py
@@ -109,7 +109,7 @@ class ShiftRequest(Document):
self.throw_overlap_error(date_overlap)
def throw_overlap_error(self, d):
- msg = _("Employee {0} has already applied for {1} between {2} and {3} : ").format(
+ msg = _("Employee {0} has already applied for {1} between {2} and {3}").format(
self.employee, d["shift_type"], formatdate(d["from_date"]), formatdate(d["to_date"])
- ) + """ {0}""".format(d["name"])
+ ) + """ : {0}""".format(d["name"])
frappe.throw(msg, OverlapError)
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
index 93a493c9d25..ce7e50f7f4a 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
@@ -91,8 +91,7 @@ class StaffingPlan(Document):
) > flt(parent_plan_details[0].total_estimated_cost):
frappe.throw(
_(
- "You can only plan for upto {0} vacancies and budget {1} \
- for {2} as per staffing plan {3} for parent company {4}."
+ "You can only plan for upto {0} vacancies and budget {1} for {2} as per staffing plan {3} for parent company {4}."
).format(
cint(parent_plan_details[0].vacancies),
parent_plan_details[0].total_estimated_cost,
@@ -128,8 +127,7 @@ class StaffingPlan(Document):
):
frappe.throw(
_(
- "{0} vacancies and {1} budget for {2} already planned for subsidiary companies of {3}. \
- You can only plan for upto {4} vacancies and and budget {5} as per staffing plan {6} for parent company {3}."
+ "{0} vacancies and {1} budget for {2} already planned for subsidiary companies of {3}. You can only plan for upto {4} vacancies and and budget {5} as per staffing plan {6} for parent company {3}."
).format(
cint(all_sibling_details.vacancies),
all_sibling_details.total_estimated_cost,
@@ -162,8 +160,7 @@ class StaffingPlan(Document):
):
frappe.throw(
_(
- "Subsidiary companies have already planned for {1} vacancies at a budget of {2}. \
- Staffing Plan for {0} should allocate more vacancies and budget for {3} than planned for its subsidiary companies"
+ "Subsidiary companies have already planned for {1} vacancies at a budget of {2}. Staffing Plan for {0} should allocate more vacancies and budget for {3} than planned for its subsidiary companies"
).format(
self.company,
cint(children_details.vacancies),
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 9033a3ac416..304d1a75c9a 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -387,13 +387,13 @@ class LoanRepayment(AccountsController):
gle_map = []
if self.shortfall_amount and self.amount_paid > self.shortfall_amount:
- remarks = _("Shortfall Repayment of {0}.\nRepayment against Loan: {1}").format(
+ remarks = _("Shortfall Repayment of {0}.
Repayment against Loan: {1}").format(
self.shortfall_amount, self.against_loan
)
elif self.shortfall_amount:
remarks = _("Shortfall Repayment of {0}").format(self.shortfall_amount)
else:
- remarks = _("Repayment against Loan: ") + self.against_loan
+ remarks = _("Repayment against Loan:") + " " + self.against_loan
if self.repay_from_salary:
payment_account = self.payroll_payable_account
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
index 256f66071f3..9a23c071061 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
@@ -250,7 +250,7 @@ class MaintenanceSchedule(TransactionBase):
_("Serial No {0} does not belong to Item {1}").format(
frappe.bold(serial_no), frappe.bold(item_code)
),
- title="Invalid",
+ title=_("Invalid"),
)
if sr_details.warranty_expiry_date and getdate(sr_details.warranty_expiry_date) >= getdate(
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
index 29a17849fd9..66f4426a0b8 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
@@ -20,7 +20,7 @@ class MaintenanceVisit(TransactionBase):
def validate_purpose_table(self):
if not self.purposes:
- frappe.throw(_("Add Items in the Purpose Table"), title="Purposes Required")
+ frappe.throw(_("Add Items in the Purpose Table"), title=_("Purposes Required"))
def validate_maintenance_date(self):
if self.maintenance_type == "Scheduled" and self.maintenance_schedule_detail:
diff --git a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
index c0affd9cada..ac2f61c5de6 100644
--- a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
+++ b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
@@ -3,6 +3,7 @@
import frappe
+from frappe import _
def execute(filters=None):
@@ -46,17 +47,22 @@ def get_exploded_items(bom, data, indent=0, qty=1):
def get_columns():
return [
{
- "label": "Item Code",
+ "label": _("Item Code"),
"fieldtype": "Link",
"fieldname": "item_code",
"width": 300,
"options": "Item",
},
- {"label": "Item Name", "fieldtype": "data", "fieldname": "item_name", "width": 100},
- {"label": "BOM", "fieldtype": "Link", "fieldname": "bom", "width": 150, "options": "BOM"},
- {"label": "Qty", "fieldtype": "data", "fieldname": "qty", "width": 100},
- {"label": "UOM", "fieldtype": "data", "fieldname": "uom", "width": 100},
- {"label": "BOM Level", "fieldtype": "Int", "fieldname": "bom_level", "width": 100},
- {"label": "Standard Description", "fieldtype": "data", "fieldname": "description", "width": 150},
- {"label": "Scrap", "fieldtype": "data", "fieldname": "scrap", "width": 100},
+ {"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 100},
+ {"label": _("BOM"), "fieldtype": "Link", "fieldname": "bom", "width": 150, "options": "BOM"},
+ {"label": _("Qty"), "fieldtype": "data", "fieldname": "qty", "width": 100},
+ {"label": _("UOM"), "fieldtype": "data", "fieldname": "uom", "width": 100},
+ {"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100},
+ {
+ "label": _("Standard Description"),
+ "fieldtype": "data",
+ "fieldname": "description",
+ "width": 150,
+ },
+ {"label": _("Scrap"), "fieldtype": "data", "fieldname": "scrap", "width": 100},
]
diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
index 17f7f5e51fa..2c8f82f2cc6 100644
--- a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
+++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
@@ -3,6 +3,7 @@
import frappe
+from frappe import _
from frappe.utils import flt
@@ -114,28 +115,28 @@ def get_purchase_order_details(filters, order_details):
def get_column(filters):
return [
{
- "label": "Finished Good",
+ "label": _("Finished Good"),
"fieldtype": "Link",
"fieldname": "item_code",
"width": 300,
"options": "Item",
},
- {"label": "Item Name", "fieldtype": "data", "fieldname": "item_name", "width": 100},
+ {"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 100},
{
- "label": "Document Type",
+ "label": _("Document Type"),
"fieldtype": "Link",
"fieldname": "document_type",
"width": 150,
"options": "DocType",
},
{
- "label": "Document Name",
+ "label": _("Document Name"),
"fieldtype": "Dynamic Link",
"fieldname": "document_name",
"width": 150,
},
- {"label": "BOM Level", "fieldtype": "Int", "fieldname": "bom_level", "width": 100},
- {"label": "Order Qty", "fieldtype": "Float", "fieldname": "qty", "width": 120},
- {"label": "Received Qty", "fieldtype": "Float", "fieldname": "produced_qty", "width": 160},
- {"label": "Pending Qty", "fieldtype": "Float", "fieldname": "pending_qty", "width": 110},
+ {"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100},
+ {"label": _("Order Qty"), "fieldtype": "Float", "fieldname": "qty", "width": 120},
+ {"label": _("Received Qty"), "fieldtype": "Float", "fieldname": "produced_qty", "width": 160},
+ {"label": _("Pending Qty"), "fieldtype": "Float", "fieldname": "pending_qty", "width": 110},
]
diff --git a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py
index c6b7e58d656..063ebba0597 100644
--- a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py
+++ b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py
@@ -3,6 +3,7 @@
import frappe
+from frappe import _
from frappe.utils import cint
@@ -99,59 +100,65 @@ def get_columns():
columns = [
{
"fieldname": "work_order",
- "label": "Work Order",
+ "label": _("Work Order"),
"fieldtype": "Link",
"options": "Work Order",
"width": 110,
},
- {"fieldname": "bom_no", "label": "BOM", "fieldtype": "Link", "options": "BOM", "width": 120},
+ {"fieldname": "bom_no", "label": _("BOM"), "fieldtype": "Link", "options": "BOM", "width": 120},
{
"fieldname": "description",
- "label": "Description",
+ "label": _("Description"),
"fieldtype": "Data",
"options": "",
"width": 230,
},
{
"fieldname": "item_code",
- "label": "Item Code",
+ "label": _("Item Code"),
"fieldtype": "Link",
"options": "Item",
"width": 110,
},
{
"fieldname": "source_warehouse",
- "label": "Source Warehouse",
+ "label": _("Source Warehouse"),
"fieldtype": "Link",
"options": "Warehouse",
"width": 110,
},
- {"fieldname": "qty", "label": "Qty to Build", "fieldtype": "Data", "options": "", "width": 110},
- {"fieldname": "status", "label": "Status", "fieldtype": "Data", "options": "", "width": 100},
+ {
+ "fieldname": "qty",
+ "label": _("Qty to Build"),
+ "fieldtype": "Data",
+ "options": "",
+ "width": 110,
+ },
+ {"fieldname": "status", "label": _("Status"), "fieldtype": "Data", "options": "", "width": 100},
{
"fieldname": "req_items",
- "label": "# Req'd Items",
+ "label": _("# Req'd Items"),
"fieldtype": "Data",
"options": "",
"width": 105,
},
{
"fieldname": "instock",
- "label": "# In Stock",
+ "label": _("# In Stock"),
"fieldtype": "Data",
"options": "",
"width": 105,
},
{
"fieldname": "buildable_qty",
- "label": "Buildable Qty",
+ "label": _("Buildable Qty"),
"fieldtype": "Data",
"options": "",
"width": 100,
},
{
"fieldname": "ready_to_build",
- "label": "Build All?",
+ "label": _("Build All?"),
"fieldtype": "Data",
"options": "",
"width": 90,
diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
index 31f26b25e73..6ec34b9e71f 100644
--- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
+++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
@@ -44,8 +44,7 @@ class EmployeeBenefitClaim(Document):
if max_benefits < claimed_amount:
frappe.throw(
_(
- "Maximum benefit of employee {0} exceeds {1} by the sum {2} of previous claimed\
- amount"
+ "Maximum benefit of employee {0} exceeds {1} by the sum {2} of previous claimed amount"
).format(self.employee, max_benefits, claimed_amount - max_benefits)
)
@@ -84,8 +83,7 @@ class EmployeeBenefitClaim(Document):
if max_benefits < pro_rata_amount + claimed_amount:
frappe.throw(
_(
- "Maximum benefit of employee {0} exceeds {1} by the sum {2} of benefit application pro-rata component\
- amount and previous claimed amount"
+ "Maximum benefit of employee {0} exceeds {1} by the sum {2} of benefit application pro-rata component amount and previous claimed amount"
).format(
self.employee, max_benefits, pro_rata_amount + claimed_amount - max_benefits
)
diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js
index 014a121c96a..7290a9eafa3 100644
--- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js
+++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js
@@ -34,7 +34,7 @@ frappe.ui.form.on('Gratuity Rule Slab', {
to_year(frm, cdt, cdn) {
let row = locals[cdt][cdn];
if (row.to_year <= row.from_year && row.to_year === 0) {
- frappe.throw(__("To(Year) year can not be less than From(year) "));
+ frappe.throw(__("To(Year) year can not be less than From(year)"));
}
}
});
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
index 496c37b2fad..62e183e59c7 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
@@ -112,7 +112,7 @@ frappe.ui.form.on('Payroll Entry', {
},
callback: function (r) {
if (r.message && !r.message.submitted) {
- frm.add_custom_button("Make Bank Entry", function () {
+ frm.add_custom_button(__("Make Bank Entry"), function () {
make_bank_entry(frm);
}).addClass("btn-primary");
}
diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py
index e5348df8864..4223f9d4fb5 100644
--- a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py
+++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py
@@ -142,21 +142,21 @@ def get_report_summary(gross_pay, total_deductions, net_pay, currency):
return [
{
"value": gross_pay,
- "label": "Total Gross Pay",
+ "label": _("Total Gross Pay"),
"indicator": "Green",
"datatype": "Currency",
"currency": currency,
},
{
"value": total_deductions,
- "label": "Total Deduction",
+ "label": _("Total Deduction"),
"datatype": "Currency",
"indicator": "Red",
"currency": currency,
},
{
"value": net_pay,
- "label": "Total Net Pay",
+ "label": _("Total Net Pay"),
"datatype": "Currency",
"indicator": "Blue",
"currency": currency,
diff --git a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
index 5c3dc2da118..17e3155e286 100644
--- a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
+++ b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
@@ -3,6 +3,7 @@
import frappe
+from frappe import _
from frappe.utils import date_diff, nowdate
@@ -83,19 +84,24 @@ def get_chart_data(data):
def get_columns():
columns = [
- {"fieldname": "name", "fieldtype": "Link", "label": "Task", "options": "Task", "width": 150},
- {"fieldname": "subject", "fieldtype": "Data", "label": "Subject", "width": 200},
- {"fieldname": "status", "fieldtype": "Data", "label": "Status", "width": 100},
- {"fieldname": "priority", "fieldtype": "Data", "label": "Priority", "width": 80},
- {"fieldname": "progress", "fieldtype": "Data", "label": "Progress (%)", "width": 120},
+ {"fieldname": "name", "fieldtype": "Link", "label": _("Task"), "options": "Task", "width": 150},
+ {"fieldname": "subject", "fieldtype": "Data", "label": _("Subject"), "width": 200},
+ {"fieldname": "status", "fieldtype": "Data", "label": _("Status"), "width": 100},
+ {"fieldname": "priority", "fieldtype": "Data", "label": _("Priority"), "width": 80},
+ {"fieldname": "progress", "fieldtype": "Data", "label": _("Progress (%)"), "width": 120},
{
"fieldname": "exp_start_date",
"fieldtype": "Date",
- "label": "Expected Start Date",
+ "label": _("Expected Start Date"),
"width": 150,
},
- {"fieldname": "exp_end_date", "fieldtype": "Date", "label": "Expected End Date", "width": 150},
- {"fieldname": "completed_on", "fieldtype": "Date", "label": "Actual End Date", "width": 130},
- {"fieldname": "delay", "fieldtype": "Data", "label": "Delay (In Days)", "width": 120},
+ {
+ "fieldname": "exp_end_date",
+ "fieldtype": "Date",
+ "label": _("Expected End Date"),
+ "width": 150,
+ },
+ {"fieldname": "completed_on", "fieldtype": "Date", "label": _("Actual End Date"), "width": 130},
+ {"fieldname": "delay", "fieldtype": "Data", "label": _("Delay (In Days)"), "width": 120},
]
return columns
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.js b/erpnext/regional/doctype/datev_settings/datev_settings.js
index f04705929fc..3c365494c49 100644
--- a/erpnext/regional/doctype/datev_settings/datev_settings.js
+++ b/erpnext/regional/doctype/datev_settings/datev_settings.js
@@ -3,6 +3,6 @@
frappe.ui.form.on('DATEV Settings', {
refresh: function(frm) {
- frm.add_custom_button('Show Report', () => frappe.set_route('query-report', 'DATEV'), "fa fa-table");
+ frm.add_custom_button(__('Show Report'), () => frappe.set_route('query-report', 'DATEV'), "fa fa-table");
}
});
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 87f277f65c8..0b48f70eab6 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -727,7 +727,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
args: {
reference_doctype: me.frm.doctype,
reference_name: me.frm.docname,
- content: __('Reason for hold: ')+data.reason_for_hold,
+ content: __('Reason for hold:') + ' ' + data.reason_for_hold,
comment_email: frappe.session.user,
comment_by: frappe.session.user_fullname
},
diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py
index 33badc37f8a..3e4bfb2ef71 100644
--- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py
+++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py
@@ -102,7 +102,7 @@ def get_data_by_time(filters, common_columns):
def get_data_by_territory(filters, common_columns):
columns = [
{
- "label": "Territory",
+ "label": _("Territory"),
"fieldname": "territory",
"fieldtype": "Link",
"options": "Territory",
diff --git a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
index 1c10a374b6f..98633cb7198 100644
--- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
+++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
@@ -65,7 +65,7 @@ def get_columns(customer_naming_type):
_("Credit Limit") + ":Currency:120",
_("Outstanding Amt") + ":Currency:100",
_("Credit Balance") + ":Currency:120",
- _("Bypass credit check at Sales Order ") + ":Check:80",
+ _("Bypass credit check at Sales Order") + ":Check:80",
_("Is Frozen") + ":Check:80",
_("Disabled") + ":Check:80",
]
diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
index 12ca7b3ff83..091c20c917f 100644
--- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
+++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
@@ -235,7 +235,7 @@ def get_chart_data(data):
return {
"data": {
"labels": labels[:30], # show max of 30 items in chart
- "datasets": [{"name": _(" Total Sales Amount"), "values": datapoints[:30]}],
+ "datasets": [{"name": _("Total Sales Amount"), "values": datapoints[:30]}],
},
"type": "bar",
}
diff --git a/erpnext/selling/report/quotation_trends/quotation_trends.py b/erpnext/selling/report/quotation_trends/quotation_trends.py
index dfcec22cca2..4e0758d7cda 100644
--- a/erpnext/selling/report/quotation_trends/quotation_trends.py
+++ b/erpnext/selling/report/quotation_trends/quotation_trends.py
@@ -49,7 +49,7 @@ def get_chart_data(data, conditions, filters):
"data": {
"labels": labels,
"datasets": [
- {"name": _("{0}").format(filters.get("period")) + _(" Quoted Amount"), "values": datapoints}
+ {"name": _(filters.get("period")) + " " + _("Quoted Amount"), "values": datapoints}
],
},
"type": "line",
diff --git a/erpnext/selling/report/sales_order_trends/sales_order_trends.py b/erpnext/selling/report/sales_order_trends/sales_order_trends.py
index 93707bd46d9..719f1c52745 100644
--- a/erpnext/selling/report/sales_order_trends/sales_order_trends.py
+++ b/erpnext/selling/report/sales_order_trends/sales_order_trends.py
@@ -47,9 +47,7 @@ def get_chart_data(data, conditions, filters):
return {
"data": {
"labels": labels,
- "datasets": [
- {"name": _("{0}").format(filters.get("period")) + _(" Sales Value"), "values": datapoints}
- ],
+ "datasets": [{"name": _(filters.get("period")) + " " + _("Sales Value"), "values": datapoints}],
},
"type": "line",
"lineOptions": {"regionFill": 1},
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 9d7c22fc8e6..535f5652096 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -464,7 +464,7 @@ class Item(Document):
frappe.msgprint(
_("It can take upto few hours for accurate stock values to be visible after merging items."),
indicator="orange",
- title="Note",
+ title=_("Note"),
)
if self.published_in_website:
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 4524914f5c4..a70ff171a9b 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -209,16 +209,14 @@ class MaterialRequest(BuyingController):
if d.ordered_qty and d.ordered_qty > allowed_qty:
frappe.throw(
_(
- "The total Issue / Transfer quantity {0} in Material Request {1} \
- cannot be greater than allowed requested quantity {2} for Item {3}"
+ "The total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than allowed requested quantity {2} for Item {3}"
).format(d.ordered_qty, d.parent, allowed_qty, d.item_code)
)
elif d.ordered_qty and d.ordered_qty > d.stock_qty:
frappe.throw(
_(
- "The total Issue / Transfer quantity {0} in Material Request {1} \
- cannot be greater than requested quantity {2} for Item {3}"
+ "The total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than requested quantity {2} for Item {3}"
).format(d.ordered_qty, d.parent, d.qty, d.item_code)
)
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index d3476a88f05..33d7745c628 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -33,7 +33,9 @@ class PickList(Document):
location.sales_order
and frappe.db.get_value("Sales Order", location.sales_order, "per_picked") == 100
):
- frappe.throw("Row " + str(location.idx) + " has been picked already!")
+ frappe.throw(
+ _("Row #{}: item {} has been picked already.").format(location.idx, location.item_code)
+ )
def before_submit(self):
for item in self.locations:
@@ -82,10 +84,9 @@ class PickList(Document):
100 + flt(frappe.db.get_single_value("Stock Settings", "over_delivery_receipt_allowance"))
):
frappe.throw(
- "You are picking more than required quantity for "
- + item_code
- + ". Check if there is any other pick list created for "
- + so_doc.name
+ _(
+ "You are picking more than required quantity for {}. Check if there is any other pick list created for {}"
+ ).format(item_code, so_doc.name)
)
frappe.db.set_value("Sales Order Item", so_item, "picked_qty", already_picked + picked_qty)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 7564bb266d7..f9baeb8e76a 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -214,7 +214,7 @@ frappe.ui.form.on('Stock Entry', {
if (frm.doc.docstatus === 1) {
if (frm.doc.add_to_transit && frm.doc.purpose=='Material Transfer' && frm.doc.per_transferred < 100) {
- frm.add_custom_button('End Transit', function() {
+ frm.add_custom_button(__('End Transit'), function() {
frappe.model.open_mapped_doc({
method: "erpnext.stock.doctype.stock_entry.stock_entry.make_stock_in_entry",
frm: frm
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
index ea27dd251da..61927f51a82 100644
--- a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
@@ -68,7 +68,7 @@ frappe.pages['warehouse-capacity-summary'].on_page_load = function(wrapper) {
options: [
{fieldname: 'stock_capacity', label: __('Capacity (Stock UOM)')},
{fieldname: 'percent_occupied', label: __('% Occupied')},
- {fieldname: 'actual_qty', label: __('Balance Qty (Stock ')}
+ {fieldname: 'actual_qty', label: __('Balance Qty (Stock)')}
]
},
change: function(sort_by, sort_order) {
diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py
index a96ffefd474..ee151b7517c 100644
--- a/erpnext/stock/reorder_item.py
+++ b/erpnext/stock/reorder_item.py
@@ -246,8 +246,7 @@ def notify_errors(exceptions_list):
_("Dear System Manager,")
+ "
"
+ _(
- "An error occured for certain Items while creating Material Requests based on Re-order level. \
- Please rectify these issues :"
+ "An error occured for certain Items while creating Material Requests based on Re-order level. Please rectify these issues :"
)
+ "
"
)
diff --git a/erpnext/stock/report/bom_search/bom_search.py b/erpnext/stock/report/bom_search/bom_search.py
index 3be87abc393..56a65c3c308 100644
--- a/erpnext/stock/report/bom_search/bom_search.py
+++ b/erpnext/stock/report/bom_search/bom_search.py
@@ -3,6 +3,7 @@
import frappe
+from frappe import _
def execute(filters=None):
@@ -34,10 +35,10 @@ def execute(filters=None):
return [
{
"fieldname": "parent",
- "label": "BOM",
+ "label": _("BOM"),
"width": 200,
"fieldtype": "Dynamic Link",
"options": "doctype",
},
- {"fieldname": "doctype", "label": "Type", "width": 200, "fieldtype": "Data"},
+ {"fieldname": "doctype", "label": _("Type"), "width": 200, "fieldtype": "Data"},
], data
diff --git a/erpnext/stock/report/item_variant_details/item_variant_details.py b/erpnext/stock/report/item_variant_details/item_variant_details.py
index d1bf2203f17..e3a2a65d8fe 100644
--- a/erpnext/stock/report/item_variant_details/item_variant_details.py
+++ b/erpnext/stock/report/item_variant_details/item_variant_details.py
@@ -71,7 +71,7 @@ def get_columns(item):
columns = [
{
"fieldname": "variant_name",
- "label": "Variant",
+ "label": _("Variant"),
"fieldtype": "Link",
"options": "Item",
"width": 200,
diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py
index 6cc9061685f..837c4a6d15c 100644
--- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py
+++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py
@@ -4,6 +4,7 @@
import json
import frappe
+from frappe import _
SLE_FIELDS = (
"name",
@@ -105,155 +106,155 @@ def get_columns():
{
"fieldname": "name",
"fieldtype": "Link",
- "label": "Stock Ledger Entry",
+ "label": _("Stock Ledger Entry"),
"options": "Stock Ledger Entry",
},
{
"fieldname": "posting_date",
"fieldtype": "Date",
- "label": "Posting Date",
+ "label": _("Posting Date"),
},
{
"fieldname": "posting_time",
"fieldtype": "Time",
- "label": "Posting Time",
+ "label": _("Posting Time"),
},
{
"fieldname": "creation",
"fieldtype": "Datetime",
- "label": "Creation",
+ "label": _("Creation"),
},
{
"fieldname": "voucher_type",
"fieldtype": "Link",
- "label": "Voucher Type",
+ "label": _("Voucher Type"),
"options": "DocType",
},
{
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
- "label": "Voucher No",
+ "label": _("Voucher No"),
"options": "voucher_type",
},
{
"fieldname": "batch_no",
"fieldtype": "Link",
- "label": "Batch",
+ "label": _("Batch"),
"options": "Batch",
},
{
"fieldname": "use_batchwise_valuation",
"fieldtype": "Check",
- "label": "Batchwise Valuation",
+ "label": _("Batchwise Valuation"),
},
{
"fieldname": "actual_qty",
"fieldtype": "Float",
- "label": "Qty Change",
+ "label": _("Qty Change"),
},
{
"fieldname": "incoming_rate",
"fieldtype": "Float",
- "label": "Incoming Rate",
+ "label": _("Incoming Rate"),
},
{
"fieldname": "consumption_rate",
"fieldtype": "Float",
- "label": "Consumption Rate",
+ "label": _("Consumption Rate"),
},
{
"fieldname": "qty_after_transaction",
"fieldtype": "Float",
- "label": "(A) Qty After Transaction",
+ "label": _("(A) Qty After Transaction"),
},
{
"fieldname": "expected_qty_after_transaction",
"fieldtype": "Float",
- "label": "(B) Expected Qty After Transaction",
+ "label": _("(B) Expected Qty After Transaction"),
},
{
"fieldname": "difference_in_qty",
"fieldtype": "Float",
- "label": "A - B",
+ "label": _("A - B"),
},
{
"fieldname": "stock_queue",
"fieldtype": "Data",
- "label": "FIFO/LIFO Queue",
+ "label": _("FIFO/LIFO Queue"),
},
{
"fieldname": "fifo_queue_qty",
"fieldtype": "Float",
- "label": "(C) Total qty in queue",
+ "label": _("(C) Total qty in queue"),
},
{
"fieldname": "fifo_qty_diff",
"fieldtype": "Float",
- "label": "A - C",
+ "label": _("A - C"),
},
{
"fieldname": "stock_value",
"fieldtype": "Float",
- "label": "(D) Balance Stock Value",
+ "label": _("(D) Balance Stock Value"),
},
{
"fieldname": "fifo_stock_value",
"fieldtype": "Float",
- "label": "(E) Balance Stock Value in Queue",
+ "label": _("(E) Balance Stock Value in Queue"),
},
{
"fieldname": "fifo_value_diff",
"fieldtype": "Float",
- "label": "D - E",
+ "label": _("D - E"),
},
{
"fieldname": "stock_value_difference",
"fieldtype": "Float",
- "label": "(F) Stock Value Difference",
+ "label": _("(F) Stock Value Difference"),
},
{
"fieldname": "stock_value_from_diff",
"fieldtype": "Float",
- "label": "Balance Stock Value using (F)",
+ "label": _("Balance Stock Value using (F)"),
},
{
"fieldname": "diff_value_diff",
"fieldtype": "Float",
- "label": "K - D",
+ "label": _("K - D"),
},
{
"fieldname": "fifo_stock_diff",
"fieldtype": "Float",
- "label": "(G) Stock Value difference (FIFO queue)",
+ "label": _("(G) Stock Value difference (FIFO queue)"),
},
{
"fieldname": "fifo_difference_diff",
"fieldtype": "Float",
- "label": "F - G",
+ "label": _("F - G"),
},
{
"fieldname": "valuation_rate",
"fieldtype": "Float",
- "label": "(H) Valuation Rate",
+ "label": _("(H) Valuation Rate"),
},
{
"fieldname": "fifo_valuation_rate",
"fieldtype": "Float",
- "label": "(I) Valuation Rate as per FIFO",
+ "label": _("(I) Valuation Rate as per FIFO"),
},
{
"fieldname": "fifo_valuation_diff",
"fieldtype": "Float",
- "label": "H - I",
+ "label": _("H - I"),
},
{
"fieldname": "balance_value_by_qty",
"fieldtype": "Float",
- "label": "(J) Valuation = Value (D) ÷ Qty (A)",
+ "label": _("(J) Valuation = Value (D) ÷ Qty (A)"),
},
{
"fieldname": "valuation_diff",
"fieldtype": "Float",
- "label": "H - J",
+ "label": _("H - J"),
},
]
diff --git a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py
index 5b51ef81c7b..57fa7bf2b7b 100644
--- a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py
+++ b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py
@@ -3,15 +3,16 @@
import frappe
+from frappe import _
def execute(filters=None):
columns = [
- {"fieldname": "creation_date", "label": "Date", "fieldtype": "Date", "width": 300},
+ {"fieldname": "creation_date", "label": _("Date"), "fieldtype": "Date", "width": 300},
{
"fieldname": "first_response_time",
"fieldtype": "Duration",
- "label": "First Response Time",
+ "label": _("First Response Time"),
"width": 300,
},
]
diff --git a/erpnext/utilities/doctype/video/video.js b/erpnext/utilities/doctype/video/video.js
index 9cb5a155ade..e6c6efbbb7c 100644
--- a/erpnext/utilities/doctype/video/video.js
+++ b/erpnext/utilities/doctype/video/video.js
@@ -4,7 +4,7 @@
frappe.ui.form.on('Video', {
refresh: function (frm) {
frm.events.toggle_youtube_statistics_section(frm);
- frm.add_custom_button("Watch Video", () => frappe.help.show_video(frm.doc.url, frm.doc.title));
+ frm.add_custom_button(__("Watch Video"), () => frappe.help.show_video(frm.doc.url, frm.doc.title));
},
toggle_youtube_statistics_section: (frm) => {
diff --git a/erpnext/utilities/report/youtube_interactions/youtube_interactions.py b/erpnext/utilities/report/youtube_interactions/youtube_interactions.py
index a65a75f3621..a2cb4e80cd8 100644
--- a/erpnext/utilities/report/youtube_interactions/youtube_interactions.py
+++ b/erpnext/utilities/report/youtube_interactions/youtube_interactions.py
@@ -67,7 +67,7 @@ def get_chart_summary_data(data):
{
"value": total_views,
"indicator": "Blue",
- "label": "Total Views",
+ "label": _("Total Views"),
"datatype": "Float",
}
]
From b91bf40f1bb11162f5589f7bde408e266f3d89bd Mon Sep 17 00:00:00 2001
From: Saqib Ansari
Date: Mon, 4 Apr 2022 14:40:07 +0530
Subject: [PATCH 21/35] fix: server error while viewing gst e-invoice
---
.../print_format/gst_e_invoice/gst_e_invoice.html | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html
index e6580493095..605ce8383e4 100644
--- a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html
+++ b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html
@@ -1,7 +1,8 @@
{%- from "templates/print_formats/standard_macros.html" import add_header, render_field, print_value -%}
-{%- set einvoice = json.loads(doc.signed_einvoice) -%}
+ {% if doc.signed_einvoice %}
+ {%- set einvoice = json.loads(doc.signed_einvoice) -%}
+ {% else %}
+
+ You must generate IRN before you can preview GST E-Invoice.
+
+ {% endif %}
+
From 12c01e2975094680bad38e74e1432bfcfce2a628 Mon Sep 17 00:00:00 2001
From: Ankush Menat
Date: Mon, 4 Apr 2022 15:22:15 +0530
Subject: [PATCH 22/35] fix: maintain FIFO queue even if outgoing_rate is not
found (#30560)
---
erpnext/stock/tests/test_valuation.py | 35 +++++++++++++++++++++++----
erpnext/stock/valuation.py | 18 +++-----------
2 files changed, 34 insertions(+), 19 deletions(-)
diff --git a/erpnext/stock/tests/test_valuation.py b/erpnext/stock/tests/test_valuation.py
index 506a666c284..e60c1caac34 100644
--- a/erpnext/stock/tests/test_valuation.py
+++ b/erpnext/stock/tests/test_valuation.py
@@ -60,9 +60,9 @@ class TestFIFOValuation(unittest.TestCase):
self.queue.remove_stock(1, 5)
self.assertEqual(self.queue, [[-1, 5]])
- # XXX
- self.queue.remove_stock(1, 10)
+ self.queue.remove_stock(1)
self.assertTotalQty(-2)
+ self.assertEqual(self.queue, [[-2, 5]])
self.queue.add_stock(2, 10)
self.assertTotalQty(0)
@@ -93,7 +93,7 @@ class TestFIFOValuation(unittest.TestCase):
self.queue.remove_stock(3, 20)
self.assertEqual(self.queue, [[1, 10], [5, 20]])
- def test_collapsing_of_queue(self):
+ def test_queue_with_unknown_rate(self):
self.queue.add_stock(1, 1)
self.queue.add_stock(1, 2)
self.queue.add_stock(1, 3)
@@ -102,8 +102,7 @@ class TestFIFOValuation(unittest.TestCase):
self.assertTotalValue(10)
self.queue.remove_stock(3, 1)
- # XXX
- self.assertEqual(self.queue, [[1, 7]])
+ self.assertEqual(self.queue, [[1, 4]])
def test_rounding_off(self):
self.queue.add_stock(1.0, 1.0)
@@ -172,6 +171,32 @@ class TestFIFOValuation(unittest.TestCase):
self.assertTotalQty(total_qty)
self.assertTotalValue(total_value)
+ @given(stock_queue_generator, st.floats(min_value=0.1, max_value=1e6))
+ def test_fifo_qty_value_nonneg_hypothesis_with_outgoing_rate(self, stock_queue, outgoing_rate):
+ self.queue = FIFOValuation([])
+ total_qty = 0.0
+ total_value = 0.0
+
+ for qty, rate in stock_queue:
+ # don't allow negative stock
+ if qty == 0 or total_qty + qty < 0 or abs(qty) < 0.1:
+ continue
+ if qty > 0:
+ self.queue.add_stock(qty, rate)
+ total_qty += qty
+ total_value += qty * rate
+ else:
+ qty = abs(qty)
+ consumed = self.queue.remove_stock(qty, outgoing_rate)
+ self.assertAlmostEqual(
+ qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}"
+ )
+ total_qty -= qty
+ total_value -= sum(q * r for q, r in consumed)
+ self.assertTotalQty(total_qty)
+ self.assertTotalValue(total_value)
+ self.assertGreaterEqual(total_value, 0)
+
class TestLIFOValuation(unittest.TestCase):
def setUp(self):
diff --git a/erpnext/stock/valuation.py b/erpnext/stock/valuation.py
index 648b2182877..35f4f12235d 100644
--- a/erpnext/stock/valuation.py
+++ b/erpnext/stock/valuation.py
@@ -60,9 +60,7 @@ class FIFOValuation(BinWiseValuation):
# specifying the attributes to save resources
# ref: https://docs.python.org/3/reference/datamodel.html#slots
- __slots__ = [
- "queue",
- ]
+ __slots__ = ["queue"]
def __init__(self, state: Optional[List[StockBin]]):
self.queue: List[StockBin] = state if state is not None else []
@@ -123,15 +121,9 @@ class FIFOValuation(BinWiseValuation):
index = idx
break
- # If no entry found with outgoing rate, collapse queue
+ # If no entry found with outgoing rate, consume as per FIFO
if index is None: # nosemgrep
- new_stock_value = sum(d[QTY] * d[RATE] for d in self.queue) - qty * outgoing_rate
- new_stock_qty = sum(d[QTY] for d in self.queue) - qty
- self.queue = [
- [new_stock_qty, new_stock_value / new_stock_qty if new_stock_qty > 0 else outgoing_rate]
- ]
- consumed_bins.append([qty, outgoing_rate])
- break
+ index = 0
else:
index = 0
@@ -172,9 +164,7 @@ class LIFOValuation(BinWiseValuation):
# specifying the attributes to save resources
# ref: https://docs.python.org/3/reference/datamodel.html#slots
- __slots__ = [
- "stack",
- ]
+ __slots__ = ["stack"]
def __init__(self, state: Optional[List[StockBin]]):
self.stack: List[StockBin] = state if state is not None else []
From 0a71cabab13075fa05a7df1942776a2f08c47089 Mon Sep 17 00:00:00 2001
From: Rohit Waghchaure
Date: Mon, 4 Apr 2022 15:10:57 +0530
Subject: [PATCH 23/35] fix: if accepted warehouse not selected during
rejection then stock ledger not created
---
erpnext/controllers/buying_controller.py | 26 ++++++++++++++----------
1 file changed, 15 insertions(+), 11 deletions(-)
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 3a69e717bd9..2bac726e5b0 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -463,6 +463,9 @@ class BuyingController(StockController, Subcontracting):
stock_items = self.get_stock_items()
for d in self.get("items"):
+ if d.item_code not in stock_items:
+ continue
+
if d.item_code in stock_items and d.warehouse:
pr_qty = flt(d.qty) * flt(d.conversion_factor)
@@ -488,6 +491,7 @@ class BuyingController(StockController, Subcontracting):
sle = self.get_sl_entries(
d, {"actual_qty": flt(pr_qty), "serial_no": cstr(d.serial_no).strip()}
)
+
if self.is_return:
outgoing_rate = get_rate_for_return(
self.doctype, self.name, d.item_code, self.return_against, item_row=d
@@ -517,18 +521,18 @@ class BuyingController(StockController, Subcontracting):
sl_entries.append(from_warehouse_sle)
- if flt(d.rejected_qty) != 0:
- sl_entries.append(
- self.get_sl_entries(
- d,
- {
- "warehouse": d.rejected_warehouse,
- "actual_qty": flt(d.rejected_qty) * flt(d.conversion_factor),
- "serial_no": cstr(d.rejected_serial_no).strip(),
- "incoming_rate": 0.0,
- },
- )
+ if flt(d.rejected_qty) != 0:
+ sl_entries.append(
+ self.get_sl_entries(
+ d,
+ {
+ "warehouse": d.rejected_warehouse,
+ "actual_qty": flt(d.rejected_qty) * flt(d.conversion_factor),
+ "serial_no": cstr(d.rejected_serial_no).strip(),
+ "incoming_rate": 0.0,
+ },
)
+ )
self.make_sl_entries_for_supplier_warehouse(sl_entries)
self.make_sl_entries(
From fed66038b5fde98cdf7c0ae98add0b629bdbdd18 Mon Sep 17 00:00:00 2001
From: Saqib Ansari
Date: Mon, 4 Apr 2022 15:59:16 +0530
Subject: [PATCH 24/35] fix(pos): do not reset search input on item selection
(#30537)
---
erpnext/selling/page/point_of_sale/pos_item_selector.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js
index 1177615aee9..b62b27bc4b3 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -243,7 +243,7 @@ erpnext.PointOfSale.ItemSelector = class {
value: "+1",
item: { item_code, batch_no, serial_no, uom, rate }
});
- me.set_search_value('');
+ me.search_field.set_focus();
});
this.search_field.$input.on('input', (e) => {
@@ -328,6 +328,7 @@ erpnext.PointOfSale.ItemSelector = class {
add_filtered_item_to_cart() {
this.$items_container.find(".item-wrapper").click();
+ this.set_search_value('');
}
resize_selector(minimize) {
From 0f2eaa2cf3d0a394d99788b13bc0b32b5b9dd02a Mon Sep 17 00:00:00 2001
From: Ankush Menat
Date: Mon, 4 Apr 2022 16:24:09 +0530
Subject: [PATCH 25/35] chore: add ui test badge
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 96093531d33..c26660c5a25 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,7 @@
[](https://github.com/frappe/erpnext/actions/workflows/server-tests.yml)
+[](https://github.com/erpnext/erpnext_ui_tests/actions/workflows/ui-tests.yml)
[](https://www.codetriage.com/frappe/erpnext)
[](https://codecov.io/gh/frappe/erpnext)
[](https://hub.docker.com/r/frappe/erpnext-worker)
From 74e9bc6d651a737f0d3d9acacb66b7dbd61a35fa Mon Sep 17 00:00:00 2001
From: Ankush Menat
Date: Mon, 4 Apr 2022 16:58:25 +0530
Subject: [PATCH 26/35] chore: loosen pandas dependency and bump redisearch
(#30249)
---
requirements.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/requirements.txt b/requirements.txt
index 39591caf922..657054fb240 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,7 @@
# frappe # https://github.com/frappe/frappe is installed during bench-init
gocardless-pro~=1.22.0
googlemaps
-pandas~=1.1.5
+pandas>=1.1.5,<2.0.0
plaid-python~=7.2.1
pycountry~=20.7.3
PyGithub~=1.55
@@ -10,4 +10,4 @@ python-youtube~=0.8.0
taxjar~=1.9.2
tweepy~=3.10.0
Unidecode~=1.2.0
-redisearch==2.0.0
\ No newline at end of file
+redisearch~=2.1.0
From de83511091189194d70b77411298fd809060063d Mon Sep 17 00:00:00 2001
From: Ankush Menat
Date: Mon, 4 Apr 2022 17:29:51 +0530
Subject: [PATCH 27/35] fix(ux): refresh update to zero val checkbox (#30567)
---
erpnext/stock/doctype/stock_entry/stock_entry.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 17774a67bef..1df56ef7b4c 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -633,7 +633,7 @@ frappe.ui.form.on('Stock Entry Detail', {
// set allow_zero_valuation_rate to 0 if s_warehouse is selected.
let item = frappe.get_doc(cdt, cdn);
if (item.s_warehouse) {
- item.allow_zero_valuation_rate = 0;
+ frappe.model.set_value(cdt, cdn, "allow_zero_valuation_rate", 0);
}
},
From 3538656a7d6b82f6e84a7031f1542f1ce2ec57f4 Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Mon, 4 Apr 2022 17:52:30 +0530
Subject: [PATCH 28/35] fix: total leaves allocated not validated and
recalculated on updates post submission
---
.../leave_allocation/leave_allocation.py | 68 ++++++++++++-------
1 file changed, 42 insertions(+), 26 deletions(-)
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
index 98408afab64..27479a5e81f 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
@@ -39,11 +39,15 @@ class LeaveAllocation(Document):
def validate(self):
self.validate_period()
self.validate_allocation_overlap()
- self.validate_back_dated_allocation()
- self.set_total_leaves_allocated()
- self.validate_total_leaves_allocated()
self.validate_lwp()
set_employee_name(self)
+ self.set_total_leaves_allocated()
+ self.validate_leave_days_and_dates()
+
+ def validate_leave_days_and_dates(self):
+ # all validations that should run on save as well as on update after submit
+ self.validate_back_dated_allocation()
+ self.validate_total_leaves_allocated()
self.validate_leave_allocation_days()
def validate_leave_allocation_days(self):
@@ -56,14 +60,19 @@ class LeaveAllocation(Document):
leave_allocated = 0
if leave_period:
leave_allocated = get_leave_allocation_for_period(
- self.employee, self.leave_type, leave_period[0].from_date, leave_period[0].to_date
+ self.employee,
+ self.leave_type,
+ leave_period[0].from_date,
+ leave_period[0].to_date,
+ exclude_allocation=self.name,
)
leave_allocated += flt(self.new_leaves_allocated)
if leave_allocated > max_leaves_allowed:
frappe.throw(
_(
- "Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period"
- ).format(self.leave_type, self.employee)
+ "Total allocated leaves are more than maximum allocation allowed for {0} leave type for employee {1} in the period"
+ ).format(self.leave_type, self.employee),
+ OverAllocationError,
)
def on_submit(self):
@@ -84,6 +93,12 @@ class LeaveAllocation(Document):
def on_update_after_submit(self):
if self.has_value_changed("new_leaves_allocated"):
self.validate_against_leave_applications()
+
+ # recalculate total leaves allocated
+ self.total_leaves_allocated = flt(self.unused_leaves) + flt(self.new_leaves_allocated)
+ # run required validations again since total leaves are being updated
+ self.validate_leave_days_and_dates()
+
leaves_to_be_added = self.new_leaves_allocated - self.get_existing_leave_count()
args = {
"leaves": leaves_to_be_added,
@@ -92,6 +107,7 @@ class LeaveAllocation(Document):
"is_carry_forward": 0,
}
create_leave_ledger_entry(self, args, True)
+ self.db_update()
def get_existing_leave_count(self):
ledger_entries = frappe.get_all(
@@ -279,27 +295,27 @@ def get_previous_allocation(from_date, leave_type, employee):
)
-def get_leave_allocation_for_period(employee, leave_type, from_date, to_date):
- leave_allocated = 0
- leave_allocations = frappe.db.sql(
- """
- select employee, leave_type, from_date, to_date, total_leaves_allocated
- from `tabLeave Allocation`
- where employee=%(employee)s and leave_type=%(leave_type)s
- and docstatus=1
- and (from_date between %(from_date)s and %(to_date)s
- or to_date between %(from_date)s and %(to_date)s
- or (from_date < %(from_date)s and to_date > %(to_date)s))
- """,
- {"from_date": from_date, "to_date": to_date, "employee": employee, "leave_type": leave_type},
- as_dict=1,
- )
+def get_leave_allocation_for_period(
+ employee, leave_type, from_date, to_date, exclude_allocation=None
+):
+ from frappe.query_builder.functions import Sum
- if leave_allocations:
- for leave_alloc in leave_allocations:
- leave_allocated += leave_alloc.total_leaves_allocated
-
- return leave_allocated
+ Allocation = frappe.qb.DocType("Leave Allocation")
+ return (
+ frappe.qb.from_(Allocation)
+ .select(Sum(Allocation.total_leaves_allocated).as_("total_allocated_leaves"))
+ .where(
+ (Allocation.employee == employee)
+ & (Allocation.leave_type == leave_type)
+ & (Allocation.docstatus == 1)
+ & (Allocation.name != exclude_allocation)
+ & (
+ (Allocation.from_date.between(from_date, to_date))
+ | (Allocation.to_date.between(from_date, to_date))
+ | ((Allocation.from_date < from_date) & (Allocation.to_date > to_date))
+ )
+ )
+ ).run()[0][0] or 0.0
@frappe.whitelist()
From ac5df1abbe80255d685966c108835cdb75f90659 Mon Sep 17 00:00:00 2001
From: Rohit Waghchaure
Date: Mon, 4 Apr 2022 15:38:42 +0530
Subject: [PATCH 29/35] test: test case to validate rejected qty without
accepted warehouse
---
erpnext/controllers/buying_controller.py | 2 +-
.../purchase_receipt/test_purchase_receipt.py | 39 +++++++++++++++++++
2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 2bac726e5b0..eda36868b9f 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -466,7 +466,7 @@ class BuyingController(StockController, Subcontracting):
if d.item_code not in stock_items:
continue
- if d.item_code in stock_items and d.warehouse:
+ if d.warehouse:
pr_qty = flt(d.qty) * flt(d.conversion_factor)
if pr_qty:
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index bfbdd562921..f3faba4f8d5 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -647,6 +647,45 @@ class TestPurchaseReceipt(FrappeTestCase):
return_pr.cancel()
pr.cancel()
+ def test_purchase_receipt_for_rejected_gle_without_accepted_warehouse(self):
+ from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
+
+ rejected_warehouse = "_Test Rejected Warehouse - TCP1"
+ if not frappe.db.exists("Warehouse", rejected_warehouse):
+ get_warehouse(
+ company="_Test Company with perpetual inventory",
+ abbr=" - TCP1",
+ warehouse_name="_Test Rejected Warehouse",
+ ).name
+
+ pr = make_purchase_receipt(
+ company="_Test Company with perpetual inventory",
+ warehouse="Stores - TCP1",
+ received_qty=2,
+ rejected_qty=2,
+ rejected_warehouse=rejected_warehouse,
+ do_not_save=True,
+ )
+
+ pr.items[0].qty = 0.0
+ pr.items[0].warehouse = ""
+ pr.submit()
+
+ actual_qty = frappe.db.get_value(
+ "Stock Ledger Entry",
+ {
+ "voucher_type": "Purchase Receipt",
+ "voucher_no": pr.name,
+ "warehouse": pr.items[0].rejected_warehouse,
+ "is_cancelled": 0,
+ },
+ "actual_qty",
+ )
+
+ self.assertEqual(actual_qty, 2)
+ self.assertFalse(pr.items[0].warehouse)
+ pr.cancel()
+
def test_purchase_return_for_serialized_items(self):
def _check_serial_no_values(serial_no, field_values):
serial_no = frappe.get_doc("Serial No", serial_no)
From 5499cecffd76f7e5414181855008cdb8e50634ef Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Mon, 4 Apr 2022 18:32:17 +0530
Subject: [PATCH 30/35] test: leave allocation validations and total value for
updates done before and after submission
---
.../leave_allocation/test_leave_allocation.py | 158 ++++++++++++++++--
1 file changed, 148 insertions(+), 10 deletions(-)
diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
index a53d4a82ba6..6b3636db355 100644
--- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
@@ -1,24 +1,26 @@
import unittest
import frappe
+from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, add_months, getdate, nowdate
import erpnext
from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.leave_allocation.leave_allocation import (
+ BackDatedAllocationError,
+ OverAllocationError,
+)
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation
from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
-class TestLeaveAllocation(unittest.TestCase):
- @classmethod
- def setUpClass(cls):
- frappe.db.sql("delete from `tabLeave Period`")
+class TestLeaveAllocation(FrappeTestCase):
+ def setUp(self):
+ frappe.db.delete("Leave Period")
+ frappe.db.delete("Leave Allocation")
emp_id = make_employee("test_emp_leave_allocation@salary.com")
- cls.employee = frappe.get_doc("Employee", emp_id)
-
- def tearDown(self):
- frappe.db.rollback()
+ self.employee = frappe.get_doc("Employee", emp_id)
def test_overlapping_allocation(self):
leaves = [
@@ -65,7 +67,7 @@ class TestLeaveAllocation(unittest.TestCase):
# invalid period
self.assertRaises(frappe.ValidationError, doc.save)
- def test_allocated_leave_days_over_period(self):
+ def test_validation_for_over_allocation(self):
doc = frappe.get_doc(
{
"doctype": "Leave Allocation",
@@ -80,7 +82,135 @@ class TestLeaveAllocation(unittest.TestCase):
)
# allocated leave more than period
- self.assertRaises(frappe.ValidationError, doc.save)
+ self.assertRaises(OverAllocationError, doc.save)
+
+ def test_validation_for_over_allocation_post_submission(self):
+ allocation = frappe.get_doc(
+ {
+ "doctype": "Leave Allocation",
+ "__islocal": 1,
+ "employee": self.employee.name,
+ "employee_name": self.employee.employee_name,
+ "leave_type": "_Test Leave Type",
+ "from_date": getdate("2015-09-1"),
+ "to_date": getdate("2015-09-30"),
+ "new_leaves_allocated": 15,
+ }
+ ).submit()
+ allocation.reload()
+ # allocated leaves more than period after submission
+ allocation.new_leaves_allocated = 35
+ self.assertRaises(OverAllocationError, allocation.save)
+
+ def test_validation_for_over_allocation_based_on_leave_setup(self):
+ frappe.delete_doc_if_exists("Leave Period", "Test Allocation Period")
+ leave_period = frappe.get_doc(
+ dict(
+ name="Test Allocation Period",
+ doctype="Leave Period",
+ from_date=add_months(nowdate(), -6),
+ to_date=add_months(nowdate(), 6),
+ company="_Test Company",
+ is_active=1,
+ )
+ ).insert()
+
+ leave_type = create_leave_type(leave_type_name="_Test Allocation Validation", is_carry_forward=1)
+ leave_type.max_leaves_allowed = 25
+ leave_type.save()
+
+ # 15 leaves allocated in this period
+ allocation = create_leave_allocation(
+ leave_type=leave_type.name,
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name,
+ from_date=leave_period.from_date,
+ to_date=nowdate(),
+ )
+ allocation.submit()
+
+ # trying to allocate additional 15 leaves
+ allocation = create_leave_allocation(
+ leave_type=leave_type.name,
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name,
+ from_date=add_days(nowdate(), 1),
+ to_date=leave_period.to_date,
+ )
+ self.assertRaises(OverAllocationError, allocation.save)
+
+ def test_validation_for_over_allocation_based_on_leave_setup_post_submission(self):
+ frappe.delete_doc_if_exists("Leave Period", "Test Allocation Period")
+ leave_period = frappe.get_doc(
+ dict(
+ name="Test Allocation Period",
+ doctype="Leave Period",
+ from_date=add_months(nowdate(), -6),
+ to_date=add_months(nowdate(), 6),
+ company="_Test Company",
+ is_active=1,
+ )
+ ).insert()
+
+ leave_type = create_leave_type(leave_type_name="_Test Allocation Validation", is_carry_forward=1)
+ leave_type.max_leaves_allowed = 30
+ leave_type.save()
+
+ # 15 leaves allocated
+ allocation = create_leave_allocation(
+ leave_type=leave_type.name,
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name,
+ from_date=leave_period.from_date,
+ to_date=nowdate(),
+ )
+ allocation.submit()
+ allocation.reload()
+
+ # allocate additional 15 leaves
+ allocation = create_leave_allocation(
+ leave_type=leave_type.name,
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name,
+ from_date=add_days(nowdate(), 1),
+ to_date=leave_period.to_date,
+ )
+ allocation.submit()
+ allocation.reload()
+
+ # trying to allocate 25 leaves in 2nd alloc within leave period
+ # total leaves = 40 which is more than `max_leaves_allowed` setting i.e. 30
+ allocation.new_leaves_allocated = 25
+ self.assertRaises(OverAllocationError, allocation.save)
+
+ def test_validate_back_dated_allocation_update(self):
+ leave_type = create_leave_type(leave_type_name="_Test_CF_leave", is_carry_forward=1)
+ leave_type.save()
+
+ # initial leave allocation = 15
+ leave_allocation = create_leave_allocation(
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name,
+ leave_type="_Test_CF_leave",
+ from_date=add_months(nowdate(), -12),
+ to_date=add_months(nowdate(), -1),
+ carry_forward=0,
+ )
+ leave_allocation.submit()
+
+ # new_leaves = 15, carry_forwarded = 10
+ leave_allocation_1 = create_leave_allocation(
+ employee=self.employee.name,
+ employee_name=self.employee.employee_name,
+ leave_type="_Test_CF_leave",
+ carry_forward=1,
+ )
+ leave_allocation_1.submit()
+
+ # try updating initial leave allocation
+ leave_allocation.reload()
+ leave_allocation.new_leaves_allocated = 20
+ self.assertRaises(BackDatedAllocationError, leave_allocation.save)
def test_carry_forward_calculation(self):
leave_type = create_leave_type(leave_type_name="_Test_CF_leave", is_carry_forward=1)
@@ -108,8 +238,10 @@ class TestLeaveAllocation(unittest.TestCase):
carry_forward=1,
)
leave_allocation_1.submit()
+ leave_allocation_1.reload()
self.assertEqual(leave_allocation_1.unused_leaves, 10)
+ self.assertEqual(leave_allocation_1.total_leaves_allocated, 25)
leave_allocation_1.cancel()
@@ -197,9 +329,12 @@ class TestLeaveAllocation(unittest.TestCase):
employee=self.employee.name, employee_name=self.employee.employee_name
)
leave_allocation.submit()
+ leave_allocation.reload()
self.assertTrue(leave_allocation.total_leaves_allocated, 15)
+
leave_allocation.new_leaves_allocated = 40
leave_allocation.submit()
+ leave_allocation.reload()
self.assertTrue(leave_allocation.total_leaves_allocated, 40)
def test_leave_subtraction_after_submit(self):
@@ -207,9 +342,12 @@ class TestLeaveAllocation(unittest.TestCase):
employee=self.employee.name, employee_name=self.employee.employee_name
)
leave_allocation.submit()
+ leave_allocation.reload()
self.assertTrue(leave_allocation.total_leaves_allocated, 15)
+
leave_allocation.new_leaves_allocated = 10
leave_allocation.submit()
+ leave_allocation.reload()
self.assertTrue(leave_allocation.total_leaves_allocated, 10)
def test_validation_against_leave_application_after_submit(self):
From 793164ac2efe9588d16e88cc27141cb03cf57d36 Mon Sep 17 00:00:00 2001
From: Rucha Mahabal
Date: Mon, 4 Apr 2022 19:08:27 +0530
Subject: [PATCH 31/35] fix(test): set company for employee in leave allocation
test setup
---
erpnext/hr/doctype/leave_allocation/test_leave_allocation.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
index 6b3636db355..dde52d7ad8e 100644
--- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
@@ -19,7 +19,7 @@ class TestLeaveAllocation(FrappeTestCase):
frappe.db.delete("Leave Period")
frappe.db.delete("Leave Allocation")
- emp_id = make_employee("test_emp_leave_allocation@salary.com")
+ emp_id = make_employee("test_emp_leave_allocation@salary.com", company="_Test Company")
self.employee = frappe.get_doc("Employee", emp_id)
def test_overlapping_allocation(self):
From e7962672148602d707377e0c44a18dbcdea00922 Mon Sep 17 00:00:00 2001
From: Sherin KR
Date: Mon, 4 Apr 2022 06:37:51 -0700
Subject: [PATCH 32/35] fix: Validation for single threshold in Tax With
Holding Category (#30382)
(cherry picked from commit 0a2c72c594963f63551985a908c1c79302556e91)
---
.../tax_withholding_category/tax_withholding_category.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 35c2f8494fc..a519d8be736 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -34,7 +34,9 @@ class TaxWithholdingCategory(Document):
def validate_thresholds(self):
for d in self.get("rates"):
- if d.cumulative_threshold and d.cumulative_threshold < d.single_threshold:
+ if (
+ d.cumulative_threshold and d.single_threshold and d.cumulative_threshold < d.single_threshold
+ ):
frappe.throw(
_("Row #{0}: Cumulative threshold cannot be less than Single Transaction threshold").format(
d.idx
From d4301d6d2f4a2396b8dcfc2845574115e05636d1 Mon Sep 17 00:00:00 2001
From: marination
Date: Tue, 5 Apr 2022 12:07:50 +0530
Subject: [PATCH 33/35] chore: Accessibility for E-commerce Doctypes
- Add Website Item routing button and dashboard link in Item master
- Group Item variant buttons together
---
erpnext/stock/doctype/item/item.js | 18 ++++++++++++------
erpnext/stock/doctype/item/item_dashboard.py | 1 +
2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 9e8b3bd4637..23301a6a78e 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -55,10 +55,15 @@ frappe.ui.form.on("Item", {
if (frm.doc.has_variants) {
frm.set_intro(__("This Item is a Template and cannot be used in transactions. Item attributes will be copied over into the variants unless 'No Copy' is set"), true);
+
frm.add_custom_button(__("Show Variants"), function() {
frappe.set_route("List", "Item", {"variant_of": frm.doc.name});
}, __("View"));
+ frm.add_custom_button(__("Item Variant Settings"), function() {
+ frappe.set_route("Form", "Item Variant Settings");
+ }, __("View"));
+
frm.add_custom_button(__("Variant Details Report"), function() {
frappe.set_route("query-report", "Item Variant Details", {"item": frm.doc.name});
}, __("View"));
@@ -110,6 +115,13 @@ frappe.ui.form.on("Item", {
}
});
}, __('Actions'));
+ } else {
+ frm.add_custom_button(__("Website Item"), function() {
+ frappe.db.get_value("Website Item", {item_code: frm.doc.name}, "name", (d) => {
+ if (!d.name) frappe.throw(__("Website Item not found"));
+ frappe.set_route("Form", "Website Item", d.name);
+ });
+ }, __("View"));
}
erpnext.item.edit_prices_button(frm);
@@ -131,12 +143,6 @@ frappe.ui.form.on("Item", {
frappe.set_route('Form', 'Item', new_item.name);
});
- if(frm.doc.has_variants) {
- frm.add_custom_button(__("Item Variant Settings"), function() {
- frappe.set_route("Form", "Item Variant Settings");
- }, __("View"));
- }
-
const stock_exists = (frm.doc.__onload
&& frm.doc.__onload.stock_exists) ? 1 : 0;
diff --git a/erpnext/stock/doctype/item/item_dashboard.py b/erpnext/stock/doctype/item/item_dashboard.py
index 33acf4bfd8a..3caed02d69b 100644
--- a/erpnext/stock/doctype/item/item_dashboard.py
+++ b/erpnext/stock/doctype/item/item_dashboard.py
@@ -32,5 +32,6 @@ def get_data():
{"label": _("Manufacture"), "items": ["Production Plan", "Work Order", "Item Manufacturer"]},
{"label": _("Traceability"), "items": ["Serial No", "Batch"]},
{"label": _("Move"), "items": ["Stock Entry"]},
+ {"label": _("E-commerce"), "items": ["Website Item"]},
],
}
From 065623ce2526c3df6f3355675d3138fb86550af0 Mon Sep 17 00:00:00 2001
From: marination
Date: Tue, 5 Apr 2022 12:30:02 +0530
Subject: [PATCH 34/35] chore: Add Prices, Stock and E-com Settings access from
Website Item
---
.../doctype/website_item/website_item.js | 25 ++++++++++++++++---
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/erpnext/e_commerce/doctype/website_item/website_item.js b/erpnext/e_commerce/doctype/website_item/website_item.js
index 7108cabfb3f..7295e4b56a0 100644
--- a/erpnext/e_commerce/doctype/website_item/website_item.js
+++ b/erpnext/e_commerce/doctype/website_item/website_item.js
@@ -2,7 +2,7 @@
// For license information, please see license.txt
frappe.ui.form.on('Website Item', {
- onload: function(frm) {
+ onload: (frm) => {
// should never check Private
frm.fields_dict["website_image"].df.is_private = 0;
@@ -13,18 +13,35 @@ frappe.ui.form.on('Website Item', {
});
},
- image: function() {
+ refresh: (frm) => {
+ frm.add_custom_button(__("Prices"), function() {
+ frappe.set_route("List", "Item Price", {"item_code": frm.doc.item_code});
+ }, __("View"));
+
+ frm.add_custom_button(__("Stock"), function() {
+ frappe.route_options = {
+ "item_code": frm.doc.item_code
+ };
+ frappe.set_route("query-report", "Stock Balance");
+ }, __("View"));
+
+ frm.add_custom_button(__("E Commerce Settings"), function() {
+ frappe.set_route("Form", "E Commerce Settings");
+ }, __("View"));
+ },
+
+ image: () => {
refresh_field("image_view");
},
- copy_from_item_group: function(frm) {
+ copy_from_item_group: (frm) => {
return frm.call({
doc: frm.doc,
method: "copy_specification_from_item_group"
});
},
- set_meta_tags(frm) {
+ set_meta_tags: (frm) => {
frappe.utils.set_meta_tag(frm.doc.route);
}
});
From c8779aa4465ddf58db7751c39e1a89c9ea92eead Mon Sep 17 00:00:00 2001
From: Saqib Ansari
Date: Tue, 5 Apr 2022 15:38:39 +0530
Subject: [PATCH 35/35] fix: fetch from fields not working in eway bill dialog
---
erpnext/regional/india/e_invoice/einvoice.js | 32 +++++++++++++++++---
1 file changed, 28 insertions(+), 4 deletions(-)
diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js
index 348f0c6feed..17b018c65b4 100644
--- a/erpnext/regional/india/e_invoice/einvoice.js
+++ b/erpnext/regional/india/e_invoice/einvoice.js
@@ -105,6 +105,30 @@ erpnext.setup_einvoice_actions = (doctype) => {
},
primary_action_label: __('Submit')
});
+ d.fields_dict.transporter.df.onchange = function () {
+ const transporter = d.fields_dict.transporter.value;
+ if (transporter) {
+ frappe.db.get_value('Supplier', transporter, ['gst_transporter_id', 'supplier_name'])
+ .then(({ message }) => {
+ d.set_value('gst_transporter_id', message.gst_transporter_id);
+ d.set_value('transporter_name', message.supplier_name);
+ });
+ } else {
+ d.set_value('gst_transporter_id', '');
+ d.set_value('transporter_name', '');
+ }
+ };
+ d.fields_dict.driver.df.onchange = function () {
+ const driver = d.fields_dict.driver.value;
+ if (driver) {
+ frappe.db.get_value('Driver', driver, ['full_name'])
+ .then(({ message }) => {
+ d.set_value('driver_name', message.full_name);
+ });
+ } else {
+ d.set_value('driver_name', '');
+ }
+ };
d.show();
};
@@ -153,7 +177,6 @@ const get_ewaybill_fields = (frm) => {
'fieldname': 'gst_transporter_id',
'label': 'GST Transporter ID',
'fieldtype': 'Data',
- 'fetch_from': 'transporter.gst_transporter_id',
'default': frm.doc.gst_transporter_id
},
{
@@ -189,9 +212,9 @@ const get_ewaybill_fields = (frm) => {
'fieldname': 'transporter_name',
'label': 'Transporter Name',
'fieldtype': 'Data',
- 'fetch_from': 'transporter.name',
'read_only': 1,
- 'default': frm.doc.transporter_name
+ 'default': frm.doc.transporter_name,
+ 'depends_on': 'transporter'
},
{
'fieldname': 'mode_of_transport',
@@ -206,7 +229,8 @@ const get_ewaybill_fields = (frm) => {
'fieldtype': 'Data',
'fetch_from': 'driver.full_name',
'read_only': 1,
- 'default': frm.doc.driver_name
+ 'default': frm.doc.driver_name,
+ 'depends_on': 'driver'
},
{
'fieldname': 'lr_date',