Merge branch 'develop' into fix-consolidation-precision-error
This commit is contained in:
commit
1a61d4e8a8
@ -14,8 +14,8 @@ pos* @nextchamp-saqib
|
||||
erpnext/buying/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/maintenance/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/manufacturing/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/quality_management/ @marination @rohitwaghchaure @s-aga-r
|
||||
erpnext/stock/ @marination @rohitwaghchaure @s-aga-r
|
||||
erpnext/quality_management/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/stock/ @rohitwaghchaure @s-aga-r
|
||||
|
||||
erpnext/crm/ @NagariaHussain
|
||||
erpnext/education/ @rutwikhdev
|
||||
|
@ -22,7 +22,8 @@
|
||||
"amount",
|
||||
"account_currency",
|
||||
"amount_in_account_currency",
|
||||
"delinked"
|
||||
"delinked",
|
||||
"remarks"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@ -136,12 +137,17 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Finance Book",
|
||||
"options": "Finance Book"
|
||||
},
|
||||
{
|
||||
"fieldname": "remarks",
|
||||
"fieldtype": "Text",
|
||||
"label": "Remarks"
|
||||
}
|
||||
],
|
||||
"in_create": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2022-07-11 09:13:54.379168",
|
||||
"modified": "2022-08-22 15:32:56.629430",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Ledger Entry",
|
||||
|
@ -34,4 +34,4 @@ class ProcessDeferredAccounting(Document):
|
||||
filters={"against_voucher_type": self.doctype, "against_voucher": self.name},
|
||||
)
|
||||
|
||||
make_gl_entries(gl_entries=gl_entries, cancel=1)
|
||||
make_gl_entries(gl_map=gl_entries, cancel=1)
|
||||
|
@ -57,3 +57,16 @@ class TestProcessDeferredAccounting(unittest.TestCase):
|
||||
]
|
||||
|
||||
check_gl_entries(self, si.name, expected_gle, "2019-01-10")
|
||||
|
||||
def test_pda_submission_and_cancellation(self):
|
||||
pda = frappe.get_doc(
|
||||
dict(
|
||||
doctype="Process Deferred Accounting",
|
||||
posting_date="2019-01-01",
|
||||
start_date="2019-01-01",
|
||||
end_date="2019-01-31",
|
||||
type="Income",
|
||||
)
|
||||
)
|
||||
pda.submit()
|
||||
pda.cancel()
|
||||
|
@ -282,7 +282,6 @@
|
||||
"label": "Discount (%) on Price List Rate with Margin",
|
||||
"oldfieldname": "adj_rate",
|
||||
"oldfieldtype": "Float",
|
||||
"precision": "2",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
@ -846,7 +845,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-06-17 05:33:15.335912",
|
||||
"modified": "2022-08-26 12:06:31.205417",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
@ -178,6 +178,11 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "show_remarks",
|
||||
"label": __("Show Remarks"),
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
{
|
||||
"fieldname": "customer_name",
|
||||
"label": __("Customer Name"),
|
||||
|
@ -119,6 +119,7 @@ class ReceivablePayableReport(object):
|
||||
party_account=ple.account,
|
||||
posting_date=ple.posting_date,
|
||||
account_currency=ple.account_currency,
|
||||
remarks=ple.remarks,
|
||||
invoiced=0.0,
|
||||
paid=0.0,
|
||||
credit_note=0.0,
|
||||
@ -697,6 +698,7 @@ class ReceivablePayableReport(object):
|
||||
ple.account_currency,
|
||||
ple.amount,
|
||||
ple.amount_in_account_currency,
|
||||
ple.remarks,
|
||||
)
|
||||
.where(ple.delinked == 0)
|
||||
.where(Criterion.all(self.qb_selection_filter))
|
||||
@ -731,6 +733,7 @@ class ReceivablePayableReport(object):
|
||||
def prepare_conditions(self):
|
||||
self.qb_selection_filter = []
|
||||
party_type_field = scrub(self.party_type)
|
||||
self.qb_selection_filter.append(self.ple.party_type == self.party_type)
|
||||
|
||||
self.add_common_filters(party_type_field=party_type_field)
|
||||
|
||||
@ -974,6 +977,9 @@ class ReceivablePayableReport(object):
|
||||
options="Supplier Group",
|
||||
)
|
||||
|
||||
if self.filters.show_remarks:
|
||||
self.add_column(label=_("Remarks"), fieldname="remarks", fieldtype="Text", width=200),
|
||||
|
||||
def add_column(self, label, fieldname=None, fieldtype="Currency", options=None, width=120):
|
||||
if not fieldname:
|
||||
fieldname = scrub(label)
|
||||
|
@ -535,7 +535,11 @@ def get_accounts(root_type, companies):
|
||||
):
|
||||
if account.account_name not in added_accounts:
|
||||
accounts.append(account)
|
||||
added_accounts.append(account.account_name)
|
||||
if account.account_number:
|
||||
account_key = account.account_number + "-" + account.account_name
|
||||
else:
|
||||
account_key = account.account_name
|
||||
added_accounts.append(account_key)
|
||||
|
||||
return accounts
|
||||
|
||||
|
@ -1424,6 +1424,7 @@ def create_payment_ledger_entry(
|
||||
"amount": dr_or_cr,
|
||||
"amount_in_account_currency": dr_or_cr_account_currency,
|
||||
"delinked": True if cancel else False,
|
||||
"remarks": gle.remarks,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -1454,12 +1454,14 @@ def create_fixed_asset_item(item_code=None, auto_create_assets=1, is_grouped_ass
|
||||
return item
|
||||
|
||||
|
||||
def set_depreciation_settings_in_company():
|
||||
company = frappe.get_doc("Company", "_Test Company")
|
||||
company.accumulated_depreciation_account = "_Test Accumulated Depreciations - _TC"
|
||||
company.depreciation_expense_account = "_Test Depreciations - _TC"
|
||||
company.disposal_account = "_Test Gain/Loss on Asset Disposal - _TC"
|
||||
company.depreciation_cost_center = "_Test Cost Center - _TC"
|
||||
def set_depreciation_settings_in_company(company=None):
|
||||
if not company:
|
||||
company = "_Test Company"
|
||||
company = frappe.get_doc("Company", company)
|
||||
company.accumulated_depreciation_account = "_Test Accumulated Depreciations - " + company.abbr
|
||||
company.depreciation_expense_account = "_Test Depreciations - " + company.abbr
|
||||
company.disposal_account = "_Test Gain/Loss on Asset Disposal - " + company.abbr
|
||||
company.depreciation_cost_center = "Main - " + company.abbr
|
||||
company.save()
|
||||
|
||||
# Enable booking asset depreciation entry automatically
|
||||
|
@ -76,7 +76,7 @@ frappe.ui.form.on('Asset Repair Consumed Item', {
|
||||
'warehouse': frm.doc.warehouse,
|
||||
'qty': item.consumed_quantity,
|
||||
'serial_no': item.serial_no,
|
||||
'company': frm.doc.company
|
||||
'company': frm.doc.company,
|
||||
};
|
||||
|
||||
frappe.call({
|
||||
|
@ -238,7 +238,6 @@
|
||||
"no_copy": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "purchase_invoice",
|
||||
"fieldtype": "Link",
|
||||
"label": "Purchase Invoice",
|
||||
@ -257,6 +256,7 @@
|
||||
"fieldname": "stock_entry",
|
||||
"fieldtype": "Link",
|
||||
"label": "Stock Entry",
|
||||
"no_copy": 1,
|
||||
"options": "Stock Entry",
|
||||
"read_only": 1
|
||||
}
|
||||
@ -264,10 +264,11 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-06-25 13:14:38.307723",
|
||||
"modified": "2022-08-16 15:55:25.023471",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Repair",
|
||||
"naming_rule": "By \"Naming Series\" field",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@ -303,6 +304,7 @@
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "asset_name",
|
||||
"track_changes": 1,
|
||||
"track_seen": 1
|
||||
|
@ -1,11 +1,11 @@
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import add_months, cint, flt, getdate, time_diff_in_hours
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
from erpnext.assets.doctype.asset.asset import get_asset_account
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
@ -17,7 +17,7 @@ class AssetRepair(AccountsController):
|
||||
self.update_status()
|
||||
|
||||
if self.get("stock_items"):
|
||||
self.set_total_value()
|
||||
self.set_stock_items_cost()
|
||||
self.calculate_total_repair_cost()
|
||||
|
||||
def update_status(self):
|
||||
@ -26,7 +26,7 @@ class AssetRepair(AccountsController):
|
||||
else:
|
||||
self.asset_doc.set_status()
|
||||
|
||||
def set_total_value(self):
|
||||
def set_stock_items_cost(self):
|
||||
for item in self.get("stock_items"):
|
||||
item.total_value = flt(item.valuation_rate) * flt(item.consumed_quantity)
|
||||
|
||||
@ -66,6 +66,7 @@ class AssetRepair(AccountsController):
|
||||
if self.get("capitalize_repair_cost"):
|
||||
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
|
||||
self.make_gl_entries(cancel=True)
|
||||
self.db_set("stock_entry", None)
|
||||
if (
|
||||
frappe.db.get_value("Asset", self.asset, "calculate_depreciation")
|
||||
and self.increase_in_asset_life
|
||||
@ -133,6 +134,7 @@ class AssetRepair(AccountsController):
|
||||
"qty": stock_item.consumed_quantity,
|
||||
"basic_rate": stock_item.valuation_rate,
|
||||
"serial_no": stock_item.serial_no,
|
||||
"cost_center": self.cost_center,
|
||||
},
|
||||
)
|
||||
|
||||
@ -142,34 +144,61 @@ class AssetRepair(AccountsController):
|
||||
self.db_set("stock_entry", stock_entry.name)
|
||||
|
||||
def increase_stock_quantity(self):
|
||||
if self.stock_entry:
|
||||
stock_entry = frappe.get_doc("Stock Entry", self.stock_entry)
|
||||
stock_entry.flags.ignore_links = True
|
||||
stock_entry.cancel()
|
||||
|
||||
def make_gl_entries(self, cancel=False):
|
||||
if flt(self.repair_cost) > 0:
|
||||
if flt(self.total_repair_cost) > 0:
|
||||
gl_entries = self.get_gl_entries()
|
||||
make_gl_entries(gl_entries, cancel)
|
||||
|
||||
def get_gl_entries(self):
|
||||
gl_entries = []
|
||||
repair_and_maintenance_account = frappe.db.get_value(
|
||||
"Company", self.company, "repair_and_maintenance_account"
|
||||
)
|
||||
|
||||
fixed_asset_account = get_asset_account(
|
||||
"fixed_asset_account", asset=self.asset, company=self.company
|
||||
)
|
||||
expense_account = (
|
||||
self.get_gl_entries_for_repair_cost(gl_entries, fixed_asset_account)
|
||||
self.get_gl_entries_for_consumed_items(gl_entries, fixed_asset_account)
|
||||
|
||||
return gl_entries
|
||||
|
||||
def get_gl_entries_for_repair_cost(self, gl_entries, fixed_asset_account):
|
||||
if flt(self.repair_cost) <= 0:
|
||||
return
|
||||
|
||||
pi_expense_account = (
|
||||
frappe.get_doc("Purchase Invoice", self.purchase_invoice).items[0].expense_account
|
||||
)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": expense_account,
|
||||
"account": fixed_asset_account,
|
||||
"debit": self.repair_cost,
|
||||
"debit_in_account_currency": self.repair_cost,
|
||||
"against": pi_expense_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
"posting_date": getdate(),
|
||||
"against_voucher_type": "Purchase Invoice",
|
||||
"against_voucher": self.purchase_invoice,
|
||||
"company": self.company,
|
||||
},
|
||||
item=self,
|
||||
)
|
||||
)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": pi_expense_account,
|
||||
"credit": self.repair_cost,
|
||||
"credit_in_account_currency": self.repair_cost,
|
||||
"against": repair_and_maintenance_account,
|
||||
"against": fixed_asset_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
@ -180,17 +209,30 @@ class AssetRepair(AccountsController):
|
||||
)
|
||||
)
|
||||
|
||||
if self.get("stock_consumption"):
|
||||
def get_gl_entries_for_consumed_items(self, gl_entries, fixed_asset_account):
|
||||
if not (self.get("stock_consumption") and self.get("stock_items")):
|
||||
return
|
||||
|
||||
# creating GL Entries for each row in Stock Items based on the Stock Entry created for it
|
||||
stock_entry = frappe.get_doc("Stock Entry", self.stock_entry)
|
||||
|
||||
default_expense_account = None
|
||||
if not erpnext.is_perpetual_inventory_enabled(self.company):
|
||||
default_expense_account = frappe.get_cached_value(
|
||||
"Company", self.company, "default_expense_account"
|
||||
)
|
||||
if not default_expense_account:
|
||||
frappe.throw(_("Please set default Expense Account in Company {0}").format(self.company))
|
||||
|
||||
for item in stock_entry.items:
|
||||
if flt(item.amount) > 0:
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": item.expense_account,
|
||||
"account": item.expense_account or default_expense_account,
|
||||
"credit": item.amount,
|
||||
"credit_in_account_currency": item.amount,
|
||||
"against": repair_and_maintenance_account,
|
||||
"against": fixed_asset_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
@ -205,23 +247,21 @@ class AssetRepair(AccountsController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": fixed_asset_account,
|
||||
"debit": self.total_repair_cost,
|
||||
"debit_in_account_currency": self.total_repair_cost,
|
||||
"against": expense_account,
|
||||
"debit": item.amount,
|
||||
"debit_in_account_currency": item.amount,
|
||||
"against": item.expense_account or default_expense_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
"posting_date": getdate(),
|
||||
"against_voucher_type": "Purchase Invoice",
|
||||
"against_voucher": self.purchase_invoice,
|
||||
"against_voucher_type": "Stock Entry",
|
||||
"against_voucher": self.stock_entry,
|
||||
"company": self.company,
|
||||
},
|
||||
item=self,
|
||||
)
|
||||
)
|
||||
|
||||
return gl_entries
|
||||
|
||||
def modify_depreciation_schedule(self):
|
||||
for row in self.asset_doc.finance_books:
|
||||
row.total_number_of_depreciations += self.increase_in_asset_life / row.frequency_of_depreciation
|
||||
|
@ -6,6 +6,7 @@ import unittest
|
||||
import frappe
|
||||
from frappe.utils import flt, nowdate
|
||||
|
||||
from erpnext.assets.doctype.asset.asset import get_asset_account
|
||||
from erpnext.assets.doctype.asset.test_asset import (
|
||||
create_asset,
|
||||
create_asset_data,
|
||||
@ -125,10 +126,109 @@ class TestAssetRepair(unittest.TestCase):
|
||||
asset_repair = create_asset_repair(capitalize_repair_cost=1, submit=1)
|
||||
self.assertTrue(asset_repair.purchase_invoice)
|
||||
|
||||
def test_gl_entries(self):
|
||||
asset_repair = create_asset_repair(capitalize_repair_cost=1, submit=1)
|
||||
gl_entry = frappe.get_last_doc("GL Entry")
|
||||
self.assertEqual(asset_repair.name, gl_entry.voucher_no)
|
||||
def test_gl_entries_with_perpetual_inventory(self):
|
||||
set_depreciation_settings_in_company(company="_Test Company with perpetual inventory")
|
||||
|
||||
asset_category = frappe.get_doc("Asset Category", "Computers")
|
||||
asset_category.append(
|
||||
"accounts",
|
||||
{
|
||||
"company_name": "_Test Company with perpetual inventory",
|
||||
"fixed_asset_account": "_Test Fixed Asset - TCP1",
|
||||
"accumulated_depreciation_account": "_Test Accumulated Depreciations - TCP1",
|
||||
"depreciation_expense_account": "_Test Depreciations - TCP1",
|
||||
},
|
||||
)
|
||||
asset_category.save()
|
||||
|
||||
asset_repair = create_asset_repair(
|
||||
capitalize_repair_cost=1,
|
||||
stock_consumption=1,
|
||||
warehouse="Stores - TCP1",
|
||||
company="_Test Company with perpetual inventory",
|
||||
submit=1,
|
||||
)
|
||||
|
||||
gl_entries = frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
account,
|
||||
sum(debit) as debit,
|
||||
sum(credit) as credit
|
||||
from `tabGL Entry`
|
||||
where
|
||||
voucher_type='Asset Repair'
|
||||
and voucher_no=%s
|
||||
group by
|
||||
account
|
||||
""",
|
||||
asset_repair.name,
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
fixed_asset_account = get_asset_account(
|
||||
"fixed_asset_account", asset=asset_repair.asset, company=asset_repair.company
|
||||
)
|
||||
pi_expense_account = (
|
||||
frappe.get_doc("Purchase Invoice", asset_repair.purchase_invoice).items[0].expense_account
|
||||
)
|
||||
stock_entry_expense_account = (
|
||||
frappe.get_doc("Stock Entry", asset_repair.stock_entry).get("items")[0].expense_account
|
||||
)
|
||||
|
||||
expected_values = {
|
||||
fixed_asset_account: [asset_repair.total_repair_cost, 0],
|
||||
pi_expense_account: [0, asset_repair.repair_cost],
|
||||
stock_entry_expense_account: [0, 100],
|
||||
}
|
||||
|
||||
for d in gl_entries:
|
||||
self.assertEqual(expected_values[d.account][0], d.debit)
|
||||
self.assertEqual(expected_values[d.account][1], d.credit)
|
||||
|
||||
def test_gl_entries_with_periodical_inventory(self):
|
||||
frappe.db.set_value(
|
||||
"Company", "_Test Company", "default_expense_account", "Cost of Goods Sold - _TC"
|
||||
)
|
||||
asset_repair = create_asset_repair(
|
||||
capitalize_repair_cost=1,
|
||||
stock_consumption=1,
|
||||
submit=1,
|
||||
)
|
||||
|
||||
gl_entries = frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
account,
|
||||
sum(debit) as debit,
|
||||
sum(credit) as credit
|
||||
from `tabGL Entry`
|
||||
where
|
||||
voucher_type='Asset Repair'
|
||||
and voucher_no=%s
|
||||
group by
|
||||
account
|
||||
""",
|
||||
asset_repair.name,
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
fixed_asset_account = get_asset_account(
|
||||
"fixed_asset_account", asset=asset_repair.asset, company=asset_repair.company
|
||||
)
|
||||
default_expense_account = frappe.get_cached_value(
|
||||
"Company", asset_repair.company, "default_expense_account"
|
||||
)
|
||||
|
||||
expected_values = {fixed_asset_account: [1100, 0], default_expense_account: [0, 1100]}
|
||||
|
||||
for d in gl_entries:
|
||||
self.assertEqual(expected_values[d.account][0], d.debit)
|
||||
self.assertEqual(expected_values[d.account][1], d.credit)
|
||||
|
||||
def test_increase_in_asset_life(self):
|
||||
asset = create_asset(calculate_depreciation=1, submit=1)
|
||||
@ -160,7 +260,7 @@ def create_asset_repair(**args):
|
||||
if args.asset:
|
||||
asset = args.asset
|
||||
else:
|
||||
asset = create_asset(is_existing_asset=1, submit=1)
|
||||
asset = create_asset(is_existing_asset=1, submit=1, company=args.company)
|
||||
asset_repair = frappe.new_doc("Asset Repair")
|
||||
asset_repair.update(
|
||||
{
|
||||
@ -192,7 +292,7 @@ def create_asset_repair(**args):
|
||||
|
||||
if args.submit:
|
||||
asset_repair.repair_status = "Completed"
|
||||
asset_repair.cost_center = "_Test Cost Center - _TC"
|
||||
asset_repair.cost_center = frappe.db.get_value("Company", asset.company, "cost_center")
|
||||
|
||||
if args.stock_consumption:
|
||||
stock_entry = frappe.get_doc(
|
||||
@ -204,6 +304,8 @@ def create_asset_repair(**args):
|
||||
"t_warehouse": asset_repair.warehouse,
|
||||
"item_code": asset_repair.stock_items[0].item_code,
|
||||
"qty": asset_repair.stock_items[0].consumed_quantity,
|
||||
"basic_rate": args.rate if args.get("rate") is not None else 100,
|
||||
"cost_center": asset_repair.cost_center,
|
||||
},
|
||||
)
|
||||
stock_entry.submit()
|
||||
@ -213,7 +315,13 @@ def create_asset_repair(**args):
|
||||
asset_repair.repair_cost = 1000
|
||||
if asset.calculate_depreciation:
|
||||
asset_repair.increase_in_asset_life = 12
|
||||
asset_repair.purchase_invoice = make_purchase_invoice().name
|
||||
pi = make_purchase_invoice(
|
||||
company=asset.company,
|
||||
expense_account=frappe.db.get_value("Company", asset.company, "default_expense_account"),
|
||||
cost_center=asset_repair.cost_center,
|
||||
warehouse=asset_repair.warehouse,
|
||||
)
|
||||
asset_repair.purchase_invoice = pi.name
|
||||
|
||||
asset_repair.submit()
|
||||
return asset_repair
|
||||
|
@ -76,7 +76,7 @@
|
||||
"label": "Subcontracting Settings"
|
||||
},
|
||||
{
|
||||
"default": "Material Transferred for Subcontract",
|
||||
"default": "BOM",
|
||||
"fieldname": "backflush_raw_materials_of_subcontract_based_on",
|
||||
"fieldtype": "Select",
|
||||
"label": "Backflush Raw Materials of Subcontract Based On",
|
||||
@ -148,7 +148,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2022-05-31 19:40:26.103909",
|
||||
"modified": "2022-09-01 18:01:34.994657",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Buying Settings",
|
||||
|
@ -41,6 +41,7 @@ class calculate_taxes_and_totals(object):
|
||||
if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
|
||||
self.doc.grand_total -= self.doc.discount_amount
|
||||
self.doc.base_grand_total -= self.doc.base_discount_amount
|
||||
self.set_rounded_total()
|
||||
|
||||
self.calculate_shipping_charges()
|
||||
|
||||
|
@ -200,7 +200,7 @@ erpnext.ProductSearch = class {
|
||||
let thumbnail = res.thumbnail || '/assets/erpnext/images/ui-states/cart-empty-state.png';
|
||||
html += `
|
||||
<div class="dropdown-item" style="display: flex;">
|
||||
<img class="item-thumb col-2" src=${thumbnail} />
|
||||
<img class="item-thumb col-2" src=${encodeURI(thumbnail)} />
|
||||
<div class="col-9" style="white-space: normal;">
|
||||
<a href="/${res.route}">${res.web_item_name}</a><br>
|
||||
<span class="brand-line">${res.brand ? "by " + res.brand : ""}</span>
|
||||
|
@ -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
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -656,6 +656,8 @@ class ProductionPlan(Document):
|
||||
row.idx = idx + 1
|
||||
self.append("sub_assembly_items", row)
|
||||
|
||||
self.set_default_supplier_for_subcontracting_order()
|
||||
|
||||
def set_sub_assembly_items_based_on_level(self, row, bom_data, manufacturing_type=None):
|
||||
"Modify bom_data, set additional details."
|
||||
for data in bom_data:
|
||||
@ -667,6 +669,32 @@ class ProductionPlan(Document):
|
||||
"Subcontract" if data.is_sub_contracted_item else "In House"
|
||||
)
|
||||
|
||||
def set_default_supplier_for_subcontracting_order(self):
|
||||
items = [
|
||||
d.production_item for d in self.sub_assembly_items if d.type_of_manufacturing == "Subcontract"
|
||||
]
|
||||
|
||||
if not items:
|
||||
return
|
||||
|
||||
default_supplier = frappe._dict(
|
||||
frappe.get_all(
|
||||
"Item Default",
|
||||
fields=["parent", "default_supplier"],
|
||||
filters={"parent": ("in", items), "default_supplier": ("is", "set")},
|
||||
as_list=1,
|
||||
)
|
||||
)
|
||||
|
||||
if not default_supplier:
|
||||
return
|
||||
|
||||
for row in self.sub_assembly_items:
|
||||
if row.type_of_manufacturing != "Subcontract":
|
||||
continue
|
||||
|
||||
row.supplier = default_supplier.get(row.production_item)
|
||||
|
||||
def combine_subassembly_items(self, sub_assembly_items_store):
|
||||
"Aggregate if same: Item, Warehouse, Inhouse/Outhouse Manu.g, BOM No."
|
||||
key_wise_data = {}
|
||||
|
@ -281,6 +281,31 @@ class TestProductionPlan(FrappeTestCase):
|
||||
pln.reload()
|
||||
pln.cancel()
|
||||
|
||||
def test_production_plan_subassembly_default_supplier(self):
|
||||
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
|
||||
|
||||
bom_tree_1 = {"Test Laptop": {"Test Motherboard": {"Test Motherboard Wires": {}}}}
|
||||
bom = create_nested_bom(bom_tree_1, prefix="")
|
||||
|
||||
item_doc = frappe.get_doc("Item", "Test Motherboard")
|
||||
company = "_Test Company"
|
||||
|
||||
item_doc.is_sub_contracted_item = 1
|
||||
for row in item_doc.item_defaults:
|
||||
if row.company == company and not row.default_supplier:
|
||||
row.default_supplier = "_Test Supplier"
|
||||
|
||||
if not item_doc.item_defaults:
|
||||
item_doc.append("item_defaults", {"company": company, "default_supplier": "_Test Supplier"})
|
||||
|
||||
item_doc.save()
|
||||
|
||||
plan = create_production_plan(item_code="Test Laptop", use_multi_level_bom=1, do_not_submit=True)
|
||||
plan.get_sub_assembly_items()
|
||||
plan.set_default_supplier_for_subcontracting_order()
|
||||
|
||||
self.assertEqual(plan.sub_assembly_items[0].supplier, "_Test Supplier")
|
||||
|
||||
def test_production_plan_combine_subassembly(self):
|
||||
"""
|
||||
Test combining Sub assembly items belonging to the same BOM in Prod Plan.
|
||||
|
@ -7,6 +7,6 @@ def get_data():
|
||||
"non_standard_fieldnames": {"Batch": "reference_name"},
|
||||
"transactions": [
|
||||
{"label": _("Transactions"), "items": ["Stock Entry", "Job Card", "Pick List"]},
|
||||
{"label": _("Reference"), "items": ["Serial No", "Batch"]},
|
||||
{"label": _("Reference"), "items": ["Serial No", "Batch", "Material Request"]},
|
||||
],
|
||||
}
|
||||
|
@ -312,3 +312,4 @@ 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
|
||||
erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger
|
||||
|
@ -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"})
|
||||
|
@ -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)
|
||||
|
@ -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()
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -892,6 +892,7 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t
|
||||
target.additional_discount_percentage = 0.0
|
||||
target.discount_amount = 0.0
|
||||
target.inter_company_order_reference = ""
|
||||
target.shipping_rule = ""
|
||||
|
||||
default_price_list = frappe.get_value("Supplier", supplier, "default_price_list")
|
||||
if default_price_list:
|
||||
@ -1010,6 +1011,7 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None):
|
||||
target.additional_discount_percentage = 0.0
|
||||
target.discount_amount = 0.0
|
||||
target.inter_company_order_reference = ""
|
||||
target.shipping_rule = ""
|
||||
target.customer = ""
|
||||
target.customer_name = ""
|
||||
target.run_method("set_missing_values")
|
||||
|
@ -85,7 +85,6 @@
|
||||
"depreciation_expense_account",
|
||||
"series_for_depreciation_entry",
|
||||
"expenses_included_in_asset_valuation",
|
||||
"repair_and_maintenance_account",
|
||||
"column_break_40",
|
||||
"disposal_account",
|
||||
"depreciation_cost_center",
|
||||
@ -234,7 +233,6 @@
|
||||
"label": "Default Warehouse for Sales Return",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
|
||||
{
|
||||
"fieldname": "country",
|
||||
"fieldtype": "Link",
|
||||
@ -678,12 +676,6 @@
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Fixed Asset Defaults"
|
||||
},
|
||||
{
|
||||
"fieldname": "repair_and_maintenance_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Repair and Maintenance Account",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_28",
|
||||
"fieldtype": "Section Break",
|
||||
@ -709,7 +701,7 @@
|
||||
"image_field": "company_logo",
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"modified": "2022-06-30 18:03:18.701314",
|
||||
"modified": "2022-08-16 16:09:02.327724",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Company",
|
||||
|
@ -562,7 +562,7 @@ $.extend(erpnext.item, {
|
||||
let selected_attributes = {};
|
||||
me.multiple_variant_dialog.$wrapper.find('.form-column').each((i, col) => {
|
||||
if(i===0) return;
|
||||
let attribute_name = $(col).find('label').html().trim();
|
||||
let attribute_name = $(col).find('.control-label').html().trim();
|
||||
selected_attributes[attribute_name] = [];
|
||||
let checked_opts = $(col).find('.checkbox input');
|
||||
checked_opts.each((i, opt) => {
|
||||
|
@ -37,7 +37,8 @@
|
||||
"tc_name",
|
||||
"terms",
|
||||
"reference",
|
||||
"job_card"
|
||||
"job_card",
|
||||
"work_order"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@ -309,16 +310,24 @@
|
||||
"label": "Transfer Status",
|
||||
"options": "\nNot Started\nIn Transit\nCompleted",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "work_order",
|
||||
"fieldtype": "Link",
|
||||
"label": "Work Order",
|
||||
"options": "Work Order",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-ticket",
|
||||
"idx": 70,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-08-17 20:16:12.737743",
|
||||
"modified": "2022-08-25 11:49:28.155048",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Material Request",
|
||||
"naming_rule": "By \"Naming Series\" field",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@ -386,5 +395,6 @@
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "title"
|
||||
}
|
@ -174,6 +174,8 @@ frappe.ui.form.on('Stock Entry', {
|
||||
if(!items.length) {
|
||||
items = frm.doc.items;
|
||||
}
|
||||
|
||||
mr.work_order = frm.doc.work_order;
|
||||
items.forEach(function(item) {
|
||||
var mr_item = frappe.model.add_child(mr, 'items');
|
||||
mr_item.item_code = item.item_code;
|
||||
|
@ -116,6 +116,7 @@ class StockEntry(StockController):
|
||||
self.validate_warehouse()
|
||||
self.validate_work_order()
|
||||
self.validate_bom()
|
||||
self.validate_purchase_order()
|
||||
|
||||
if self.purpose in ("Manufacture", "Repack"):
|
||||
self.mark_finished_and_scrap_items()
|
||||
@ -946,6 +947,19 @@ class StockEntry(StockController):
|
||||
item_code = d.original_item or d.item_code
|
||||
validate_bom_no(item_code, d.bom_no)
|
||||
|
||||
def validate_purchase_order(self):
|
||||
if self.purpose == "Send to Subcontractor" and self.get("purchase_order"):
|
||||
is_old_subcontracting_flow = frappe.db.get_value(
|
||||
"Purchase Order", self.purchase_order, "is_old_subcontracting_flow"
|
||||
)
|
||||
|
||||
if not is_old_subcontracting_flow:
|
||||
frappe.throw(
|
||||
_("Please select Subcontracting Order instead of Purchase Order {0}").format(
|
||||
self.purchase_order
|
||||
)
|
||||
)
|
||||
|
||||
def mark_finished_and_scrap_items(self):
|
||||
if any([d.item_code for d in self.items if (d.is_finished_item and d.t_warehouse)]):
|
||||
return
|
||||
@ -2215,7 +2229,7 @@ class StockEntry(StockController):
|
||||
return sorted(list(set(get_serial_nos(self.pro_doc.serial_no)) - set(used_serial_nos)))
|
||||
|
||||
def update_subcontracting_order_status(self):
|
||||
if self.subcontracting_order and self.purpose == "Send to Subcontractor":
|
||||
if self.subcontracting_order and self.purpose in ["Send to Subcontractor", "Material Transfer"]:
|
||||
from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
|
||||
update_subcontracting_order_status,
|
||||
)
|
||||
@ -2554,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,
|
||||
@ -2582,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
|
||||
|
||||
|
||||
|
@ -107,9 +107,9 @@ frappe.ui.form.on('Subcontracting Order', {
|
||||
get_materials_from_supplier: function (frm) {
|
||||
let sco_rm_details = [];
|
||||
|
||||
if (frm.doc.supplied_items && (frm.doc.per_received == 100)) {
|
||||
if (frm.doc.supplied_items && frm.doc.per_received > 0) {
|
||||
frm.doc.supplied_items.forEach(d => {
|
||||
if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) {
|
||||
if (d.total_supplied_qty > 0 && d.total_supplied_qty != d.consumed_qty) {
|
||||
sco_rm_details.push(d.name);
|
||||
}
|
||||
});
|
||||
@ -160,14 +160,11 @@ erpnext.buying.SubcontractingOrderController = class SubcontractingOrderControll
|
||||
var me = this;
|
||||
|
||||
if (doc.docstatus == 1) {
|
||||
if (doc.status != 'Completed') {
|
||||
if (!['Closed', 'Completed'].includes(doc.status)) {
|
||||
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',
|
||||
|
@ -153,7 +153,7 @@ class SubcontractingOrder(SubcontractingController):
|
||||
else:
|
||||
self.set_missing_values()
|
||||
|
||||
def update_status(self, status=None, update_modified=False):
|
||||
def update_status(self, status=None, update_modified=True):
|
||||
if self.docstatus >= 1 and not status:
|
||||
if self.docstatus == 1:
|
||||
if self.status == "Draft":
|
||||
@ -162,6 +162,10 @@ class SubcontractingOrder(SubcontractingController):
|
||||
status = "Completed"
|
||||
elif self.per_received > 0 and self.per_received < 100:
|
||||
status = "Partially Received"
|
||||
for item in self.supplied_items:
|
||||
if item.returned_qty:
|
||||
status = "Closed"
|
||||
break
|
||||
else:
|
||||
total_required_qty = total_supplied_qty = 0
|
||||
for item in self.supplied_items:
|
||||
@ -176,7 +180,10 @@ class SubcontractingOrder(SubcontractingController):
|
||||
elif self.docstatus == 2:
|
||||
status = "Cancelled"
|
||||
|
||||
frappe.db.set_value("Subcontracting Order", self.name, "status", status, update_modified)
|
||||
if status:
|
||||
frappe.db.set_value(
|
||||
"Subcontracting Order", self.name, "status", status, update_modified=update_modified
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
@ -10,6 +10,7 @@ frappe.listview_settings['Subcontracting Order'] = {
|
||||
"Completed": "green",
|
||||
"Partial Material Transferred": "purple",
|
||||
"Material Transferred": "blue",
|
||||
"Closed": "red",
|
||||
};
|
||||
return [__(doc.status), status_colors[doc.status], "status,=," + doc.status];
|
||||
},
|
||||
|
@ -7,7 +7,10 @@ import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_subcontracting_order
|
||||
from erpnext.controllers.subcontracting_controller import make_rm_stock_entry
|
||||
from erpnext.controllers.subcontracting_controller import (
|
||||
get_materials_from_supplier,
|
||||
make_rm_stock_entry,
|
||||
)
|
||||
from erpnext.controllers.tests.test_subcontracting_controller import (
|
||||
get_rm_items,
|
||||
get_subcontracting_order,
|
||||
@ -89,6 +92,16 @@ class TestSubcontractingOrder(FrappeTestCase):
|
||||
sco.load_from_db()
|
||||
self.assertEqual(sco.status, "Partially Received")
|
||||
|
||||
# Closed
|
||||
ste = get_materials_from_supplier(sco.name, [d.name for d in sco.supplied_items])
|
||||
ste.save()
|
||||
ste.submit()
|
||||
sco.load_from_db()
|
||||
self.assertEqual(sco.status, "Closed")
|
||||
ste.cancel()
|
||||
sco.load_from_db()
|
||||
self.assertEqual(sco.status, "Partially Received")
|
||||
|
||||
# Completed
|
||||
scr = make_subcontracting_receipt(sco.name)
|
||||
scr.save()
|
||||
|
@ -150,8 +150,7 @@
|
||||
"label": "Returned Qty",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"hidden": 1
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "total_supplied_qty",
|
||||
@ -166,7 +165,7 @@
|
||||
"hide_toolbar": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-04-07 12:58:28.208847",
|
||||
"modified": "2022-08-26 16:04:56.125951",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Subcontracting",
|
||||
"name": "Subcontracting Order Supplied Item",
|
||||
|
@ -369,7 +369,7 @@
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"no_copy": 1,
|
||||
"options": "\nDraft\nCompleted\nReturn\nReturn Issued\nCancelled",
|
||||
"options": "\nDraft\nCompleted\nReturn\nReturn Issued\nCancelled\nClosed",
|
||||
"print_hide": 1,
|
||||
"print_width": "150px",
|
||||
"read_only": 1,
|
||||
@ -628,7 +628,7 @@
|
||||
"in_create": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-08-22 17:30:40.827517",
|
||||
"modified": "2022-08-26 21:02:26.353870",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Subcontracting",
|
||||
"name": "Subcontracting Receipt",
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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",
|
||||
@ -21,6 +20,9 @@ dependencies = [
|
||||
"python-youtube~=0.8.0",
|
||||
"taxjar~=1.9.2",
|
||||
"tweepy~=3.10.0",
|
||||
|
||||
# Not used directly - required by PyQRCode for PNG generation
|
||||
"pypng~=0.20220715.0",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
|
Loading…
Reference in New Issue
Block a user