def get_fetch_fields(doctype, linked_to, ignore_doctypes=None): """ doctype = Master DocType in which the changes are being made linked_to = DocType name of the field thats being updated in Master This function fetches list of all DocType where both doctype and linked_to is found as link fields. Forms a list of dict in the form - [{doctype: , master_fieldname: , linked_to_fieldname: ] where doctype = DocType where changes need to be made master_fieldname = Fieldname where options = doctype linked_to_fieldname = Fieldname where options = linked_to """ out = [] master_list = get_link_fields(doctype) linked_to_list = get_link_fields(linked_to) product_list = product(master_list, linked_to_list) for d in product_list: linked_doctype_info = frappe._dict() if (d[0]["parent"] == d[1]["parent"] and (not ignore_doctypes or d[0]["parent"] not in ignore_doctypes) and not d[1]["issingle"]): linked_doctype_info.doctype = d[0]["parent"] linked_doctype_info.master_fieldname = d[0]["fieldname"] linked_doctype_info.linked_to_fieldname = d[1]["fieldname"] out.append(linked_doctype_info) return out
def check_if_doc_is_linked(doc, method="Delete"): """ Raises excption if the given doc(dt, dn) is linked in another record. """ from frappe.model.rename_doc import get_link_fields link_fields = get_link_fields(doc.doctype) link_fields = [[lf['parent'], lf['fieldname'], lf['issingle']] for lf in link_fields] for link_dt, link_field, issingle in link_fields: if not issingle: item = frappe.db.get_value( link_dt, {link_field: doc.name}, ["name", "parent", "parenttype", "docstatus"], as_dict=True) if item and item.parent != doc.name and ( (method == "Delete" and item.docstatus < 2) or (method == "Cancel" and item.docstatus == 1)): # raise exception only if # linked to an non-cancelled doc when deleting # or linked to a submitted doc when cancelling frappe.throw( _("Cannot delete or cancel because {0} {1} is linked with {2} {3}" ).format(doc.doctype, doc.name, item.parenttype if item.parent else link_dt, item.parent or item.name), frappe.LinkExistsError)
def check_if_doc_is_linked(doc, method="Delete"): """ Raises excption if the given doc(dt, dn) is linked in another record. """ from frappe.model.rename_doc import get_link_fields link_fields = get_link_fields(doc.doctype) link_fields = [[lf['parent'], lf['fieldname'], lf['issingle']] for lf in link_fields] for link_dt, link_field, issingle in link_fields: if not issingle: for item in frappe.db.get_values( link_dt, {link_field: doc.name}, ["name", "parent", "parenttype", "docstatus"], as_dict=True): linked_doctype = item.parenttype if item.parent else link_dt if linked_doctype in ("Communication", "ToDo", "DocShare", "Email Unsubscribe", 'File', 'Version'): # don't check for communication and todo! continue if item and ((item.parent or item.name) != doc.name) \ and ((method=="Delete" and item.docstatus<2) or (method=="Cancel" and item.docstatus==1)): # raise exception only if # linked to an non-cancelled doc when deleting # or linked to a submitted doc when cancelling frappe.throw( _('Cannot delete or cancel because {0} <a href="#Form/{0}/{1}">{1}</a> is linked with {2} <a href="#Form/{2}/{3}">{3}</a>' ).format(doc.doctype, doc.name, linked_doctype, item.parent or item.name), frappe.LinkExistsError)
def check_if_doc_is_linked(doc, method="Delete"): """ Raises excption if the given doc(dt, dn) is linked in another record. """ from frappe.model.rename_doc import get_link_fields link_fields = get_link_fields(doc.doctype) link_fields = [[lf['parent'], lf['fieldname'], lf['issingle']] for lf in link_fields] for link_dt, link_field, issingle in link_fields: if not issingle: for item in frappe.db.get_values(link_dt, {link_field:doc.name}, ["name", "parent", "parenttype", "docstatus"], as_dict=True): linked_doctype = item.parenttype if item.parent else link_dt if linked_doctype in ("Communication", "ToDo", "DocShare", "Email Unsubscribe", 'File', 'Version', "Activity Log", 'Comment'): # don't check for communication and todo! continue if not item: continue elif (method != "Delete" or item.docstatus == 2) and (method != "Cancel" or item.docstatus != 1): # don't raise exception if not # linked to a non-cancelled doc when deleting or to a submitted doc when cancelling continue elif link_dt == doc.doctype and (item.parent or item.name) == doc.name: # don't raise exception if not # linked to same item or doc having same name as the item continue else: reference_docname = item.parent or item.name raise_link_exists_exception(doc, linked_doctype, reference_docname) else: if frappe.db.get_value(link_dt, None, link_field) == doc.name: raise_link_exists_exception(doc, link_dt, link_dt)
def check_if_doc_is_linked(doc, method="Delete"): """ Raises excption if the given doc(dt, dn) is linked in another record. """ from frappe.model.rename_doc import get_link_fields link_fields = get_link_fields(doc.doctype) link_fields = [[lf['parent'], lf['fieldname'], lf['issingle']] for lf in link_fields] for link_dt, link_field, issingle in link_fields: if not issingle: for item in frappe.db.get_values( link_dt, {link_field: doc.name}, ["name", "parent", "parenttype", "docstatus"], as_dict=True): linked_doctype = item.parenttype if item.parent else link_dt if linked_doctype in ("Communication", "ToDo", "DocShare", "Email Unsubscribe", 'File', 'Version'): # don't check for communication and todo! continue if item and ((item.parent or item.name) != doc.name) \ and ((method=="Delete" and item.docstatus<2) or (method=="Cancel" and item.docstatus==1)): # raise exception only if # linked to an non-cancelled doc when deleting # or linked to a submitted doc when cancelling reference_docname = item.parent or item.name raise_link_exists_exception(doc, linked_doctype, reference_docname) else: if frappe.db.get_value(link_dt, None, link_field) == doc.name: raise_link_exists_exception(doc, link_dt, link_dt)
def check_if_doc_is_linked(doc, method="Delete"): """ Raises excption if the given doc(dt, dn) is linked in another record. """ from frappe.model.rename_doc import get_link_fields link_fields = get_link_fields(doc.doctype) link_fields = [[lf['parent'], lf['fieldname'], lf['issingle']] for lf in link_fields] for link_dt, link_field, issingle in link_fields: if not issingle: for item in frappe.db.get_values(link_dt, {link_field:doc.name}, ["name", "parent", "parenttype", "docstatus"], as_dict=True): linked_doctype = item.parenttype if item.parent else link_dt if linked_doctype in ("Communication", "ToDo", "DocShare", "Email Unsubscribe", 'File', 'Version'): # don't check for communication and todo! continue if item and ((item.parent or item.name) != doc.name) \ and ((method=="Delete" and item.docstatus<2) or (method=="Cancel" and item.docstatus==1)): # raise exception only if # linked to an non-cancelled doc when deleting # or linked to a submitted doc when cancelling frappe.throw(_('Cannot delete or cancel because {0} <a href="#Form/{0}/{1}">{1}</a> is linked with {2} <a href="#Form/{2}/{3}">{3}</a>') .format(doc.doctype, doc.name, linked_doctype, item.parent or item.name), frappe.LinkExistsError)
def check_if_doc_is_linked(doc, method="Delete"): """ Raises excption if the given doc(dt, dn) is linked in another record. """ from frappe.model.rename_doc import get_link_fields link_fields = get_link_fields(doc.doctype) link_fields = [[lf['parent'], lf['fieldname'], lf['issingle']] for lf in link_fields] for link_dt, link_field, issingle in link_fields: if not issingle: for item in frappe.db.get_values(link_dt, {link_field:doc.name}, ["name", "parent", "parenttype", "docstatus"], as_dict=True): linked_doctype = item.parenttype if item.parent else link_dt if linked_doctype in ("Communication", "ToDo", "DocShare", "Email Unsubscribe", 'File', 'Version', "Activity Log"): # don't check for communication and todo! continue if item and ((item.parent or item.name) != doc.name) \ and ((method=="Delete" and item.docstatus<2) or (method=="Cancel" and item.docstatus==1)): # raise exception only if # linked to an non-cancelled doc when deleting # or linked to a submitted doc when cancelling reference_docname = item.parent or item.name raise_link_exists_exception(doc, linked_doctype, reference_docname) else: if frappe.db.get_value(link_dt, None, link_field) == doc.name: raise_link_exists_exception(doc, link_dt, link_dt)
def update_for_linked_docs(timeline_doctype): for df in get_link_fields(timeline_doctype): if df.issingle: continue reference_doctype = df.parent if not is_valid_timeline_doctype(reference_doctype, timeline_doctype): continue for doc in frappe.get_all(reference_doctype, fields=["name", df.fieldname]): timeline_name = doc.get(df.fieldname) update_communication(timeline_doctype, timeline_name, reference_doctype, doc.name)
def check_if_doc_is_linked(doc, method="Delete"): """ Raises excption if the given doc(dt, dn) is linked in another record. """ from frappe.model.rename_doc import get_link_fields link_fields = get_link_fields(doc.doctype) link_fields = [[lf["parent"], lf["fieldname"], lf["issingle"]] for lf in link_fields] for link_dt, link_field, issingle in link_fields: if not issingle: for item in frappe.db.get_values( link_dt, {link_field: doc.name}, ["name", "parent", "parenttype", "docstatus"], as_dict=True): linked_doctype = item.parenttype if item.parent else link_dt ignore_linked_doctypes = doc.get( "ignore_linked_doctypes") or [] if linked_doctype in doctypes_to_skip or ( linked_doctype in ignore_linked_doctypes and method == "Cancel"): # don't check for communication and todo! continue if not item: continue elif method != "Delete" and (method != "Cancel" or item.docstatus != 1): # don't raise exception if not # linked to a non-cancelled doc when deleting or to a submitted doc when cancelling continue elif link_dt == doc.doctype and (item.parent or item.name) == doc.name: # don't raise exception if not # linked to same item or doc having same name as the item continue else: reference_docname = item.parent or item.name raise_link_exists_exception(doc, linked_doctype, reference_docname) else: if frappe.db.get_value(link_dt, None, link_field) == doc.name: raise_link_exists_exception(doc, link_dt, link_dt)
def check_if_doc_is_linked(doc, method="Delete"): """ Raises excption if the given doc(dt, dn) is linked in another record. """ from frappe.model.rename_doc import get_link_fields link_fields = get_link_fields(doc.doctype) link_fields = [[lf['parent'], lf['fieldname'], lf['issingle']] for lf in link_fields] for link_dt, link_field, issingle in link_fields: if not issingle: item = frappe.db.get_value(link_dt, {link_field:doc.name}, ["name", "parent", "parenttype", "docstatus"], as_dict=True) if item and item.parent != doc.name and ((method=="Delete" and item.docstatus<2) or (method=="Cancel" and item.docstatus==1)): frappe.throw(_("Cannot delete or cancel because {0} {1} is linked with {2} {3}").format(_(doc.doctype), doc.name, _(item.parent) or item.name, item.parenttype if item.parent else _(link_dt)), frappe.LinkExistsError)
def check_if_doc_is_linked(doc, method="Delete"): """ Raises excption if the given doc(dt, dn) is linked in another record. """ from frappe.model.rename_doc import get_link_fields link_fields = get_link_fields(doc.doctype) link_fields = [[lf['parent'], lf['fieldname'], lf['issingle']] for lf in link_fields] for link_dt, link_field, issingle in link_fields: if not issingle: for item in frappe.db.get_values(link_dt, {link_field:doc.name}, ["name", "parent", "parenttype", "docstatus"], as_dict=True): if item and ((item.parent or item.name) != doc.name) \ and ((method=="Delete" and item.docstatus<2) or (method=="Cancel" and item.docstatus==1)): # raise exception only if # linked to an non-cancelled doc when deleting # or linked to a submitted doc when cancelling frappe.throw(_("Cannot delete or cancel because {0} {1} is linked with {2} {3}") .format(doc.doctype, doc.name, item.parenttype if item.parent else link_dt, item.parent or item.name), frappe.LinkExistsError)
def check_if_doc_is_linked(dt, dn, method="Delete"): """ Raises excption if the given doc(dt, dn) is linked in another record. """ from frappe.model.rename_doc import get_link_fields link_fields = get_link_fields(dt) link_fields = [[lf['parent'], lf['fieldname'], lf['issingle']] for lf in link_fields] for link_dt, link_field, issingle in link_fields: if not issingle: item = frappe.db.get_value( link_dt, {link_field: dn}, ["name", "parent", "parenttype", "docstatus"], as_dict=True) if item and item.parent != dn and (method == "Delete" or (method == "Cancel" and item.docstatus == 1)): frappe.msgprint(method + " " + _("Error") + ":"+\ ("%s (%s) " % (dn, dt)) + _("is linked in") + (" %s (%s)") % (item.parent or item.name, item.parent and item.parenttype or link_dt), raise_exception=LinkExistsError)
def convert_item_to_batched(item_code): """ - how is batch defined ? where is the qty details of the batch stored ? where is it fetched ? and on move where is it taken ? --> no qty info stored in tabBatch everything from stock ledger - One batch per item ? yes - How is reference to an item tracked on deletion ? - check_if_doc_is_linked `from frappe.model.rename_doc import get_link_fields(doctype)` - parent doctype is obtained if doctype contains field batch_no: update; --> obtain field list `frappe.db.get_table(doctype)` - check_if_doc_is_dynamically_linked create new batch named 'x' for item y with total qty = total incoming qty total qty = sum(positive actual_qty from tabStock Ledger Entry) - no need to update the qtys anywhere all such details comes from stock ledger entries """ if frappe.db.get_value("Item", item_code, "has_batch_no"): print("Already batched item.") return frappe.db.begin() try: frappe.db.set_value("Item", item_code, "has_batch_no", 1) frappe.db.set_value("Item", item_code, "create_new_batch", 1) batch_doc = frappe.new_doc("Batch") temp = None while not temp: temp = frappe.generate_hash()[:7].upper() if frappe.db.exists("Batch", temp): temp = None batch_doc.batch_id = temp batch_doc.item = item_code batch_doc.description = "Auto Generated - Console ERP Solutions" batch_doc.insert() # static links # ignoring dynamic links # refer frappe.model.delete_doc.check_if_doc_is_dynamically_linked from frappe.model.rename_doc import get_link_fields links = get_link_fields("Item") for link_field in links: if link_field.issingle: continue columns = frappe.db.get_table_columns(link_field.parent) if not "item_code" in columns or not "batch_no" in columns: continue frappe.db.sql("UPDATE `%s` SET batch_no=%s where item_code=%s;" % ('tab' + link_field.parent, "%s", "%s"), (batch_doc.batch_id, item_code), debug=1) frappe.db.sql("UPDATE `tabStock Ledger Entry` SET batch_no=%s WHERE item_code=%s;", (batch_doc.batch_id, item_code), debug=1) from frappe.sessions import clear_cache print("Successfully converted") except Exception: frappe.db.rollback() raise else: frappe.db.commit()