You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

817 lines
24 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # License: GNU General Public License v3. See license.txt
  3. import json
  4. from collections import defaultdict
  5. import frappe
  6. from frappe import scrub
  7. from frappe.desk.reportview import get_filters_cond, get_match_cond
  8. from frappe.utils import nowdate, unique
  9. import erpnext
  10. from erpnext.stock.get_item_details import _get_item_tax_template
  11. # searches for active employees
  12. @frappe.whitelist()
  13. @frappe.validate_and_sanitize_search_inputs
  14. def employee_query(doctype, txt, searchfield, start, page_len, filters):
  15. doctype = "Employee"
  16. conditions = []
  17. fields = get_fields(doctype, ["name", "employee_name"])
  18. return frappe.db.sql(
  19. """select {fields} from `tabEmployee`
  20. where status in ('Active', 'Suspended')
  21. and docstatus < 2
  22. and ({key} like %(txt)s
  23. or employee_name like %(txt)s)
  24. {fcond} {mcond}
  25. order by
  26. (case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
  27. (case when locate(%(_txt)s, employee_name) > 0 then locate(%(_txt)s, employee_name) else 99999 end),
  28. idx desc,
  29. name, employee_name
  30. limit %(page_len)s offset %(start)s""".format(
  31. **{
  32. "fields": ", ".join(fields),
  33. "key": searchfield,
  34. "fcond": get_filters_cond(doctype, filters, conditions),
  35. "mcond": get_match_cond(doctype),
  36. }
  37. ),
  38. {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
  39. )
  40. # searches for leads which are not converted
  41. @frappe.whitelist()
  42. @frappe.validate_and_sanitize_search_inputs
  43. def lead_query(doctype, txt, searchfield, start, page_len, filters):
  44. doctype = "Lead"
  45. fields = get_fields(doctype, ["name", "lead_name", "company_name"])
  46. return frappe.db.sql(
  47. """select {fields} from `tabLead`
  48. where docstatus < 2
  49. and ifnull(status, '') != 'Converted'
  50. and ({key} like %(txt)s
  51. or lead_name like %(txt)s
  52. or company_name like %(txt)s)
  53. {mcond}
  54. order by
  55. (case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
  56. (case when locate(%(_txt)s, lead_name) > 0 then locate(%(_txt)s, lead_name) else 99999 end),
  57. (case when locate(%(_txt)s, company_name) > 0 then locate(%(_txt)s, company_name) else 99999 end),
  58. idx desc,
  59. name, lead_name
  60. limit %(page_len)s offset %(start)s""".format(
  61. **{"fields": ", ".join(fields), "key": searchfield, "mcond": get_match_cond(doctype)}
  62. ),
  63. {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
  64. )
  65. # searches for customer
  66. @frappe.whitelist()
  67. @frappe.validate_and_sanitize_search_inputs
  68. def customer_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
  69. doctype = "Customer"
  70. conditions = []
  71. cust_master_name = frappe.defaults.get_user_default("cust_master_name")
  72. fields = ["name"]
  73. if cust_master_name != "Customer Name":
  74. fields.append("customer_name")
  75. fields = get_fields(doctype, fields)
  76. searchfields = frappe.get_meta(doctype).get_search_fields()
  77. searchfields = " or ".join(field + " like %(txt)s" for field in searchfields)
  78. return frappe.db.sql(
  79. """select {fields} from `tabCustomer`
  80. where docstatus < 2
  81. and ({scond}) and disabled=0
  82. {fcond} {mcond}
  83. order by
  84. (case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
  85. (case when locate(%(_txt)s, customer_name) > 0 then locate(%(_txt)s, customer_name) else 99999 end),
  86. idx desc,
  87. name, customer_name
  88. limit %(page_len)s offset %(start)s""".format(
  89. **{
  90. "fields": ", ".join(fields),
  91. "scond": searchfields,
  92. "mcond": get_match_cond(doctype),
  93. "fcond": get_filters_cond(doctype, filters, conditions).replace("%", "%%"),
  94. }
  95. ),
  96. {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
  97. as_dict=as_dict,
  98. )
  99. # searches for supplier
  100. @frappe.whitelist()
  101. @frappe.validate_and_sanitize_search_inputs
  102. def supplier_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
  103. doctype = "Supplier"
  104. supp_master_name = frappe.defaults.get_user_default("supp_master_name")
  105. fields = ["name"]
  106. if supp_master_name != "Supplier Name":
  107. fields.append("supplier_name")
  108. fields = get_fields(doctype, fields)
  109. return frappe.db.sql(
  110. """select {field} from `tabSupplier`
  111. where docstatus < 2
  112. and ({key} like %(txt)s
  113. or supplier_name like %(txt)s) and disabled=0
  114. and (on_hold = 0 or (on_hold = 1 and CURRENT_DATE > release_date))
  115. {mcond}
  116. order by
  117. (case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
  118. (case when locate(%(_txt)s, supplier_name) > 0 then locate(%(_txt)s, supplier_name) else 99999 end),
  119. idx desc,
  120. name, supplier_name
  121. limit %(page_len)s offset %(start)s""".format(
  122. **{"field": ", ".join(fields), "key": searchfield, "mcond": get_match_cond(doctype)}
  123. ),
  124. {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
  125. as_dict=as_dict,
  126. )
  127. @frappe.whitelist()
  128. @frappe.validate_and_sanitize_search_inputs
  129. def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
  130. doctype = "Account"
  131. company_currency = erpnext.get_company_currency(filters.get("company"))
  132. def get_accounts(with_account_type_filter):
  133. account_type_condition = ""
  134. if with_account_type_filter:
  135. account_type_condition = "AND account_type in %(account_types)s"
  136. accounts = frappe.db.sql(
  137. """
  138. SELECT name, parent_account
  139. FROM `tabAccount`
  140. WHERE `tabAccount`.docstatus!=2
  141. {account_type_condition}
  142. AND is_group = 0
  143. AND company = %(company)s
  144. AND disabled = %(disabled)s
  145. AND (account_currency = %(currency)s or ifnull(account_currency, '') = '')
  146. AND `{searchfield}` LIKE %(txt)s
  147. {mcond}
  148. ORDER BY idx DESC, name
  149. LIMIT %(limit)s offset %(offset)s
  150. """.format(
  151. account_type_condition=account_type_condition,
  152. searchfield=searchfield,
  153. mcond=get_match_cond(doctype),
  154. ),
  155. dict(
  156. account_types=filters.get("account_type"),
  157. company=filters.get("company"),
  158. disabled=filters.get("disabled", 0),
  159. currency=company_currency,
  160. txt="%{}%".format(txt),
  161. offset=start,
  162. limit=page_len,
  163. ),
  164. )
  165. return accounts
  166. tax_accounts = get_accounts(True)
  167. if not tax_accounts:
  168. tax_accounts = get_accounts(False)
  169. return tax_accounts
  170. @frappe.whitelist()
  171. @frappe.validate_and_sanitize_search_inputs
  172. def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
  173. doctype = "Item"
  174. conditions = []
  175. if isinstance(filters, str):
  176. filters = json.loads(filters)
  177. # Get searchfields from meta and use in Item Link field query
  178. meta = frappe.get_meta(doctype, cached=True)
  179. searchfields = meta.get_search_fields()
  180. columns = ""
  181. extra_searchfields = [field for field in searchfields if not field in ["name", "description"]]
  182. if extra_searchfields:
  183. columns += ", " + ", ".join(extra_searchfields)
  184. if "description" in searchfields:
  185. columns += """, if(length(tabItem.description) > 40, \
  186. concat(substr(tabItem.description, 1, 40), "..."), description) as description"""
  187. searchfields = searchfields + [
  188. field
  189. for field in [searchfield or "name", "item_code", "item_group", "item_name"]
  190. if not field in searchfields
  191. ]
  192. searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
  193. if filters and isinstance(filters, dict):
  194. if filters.get("customer") or filters.get("supplier"):
  195. party = filters.get("customer") or filters.get("supplier")
  196. item_rules_list = frappe.get_all(
  197. "Party Specific Item", filters={"party": party}, fields=["restrict_based_on", "based_on_value"]
  198. )
  199. filters_dict = {}
  200. for rule in item_rules_list:
  201. if rule["restrict_based_on"] == "Item":
  202. rule["restrict_based_on"] = "name"
  203. filters_dict[rule.restrict_based_on] = []
  204. for rule in item_rules_list:
  205. filters_dict[rule.restrict_based_on].append(rule.based_on_value)
  206. for filter in filters_dict:
  207. filters[scrub(filter)] = ["in", filters_dict[filter]]
  208. if filters.get("customer"):
  209. del filters["customer"]
  210. else:
  211. del filters["supplier"]
  212. else:
  213. filters.pop("customer", None)
  214. filters.pop("supplier", None)
  215. description_cond = ""
  216. if frappe.db.count(doctype, cache=True) < 50000:
  217. # scan description only if items are less than 50000
  218. description_cond = "or tabItem.description LIKE %(txt)s"
  219. return frappe.db.sql(
  220. """select
  221. tabItem.name {columns}
  222. from tabItem
  223. where tabItem.docstatus < 2
  224. and tabItem.disabled=0
  225. and tabItem.has_variants=0
  226. and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00')
  227. and ({scond} or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s)
  228. {description_cond})
  229. {fcond} {mcond}
  230. order by
  231. if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
  232. if(locate(%(_txt)s, item_name), locate(%(_txt)s, item_name), 99999),
  233. idx desc,
  234. name, item_name
  235. limit %(start)s, %(page_len)s """.format(
  236. columns=columns,
  237. scond=searchfields,
  238. fcond=get_filters_cond(doctype, filters, conditions).replace("%", "%%"),
  239. mcond=get_match_cond(doctype).replace("%", "%%"),
  240. description_cond=description_cond,
  241. ),
  242. {
  243. "today": nowdate(),
  244. "txt": "%%%s%%" % txt,
  245. "_txt": txt.replace("%", ""),
  246. "start": start,
  247. "page_len": page_len,
  248. },
  249. as_dict=as_dict,
  250. )
  251. @frappe.whitelist()
  252. @frappe.validate_and_sanitize_search_inputs
  253. def bom(doctype, txt, searchfield, start, page_len, filters):
  254. doctype = "BOM"
  255. conditions = []
  256. fields = get_fields(doctype, ["name", "item"])
  257. return frappe.db.sql(
  258. """select {fields}
  259. from `tabBOM`
  260. where `tabBOM`.docstatus=1
  261. and `tabBOM`.is_active=1
  262. and `tabBOM`.`{key}` like %(txt)s
  263. {fcond} {mcond}
  264. order by
  265. (case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
  266. idx desc, name
  267. limit %(page_len)s offset %(start)s""".format(
  268. fields=", ".join(fields),
  269. fcond=get_filters_cond(doctype, filters, conditions).replace("%", "%%"),
  270. mcond=get_match_cond(doctype).replace("%", "%%"),
  271. key=searchfield,
  272. ),
  273. {
  274. "txt": "%" + txt + "%",
  275. "_txt": txt.replace("%", ""),
  276. "start": start or 0,
  277. "page_len": page_len or 20,
  278. },
  279. )
  280. @frappe.whitelist()
  281. @frappe.validate_and_sanitize_search_inputs
  282. def get_project_name(doctype, txt, searchfield, start, page_len, filters):
  283. doctype = "Project"
  284. cond = ""
  285. if filters and filters.get("customer"):
  286. cond = """(`tabProject`.customer = %s or
  287. ifnull(`tabProject`.customer,"")="") and""" % (
  288. frappe.db.escape(filters.get("customer"))
  289. )
  290. fields = get_fields(doctype, ["name", "project_name"])
  291. searchfields = frappe.get_meta(doctype).get_search_fields()
  292. searchfields = " or ".join(["`tabProject`." + field + " like %(txt)s" for field in searchfields])
  293. return frappe.db.sql(
  294. """select {fields} from `tabProject`
  295. where
  296. `tabProject`.status not in ('Completed', 'Cancelled')
  297. and {cond} {scond} {match_cond}
  298. order by
  299. (case when locate(%(_txt)s, `tabProject`.name) > 0 then locate(%(_txt)s, `tabProject`.name) else 99999 end),
  300. `tabProject`.idx desc,
  301. `tabProject`.name asc
  302. limit {page_len} offset {start}""".format(
  303. fields=", ".join(["`tabProject`.{0}".format(f) for f in fields]),
  304. cond=cond,
  305. scond=searchfields,
  306. match_cond=get_match_cond(doctype),
  307. start=start,
  308. page_len=page_len,
  309. ),
  310. {"txt": "%{0}%".format(txt), "_txt": txt.replace("%", "")},
  311. )
  312. @frappe.whitelist()
  313. @frappe.validate_and_sanitize_search_inputs
  314. def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict):
  315. doctype = "Delivery Note"
  316. fields = get_fields(doctype, ["name", "customer", "posting_date"])
  317. return frappe.db.sql(
  318. """
  319. select %(fields)s
  320. from `tabDelivery Note`
  321. where `tabDelivery Note`.`%(key)s` like %(txt)s and
  322. `tabDelivery Note`.docstatus = 1
  323. and status not in ('Stopped', 'Closed') %(fcond)s
  324. and (
  325. (`tabDelivery Note`.is_return = 0 and `tabDelivery Note`.per_billed < 100)
  326. or (`tabDelivery Note`.grand_total = 0 and `tabDelivery Note`.per_billed < 100)
  327. or (
  328. `tabDelivery Note`.is_return = 1
  329. and return_against in (select name from `tabDelivery Note` where per_billed < 100)
  330. )
  331. )
  332. %(mcond)s order by `tabDelivery Note`.`%(key)s` asc limit %(page_len)s offset %(start)s
  333. """
  334. % {
  335. "fields": ", ".join(["`tabDelivery Note`.{0}".format(f) for f in fields]),
  336. "key": searchfield,
  337. "fcond": get_filters_cond(doctype, filters, []),
  338. "mcond": get_match_cond(doctype),
  339. "start": start,
  340. "page_len": page_len,
  341. "txt": "%(txt)s",
  342. },
  343. {"txt": ("%%%s%%" % txt)},
  344. as_dict=as_dict,
  345. )
  346. @frappe.whitelist()
  347. @frappe.validate_and_sanitize_search_inputs
  348. def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
  349. doctype = "Batch"
  350. cond = ""
  351. if filters.get("posting_date"):
  352. cond = "and (batch.expiry_date is null or batch.expiry_date >= %(posting_date)s)"
  353. batch_nos = None
  354. args = {
  355. "item_code": filters.get("item_code"),
  356. "warehouse": filters.get("warehouse"),
  357. "posting_date": filters.get("posting_date"),
  358. "txt": "%{0}%".format(txt),
  359. "start": start,
  360. "page_len": page_len,
  361. }
  362. having_clause = "having sum(sle.actual_qty) > 0"
  363. if filters.get("is_return"):
  364. having_clause = ""
  365. meta = frappe.get_meta(doctype, cached=True)
  366. searchfields = meta.get_search_fields()
  367. search_columns = ""
  368. search_cond = ""
  369. if searchfields:
  370. search_columns = ", " + ", ".join(searchfields)
  371. search_cond = " or " + " or ".join([field + " like %(txt)s" for field in searchfields])
  372. if args.get("warehouse"):
  373. searchfields = ["batch." + field for field in searchfields]
  374. if searchfields:
  375. search_columns = ", " + ", ".join(searchfields)
  376. search_cond = " or " + " or ".join([field + " like %(txt)s" for field in searchfields])
  377. batch_nos = frappe.db.sql(
  378. """select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom,
  379. concat('MFG-',batch.manufacturing_date), concat('EXP-',batch.expiry_date)
  380. {search_columns}
  381. from `tabStock Ledger Entry` sle
  382. INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
  383. where
  384. batch.disabled = 0
  385. and sle.is_cancelled = 0
  386. and sle.item_code = %(item_code)s
  387. and sle.warehouse = %(warehouse)s
  388. and (sle.batch_no like %(txt)s
  389. or batch.expiry_date like %(txt)s
  390. or batch.manufacturing_date like %(txt)s
  391. {search_cond})
  392. and batch.docstatus < 2
  393. {cond}
  394. {match_conditions}
  395. group by batch_no {having_clause}
  396. order by batch.expiry_date, sle.batch_no desc
  397. limit %(page_len)s offset %(start)s""".format(
  398. search_columns=search_columns,
  399. cond=cond,
  400. match_conditions=get_match_cond(doctype),
  401. having_clause=having_clause,
  402. search_cond=search_cond,
  403. ),
  404. args,
  405. )
  406. return batch_nos
  407. else:
  408. return frappe.db.sql(
  409. """select name, concat('MFG-', manufacturing_date), concat('EXP-',expiry_date)
  410. {search_columns}
  411. from `tabBatch` batch
  412. where batch.disabled = 0
  413. and item = %(item_code)s
  414. and (name like %(txt)s
  415. or expiry_date like %(txt)s
  416. or manufacturing_date like %(txt)s
  417. {search_cond})
  418. and docstatus < 2
  419. {0}
  420. {match_conditions}
  421. order by expiry_date, name desc
  422. limit %(page_len)s offset %(start)s""".format(
  423. cond,
  424. search_columns=search_columns,
  425. search_cond=search_cond,
  426. match_conditions=get_match_cond(doctype),
  427. ),
  428. args,
  429. )
  430. @frappe.whitelist()
  431. @frappe.validate_and_sanitize_search_inputs
  432. def get_account_list(doctype, txt, searchfield, start, page_len, filters):
  433. doctype = "Account"
  434. filter_list = []
  435. if isinstance(filters, dict):
  436. for key, val in filters.items():
  437. if isinstance(val, (list, tuple)):
  438. filter_list.append([doctype, key, val[0], val[1]])
  439. else:
  440. filter_list.append([doctype, key, "=", val])
  441. elif isinstance(filters, list):
  442. filter_list.extend(filters)
  443. if "is_group" not in [d[1] for d in filter_list]:
  444. filter_list.append(["Account", "is_group", "=", "0"])
  445. if searchfield and txt:
  446. filter_list.append([doctype, searchfield, "like", "%%%s%%" % txt])
  447. return frappe.desk.reportview.execute(
  448. doctype,
  449. filters=filter_list,
  450. fields=["name", "parent_account"],
  451. limit_start=start,
  452. limit_page_length=page_len,
  453. as_list=True,
  454. )
  455. @frappe.whitelist()
  456. @frappe.validate_and_sanitize_search_inputs
  457. def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters):
  458. return frappe.db.sql(
  459. """select distinct bo.name, bo.blanket_order_type, bo.to_date
  460. from `tabBlanket Order` bo, `tabBlanket Order Item` boi
  461. where
  462. boi.parent = bo.name
  463. and boi.item_code = {item_code}
  464. and bo.blanket_order_type = '{blanket_order_type}'
  465. and bo.company = {company}
  466. and bo.docstatus = 1""".format(
  467. item_code=frappe.db.escape(filters.get("item")),
  468. blanket_order_type=filters.get("blanket_order_type"),
  469. company=frappe.db.escape(filters.get("company")),
  470. )
  471. )
  472. @frappe.whitelist()
  473. @frappe.validate_and_sanitize_search_inputs
  474. def get_income_account(doctype, txt, searchfield, start, page_len, filters):
  475. from erpnext.controllers.queries import get_match_cond
  476. # income account can be any Credit account,
  477. # but can also be a Asset account with account_type='Income Account' in special circumstances.
  478. # Hence the first condition is an "OR"
  479. if not filters:
  480. filters = {}
  481. doctype = "Account"
  482. condition = ""
  483. if filters.get("company"):
  484. condition += "and tabAccount.company = %(company)s"
  485. return frappe.db.sql(
  486. """select tabAccount.name from `tabAccount`
  487. where (tabAccount.report_type = "Profit and Loss"
  488. or tabAccount.account_type in ("Income Account", "Temporary"))
  489. and tabAccount.is_group=0
  490. and tabAccount.`{key}` LIKE %(txt)s
  491. {condition} {match_condition}
  492. order by idx desc, name""".format(
  493. condition=condition, match_condition=get_match_cond(doctype), key=searchfield
  494. ),
  495. {"txt": "%" + txt + "%", "company": filters.get("company", "")},
  496. )
  497. @frappe.whitelist()
  498. @frappe.validate_and_sanitize_search_inputs
  499. def get_filtered_dimensions(
  500. doctype, txt, searchfield, start, page_len, filters, reference_doctype=None
  501. ):
  502. from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
  503. get_dimension_filter_map,
  504. )
  505. dimension_filters = get_dimension_filter_map()
  506. dimension_filters = dimension_filters.get((filters.get("dimension"), filters.get("account")))
  507. query_filters = []
  508. or_filters = []
  509. fields = ["name"]
  510. searchfields = frappe.get_meta(doctype).get_search_fields()
  511. meta = frappe.get_meta(doctype)
  512. if meta.is_tree:
  513. query_filters.append(["is_group", "=", 0])
  514. if meta.has_field("disabled"):
  515. query_filters.append(["disabled", "!=", 1])
  516. if meta.has_field("company"):
  517. query_filters.append(["company", "=", filters.get("company")])
  518. for field in searchfields:
  519. or_filters.append([field, "LIKE", "%%%s%%" % txt])
  520. fields.append(field)
  521. if dimension_filters:
  522. if dimension_filters["allow_or_restrict"] == "Allow":
  523. query_selector = "in"
  524. else:
  525. query_selector = "not in"
  526. if len(dimension_filters["allowed_dimensions"]) == 1:
  527. dimensions = tuple(dimension_filters["allowed_dimensions"] * 2)
  528. else:
  529. dimensions = tuple(dimension_filters["allowed_dimensions"])
  530. query_filters.append(["name", query_selector, dimensions])
  531. output = frappe.get_list(
  532. doctype,
  533. fields=fields,
  534. filters=query_filters,
  535. or_filters=or_filters,
  536. as_list=1,
  537. reference_doctype=reference_doctype,
  538. )
  539. return [tuple(d) for d in set(output)]
  540. @frappe.whitelist()
  541. @frappe.validate_and_sanitize_search_inputs
  542. def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
  543. from erpnext.controllers.queries import get_match_cond
  544. if not filters:
  545. filters = {}
  546. doctype = "Account"
  547. condition = ""
  548. if filters.get("company"):
  549. condition += "and tabAccount.company = %(company)s"
  550. return frappe.db.sql(
  551. """select tabAccount.name from `tabAccount`
  552. where (tabAccount.report_type = "Profit and Loss"
  553. or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed", "Capital Work in Progress"))
  554. and tabAccount.is_group=0
  555. and tabAccount.docstatus!=2
  556. and tabAccount.{key} LIKE %(txt)s
  557. {condition} {match_condition}""".format(
  558. condition=condition, key=searchfield, match_condition=get_match_cond(doctype)
  559. ),
  560. {"company": filters.get("company", ""), "txt": "%" + txt + "%"},
  561. )
  562. @frappe.whitelist()
  563. @frappe.validate_and_sanitize_search_inputs
  564. def warehouse_query(doctype, txt, searchfield, start, page_len, filters):
  565. # Should be used when item code is passed in filters.
  566. doctype = "Warehouse"
  567. conditions, bin_conditions = [], []
  568. filter_dict = get_doctype_wise_filters(filters)
  569. query = """select `tabWarehouse`.name,
  570. CONCAT_WS(' : ', 'Actual Qty', ifnull(round(`tabBin`.actual_qty, 2), 0 )) actual_qty
  571. from `tabWarehouse` left join `tabBin`
  572. on `tabBin`.warehouse = `tabWarehouse`.name {bin_conditions}
  573. where
  574. `tabWarehouse`.`{key}` like {txt}
  575. {fcond} {mcond}
  576. order by ifnull(`tabBin`.actual_qty, 0) desc
  577. limit
  578. {page_len} offset {start}
  579. """.format(
  580. bin_conditions=get_filters_cond(
  581. doctype, filter_dict.get("Bin"), bin_conditions, ignore_permissions=True
  582. ),
  583. key=searchfield,
  584. fcond=get_filters_cond(doctype, filter_dict.get("Warehouse"), conditions),
  585. mcond=get_match_cond(doctype),
  586. start=start,
  587. page_len=page_len,
  588. txt=frappe.db.escape("%{0}%".format(txt)),
  589. )
  590. return frappe.db.sql(query)
  591. def get_doctype_wise_filters(filters):
  592. # Helper function to seperate filters doctype_wise
  593. filter_dict = defaultdict(list)
  594. for row in filters:
  595. filter_dict[row[0]].append(row)
  596. return filter_dict
  597. @frappe.whitelist()
  598. @frappe.validate_and_sanitize_search_inputs
  599. def get_batch_numbers(doctype, txt, searchfield, start, page_len, filters):
  600. query = """select batch_id from `tabBatch`
  601. where disabled = 0
  602. and (expiry_date >= CURRENT_DATE or expiry_date IS NULL)
  603. and name like {txt}""".format(
  604. txt=frappe.db.escape("%{0}%".format(txt))
  605. )
  606. if filters and filters.get("item"):
  607. query += " and item = {item}".format(item=frappe.db.escape(filters.get("item")))
  608. return frappe.db.sql(query, filters)
  609. @frappe.whitelist()
  610. @frappe.validate_and_sanitize_search_inputs
  611. def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters):
  612. item_filters = [
  613. ["manufacturer", "like", "%" + txt + "%"],
  614. ["item_code", "=", filters.get("item_code")],
  615. ]
  616. item_manufacturers = frappe.get_all(
  617. "Item Manufacturer",
  618. fields=["manufacturer", "manufacturer_part_no"],
  619. filters=item_filters,
  620. limit_start=start,
  621. limit_page_length=page_len,
  622. as_list=1,
  623. )
  624. return item_manufacturers
  625. @frappe.whitelist()
  626. @frappe.validate_and_sanitize_search_inputs
  627. def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters):
  628. query = """
  629. select pr.name
  630. from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pritem
  631. where pr.docstatus = 1 and pritem.parent = pr.name
  632. and pr.name like {txt}""".format(
  633. txt=frappe.db.escape("%{0}%".format(txt))
  634. )
  635. if filters and filters.get("item_code"):
  636. query += " and pritem.item_code = {item_code}".format(
  637. item_code=frappe.db.escape(filters.get("item_code"))
  638. )
  639. return frappe.db.sql(query, filters)
  640. @frappe.whitelist()
  641. @frappe.validate_and_sanitize_search_inputs
  642. def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters):
  643. query = """
  644. select pi.name
  645. from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` piitem
  646. where pi.docstatus = 1 and piitem.parent = pi.name
  647. and pi.name like {txt}""".format(
  648. txt=frappe.db.escape("%{0}%".format(txt))
  649. )
  650. if filters and filters.get("item_code"):
  651. query += " and piitem.item_code = {item_code}".format(
  652. item_code=frappe.db.escape(filters.get("item_code"))
  653. )
  654. return frappe.db.sql(query, filters)
  655. @frappe.whitelist()
  656. @frappe.validate_and_sanitize_search_inputs
  657. def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
  658. item_doc = frappe.get_cached_doc("Item", filters.get("item_code"))
  659. item_group = filters.get("item_group")
  660. company = filters.get("company")
  661. taxes = item_doc.taxes or []
  662. while item_group:
  663. item_group_doc = frappe.get_cached_doc("Item Group", item_group)
  664. taxes += item_group_doc.taxes or []
  665. item_group = item_group_doc.parent_item_group
  666. if not taxes:
  667. return frappe.get_all(
  668. "Item Tax Template", filters={"disabled": 0, "company": company}, as_list=True
  669. )
  670. else:
  671. valid_from = filters.get("valid_from")
  672. valid_from = valid_from[1] if isinstance(valid_from, list) else valid_from
  673. args = {
  674. "item_code": filters.get("item_code"),
  675. "posting_date": valid_from,
  676. "tax_category": filters.get("tax_category"),
  677. "company": company,
  678. }
  679. taxes = _get_item_tax_template(args, taxes, for_validate=True)
  680. return [(d,) for d in set(taxes)]
  681. def get_fields(doctype, fields=None):
  682. if fields is None:
  683. fields = []
  684. meta = frappe.get_meta(doctype)
  685. fields.extend(meta.get_search_fields())
  686. if meta.title_field and not meta.title_field.strip() in fields:
  687. fields.insert(1, meta.title_field.strip())
  688. return unique(fields)