[ { "allow_guest": 0, "api_method": null, "cron_format": null, "disabled": 0, "docstatus": 0, "doctype": "Server Script", "doctype_event": "Before Insert", "enable_rate_limit": 0, "event_frequency": "Daily", "modified": "2026-06-09 13:40:45.248212", "module": "SFDA Parser", "name": "Check the Serial nos present from sfda parser", "rate_limit_count": 5, "rate_limit_seconds": 86400, "reference_doctype": null, "script": "def normalize_values(raw):\r\n if not raw:\r\n return []\r\n return [v.strip() for v in str(raw).split(\",\") if v.strip()]\r\n\r\n\r\n# Users having BOTH \"System Manager\" and \"Cluster Manager\"\r\nboth_roles = frappe.get_all(\r\n \"Has Role\",\r\n filters={\r\n \"role\": [\"in\", [\"System Manager\", \"Cluster Manager\"]],\r\n \"parenttype\": \"User\"\r\n },\r\n fields=[\"parent\", \"role\"],\r\n limit_page_length=0\r\n)\r\n\r\nrole_map = {}\r\nfor r in both_roles:\r\n role_map.setdefault(r.parent, set()).add(r.role)\r\n\r\nrecall_users = [\r\n u for u, roles in role_map.items()\r\n if {\"System Manager\", \"Cluster Manager\"}.issubset(roles)\r\n]\r\n\r\nrecall_users = frappe.get_all(\r\n \"User\",\r\n filters={\"name\": [\"in\", recall_users], \"enabled\": 1},\r\n pluck=\"name\",\r\n limit_page_length=0\r\n) if recall_users else []\r\n\r\nsite_url = frappe.utils.get_url()\r\n\r\nsfda_entries = frappe.get_all(\r\n \"SFDA Entries\",\r\n filters={\"passed\": 0},\r\n fields=[\"name\"],\r\n limit_page_length=0\r\n)\r\n\r\nfor entry in sfda_entries:\r\n doc = frappe.get_doc(\"SFDA Entries\", entry.name)\r\n\r\n entry_link = f\"{site_url}/asm_app/sfda-entries/{doc.name}\"\r\n\r\n # rows that matched (asset found) and rows that did NOT match\r\n matched_rows = [] # list of dicts: {ncmdr_ref, serial, asset}\r\n unmatched_refs = [] # list of ncmdr_ref values with no match\r\n\r\n for row in doc.device_list:\r\n catalog_numbers = normalize_values(row.catalog_number)\r\n serial_numbers = normalize_values(row.serial_no)\r\n all_values = list(set(catalog_numbers + serial_numbers))\r\n\r\n row_matched = False\r\n\r\n if all_values:\r\n matched_assets = frappe.get_all(\r\n \"Asset\",\r\n filters={\"custom_serial_number\": [\"in\", all_values]},\r\n fields=[\"name\", \"custom_serial_number\"],\r\n limit_page_length=0\r\n )\r\n\r\n for asset in matched_assets:\r\n frappe.db.set_value(\r\n \"Asset\", asset.name, \"custom_recalled\", \"Yes\"\r\n )\r\n matched_rows.append({\r\n \"ncmdr_ref\": row.ncmdr_ref or \"N/A\",\r\n \"serial\": asset.custom_serial_number,\r\n \"asset\": asset.name\r\n })\r\n row_matched = True\r\n\r\n # Set Serial No Matching on the child row\r\n row.serial_no_matching = \"Yes\" if row_matched else \"No\"\r\n\r\n if not row_matched:\r\n unmatched_refs.append(row.ncmdr_ref or \"N/A\")\r\n\r\n # Persist serial_no_matching changes on child rows\r\n doc.save(ignore_permissions=True)\r\n\r\n # ------------------------------------------------------------------\r\n # NOTIFICATION 1 -> Matching list (serial no + asset id)\r\n # ------------------------------------------------------------------\r\n if matched_rows:\r\n rows_html = \"\".join(\r\n f\"
The following assets matched a recall in SFDA Entry {doc.name}\r\n and have been marked as recalled.
\r\n\r\n| NCMDR Ref | \r\nSerial / Catalog No | \r\nAsset ID | \r\n
|---|
\r\n \r\n Open SFDA Entry\r\n \r\n
\r\n \"\"\"\r\n\r\n for user in recall_users:\r\n frappe.get_doc({\r\n \"doctype\": \"Notification Log\",\r\n \"for_user\": user,\r\n \"type\": \"Alert\",\r\n \"subject\": subject,\r\n \"email_content\": message,\r\n \"document_type\": \"SFDA Entries\",\r\n \"document_name\": doc.name\r\n }).insert(ignore_permissions=True)\r\n\r\n if recall_users:\r\n frappe.sendmail(\r\n recipients=recall_users,\r\n subject=subject,\r\n message=message,\r\n reference_doctype=\"SFDA Entries\",\r\n reference_name=doc.name\r\n )\r\n\r\n frappe.log_error(\r\n title=f\"SFDA Recall Matched - {doc.name}\",\r\n message=(\r\n f\"SFDA Entry: {doc.name}\\n\"\r\n f\"Matched: {matched_rows}\\n\"\r\n f\"Recipients: {', '.join(recall_users)}\"\r\n )\r\n )\r\n\r\n # ------------------------------------------------------------------\r\n # NOTIFICATION 2 -> Non-matching list (NCMDR Ref values)\r\n # ------------------------------------------------------------------\r\n if unmatched_refs:\r\n # de-duplicate while preserving order\r\n seen = set()\r\n unique_refs = [\r\n r for r in unmatched_refs if not (r in seen or seen.add(r))\r\n ]\r\n\r\n refs_html = \"\".join(\r\n f\"No matching asset was found for the following recall references in\r\n SFDA Entry {doc.name}. Please review these manually.
\r\n\r\n\r\n \r\n Open SFDA Entry\r\n \r\n
\r\n \"\"\"\r\n\r\n for user in recall_users:\r\n frappe.get_doc({\r\n \"doctype\": \"Notification Log\",\r\n \"for_user\": user,\r\n \"type\": \"Alert\",\r\n \"subject\": subject,\r\n \"email_content\": message,\r\n \"document_type\": \"SFDA Entries\",\r\n \"document_name\": doc.name\r\n }).insert(ignore_permissions=True)\r\n\r\n if recall_users:\r\n frappe.sendmail(\r\n recipients=recall_users,\r\n subject=subject,\r\n message=message,\r\n reference_doctype=\"SFDA Entries\",\r\n reference_name=doc.name\r\n )\r\n\r\n frappe.log_error(\r\n title=f\"SFDA Manual Review Required - {doc.name}\",\r\n message=(\r\n f\"SFDA Entry: {doc.name}\\n\"\r\n f\"Unmatched Refs: {', '.join(unique_refs)}\\n\"\r\n f\"Recipients: {', '.join(recall_users)}\"\r\n )\r\n )\r\n\r\n # Mark entry as processed\r\n doc.passed = 1\r\n doc.passed_date = frappe.utils.now_datetime()\r\n doc.save(ignore_permissions=True)\r\n\r\nfrappe.db.commit()", "script_type": "Scheduler Event" } ]