${res.web_item_name}
${res.brand ? "by " + res.brand : ""}
@@ -241,4 +241,4 @@ erpnext.ProductSearch = class {
this.category_container.html(html);
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/e_commerce/redisearch_utils.py b/erpnext/e_commerce/redisearch_utils.py
index 1f649c7b48..87ca9bd83d 100644
--- a/erpnext/e_commerce/redisearch_utils.py
+++ b/erpnext/e_commerce/redisearch_utils.py
@@ -7,7 +7,9 @@ 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
+from redis.commands.search.field import TagField, TextField
+from redis.commands.search.indexDefinition import IndexDefinition
+from redis.commands.search.suggestion import Suggestion
WEBSITE_ITEM_INDEX = "website_items_index"
WEBSITE_ITEM_KEY_PREFIX = "website_item:"
@@ -35,12 +37,9 @@ def is_redisearch_enabled():
def is_search_module_loaded():
try:
cache = frappe.cache()
- out = cache.execute_command("MODULE LIST")
-
- parsed_output = " ".join(
- (" ".join([frappe.as_unicode(s) for s in o if not isinstance(s, int)]) for o in out)
- )
- return "search" in parsed_output
+ for module in cache.module_list():
+ if module.get(b"name") == b"search":
+ return True
except Exception:
return False # handling older redis versions
@@ -58,18 +57,18 @@ def if_redisearch_enabled(function):
def make_key(key):
- return "{0}|{1}".format(frappe.conf.db_name, key).encode("utf-8")
+ return frappe.cache().make_key(key)
@if_redisearch_enabled
def create_website_items_index():
"Creates Index Definition."
- # CREATE index
- client = Client(make_key(WEBSITE_ITEM_INDEX), conn=frappe.cache())
+ redis = frappe.cache()
+ index = redis.ft(WEBSITE_ITEM_INDEX)
try:
- client.drop_index() # drop if already exists
+ index.dropindex() # drop if already exists
except ResponseError:
# will most likely raise a ResponseError if index does not exist
# ignore and create index
@@ -86,9 +85,10 @@ def create_website_items_index():
if "web_item_name" in idx_fields:
idx_fields.remove("web_item_name")
- idx_fields = list(map(to_search_field, idx_fields))
+ idx_fields = [to_search_field(f) for f in idx_fields]
- client.create_index(
+ # TODO: sortable?
+ index.create_index(
[TextField("web_item_name", sortable=True)] + idx_fields,
definition=idx_def,
)
@@ -119,8 +119,8 @@ def insert_item_to_index(website_item_doc):
@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))
+ ac = frappe.cache().ft()
+ ac.sugadd(WEBSITE_ITEM_NAME_AUTOCOMPLETE, Suggestion(web_name, payload=doc_name))
def create_web_item_map(website_item_doc):
@@ -157,9 +157,8 @@ def delete_item_from_index(website_item_doc):
@if_redisearch_enabled
def delete_from_ac_dict(website_item_doc):
"""Removes this items's name from autocomplete dictionary"""
- cache = frappe.cache()
- name_ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=cache)
- name_ac.delete(website_item_doc.web_item_name)
+ ac = frappe.cache().ft()
+ ac.sugdel(website_item_doc.web_item_name)
@if_redisearch_enabled
@@ -170,8 +169,6 @@ def define_autocomplete_dictionary():
"""
cache = frappe.cache()
- 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:
@@ -180,38 +177,43 @@ def define_autocomplete_dictionary():
except Exception:
raise_redisearch_error()
- create_items_autocomplete_dict(autocompleter=item_ac)
- create_item_groups_autocomplete_dict(autocompleter=item_group_ac)
+ create_items_autocomplete_dict()
+ create_item_groups_autocomplete_dict()
@if_redisearch_enabled
-def create_items_autocomplete_dict(autocompleter):
+def create_items_autocomplete_dict():
"Add items as suggestions in Autocompleter."
+
+ ac = frappe.cache().ft()
items = frappe.get_all(
"Website Item", fields=["web_item_name", "item_group"], filters={"published": 1}
)
-
for item in items:
- autocompleter.add_suggestions(Suggestion(item.web_item_name))
+ ac.sugadd(WEBSITE_ITEM_NAME_AUTOCOMPLETE, Suggestion(item.web_item_name))
@if_redisearch_enabled
-def create_item_groups_autocomplete_dict(autocompleter):
+def create_item_groups_autocomplete_dict():
"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
+ ac = frappe.cache().ft()
+
for item_group in published_item_groups:
payload = json.dumps({"name": item_group.name, "route": item_group.route})
- autocompleter.add_suggestions(
+ ac.sugadd(
+ WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE,
Suggestion(
string=item_group.name,
score=frappe.utils.flt(item_group.weightage) or 1.0,
payload=payload, # additional info that can be retrieved later
- )
+ ),
)
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
index 6d62aefdca..cac3f1f0f3 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
@@ -236,7 +236,7 @@ def get_term_loans(date, term_loan=None, loan_type=None):
AND l.is_term_loan =1
AND rs.payment_date <= %s
AND rs.is_accrued=0 {0}
- AND rs.interest_amount > 0
+ AND rs.principal_amount > 0
AND l.status = 'Disbursed'
ORDER BY rs.payment_date""".format(
condition
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 29da988ce4..018832c7d7 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -735,6 +735,7 @@ def get_amounts(amounts, against_loan, posting_date):
)
amounts["pending_accrual_entries"] = pending_accrual_entries
amounts["unaccrued_interest"] = flt(unaccrued_interest, precision)
+ amounts["written_off_amount"] = flt(against_loan_doc.written_off_amount, precision)
if final_due_date:
amounts["due_date"] = final_due_date
diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py
index 81464a36c3..25c72d91a7 100644
--- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py
@@ -57,7 +57,7 @@ def process_loan_interest_accrual_for_demand_loans(
def process_loan_interest_accrual_for_term_loans(posting_date=None, loan_type=None, loan=None):
- if not term_loan_accrual_pending(posting_date or nowdate()):
+ if not term_loan_accrual_pending(posting_date or nowdate(), loan=loan):
return
loan_process = frappe.new_doc("Process Loan Interest Accrual")
@@ -71,9 +71,12 @@ def process_loan_interest_accrual_for_term_loans(posting_date=None, loan_type=No
return loan_process.name
-def term_loan_accrual_pending(date):
- pending_accrual = frappe.db.get_value(
- "Repayment Schedule", {"payment_date": ("<=", date), "is_accrued": 0}
- )
+def term_loan_accrual_pending(date, loan=None):
+ filters = {"payment_date": ("<=", date), "is_accrued": 0}
+
+ if loan:
+ filters.update({"parent": loan})
+
+ pending_accrual = frappe.db.get_value("Repayment Schedule", filters)
return pending_accrual
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index d92353aca4..4729add16b 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -311,4 +311,5 @@ erpnext.patches.v14_0.remove_india_localisation # 14-07-2022
erpnext.patches.v13_0.fix_number_and_frequency_for_monthly_depreciation
erpnext.patches.v14_0.remove_hr_and_payroll_modules # 20-07-2022
erpnext.patches.v14_0.fix_crm_no_of_employees
-erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes
\ No newline at end of file
+erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes
+erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger
diff --git a/erpnext/patches/v13_0/add_doctype_to_sla.py b/erpnext/patches/v13_0/add_doctype_to_sla.py
index 5f5974f65d..2d3b0de5b5 100644
--- a/erpnext/patches/v13_0/add_doctype_to_sla.py
+++ b/erpnext/patches/v13_0/add_doctype_to_sla.py
@@ -14,7 +14,8 @@ def execute():
for sla in frappe.get_all("Service Level Agreement"):
agreement = frappe.get_doc("Service Level Agreement", sla.name)
- agreement.document_type = "Issue"
+ agreement.db_set("document_type", "Issue")
+ agreement.reload()
agreement.apply_sla_for_resolution = 1
agreement.append("sla_fulfilled_on", {"status": "Resolved"})
agreement.append("sla_fulfilled_on", {"status": "Closed"})
diff --git a/erpnext/patches/v13_0/delete_old_sales_reports.py b/erpnext/patches/v13_0/delete_old_sales_reports.py
index b31c9d17d7..1b53da755c 100644
--- a/erpnext/patches/v13_0/delete_old_sales_reports.py
+++ b/erpnext/patches/v13_0/delete_old_sales_reports.py
@@ -16,18 +16,18 @@ def execute():
delete_auto_email_reports(report)
check_and_delete_linked_reports(report)
- frappe.delete_doc("Report", report)
+ frappe.delete_doc("Report", report, force=True)
def delete_auto_email_reports(report):
"""Check for one or multiple Auto Email Reports and delete"""
auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"])
for auto_email_report in auto_email_reports:
- frappe.delete_doc("Auto Email Report", auto_email_report[0])
+ frappe.delete_doc("Auto Email Report", auto_email_report[0], force=True)
def delete_links_from_desktop_icons(report):
"""Check for one or multiple Desktop Icons and delete"""
desktop_icons = frappe.db.get_values("Desktop Icon", {"_report": report}, ["name"])
for desktop_icon in desktop_icons:
- frappe.delete_doc("Desktop Icon", desktop_icon[0])
+ frappe.delete_doc("Desktop Icon", desktop_icon[0], force=True)
diff --git a/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py b/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py
new file mode 100644
index 0000000000..062d24b78b
--- /dev/null
+++ b/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py
@@ -0,0 +1,56 @@
+import frappe
+from frappe import qb
+from frappe.utils import create_batch
+
+
+def execute():
+ if frappe.reload_doc("accounts", "doctype", "payment_ledger_entry"):
+
+ gle = qb.DocType("GL Entry")
+ ple = qb.DocType("Payment Ledger Entry")
+
+ # get ple and their remarks from GL Entry
+ pl_entries = (
+ qb.from_(ple)
+ .left_join(gle)
+ .on(
+ (ple.account == gle.account)
+ & (ple.party_type == gle.party_type)
+ & (ple.party == gle.party)
+ & (ple.voucher_type == gle.voucher_type)
+ & (ple.voucher_no == gle.voucher_no)
+ & (ple.company == gle.company)
+ )
+ .select(
+ ple.company,
+ ple.account,
+ ple.party_type,
+ ple.party,
+ ple.voucher_type,
+ ple.voucher_no,
+ gle.remarks.as_("gle_remarks"),
+ )
+ .where((ple.delinked == 0) & (gle.is_cancelled == 0))
+ .run(as_dict=True)
+ )
+
+ if pl_entries:
+ # split into multiple batches, update and commit for each batch
+ batch_size = 1000
+ for batch in create_batch(pl_entries, batch_size):
+ for entry in batch:
+ query = (
+ qb.update(ple)
+ .set(ple.remarks, entry.gle_remarks)
+ .where(
+ (ple.company == entry.company)
+ & (ple.account == entry.account)
+ & (ple.party_type == entry.party_type)
+ & (ple.party == entry.party)
+ & (ple.voucher_type == entry.voucher_type)
+ & (ple.voucher_no == entry.voucher_no)
+ )
+ )
+ query.run()
+
+ frappe.db.commit()
diff --git a/erpnext/projects/doctype/task_type/task_type.json b/erpnext/projects/doctype/task_type/task_type.json
index 3254444a48..b04264e9c7 100644
--- a/erpnext/projects/doctype/task_type/task_type.json
+++ b/erpnext/projects/doctype/task_type/task_type.json
@@ -1,127 +1,70 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
+ "actions": [],
"autoname": "Prompt",
- "beta": 0,
"creation": "2019-04-19 15:04:05.317138",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
- "document_type": "",
- "editable_grid": 0,
"engine": "InnoDB",
+ "field_order": [
+ "weight",
+ "description"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "weight",
"fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Weight",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Weight"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "description",
"fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Description",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Description"
}
],
- "has_web_view": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-04-19 15:31:48.080164",
+ "links": [],
+ "modified": "2022-08-29 17:46:41.342979",
"modified_by": "Administrator",
"module": "Projects",
"name": "Task Type",
- "name_case": "",
+ "naming_rule": "Set by user",
"owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Projects Manager",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Projects User",
+ "share": 1
}
],
"quick_entry": 1,
- "read_only": 0,
- "show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "ASC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ "states": [],
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 863fbc4059..96092b1523 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -268,7 +268,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
def set_expired_status():
# filter out submitted non expired quotations whose validity has been ended
- cond = "`tabQuotation`.docstatus = 1 and `tabQuotation`.status != 'Expired' and `tabQuotation`.valid_till < %s"
+ cond = "`tabQuotation`.docstatus = 1 and `tabQuotation`.status NOT IN ('Expired', 'Lost') and `tabQuotation`.valid_till < %s"
# check if those QUO have SO against it
so_against_quo = """
SELECT
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 7721efb639..d70952282d 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -2568,27 +2568,26 @@ def get_supplied_items(
@frappe.whitelist()
def get_items_from_subcontracting_order(source_name, target_doc=None):
- sco = frappe.get_doc("Subcontracting Order", source_name)
+ def post_process(source, target):
+ target.stock_entry_type = target.purpose = "Send to Subcontractor"
+ target.subcontracting_order = source_name
- if sco.docstatus == 1:
- if target_doc and isinstance(target_doc, str):
- target_doc = frappe.get_doc(json.loads(target_doc))
-
- if target_doc.items:
- target_doc.items = []
+ if target.items:
+ target.items = []
warehouses = {}
- for item in sco.items:
+ for item in source.items:
warehouses[item.name] = item.warehouse
- for item in sco.supplied_items:
- target_doc.append(
+ for item in source.supplied_items:
+ target.append(
"items",
{
"s_warehouse": warehouses.get(item.reference_name),
- "t_warehouse": sco.supplier_warehouse,
+ "t_warehouse": source.supplier_warehouse,
+ "subcontracted_item": item.main_item_code,
"item_code": item.rm_item_code,
- "qty": item.required_qty,
+ "qty": max(item.required_qty - item.total_supplied_qty, 0),
"transfer_qty": item.required_qty,
"uom": item.stock_uom,
"stock_uom": item.stock_uom,
@@ -2596,6 +2595,23 @@ def get_items_from_subcontracting_order(source_name, target_doc=None):
},
)
+ target_doc = get_mapped_doc(
+ "Subcontracting Order",
+ source_name,
+ {
+ "Subcontracting Order": {
+ "doctype": "Stock Entry",
+ "field_no_map": ["purchase_order"],
+ "validation": {
+ "docstatus": ["=", 1],
+ },
+ },
+ },
+ target_doc,
+ post_process,
+ ignore_child_tables=True,
+ )
+
return target_doc
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
index bbc58fe29b..065ef39db3 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
@@ -164,10 +164,7 @@ erpnext.buying.SubcontractingOrderController = class SubcontractingOrderControll
if (flt(doc.per_received) < 100) {
cur_frm.add_custom_button(__('Subcontracting Receipt'), this.make_subcontracting_receipt, __('Create'));
if (me.has_unsupplied_items()) {
- cur_frm.add_custom_button(__('Material to Supplier'),
- () => {
- me.make_stock_entry();
- }, __('Transfer'));
+ cur_frm.add_custom_button(__('Material to Supplier'), this.make_stock_entry, __('Transfer'));
}
}
cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
@@ -195,120 +192,6 @@ erpnext.buying.SubcontractingOrderController = class SubcontractingOrderControll
transaction_controller.autofill_warehouse(child_table, warehouse_field, warehouse);
}
- make_stock_entry() {
- var items = $.map(cur_frm.doc.items, (d) => d.bom ? d.item_code : false);
- var me = this;
-
- if (items.length >= 1) {
- me.raw_material_data = [];
- me.show_dialog = 1;
- let title = __('Transfer Material to Supplier');
- let fields = [
- { fieldtype: 'Section Break', label: __('Raw Materials') },
- {
- fieldname: 'sub_con_rm_items', fieldtype: 'Table', label: __('Items'),
- fields: [
- {
- fieldtype: 'Data',
- fieldname: 'item_code',
- label: __('Item'),
- read_only: 1,
- in_list_view: 1
- },
- {
- fieldtype: 'Data',
- fieldname: 'rm_item_code',
- label: __('Raw Material'),
- read_only: 1,
- in_list_view: 1
- },
- {
- fieldtype: 'Float',
- read_only: 1,
- fieldname: 'qty',
- label: __('Quantity'),
- in_list_view: 1
- },
- {
- fieldtype: 'Data',
- read_only: 1,
- fieldname: 'warehouse',
- label: __('Reserve Warehouse'),
- in_list_view: 1
- },
- {
- fieldtype: 'Float',
- read_only: 1,
- fieldname: 'rate',
- label: __('Rate'),
- hidden: 1
- },
- {
- fieldtype: 'Float',
- read_only: 1,
- fieldname: 'amount',
- label: __('Amount'),
- hidden: 1
- },
- {
- fieldtype: 'Link',
- read_only: 1,
- fieldname: 'uom',
- label: __('UOM'),
- hidden: 1
- }
- ],
- data: me.raw_material_data,
- get_data: () => me.raw_material_data
- }
- ];
-
- me.dialog = new frappe.ui.Dialog({
- title: title, fields: fields
- });
-
- if (me.frm.doc['supplied_items']) {
- me.frm.doc['supplied_items'].forEach((item) => {
- if (item.rm_item_code && item.main_item_code && item.required_qty - item.supplied_qty != 0) {
- me.raw_material_data.push({
- 'name': item.name,
- 'item_code': item.main_item_code,
- 'rm_item_code': item.rm_item_code,
- 'item_name': item.rm_item_code,
- 'qty': item.required_qty - item.supplied_qty,
- 'warehouse': item.reserve_warehouse,
- 'rate': item.rate,
- 'amount': item.amount,
- 'stock_uom': item.stock_uom
- });
- me.dialog.fields_dict.sub_con_rm_items.grid.refresh();
- }
- });
- }
-
- me.dialog.get_field('sub_con_rm_items').check_all_rows();
-
- me.dialog.show();
- this.dialog.set_primary_action(__('Transfer'), () => {
- me.values = me.dialog.get_values();
- if (me.values) {
- me.values.sub_con_rm_items.map((row, i) => {
- if (!row.item_code || !row.rm_item_code || !row.warehouse || !row.qty || row.qty === 0) {
- let row_id = i + 1;
- frappe.throw(__('Item Code, warehouse and quantity are required on row {0}', [row_id]));
- }
- });
- me.make_rm_stock_entry(me.dialog.fields_dict.sub_con_rm_items.grid.get_selected_children());
- me.dialog.hide();
- }
- });
- }
-
- me.dialog.get_close_btn().on('click', () => {
- me.dialog.hide();
- });
- }
-
has_unsupplied_items() {
return this.frm.doc['supplied_items'].some(item => item.required_qty > item.supplied_qty);
}
@@ -321,6 +204,15 @@ erpnext.buying.SubcontractingOrderController = class SubcontractingOrderControll
});
}
+ make_stock_entry() {
+ frappe.model.open_mapped_doc({
+ method: 'erpnext.stock.doctype.stock_entry.stock_entry.get_items_from_subcontracting_order',
+ source_name: cur_frm.doc.name,
+ freeze: true,
+ freeze_message: __('Creating Stock Entry ...')
+ });
+ }
+
make_rm_stock_entry(rm_items) {
frappe.call({
method: 'erpnext.controllers.subcontracting_controller.make_rm_stock_entry',
diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py
index 0768cc3fa6..f40fd479f4 100644
--- a/erpnext/templates/pages/product_search.py
+++ b/erpnext/templates/pages/product_search.py
@@ -5,14 +5,13 @@ import json
import frappe
from frappe.utils import cint, cstr
-from redisearch import AutoCompleter, Client, Query
+from redis.commands.search.query import Query
from erpnext.e_commerce.redisearch_utils import (
WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE,
WEBSITE_ITEM_INDEX,
WEBSITE_ITEM_NAME_AUTOCOMPLETE,
is_redisearch_enabled,
- make_key,
)
from erpnext.e_commerce.shopping_cart.product_info import set_product_info_for_website
from erpnext.setup.doctype.item_group.item_group import get_item_for_list_in_html
@@ -88,15 +87,17 @@ def product_search(query, limit=10, fuzzy_search=True):
if not query:
return search_results
- red = frappe.cache()
+ redis = 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(
- query, num=limit, fuzzy=fuzzy_search and len(query) > 3 # Fuzzy on length < 3 can be real slow
+ redisearch = redis.ft(WEBSITE_ITEM_INDEX)
+ suggestions = redisearch.sugget(
+ WEBSITE_ITEM_NAME_AUTOCOMPLETE,
+ query,
+ num=limit,
+ fuzzy=fuzzy_search and len(query) > 3,
)
# Build a query
@@ -106,8 +107,8 @@ def product_search(query, limit=10, fuzzy_search=True):
query_string += f"|('{clean_up_query(s.string)}')"
q = Query(query_string)
+ results = redisearch.search(q)
- results = client.search(q)
search_results["results"] = list(map(convert_to_dict, results.docs))
search_results["results"] = sorted(
search_results["results"], key=lambda k: frappe.utils.cint(k["ranking"]), reverse=True
@@ -141,8 +142,8 @@ def get_category_suggestions(query):
if not query:
return search_results
- ac = AutoCompleter(make_key(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE), conn=frappe.cache())
- suggestions = ac.get_suggestions(query, num=10, with_payloads=True)
+ ac = frappe.cache().ft()
+ suggestions = ac.sugget(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE, query, num=10, with_payloads=True)
results = [json.loads(s.payload) for s in suggestions]
diff --git a/pyproject.toml b/pyproject.toml
index 5acfd39272..14684f3491 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -12,7 +12,6 @@ dependencies = [
"pycountry~=20.7.3",
"python-stdnum~=1.16",
"Unidecode~=1.2.0",
- "redisearch~=2.1.0",
# integration dependencies
"gocardless-pro~=1.22.0",