[
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 1,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "Before Save",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2024-12-11 10:38:15.324777",
  "module": null,
  "name": "Auto-Populating Assigned Addresses",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Pre-Built Routes",
  "script": "# Ensure Area/Zone is selected\r\nif not doc.areazone:\r\n    frappe.throw(\"Please select an Area/Zone before saving the route.\")\r\n\r\n# Fetch addresses linked to the selected Area/Zone\r\naddresses = frappe.get_all(\r\n    \"Address\", \r\n    filters={\"area_zone\": doc.areazone},\r\n    fields=[\"name\", \"address_line1\", \"city\"]\r\n)\r\n\r\n# Get existing assigned addresses across all routes to avoid duplicates\r\nexisting_addresses = frappe.get_all(\r\n    \"Assigned Addresses\",\r\n    fields=[\"address\"],\r\n    distinct=True\r\n)\r\nexisting_address_names = [entry[\"address\"] for entry in existing_addresses]\r\n\r\n# Clear the current child table to avoid duplication on re-save\r\ndoc.assigned_addresses = []\r\n\r\n# Populate the child table\r\nfor address in addresses:\r\n    if address[\"name\"] not in existing_address_names:\r\n        doc.append(\"assigned_addresses\", {\r\n            \"address\": address[\"name\"],\r\n            \"address_line1\": address[\"address_line1\"],\r\n            \"city\": address[\"city\"]\r\n        })\r\n",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "After Save",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2024-12-11 12:59:24.278281",
  "module": null,
  "name": "Connecting Addresses To Route Numbers",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Pre-Built Routes",
  "script": "# Iterate over Assigned Addresses in the Pre-Built Route\r\nfor assigned in doc.assigned_addresses:\r\n    # Ensure the address_name field is not empty\r\n    if not assigned.address_name:\r\n        frappe.throw(f\"Address is missing for an entry in the Assigned Addresses table.\")\r\n    \r\n    # Fetch the corresponding Address record using address_name\r\n    address = frappe.get_doc(\"Address\", assigned.address_name)\r\n    \r\n    # Update the Service Route (Route Number) in Address\r\n    address.custom_service_route = doc.name  # Use `doc.name` for the final Route Number\r\n\r\n    # Sync the Confirmation Status from Assigned Address to Address\r\n    address.custom_confirmation_status = assigned.status\r\n\r\n    \r\n\r\n    # Save the updated Address record\r\n    address.save(ignore_permissions=True)\r\n    \r\n    # Update the Assigned Address child table with the latest Confirmation Status\r\n    assigned.status = address.custom_confirmation_status\r\n\r\n",
  "script_type": "DocType Event"
 },
 {
  "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": "All",
  "modified": "2024-12-12 13:57:00.619063",
  "module": "Programming Module",
  "name": "dig ticket dates",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Locate Log",
  "script": "\r\n\r\n\r\n# Helper function to calculate the legal start date (2 business days after the locate date)\r\ndef calculate_legal_start_date(locate_date):\r\n    # Add 2 business days to the locate date (exclude weekends)\r\n    current_date = getdate(locate_date)\r\n    while True:\r\n        current_date = add_days(current_date, 1)\r\n        if current_date.weekday() < 5:  # Weekdays are 0-4 (Monday to Friday)\r\n            if current_date.weekday() == 4:  # If it's Friday, skip to Monday\r\n                current_date = add_days(current_date, 2)\r\n            break\r\n    return current_date\r\n\r\n# Helper function to calculate the expiration date based on the state\r\ndef calculate_expiration_date(state, locate_date):\r\n    expiration_days = 45 if state == 'Washington' else 28\r\n    expiration_date = add_days(locate_date, expiration_days)\r\n    return expiration_date\r\n    \r\n  \r\n\r\n    # Calculate the legal start date and expiration date\r\n    legal_start_date = calculate_legal_start_date(locate_date)\r\n    expiration_date = calculate_expiration_date(state, locate_date)\r\n\r\n  ",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 1,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "Before Save",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2024-12-18 11:41:19.792616",
  "module": "ERPNext Integrations",
  "name": "Automate Job queue creation",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Sales Order",
  "script": "# Trigger: Before Save\r\nif doc.company == \"Lowe Fencing\":\r\n    # Check if a Fencing Job Queue entry already exists for this Sales Order\r\n    existing_job = frappe.db.exists(\"Fencing Job Queue\", {\"sales_order\": doc.name})\r\n    \r\n    if not existing_job:\r\n        # Create a new Fencing Job Queue entry\r\n        new_job = frappe.get_doc({\r\n            \"doctype\": \"Fencing Job Queue\",\r\n            \"sales_order\": doc.name,\r\n            \"customer\": doc.customer,\r\n            \"job_description\": doc.get(\"job_description\", \"No description provided\"),  # Modify if necessary\r\n            \"status\": \"Pending\"\r\n        })\r\n        new_job.insert()\r\n        frappe.msgprint(f\"Fencing Job Queue entry created for Sales Order {doc.name}.\")\r\n",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "After Insert",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-02-04 03:26:51.295414",
  "module": null,
  "name": "QB Export Generate Entries",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "QB Export",
  "script": "\naccounts = frappe.db.get_list('GL Entry',\n    fields=[\n        'account',\n        'sum(debit) as debits',\n        'sum(credit) as credits'\n    ],\n    group_by='account',\n    filters={\n        'company': doc.company,\n        'posting_date': ['between', [doc.start_date, doc.end_date]]\n    }\n)\n\nfor account in accounts:\n    doc.append('entries', dict(\n        doctype = \"QB Export Entry\",\n        account = account.account,\n        debits = account.debits,\n        credits = account.credits\n    ))\n    \ndoc.save()",
  "script_type": "DocType Event"
 },
 {
  "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": "All",
  "modified": "2025-03-05 06:21:11.448124",
  "module": null,
  "name": "Prevent duplicate customer",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Customer",
  "script": "if frappe.db.exists(\"Customer\", {\"customer_name\": doc.customer_name}):\r\n    frappe.throw(f\"Customer with the name '{doc.customer_name}' already exists\")\r\n",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": "create_deposit",
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "Before Insert",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-02-26 10:50:57.293076",
  "module": "Accounts",
  "name": "API - Create Deposit",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": null,
  "script": "bank_account_name = frappe.form_dict.bank\ncompany = frappe.form_dict.company\ndate = frappe.form_dict.date\nreference = frappe.form_dict.ref\npayment_entry_names = frappe.form_dict.payment_entries.split(',') if frappe.form_dict.payment_entries is not None else []\npayment_entries = [frappe.get_doc('Payment Entry', name) for name in payment_entry_names]\n\namount = sum([pe.paid_amount for pe in payment_entries])\n\naccounts = frappe.db.get_list(\"Account\", filters={ 'company': company }, fields=['name'])\nundeposited_funds_accounts = [a.name for a in accounts if a.name.startswith('Undeposited Funds -')]\nif len(undeposited_funds_accounts) == 0:\n    raise frappe.throw(f\"No Undeposited Funds account for {company}\")\n\nbank_account = frappe.get_doc(\"Bank Account\", bank_account_name)\nif not bank_account or not bank_account.account_name:\n    raise frappe.throw(f\"Can't find bank account {bank_account_name}\")\n\nif not bank_account.is_company_account:\n    raise frappe.throw(f\"Bank account {bank_account_name} is not a company account\")\n    \nif bank_account.company != company:\n    raise frappe.throw(f\"Bank account {bank_account_name} company is not {company}\")\n\ntransfer_entry = frappe.get_doc(dict(\n\tdoctype = 'Journal Entry',\n\tvoucher_type = \"Journal Entry\",\n\tcompany = company,\n\tposting_date = date,\n\tcheque_no=reference\n))\ntransfer_entry.append(\"accounts\", dict(\n    doctype = \"Journal Entry Account\",\n\taccount = bank_account.account,\n\tdebit_in_account_currency = amount\n))\ntransfer_entry.append(\"accounts\", dict(\n    doctype = \"Journal Entry Account\",\n\taccount = undeposited_funds_accounts[0],\n\tcredit_in_account_currency = amount\n))\ntransfer_entry.insert()\n#transfer_entry.submit()\n\n# TODO: for credit card payments, make an additional journal entry to transfer into the fee accounts, based on the fees in the payment entry\n\ntransaction = frappe.get_doc(dict(\n    doctype='Bank Transaction',\n    date=date,\n    status='Pending',\n    bank_account=bank_account_name,\n    company=company,\n    deposit=amount,\n    custom_journal_entry=transfer_entry.name,\n    reference_number=reference,\n    currency=bank_account.currency\n))\nfor pe in payment_entries:\n    transaction.append('payment_entries', dict(\n        payment_document='Payment Entry',\n        payment_entry=pe.name,\n        allocated_amount=pe.paid_amount\n    ))\ntransaction.insert()\n#transaction.submit()\n\nfrappe.response['message'] = 'Created Bank Transaction & Journal Entry'",
  "script_type": "API"
 },
 {
  "allow_guest": 1,
  "api_method": "quote-action",
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "Before Insert",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-05-10 02:56:30.607706",
  "module": null,
  "name": "Quote action from email",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": null,
  "script": "quote = frappe.get_doc(\"Quotation\", frappe.request.args.get('name'))\nif quote.status == 'Open' and frappe.request.args.get('response') is not None:\n    quote.flags.ignore_permissions = True\n    quote.custom_response = frappe.request.args.get('response')\n    quote.save()\n    frappe.db.commit()\n    frappe.response['success'] = True\nelse:\n    raise frappe.ValidationError",
  "script_type": "API"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "After Save",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-05-08 12:30:28.500389",
  "module": null,
  "name": "Distribute Foreman Time",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Timesheet",
  "script": "for entry in doc.time_logs:\n    # We only want to copy today's entries\n    if str(frappe.utils.now()).split(' ')[0] != str(entry.from_time).split(' ')[0]:\n        continue\n    \n    if entry.hours is not None and entry.hours > 0 and entry.project is not None:\n        employee = frappe.get_doc(\"Employee\", doc.employee)\n        if employee.custom_crew is not None and (employee.designation == \"Foreman\" or employee.designation == \"Field Manager\"): # Is crew lead and foreman the same?\n            crew_members = frappe.db.get_list(\"Employee\", filters={ 'custom_crew': employee.custom_crew })\n            for member in crew_members:\n                if member.name == doc.employee:\n                    continue\n                \n                new_entry = entry.as_dict()\n                del new_entry['name']\n                \n                # Find or create timesheet to add to\n                timesheet_to_add_to = None\n                existing_timesheets = frappe.db.get_list(\"Timesheet\", filters={ 'employee': member.name, 'status': 'Draft' }, fields=['name'])\n                if len(existing_timesheets) > 0:\n                    existing = frappe.get_doc(\"Timesheet\", existing_timesheets[0].name)\n                    \n                    # If there is already a matching entry (matching on start time), remove it so that we can insert fresh\n                    for sub_entry in existing.time_logs:\n                        if str(sub_entry.from_time) == str(entry.from_time):\n                            existing.remove(sub_entry)\n                            break\n                            \n                    existing.append('time_logs', new_entry)\n                    existing.save()\n                else:\n                    new_timesheet = frappe.get_doc({\n                        'doctype': 'Timesheet',\n                        'employee': member.name\n                    })\n                    new_timesheet.append('time_logs', new_entry)\n                    new_timesheet.insert()",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": "snw-foreman-clock-in-out",
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "Before Insert",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-05-10 06:45:34.404563",
  "module": "HR",
  "name": "SNW Foreman - Clock In/Out",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": null,
  "script": "employees = frappe.db.get_list(\"Employee\", filters={ 'user_id': frappe.session.user }, fields=['name'])\nif len(employees) == 0:\n    frappe.throw(f\"Could not find employee for current user: {frappe.session.user}\")\n\ntimesheets = frappe.db.get_list(\"Timesheet\", filters={ 'employee': employees[0].name, 'status': 'Draft' }, fields=['name'])\n\ntimesheet = None\nis_new = False\nif len(timesheets) > 0:\n    timesheet = frappe.get_doc('Timesheet', timesheets[0])\nelse:\n    timesheet = frappe.get_doc({\n        'doctype': 'Timesheet',\n        'employee': employees[0].name\n    })\n    is_new = True\n\nexisting_entry = None\nfor entry in timesheet.time_logs:\n    if entry.from_time is not None and entry.to_time is None:\n        existing_entry = entry\n        break\n    \nif frappe.request.method == \"GET\":\n    if existing_entry is None:\n        frappe.response['running'] = None\n    elif existing_entry.project is None:\n        frappe.response['running'] = 'driving'\n    else:\n        frappe.response['running'] = existing_entry.task\n    jobs = frappe.db.get_list('Task', filters={ 'custom_foreman': employees[0].name }, order_by='exp_start_date', fields=['name', 'project', 'exp_start_date', 'exp_end_date'])\n    for job in jobs:\n        job['installation_address'] = frappe.get_doc('Project', job.project).custom_installation_address\n    frappe.response['jobs'] = jobs\nelif frappe.request.method == \"POST\":\n    frappe.response['running'] = None\n    if existing_entry is not None:\n        existing_entry.to_time = frappe.utils.now()\n    \n    if existing_entry is None or existing_entry.project != frappe.form_dict.project:\n        timesheet.append('time_logs', {\n            'doctype': 'Timesheet Detail',\n            'from_time': frappe.utils.now(),\n            'activity_type': 'Driving' if frappe.form_dict.project is None else 'Landscaping',\n            'project': frappe.form_dict.project,\n            'task': frappe.form_dict.task,\n        })\n        frappe.response['running'] = 'driving' if frappe.form_dict.task is None else frappe.form_dict.task\n    \n    if is_new:\n        timesheet.insert()\n    else:\n        timesheet.save()",
  "script_type": "API"
 },
 {
  "allow_guest": 0,
  "api_method": "schedule_job",
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "Before Insert",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-05-14 10:02:59.043326",
  "module": "Projects",
  "name": "Schedule Project",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": null,
  "script": "task = frappe.get_doc('Task', frappe.form_dict.task)\nif frappe.form_dict.foreman == 'unassigned':\n    task.custom_foreman = None\nelse:\n    task.custom_foreman = frappe.form_dict.foreman\n\nif frappe.form_dict.type == \"move\": # Reschedule task\n    diff = 0 # By default, everything will take one day\n    if task.exp_start_date is not None and task.exp_end_date is not None:\n        diff = frappe.utils.date_diff(task.exp_end_date, task.exp_start_date)\n    task.exp_start_date = frappe.form_dict.date\n    task.exp_end_date = frappe.utils.add_to_date(frappe.form_dict.date, days=diff)\nelif frappe.form_dict.type == \"resize\": # Change task duration\n    if frappe.utils.date_diff(frappe.form_dict.date, task.exp_start_date) >= 0:\n        task.exp_end_date = frappe.form_dict.date\n    else: # If they try to move the end date before the start date, just set the end date to the start date\n        task.exp_end_date = task.exp_start_date\ntask.save()\n    ",
  "script_type": "API"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "After Save",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-04-18 10:37:51.312654",
  "module": null,
  "name": "Change sale order status to \"To Bill\" when project completed",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Project",
  "script": "if doc.status == \"Completed\":\n    sales_orders = frappe.db.get_list(\"Sales Order\", filters={ 'project': doc.name }, fields=['name'])\n    if len(sales_orders) > 0:\n        sale_order = frappe.get_doc(\"Sales Order\", sales_orders[0].name)\n        sale_order.custom_project_complete = True\n        sale_order.save()\n",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "After Save",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-05-07 09:23:35.688555",
  "module": null,
  "name": "Create or link customer to address",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Address",
  "script": "# If there is already a customer linked, update the customer & contact to match the details\n#  provided on the contact, if needed\n# This allows the front-office to update all the records using the address only\n# If the address is missing any info that the customer has, pull it onto the address\n# If this customer is referenced on another address, that address will also be updated\n#  due to the other server script on Customer that updates all linked addresses\nif doc.custom_customer_to_bill is not None and doc.custom_customer_to_bill != '':\n    customer = frappe.get_doc('Customer', doc.custom_customer_to_bill)\n    \n    if customer.customer_type == \"Individual\":\n        address_changed = False\n        customer_changed = False\n\n        # Customer email_id and mobile_no will be updated automatically by updating the contact\n        if customer.customer_primary_contact is not None:\n            contact_changed = False\n            contact = frappe.get_doc('Contact', customer.customer_primary_contact)\n            \n            if doc.custom_contact_name is not None and doc.custom_contact_name != '':\n                name_parts = doc.custom_contact_name.split(' ')\n                first_name = name_parts[0]\n                last_name = ' '.join(name_parts[1:]) if len(name_parts) > 1 else None\n                if contact.first_name != first_name or contact.last_name != last_name:\n                    contact.first_name = first_name\n                    contact.last_name = last_name\n                    contact_changed = True\n            \n            if contact.email_id != doc.email_id:\n                if doc.email_id is not None and doc.email_id != '':\n                    for email in contact.email_ids:\n                        if email.is_primary:\n                            email.email_id = doc.email_id\n                    contact_changed = True\n                    customer_changed = True\n                else:\n                    doc.email_id = contact.email_id\n                    address_changed = True\n                    \n            if contact.mobile_no != doc.phone:\n                if doc.phone is not None and doc.phone != '':\n                    for phone in contact.phone_nos:\n                        if phone.is_primary_mobile_no:\n                            phone.phone = doc.phone\n                    contact_changed = True\n                    customer.mobile_no = doc.phone\n                    customer_changed = True\n                else:\n                    doc.phone = contact.mobile_no\n                    address_changed = True\n                        \n            if contact_changed:\n                contact.save()\n                frappe.msgprint(\"Updated contact\")\n        \n        if customer.customer_name != doc.custom_contact_name:\n            if doc.custom_contact_name is not None and doc.custom_contact_name != '':\n                customer.customer_name = doc.custom_contact_name\n                customer.save()\n                frappe.rename_doc('Customer', customer.name, doc.custom_contact_name)\n                frappe.msgprint(\"Updated customer\")\n            elif customer.customer_name is not None and customer.customer_name != '':\n                doc.custom_contact_name = customer.customer_name\n                address_changed = True\n        elif customer_changed:\n            customer.save()\n            frappe.msgprint(\"Updated customer\")\n        \n        if address_changed:\n            doc.save()\n            frappe.msgprint(\"Pulled details from customer\")\n\nelse:\n    if (doc.custom_contact_name is not None and doc.custom_contact_name != '') and (doc.email_id is None or doc.email_id == ''):\n        raise frappe.throw(\"If you enter a Contact Name, you must also enter an Email Address\")\n        \n    # If there is not already a customer linked, try to find one to link\n    if doc.email_id is not None and doc.email_id != '':\n        potential_customers = frappe.db.get_list('Customer', filters={ 'email_id': doc.email_id }, fields=['name', 'email_id', 'customer_primary_contact'])\n        if len(potential_customers) > 0:\n            doc.custom_customer_to_bill = potential_customers[0].name;\n            doc.save()\n            # phone and name will be pulled from customer/contact in next pass\n        else:\n            # Ok, we'll create a new customer since we couldn't find one\n            # See if we can use an existing contact, or if we should create a new contact as well\n            potential_contacts = frappe.get_all('Contact Email', filters={'email_id': doc.email_id}, fields=['parent'])\n            contact = None\n            if len(potential_contacts) > 0:\n                contact = frappe.get_doc('Contact', potential_contacts[0].parent)\n            \n            new_customer = frappe.get_doc({\n                'doctype': 'Customer',\n                'customer_name': doc.custom_contact_name if doc.custom_contact_name is not None else doc.address_title,\n                # Customer should auto-create it's primary contact, if we don't link an existing one\n                'customer_primary_contact': contact.name if contact is not None else None,\n                'customer_type': 'Individual',\n                'customer_primary_address': doc.name,\n                'email_id': doc.email_id,\n                'mobile_no': doc.phone\n            })\n            new_customer.insert()\n            \n            doc.custom_customer_to_bill = new_customer.name\n            doc.save()",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "After Submit",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-04-25 03:36:27.460012",
  "module": null,
  "name": "Sales Order Status Linked to Payment Entry",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Payment Entry",
  "script": "# Check if this is a Customer payment against a Sales Order\r\nif doc.party_type == \"Customer\" and doc.references:\r\n    for ref in doc.references:\r\n        if ref.reference_doctype == \"Sales Order\" and ref.reference_name:\r\n            sales_order = frappe.get_doc(\"Sales Order\", ref.reference_name)\r\n\r\n            # Only act if Sales Order is currently On Hold and requires half-down\r\n            if sales_order.status == \"On Hold\" and sales_order.custom_requires_halfdown:\r\n                sales_order.db_set(\"status\", \"To Deliver\")\r\n                \r\n",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "After Submit",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-04-25 03:41:36.948328",
  "module": null,
  "name": "Sales Order Half Down Required Script",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Sales Order",
  "script": "# Set to On Hold if requires_half_down is checked\r\nif doc.custom_requires_halfdown:\r\n    doc.status = \"On Hold\"\r\n    frappe.db.set_value(\"Sales Order\", doc.name, \"status\", \"On Hold\")\r\n    \r\n",
  "script_type": "DocType Event"
 },
 {
  "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": "All",
  "modified": "2025-05-02 10:44:23.201852",
  "module": null,
  "name": "Project Creation from Sales Order",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Project",
  "script": "if doc.sales_order:\r\n    sales_order = frappe.get_doc(\"Sales Order\", doc.sales_order)\r\n    if sales_order.get(\"custom_installation_address\"):\r\n        doc.custom_installation_address = sales_order.custom_installation_address\r\n        doc.project_name = sales_order.custom_installation_address\r\n",
  "script_type": "DocType Event"
 },
 {
  "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": "All",
  "modified": "2025-05-02 10:45:31.652205",
  "module": null,
  "name": "Sales Order Installation Address from Quotation",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Sales Order",
  "script": "if doc.quotation:\r\n    quotation = frappe.get_doc(\"Quotation\", doc.quotation)\r\n    if quotation.get(\"custom_installation_address\"):\r\n        doc.custom_installation_address = quotation.custom_installation_address\r\n",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "After Save",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-05-07 09:03:15.200325",
  "module": null,
  "name": "Update address contact info",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Customer",
  "script": "if doc.customer_type == \"Individual\":\n    addresses = frappe.db.get_list('Address', filters={ 'custom_customer_to_bill': doc.name }, fields=['name'])\n    for address_item in addresses:\n        address = frappe.get_doc('Address', address_item.name)\n        address_changed = False\n        if address.custom_contact_name != doc.customer_name:\n            address.custom_contact_name = doc.customer_name\n            address_changed = True\n            \n        if address.phone != doc.mobile_no:\n            address.phone = doc.mobile_no\n            address_changed = True\n            \n        if address.email_id != doc.email_id:\n            address.email_id = doc.email_id\n            address_changed = True\n        \n        if address_changed:\n            address.save()",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "After Save",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-05-30 09:25:42.446239",
  "module": null,
  "name": "Quotation - mark as sent",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Email Queue",
  "script": "if doc.status == \"Sent\" and doc.communication is not None:\n    comm = frappe.get_doc(\"Communication\", doc.communication)\n    if comm.reference_doctype == \"Quotation\":\n        quote = frappe.get_doc(\"Quotation\", comm.reference_name)\n        quote.custom_sent = True\n        quote.save()",
  "script_type": "DocType Event"
 },
 {
  "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": "All",
  "modified": "2025-05-10 05:08:17.832989",
  "module": null,
  "name": "Unset task expected dates",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Task",
  "script": "doc.exp_start_date = None\ndoc.exp_end_date = None",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "After Save",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-05-15 04:08:51.615310",
  "module": null,
  "name": "Create project close-out tasks when work is complete",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Task",
  "script": "if doc.type == 'Labor' and (doc.status == 'Completed' or doc.status == 'Cancelled'):\n    pending_labor_tasks_count = frappe.db.count('Task', { 'project': doc.project, 'type': 'Labor', 'status': ['not in', ['Completed', 'Cancelled']] })\n    if pending_labor_tasks_count == 0:\n        # If all labor tasks are complete, add the QA and permit close out tasks to the project\n        qa_task_template = frappe.get_doc('Task', 'TASK-2025-00007')\n        permit_close_task_template = frappe.get_doc('Task', 'TASK-2025-00008')\n        \n        qa_task_template.name = None\n        permit_close_task_template.name = None\n        \n        qa_task_template.is_template = False\n        permit_close_task_template.is_template = False\n        \n        qa_task_template.project = doc.project\n        permit_close_task_template.project = doc.project\n        \n        qa_task_template.status = \"Open\"\n        permit_close_task_template.status = \"Open\"\n        \n        qa_task_template.insert()\n        permit_close_task_template.insert()",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "Before Save",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-05-15 08:30:36.340161",
  "module": null,
  "name": "Lock - New Invoice",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Sales Invoice",
  "script": "current_month = '-'.join(frappe.utils.today().split('-')[:2])\npost_month = '-'.join(doc.posting_date.split('-')[:2])\nif current_month != post_month:\n    frappe.throw(\"Cannot post an invoice to a previous month\")",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "Before Cancel",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-05-15 08:32:11.919798",
  "module": null,
  "name": "Lock - Cancel Invoice",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Sales Invoice",
  "script": "current_month = '-'.join(frappe.utils.today().split('-')[:2])\npost_month = '-'.join(str(doc.posting_date).split('-')[:2])\nif current_month != post_month:\n    frappe.throw(\"Cannot cancel an invoice in a previous month\")",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "Before Save",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-05-15 08:44:01.489721",
  "module": null,
  "name": "Lock past month GL Entries",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "GL Entry",
  "script": "current_month = '-'.join(frappe.utils.today().split('-')[:2])\npost_month = '-'.join(doc.posting_date.split('-')[:2])\nif current_month != post_month:\n    frappe.throw(\"Cannot modify accounting entries in a previous month\")",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "Before Save",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-05-15 12:21:02.649125",
  "module": null,
  "name": "Add coordinates to address",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Address",
  "script": "\nif doc.address_line1 is not None and doc.city is not None and doc.country is not None:\n    \n    address_components = [\n        doc.address_line1,\n        doc.address_line2 or '',\n        doc.city,\n        doc.state or '',\n        doc.pincode or '',\n        doc.country\n    ]\n    \n    custom_address_for_coordinates = ', '.join([cmp for cmp in address_components if cmp != ''])\n    \n    # Store custom_address_for_coordinates on doc so that we only re-compute the coordinates if the address changes\n    if custom_address_for_coordinates != doc.custom_address_for_coordinates:\n\n        api_key = 'AIzaSyCd3ALZe6wjt3xnc7X_rRItfKAEJugfuZ4';\n        safe_chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~\"\n        encoded_address = ''\n        for c in custom_address_for_coordinates:\n            if c in safe_chars:\n                encoded_address = encoded_address + c\n            else:\n                encoded_address = encoded_address + '%%%02X' % ord(c)\n        geocode_url = f\"https://maps.googleapis.com/maps/api/geocode/json?address={encoded_address}&key={api_key}\"\n    \n        data = frappe.make_get_request(geocode_url)\n        if data['status'] == 'OK' and data['results'][0] is not None:\n            location = data['results'][0]['geometry']['location']\n            doc.custom_latitude = location['lat']\n            doc.custom_longitude = location['lng']\n            address_components = data['results'][0]['address_components'];\n            for comp in address_components:\n                if 'postal_code' in comp['types']:\n                    doc.pincode = comp['long_name']\n                    break\n        \n        doc.custom_address_for_coordinates = custom_address_for_coordinates",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": null,
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "Before Save",
  "enable_rate_limit": 0,
  "event_frequency": "All",
  "modified": "2025-08-29 17:23:47.107509",
  "module": null,
  "name": "Populate Completed Date",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": "Project",
  "script": "if \"Completed\" in doc.status and doc.custom_completion_date == None:\n    doc.custom_completion_date = frappe.utils.today()\n    if \"SNW Install\" in doc.project_template and doc.custom_warranty_expiration_date == None:\n        doc.custom_warranty_expiration_date = frappe.utils.add_to_date(frappe.utils.today(), days=doc.custom_warranty_duration_days)",
  "script_type": "DocType Event"
 },
 {
  "allow_guest": 0,
  "api_method": "test-followup",
  "cron_format": null,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Server Script",
  "doctype_event": "Before Insert",
  "enable_rate_limit": 0,
  "event_frequency": "Daily",
  "modified": "2025-04-18 06:47:24.351220",
  "module": null,
  "name": "Quotation - set follow-up needed",
  "rate_limit_count": 5,
  "rate_limit_seconds": 86400,
  "reference_doctype": null,
  "script": "quotes = frappe.db.get_list('Quotation', filters={ 'status': 'Open' }, fields=['name'])\n\nfor quote in quotes:\n    communications = frappe.db.get_list(\n        \"Communication\",\n        filters={\n            \"reference_doctype\": \"Quotation\",\n            \"reference_name\": quote.name\n        },\n        fields=[\"communication_date\"]\n    )\n    if len(communications) > 0:\n        latest_date = communications[0].communication_date\n        for com in communications:\n            if com.communication_date > latest_date:\n                latest_date = com.communication_date\n        full_quote = frappe.get_doc('Quotation', quote.name)\n        full_quote.db_set('custom_followup_needed', frappe.utils.date_diff(frappe.utils.getdate(), latest_date) >= 5)\n        frappe.db.commit()",
  "script_type": "Scheduler Event"
 }
]