[
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Opportunity",
  "enabled": 1,
  "modified": "2024-06-28 04:58:27.527685",
  "module": null,
  "name": "Set Item Price",
  "script": "frappe.ui.form.on('Opportunity Item', {\r\n    item_code: function(frm, cdt, cdn) {\r\n        var row = locals[cdt][cdn];\r\n        if (row.item_code) {\r\n            frappe.call({\r\n                method: 'frappe.client.get_value',\r\n                args: {\r\n                    'doctype': 'Item Price',\r\n                    'filters': {\r\n                        'item_code': row.item_code,\r\n                        'selling': 1\r\n                    },\r\n                    'fieldname': ['price_list_rate']\r\n                },\r\n                callback: function(r) {\r\n                    if (r.message) {\r\n                        frappe.model.set_value(cdt, cdn, 'rate', r.message.price_list_rate);\r\n                        frappe.model.set_value(cdt, cdn, 'amount', row.qty * r.message.price_list_rate);\r\n                    }\r\n                }\r\n            });\r\n        }\r\n    },\r\n    qty: function(frm, cdt, cdn) {\r\n        var row = locals[cdt][cdn];\r\n        if (row.qty && row.rate) {\r\n            frappe.model.set_value(cdt, cdn, 'amount', row.qty * row.rate);\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "SNW Jobs",
  "enabled": 1,
  "modified": "2024-07-18 02:56:08.345504",
  "module": null,
  "name": "SNW Jobs - auto populate days",
  "script": "frappe.ui.form.on('SNW Jobs', {\n    refresh(frm) {\n        // your code here (if needed)\n    },\n    start_date(frm) {\n        calculate_total_days(frm);\n    },\n    end_date(frm) {\n        calculate_total_days(frm);\n    }\n});\n\nfunction calculate_total_days(frm) {\n    if (frm.doc.start_date && frm.doc.end_date) {\n        const startDate = new Date(frm.doc.start_date);\n        const endDate = new Date(frm.doc.end_date);\n        const timeDiff = endDate - startDate;\n        const dayDiff = timeDiff / (1000 * 3600 * 24);\n        frm.set_value('number_of_days', dayDiff);\n    }\n}",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "SNW Jobs",
  "enabled": 1,
  "modified": "2024-07-18 03:04:59.760299",
  "module": null,
  "name": "SNW Jobs - Calculate Balance",
  "script": "frappe.ui.form.on('SNW Jobs', {\r\n    refresh(frm) {\r\n        // Your code here (if needed)\r\n    },\r\n    total_expected_price(frm) {\r\n        calculate_balance(frm);\r\n    },\r\n    paid(frm) {\r\n        calculate_balance(frm);\r\n    }\r\n});\r\n\r\nfunction calculate_balance(frm) {\r\n    if (frm.doc.total_expected_price != null && frm.doc.paid != null) {\r\n        const balance = frm.doc.total_expected_price - frm.doc.paid;\r\n        frm.set_value('balance', balance);\r\n    }\r\n}\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "SNW Jobs",
  "enabled": 1,
  "modified": "2024-07-18 03:12:21.825901",
  "module": null,
  "name": "SNW Jobs - pull crew lead employees",
  "script": "frappe.ui.form.on('SNW Jobs', {\n\trefresh(frm) {\n\t\t// your code here\n\t},\n\t onload(frm) {\n        frm.set_query('crew_leader', function() {\n            return {\n                filters: {\n                    designation: 'Crew Lead'\n                }\n            };\n        });\n    }\n})",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Follow Up Checklist",
  "enabled": 1,
  "modified": "2024-09-16 05:58:34.623817",
  "module": null,
  "name": "auto populate follow up form",
  "script": "frappe.ui.form.on('Follow Up Checklist', {\n    customer:function(frm) {\n\t\t // Fetch customer details like phone and email\n        frappe.db.get_value(\"Address\", {\"customer\": frm.doc.customer, \"customer_primary_address\": 1}, \"address_line1\", function(r) {\n            if(r && r.address_line1) {\n                frm.set_value(\"address\", r.address_line1);  // Set the address field\n            }\n        });\n    }\n});",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Quotation",
  "enabled": 1,
  "modified": "2025-04-24 05:36:48.042696",
  "module": null,
  "name": "Item Markup Script",
  "script": "frappe.ui.form.on('Quotation Item', {\r\n    item_code: function(frm, cdt, cdn) {\r\n        let row = locals[cdt][cdn];\r\n        if (row.item_code) {\r\n            // Fetch the item price from the Item Price List\r\n            frappe.call({\r\n                method: 'frappe.client.get_list',\r\n                args: {\r\n                    doctype: 'Item Price',\r\n                    filters: {\r\n                        item_code: row.item_code,\r\n                        price_list: frm.doc.selling_price_list  // Assuming the price list is set in the Quotation\r\n                    },\r\n                    fields: ['price_list_rate']\r\n                },\r\n                callback: function(response) {\r\n                    if (response.message && response.message.length > 0) {\r\n                        // Get the price from the Item Price List\r\n                        let base_rate = response.message[0].price_list_rate || 0;\r\n\r\n                        // Fetch the markup percentage from the Item master\r\n                        frappe.call({\r\n                            method: 'frappe.client.get',\r\n                            args: {\r\n                                doctype: 'Item',\r\n                                name: row.item_code\r\n                            },\r\n                            callback: function(r) {\r\n                                if (r.message) {\r\n                                    // Fetch the markup percentage from the Item master\r\n                                    let markup = r.message.custom_markup_percentage || 0;  // Default to 0% if not set\r\n                                    \r\n                                    // Calculate the new rate with markup\r\n                                    let new_rate = base_rate + (base_rate * (markup / 100));\r\n                                    frappe.model.set_value(cdt, cdn, 'rate', new_rate);\r\n                                    \r\n                                    // Refresh the items table to show the updated rate\r\n                                    frm.refresh_field('items');\r\n                                }\r\n                            }\r\n                        });\r\n                    }\r\n                }\r\n            });\r\n        }\r\n    }\r\n});\r\n\r\n// Optional: Recalculate all items on form refresh or load\r\nfrappe.ui.form.on('Quotation', {\r\n    refresh: function(frm) {\r\n        frm.doc.items.forEach(function(item) {\r\n            //frappe.model.trigger('item_code', item.name);\r\n        });\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Quotation",
  "enabled": 1,
  "modified": "2025-01-08 05:04:26.743210",
  "module": null,
  "name": "Quotation - Set Same Valid Until Date",
  "script": "frappe.ui.form.on(\"Quotation\", {\n    onload: function(frm) {\n        frm.set_value('valid_till', '2025-12-31');\n    }\n});",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Note",
  "enabled": 1,
  "modified": "2024-10-24 23:43:27.548340",
  "module": null,
  "name": "Open Note in Edit Mode",
  "script": "frappe.ui.form.on('Note', {\r\n    onload_post_render: function(frm) {\r\n        // Check if this is a new document or in read mode, then switch to edit mode\r\n        if (frm.is_new() || frm.doc.__unsaved) {\r\n            frm.page.set_primary_action(__('Save'), () => frm.save());\r\n            frm.page.wrapper.find('.btn-primary').removeClass('hidden');\r\n            frm.enable_save();\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Address",
  "enabled": 1,
  "modified": "2024-12-05 11:55:14.179233",
  "module": null,
  "name": "Address Doctype Linked City Auto Populate",
  "script": "frappe.ui.form.on('Address', {\r\n    custom_linked_city: function (frm) {\r\n        if (frm.doc.custom_linked_city) {\r\n            frappe.db.get_doc('City', frm.doc.custom_linked_city).then(city_doc => {\r\n                frm.set_value('city', city_doc.city_name); // Sync to mandatory City field\r\n                frm.set_value('state', city_doc.state);    // Populate State\r\n                if (city_doc.zip_code) {\r\n                    frm.set_value('zip_code', city_doc.zip_code); // Populate Zip Code\r\n                }\r\n            });\r\n        }\r\n    },\r\n    city: function (frm) {\r\n        // Optionally, sync back to custom_linked_city when the mandatory City field is used\r\n        if (!frm.doc.custom_linked_city && frm.doc.city) {\r\n            frappe.msgprint(__('Consider selecting a linked city for better accuracy.'));\r\n        }\r\n    }\r\n});",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Address",
  "enabled": 1,
  "modified": "2024-12-06 05:15:31.487671",
  "module": null,
  "name": "Address - Irrigation District Visibility Condition",
  "script": "frappe.ui.form.on('Address', {\r\n    refresh: function(frm) {\r\n        let show_irrigation_district = false;\r\n\r\n        // Check if 'is_shipping_address' is checked\r\n        if (frm.doc.is_shipping_address) {\r\n            // Required companies list\r\n            const required_companies = ['Sprinklers Northwest', 'Nuco Yard Care'];\r\n\r\n            // Check child table rows\r\n            if (frm.doc.custom_linked_companies) {\r\n                show_irrigation_district = frm.doc.custom_linked_companies.some(row =>\r\n                    required_companies.includes(row.company)\r\n                );\r\n            }\r\n        }\r\n\r\n        // Show or hide the custom_irrigation_district field\r\n        frm.set_df_property('custom_irrigation_district', 'hidden', !show_irrigation_district);\r\n    },\r\n\r\n    is_service_address: function(frm) {\r\n        // Re-run visibility logic when 'is_service_address' changes\r\n        frm.trigger('refresh');\r\n    },\r\n\r\n    custom_linked_companies: function(frm) {\r\n        // Re-run visibility logic when the child table is updated\r\n        frm.trigger('refresh');\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Address",
  "enabled": 1,
  "modified": "2024-12-06 05:27:19.840296",
  "module": null,
  "name": "Address - Installed By Snw Visibility Condition",
  "script": "frappe.ui.form.on('Address', {\r\n    refresh: function(frm) {\r\n        frm.trigger('toggle_irrigation_fields');\r\n    },\r\n\r\n    custom_installed_by_sprinklers_nw: function(frm) {\r\n        frm.trigger('toggle_irrigation_fields');\r\n    },\r\n\r\n    toggle_irrigation_fields: function(frm) {\r\n        const is_installed = frm.doc.custom_installed_by_sprinklers_nw;\r\n\r\n        // Toggle visibility for irrigation-related fields\r\n        frm.set_df_property('custom_install_month', 'hidden', !is_installed);\r\n        frm.set_df_property('custom_install_year', 'hidden', !is_installed);\r\n        frm.set_df_property('custom_backflow_test_report', 'hidden', !is_installed);\r\n        frm.set_df_property('custom_photo_attachment', 'hidden', !is_installed);\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Address",
  "enabled": 0,
  "modified": "2024-12-13 07:14:26.487456",
  "module": null,
  "name": "Address - Google Map Display",
  "script": "frappe.ui.form.on('Address', {\r\n    refresh: function(frm) {\r\n        // Render Google Map if address fields are available and saved\r\n        if (!frm.is_new() && frm.doc.address_line1 && frm.doc.city && frm.doc.country) {\r\n            frm.trigger('render_google_map');\r\n        }\r\n    },\r\n\r\n    render_google_map: function(frm) {\r\n        // Construct the full address string\r\n        const address = [\r\n            frm.doc.address_line1,\r\n            frm.doc.address_line2 || '', // Optional\r\n            frm.doc.city,\r\n            frm.doc.state || '', // Optional\r\n            frm.doc.pincode || '', // Optional\r\n            frm.doc.country\r\n        ].filter(Boolean).join(', '); // Remove empty fields\r\n\r\n        // Replace with your Google Maps API Key\r\n        const apiKey = 'AIzaSyB2uNXSQpMp-lGJHIWFpzloWxs76zjkU8Y';\r\n\r\n        // Generate the embed URL\r\n        const mapUrl = `https://www.google.com/maps/embed/v1/place?key=${apiKey}&q=${encodeURIComponent(address)}`;\r\n\r\n        // Render the iframe in the HTML field\r\n        frm.fields_dict.custom_google_map.$wrapper.html(`\r\n            <iframe \r\n                width=\"100%\" \r\n                height=\"400\" \r\n                frameborder=\"0\" \r\n                style=\"border:0\" \r\n                src=\"${mapUrl}\" \r\n                allowfullscreen>\r\n            </iframe>\r\n        `);\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Address",
  "enabled": 0,
  "modified": "2025-05-15 09:00:39.086280",
  "module": null,
  "name": "Address - Auto-Filling Mutiple Contact Email and Phone",
  "script": "frappe.ui.form.on('Address Contact Role', {\r\n    contact: function(frm, cdt, cdn) {\r\n        let row = frappe.get_doc(cdt, cdn);\r\n\r\n        if (row.contact) {\r\n            // Fetch email and phone from the selected Contact\r\n            frappe.db.get_doc('Contact', row.contact).then(contact => {\r\n                if (contact) {\r\n                    // Pull primary email and phone from the Contact\r\n                    row.email = contact.email_id || '';\r\n                    row.phone = contact.phone || contact.mobile_no || ''; // Prefer phone, fallback to mobile_no\r\n                    frm.refresh_field('custom_linked_contacts');\r\n                }\r\n            });\r\n        } else {\r\n            // Clear fields if no Contact selected\r\n            row.email = '';\r\n            row.phone = '';\r\n            frm.refresh_field('custom_linked_contacts');\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Locate Log",
  "enabled": 1,
  "modified": "2024-12-11 12:11:35.789512",
  "module": null,
  "name": "Locate Button",
  "script": "frappe.ui.form.on('Locate Log', {\r\n    refresh: function(frm) {\r\n        // Add the custom button \"Mark as Located\"\r\n        if (frm.doc.status !== 'Completed') {\r\n            frm.add_custom_button(__('Mark as Located'), function() {\r\n                frm.trigger('mark_as_located');\r\n            });\r\n        }\r\n    },\r\n    mark_as_located: function(frm) {\r\n        // Check if Dig Ticket # is provided\r\n        if (!frm.doc.dig_ticket_number) {\r\n            frappe.msgprint(__('Please enter a Dig Ticket #.'));\r\n            return;\r\n        }\r\n\r\n        // Trigger the backend method to update the Job Queue and Locate Log\r\n        frappe.call({\r\n            method: 'my_app.my_module.api.update_locate_log_and_job_queue',\r\n            args: {\r\n                locate_log: frm.doc.name,\r\n                dig_ticket_number: frm.doc.dig_ticket_number\r\n            },\r\n            callback: function(r) {\r\n                if (r.message) {\r\n                    frappe.msgprint(__('Locate marked as completed.'));\r\n                    frm.reload_doc(); // Reload the form to reflect changes\r\n                }\r\n            }\r\n        });\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Pre-Built Routes",
  "enabled": 0,
  "modified": "2024-12-11 13:24:02.894613",
  "module": null,
  "name": "Real-Time Updates for Address -Route Connection",
  "script": "frappe.ui.form.on('Assigned Address', {\r\n    address_name: function (frm, cdt, cdn) {\r\n        const row = locals[cdt][cdn];\r\n\r\n        // Ensure the address_name field is set\r\n        if (row.address_name) {\r\n            // Fetch the custom_confirmation_status from the Address Doctype\r\n            frappe.db.get_value('Address', row.address_name, 'custom_confirmation_status', (value) => {\r\n                if (value && value.custom_confirmation_status) {\r\n                    // Set the status in the child table to match the Address confirmation status\r\n                    frappe.model.set_value(cdt, cdn, 'status', value.custom_confirmation_status);\r\n                } else {\r\n                    // Default to No Response if Address confirmation status is not set\r\n                    frappe.model.set_value(cdt, cdn, 'status', 'No Response');\r\n                }\r\n            });\r\n        } else {\r\n            // If no address_name is set, default status to No Response\r\n            frappe.model.set_value(cdt, cdn, 'status', 'No Response');\r\n        }\r\n    }\r\n});\r\n\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Address",
  "enabled": 0,
  "modified": "2024-12-11 13:09:36.908372",
  "module": null,
  "name": "Real Time Updates for Address Doctype Connection to Pre-Built",
  "script": "frappe.ui.form.on('Address', {\r\n    custom_confirmation_status: function (frm) {\r\n        // Check if the Service Route is set\r\n        if (!frm.doc.custom_service_route) {\r\n            frappe.msgprint(__('Service Route is not assigned for this address.'));\r\n            return;\r\n        }\r\n\r\n        // Retrieve the Assigned Address row where this address is linked\r\n        frappe.call({\r\n            method: 'frappe.client.get_value',\r\n            args: {\r\n                doctype: 'Assigned Address',\r\n                filters: { address_name: frm.doc.name },\r\n                fieldname: 'name'\r\n            },\r\n            callback: function (response) {\r\n                if (response.message) {\r\n                    const assigned_row_name = response.message.name;\r\n                    // Update the Status in the Assigned Address row\r\n                    frappe.call({\r\n                        method: 'frappe.client.set_value',\r\n                        args: {\r\n                            doctype: 'Assigned Address',\r\n                            name: assigned_row_name,\r\n                            fieldname: 'status',\r\n                            value: frm.doc.custom_confirmation_status\r\n                        },\r\n                        callback: function (response) {\r\n                            if (!response.exc) {\r\n                                frappe.msgprint(__('Status updated in Pre-Built Route.'));\r\n                            }\r\n                        }\r\n                    });\r\n                } else {\r\n                    frappe.msgprint(__('This address is not linked to any Pre-Built Route.'));\r\n                }\r\n            }\r\n        });\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Pre-Built Routes",
  "enabled": 1,
  "modified": "2024-12-13 09:14:52.635425",
  "module": null,
  "name": "Color Code Route Child Table",
  "script": "frappe.ui.form.on('Pre-Built Routes', {\r\n    refresh: function (frm) {\r\n        // Loop through the rows in the Assigned Addresses table\r\n        frm.fields_dict['assigned_addresses'].grid.wrapper.find('.grid-row').each(function () {\r\n            const row = $(this);\r\n            const doc = row.data('doc'); // Get the child table row data\r\n\r\n            if (doc && doc.status) {\r\n                // Apply color based on the Status field\r\n                switch (doc.status) {\r\n                    case 'Confirmed':\r\n                        row.css('background-color', '#D4EDDA'); // Green for Scheduled\r\n                        break;\r\n                    case 'Reschedule':\r\n                        row.css('background-color', '#FFF3CD'); // Yellow for Completed\r\n                        break;\r\n                    case 'Declined':\r\n                        row.css('background-color', '#F8D7DA'); // Red for Pending Reschedule\r\n                        break;\r\n                    default:\r\n                        row.css('background-color', '#E2E0E0',); // Reset to default if no match\r\n                        break;\r\n                }\r\n            }\r\n        });\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Pre-Built Routes",
  "enabled": 1,
  "modified": "2024-12-11 13:25:17.161414",
  "module": null,
  "name": "Dynamically Update Confirmation Status on Pre-Built Route",
  "script": "frappe.ui.form.on('Assigned Address', {\r\n    address_name: function (frm, cdt, cdn) {\r\n        const row = locals[cdt][cdn];\r\n\r\n        // Ensure the address_name field is set\r\n        if (row.address_name) {\r\n            // Fetch the custom_confirmation_status from the Address Doctype\r\n            frappe.db.get_value('Address', row.address_name, 'custom_confirmation_status', (value) => {\r\n                if (value && value.custom_confirmation_status) {\r\n                    // Set the status in the child table to match the Address confirmation status\r\n                    frappe.model.set_value(cdt, cdn, 'status', value.custom_confirmation_status);\r\n                } else {\r\n                    // Default to No Response if Address confirmation status is not set\r\n                    frappe.model.set_value(cdt, cdn, 'status', 'No Response');\r\n                }\r\n            });\r\n        } else {\r\n            // If no address_name is set, default status to No Response\r\n            frappe.model.set_value(cdt, cdn, 'status', 'No Response');\r\n        }\r\n    }\r\n});",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Pre-Built Routes",
  "enabled": 0,
  "modified": "2024-12-13 09:03:06.173211",
  "module": null,
  "name": "Route Map",
  "script": "frappe.ui.form.on('Pre-Built Routes', {\r\n    refresh: function (frm) {\r\n        // Check if Google Maps API is loaded\r\n        if (!window.google || !window.google.maps) {\r\n            frappe.msgprint(__('Google Maps API is not loaded. Attempting to load...'));\r\n\r\n            // Dynamically load the Google Maps API\r\n            const script = document.createElement('script');\r\n            script.src = `https://maps.googleapis.com/maps/api/js?key=AIzaSyB2uNXSQpMp-lGJHIWFpzloWxs76zjkU8Y`;\r\n            script.async = true;\r\n            script.defer = true;\r\n\r\n            // Call renderMap after the API is loaded\r\n            script.onload = function () {\r\n                console.log(\"Google Maps API loaded successfully.\");\r\n                renderMap(frm); // Render the map after API loads\r\n            };\r\n\r\n            // Handle API load failure\r\n            script.onerror = function () {\r\n                frappe.msgprint(__('Failed to load Google Maps API. Please check your API key.'));\r\n            };\r\n\r\n            document.head.appendChild(script);\r\n        } else {\r\n            // If API is already loaded, render the map immediately\r\n            renderMap(frm);\r\n        }\r\n    }\r\n});\r\n\r\nfunction renderMap(frm) {\r\n    // Ensure the form has assigned addresses\r\n    const addresses = frm.doc.assigned_addresses || [];\r\n    if (!addresses.length) {\r\n        frappe.msgprint(__('No addresses found for this route.'));\r\n        return;\r\n    }\r\n\r\n    // Prepare points for the map\r\n    const points = addresses.map((row) => ({\r\n        lat: parseFloat(row.latitude || 0),\r\n        lng: parseFloat(row.longitude || 0),\r\n        status: row.status || 'No Response',\r\n        title: row.address_name || 'Unknown Address'\r\n    }));\r\n\r\n    console.log(\"Map points:\", points); // Debug the points array\r\n\r\n    // Define marker colors based on status\r\n    const statusColors = {\r\n        'Confirmed': 'green',\r\n        'Reschedule': 'yellow',\r\n        'Decline': 'red',\r\n        'No Response': 'white'\r\n    };\r\n\r\n    // Initialize map container\r\n    const mapContainer = frm.fields_dict['route_map'].wrapper;\r\n    mapContainer.innerHTML = '<div id=\"route-map\" style=\"height: 400px;\"></div>';\r\n    const map = new google.maps.Map(document.getElementById('route-map'), {\r\n        zoom: 10,\r\n        mapTypeId: 'roadmap'\r\n    });\r\n\r\n    // Fit map bounds to all points\r\n    const bounds = new google.maps.LatLngBounds();\r\n\r\n    // Add markers for each point\r\n    points.forEach((point) => {\r\n        if (point.lat && point.lng) {\r\n            const marker = new google.maps.Marker({\r\n                position: { lat: point.lat, lng: point.lng },\r\n                map: map,\r\n                title: point.title,\r\n                icon: {\r\n                    path: google.maps.SymbolPath.CIRCLE,\r\n                    scale: 8,\r\n                    fillColor: statusColors[point.status],\r\n                    fillOpacity: 1,\r\n                    strokeWeight: 0\r\n                }\r\n            });\r\n            bounds.extend(marker.getPosition());\r\n        }\r\n    });\r\n\r\n    // Adjust map view to fit all markers\r\n    if (!bounds.isEmpty()) {\r\n        map.fitBounds(bounds);\r\n    } else {\r\n        frappe.msgprint(__('No valid points to display on the map.'));\r\n    }\r\n}\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Address",
  "enabled": 1,
  "modified": "2025-05-15 12:34:32.006687",
  "module": null,
  "name": "Test - Update Address Map Display with lat/long",
  "script": "frappe.ui.form.on('Address', {\r\n    refresh: function(frm) {\r\n        frm.trigger('render_google_map');\r\n    },\r\n\r\n    render_google_map: function(frm) {\r\n        if  (!frm.is_new() && frm.doc.address_line1 && frm.doc.city && frm.doc.country) {\r\n            // Render Google Map if address fields are available and saved\r\n\r\n            const address = [\r\n                frm.doc.address_line1,\r\n                frm.doc.address_line2 || '',\r\n                frm.doc.city,\r\n                frm.doc.state || '',\r\n                frm.doc.pincode || '',\r\n                frm.doc.country\r\n            ].filter(Boolean).join(', ');\r\n    \r\n            const apiKey = 'AIzaSyCd3ALZe6wjt3xnc7X_rRItfKAEJugfuZ4';\r\n            const mapUrl = `https://www.google.com/maps/embed/v1/place?key=${apiKey}&q=${encodeURIComponent(address)}`;\r\n    \r\n            frm.fields_dict.custom_google_map.$wrapper.html(`\r\n                <iframe \r\n                    width=\"100%\" \r\n                    height=\"400\" \r\n                    frameborder=\"0\" \r\n                    style=\"border:0\" \r\n                    src=\"${mapUrl}\" \r\n                    allowfullscreen>\r\n                </iframe>\r\n            `);\r\n        } else {\r\n            frm.fields_dict.custom_google_map.$wrapper.html('');\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Pre-Built Routes",
  "enabled": 1,
  "modified": "2024-12-13 08:47:49.392360",
  "module": null,
  "name": "Test - Pull The lat/long into Assigned Address",
  "script": "frappe.ui.form.on('Assigned Address', {\r\n    address_name: function(frm, cdt, cdn) {\r\n        let row = locals[cdt][cdn];\r\n        if (row.address_name) {\r\n            frappe.db.get_value('Address', row.address_name, ['custom_latitude', 'custom_longitude'], (value) => {\r\n                if (value) {\r\n                    frappe.model.set_value(cdt, cdn, 'latitude', value.custom_latitude);\r\n                    frappe.model.set_value(cdt, cdn, 'longitude', value.custom_longitude);\r\n                } else {\r\n                    frappe.msgprint(__('Could not fetch coordinates for the selected address.'));\r\n                }\r\n            });\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Pre-Built Routes",
  "enabled": 1,
  "modified": "2024-12-13 09:07:36.356251",
  "module": null,
  "name": "Test - Render color coded map",
  "script": "frappe.ui.form.on('Pre-Built Routes', {\r\n    refresh: function(frm) {\r\n        // Dynamically load Google Maps API if not already loaded\r\n        if (!window.google || !window.google.maps) {\r\n            const apiKey = 'AIzaSyCd3ALZe6wjt3xnc7X_rRItfKAEJugfuZ4'; // Replace with your actual API key\r\n            const script = document.createElement('script');\r\n            script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}`;\r\n            script.async = true;\r\n            script.defer = true;\r\n            script.onload = function () {\r\n                console.log(\"Google Maps API loaded successfully.\");\r\n                renderMap(frm); // Render map after API is loaded\r\n            };\r\n            script.onerror = function () {\r\n                frappe.msgprint(__('Failed to load Google Maps API. Please check your API key.'));\r\n            };\r\n            document.head.appendChild(script);\r\n            return;\r\n        }\r\n\r\n        // If API is already loaded, render the map immediately\r\n        renderMap(frm);\r\n    }\r\n});\r\n\r\nfunction renderMap(frm) {\r\n    const addresses = frm.doc.assigned_addresses || [];\r\n    if (!addresses.length) {\r\n        frappe.msgprint(__('No addresses to display on the map.'));\r\n        return;\r\n    }\r\n\r\n    // Prepare points for the map\r\n    const points = addresses.map(row => ({\r\n        lat: parseFloat(row.latitude || 0),\r\n        lng: parseFloat(row.longitude || 0),\r\n        status: row.status || 'No Response',\r\n        title: row.address_name || 'Unknown Address'\r\n    }));\r\n\r\n    // Define marker colors based on status\r\n    const statusColors = {\r\n        'Confirmed': 'green',\r\n        'Reschedule': 'yellow',\r\n        'Declined': 'red',\r\n        'No Response': 'gray'\r\n    };\r\n\r\n    const mapContainer = frm.fields_dict['route_map'].wrapper;\r\n    mapContainer.innerHTML = '<div id=\"route-map\" style=\"height: 400px;\"></div>';\r\n    const map = new google.maps.Map(document.getElementById('route-map'), {\r\n        zoom: 10,\r\n        mapTypeId: 'roadmap'\r\n    });\r\n\r\n    const bounds = new google.maps.LatLngBounds();\r\n\r\n    // Add markers for each point\r\n    points.forEach(point => {\r\n        if (point.lat && point.lng) {\r\n            const marker = new google.maps.Marker({\r\n                position: { lat: point.lat, lng: point.lng },\r\n                map: map,\r\n                title: point.title,\r\n                icon: {\r\n                    path: google.maps.SymbolPath.CIRCLE,\r\n                    scale: 8,\r\n                    fillColor: statusColors[point.status],\r\n                    fillOpacity: 1,\r\n                    strokeWeight: 0\r\n                }\r\n            });\r\n            bounds.extend(marker.getPosition());\r\n        }\r\n    });\r\n\r\n    if (!bounds.isEmpty()) {\r\n        map.fitBounds(bounds);\r\n    }\r\n}\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Locate Log",
  "enabled": 1,
  "modified": "2024-12-16 14:47:43.738700",
  "module": null,
  "name": "calculated start date",
  "script": "frappe.ui.form.on('Your DocType', {\r\n    date_located: function(frm) {\r\n        if (frm.doc.date_located) {\r\n            var input_date = frm.doc.date_located;\r\n            var calculated_date = addBusinessDays(input_date, 2);\r\n            frm.set_value('legal_start_date', calculated_date);\r\n        }\r\n    }\r\n});\r\n\r\n// Function to calculate date after a specified number of business days\r\nfunction addBusinessDays(startDate, numBusinessDays) {\r\n    var date = new Date(startDate);\r\n    var daysAdded = 0;\r\n    \r\n    while (daysAdded < numBusinessDays) {\r\n        date.setDate(date.getDate() + 1);\r\n        // Check if the new date is a weekend (Saturday = 6, Sunday = 0)\r\n        if (date.getDay() !== 0 && date.getDay() !== 6) {\r\n            daysAdded++;\r\n        }\r\n    }\r\n    \r\n    // Return the calculated date in the correct format for ERPNext\r\n    return frappe.datetime.add_days(date, 0);\r\n}\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Locate Log",
  "enabled": 1,
  "modified": "2024-12-16 14:47:43.716572",
  "module": null,
  "name": "calculated start date-list",
  "script": "frappe.ui.form.on('Your DocType', {\r\n    date_located: function(frm) {\r\n        if (frm.doc.date_located) {\r\n            var input_date = frm.doc.date_located;\r\n            var calculated_date = addBusinessDays(input_date, 2);\r\n            frm.set_value('legal_start_date', calculated_date);\r\n        }\r\n    }\r\n});\r\n\r\n// Function to calculate date after a specified number of business days\r\nfunction addBusinessDays(startDate, numBusinessDays) {\r\n    var date = new Date(startDate);\r\n    var daysAdded = 0;\r\n    \r\n    while (daysAdded < numBusinessDays) {\r\n        date.setDate(date.getDate() + 1);\r\n        // Check if the new date is a weekend (Saturday = 6, Sunday = 0)\r\n        if (date.getDay() !== 0 && date.getDay() !== 6) {\r\n            daysAdded++;\r\n        }\r\n    }\r\n    \r\n    // Return the calculated date in the correct format for ERPNext\r\n    return frappe.datetime.add_days(date, 0);\r\n}\r\n",
  "view": "List"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Locate Log",
  "enabled": 1,
  "modified": "2024-12-16 14:47:18.813864",
  "module": null,
  "name": "expiration date",
  "script": "frappe.ui.form.on('Your DocType', {\r\n    // Trigger when the \"Marked as Located\" button is clicked\r\n    refresh: function(frm) {\r\n        frm.add_custom_button(__('Marked as Located'), function() {\r\n            update_expiration_date(frm);\r\n        });\r\n    }\r\n});\r\n\r\n// Function to update the expiration_date based on legal_start_date and state\r\nfunction update_expiration_date(frm) {\r\n    if (frm.doc.legal_start_date && frm.doc.state) {\r\n        var legalStartDate = frm.doc.legal_start_date;\r\n        var expirationDate = null;\r\n        \r\n        // Check the state and calculate expiration date accordingly\r\n        if (frm.doc.state === \"ID\") {\r\n            expirationDate = frappe.datetime.add_days(legalStartDate, 28);  // 28 days for ID\r\n        } else if (frm.doc.state === \"WA\") {\r\n            expirationDate = frappe.datetime.add_days(legalStartDate, 45);  // 45 days for WA\r\n        }\r\n\r\n        // Set the calculated expiration date if a valid state is selected\r\n        if (expirationDate) {\r\n            frm.set_value('expiration_date', expirationDate);\r\n            frappe.msgprint(__('Expiration Date has been calculated and updated.'));\r\n        } else {\r\n            frappe.msgprint(__('Please make sure both legal start date and state are filled.'));\r\n        }\r\n    } else {\r\n        frappe.msgprint(__('Please make sure both legal start date and state are filled.'));\r\n    }\r\n}\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Locate Log",
  "enabled": 1,
  "modified": "2024-12-16 14:53:13.214793",
  "module": null,
  "name": "expiration date -list",
  "script": "frappe.ui.form.on('Your DocType', {\r\n    date_located: function(frm) {\r\n        if (frm.doc.date_located) {\r\n            var input_date = frm.doc.date_located;\r\n            var calculated_date = addBusinessDays(input_date, 30);\r\n            frm.set_value('expiration_date', calculated_date);\r\n        }\r\n    }\r\n});\r\n\r\n// Function to calculate date after a specified number of business days\r\nfunction addBusinessDays(startDate, numBusinessDays) {\r\n    var date = new Date(startDate);\r\n    var daysAdded = 0;\r\n    \r\n    while (daysAdded < numBusinessDays) {\r\n        date.setDate(date.getDate() + 1);\r\n        // Check if the new date is a weekend (Saturday = 6, Sunday = 0)\r\n        if (date.getDay() !== 0 && date.getDay() !== 6) {\r\n            daysAdded++;\r\n        }\r\n    }\r\n    \r\n    // Return the calculated date in the correct format for ERPNext\r\n    return frappe.datetime.add_days(date, 0);\r\n}\r\n",
  "view": "List"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Quotation",
  "enabled": 0,
  "modified": "2025-01-24 13:24:25.882893",
  "module": null,
  "name": "Auto populate address from customer - test",
  "script": "frappe.ui.form.on('Quotation', {\r\n    customer: function(frm) {\r\n        // Trigger when the customer field is set or changed\r\n        if (frm.doc.customer) {\r\n            frappe.call({\r\n                method: 'frappe.client.get_value',\r\n                args: {\r\n                    doctype: 'Customer',\r\n                    filters: { name: frm.doc.customer },\r\n                    fieldname: 'address_html'\r\n                },\r\n                callback: function(response) {\r\n                    if (response.message && response.message.address_html) {\r\n                        // Set the customer_address field in the Quotation\r\n                        frm.set_value('customer_address', response.message.address_html);\r\n                    } else {\r\n                        frappe.msgprint(__('No address found for the selected customer.'));\r\n                    }\r\n                }\r\n            });\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Address",
  "enabled": 0,
  "modified": "2025-01-24 14:05:52.607856",
  "module": "Brotherton SOP",
  "name": "address concatenate",
  "script": "frappe.ui.form.on('<Address>', {\n    validate: function(frm) {\n        // Concatenate the fields\n        frm.set_value('address_line1', \n            (frm.doc.custom_street_number || '') + ', ' + \n            (frm.doc.custom_directional || '') + ', ' + \n            (frm.doc.custom_street_name || '') + ', ' + \n            (frm.doc.custom_street_suffix || '')\n        );\n    }\n});",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Address",
  "enabled": 1,
  "modified": "2025-05-15 12:30:18.280633",
  "module": null,
  "name": "Address Validation",
  "script": "const renameInProgress = new Set();\r\nfunction rename(frm) {\r\n    const newName = frm.doc.address_line1;\r\n    if (!renameInProgress.has(newName)) {\r\n        renameInProgress.add(newName);\r\n        return frappe.call({\r\n            method: 'frappe.rename_doc',\r\n            freeze: true,\r\n    \t\tfreeze_message: \"Updating name...\",\r\n            args: {\r\n                doctype: 'Address',\r\n                old: frm.doc.name,\r\n                new: newName,\r\n                merge: 0\r\n            },\r\n            \r\n            // From https://github.com/frappe/frappe/blob/f708acb59e3cdc9ec1a91bcdfc0f36d6d012cbf5/frappe/public/js/frappe/model/model.js#L787\r\n            callback: function (r, rt) {\r\n    \t\t\tif (!r.exc) {\r\n    \t\t\t\t$(document).trigger(\"rename\", [\r\n    \t\t\t\t\t'Address',\r\n    \t\t\t\t\tfrm.doc.name,\r\n    \t\t\t\t\tr.message || newName,\r\n    \t\t\t\t]);\r\n    \t\t\t}\r\n    \t\t\trenameInProgress.delete(newName);\r\n    \t\t},\r\n        })\r\n    }\r\n}\r\n\r\nfrappe.ui.form.on('Address', {\r\n    // Trigger validation and formatting on refresh\r\n    refresh: function (frm) {\r\n        // Trigger field validation when the form is refreshed\r\n        frm.trigger('format_address_fields');\r\n        \r\n        // Rename to remove appended type, if needed\r\n        if (!frm.is_new() && frm.doc.name !== frm.doc.address_line1 && frm.doc.address_type === \"Other\") {\r\n            // Trial and error, seems like 1 second is needed to not cause issues\r\n            setTimeout(() => rename(frm), 1000);\r\n        }\r\n    },\r\n\r\n    // Watch for changes in address_line1 and address_line2 fields\r\n    address_line1: function (frm) {\r\n        frm.trigger('format_address_fields');\r\n         if (frm.doc.address_line1 && frm.doc.address_title !== frm.doc.address_line1) {\r\n            frm.set_value('address_title', frm.doc.address_line1);\r\n        }\r\n    },\r\n    address_line2: function (frm) {\r\n        frm.trigger('format_address_fields');\r\n    },\r\n\r\n    // Format and validate address fields\r\n    format_address_fields: function (frm) {\r\n        // Helper function to capitalize text and remove punctuation\r\n        function format_text(field) {\r\n            let value = frm.doc[field] || '';\r\n            // Remove punctuation and capitalize the text\r\n            let formatted_value = value\r\n                .replace(/[.,!?;:']/g, '') // Remove punctuation\r\n                .toUpperCase(); // Capitalize text\r\n            return formatted_value;\r\n        }\r\n\r\n        // Format address_line1 and address_line2\r\n        const formatted_line1 = format_text('address_line1');\r\n        const formatted_line2 = format_text('address_line2');\r\n\r\n        // Set the formatted values back to the form\r\n        if (formatted_line1 !== frm.doc.address_line1) {\r\n            frm.set_value('address_line1', formatted_line1);\r\n        }\r\n        if (formatted_line2 !== frm.doc.address_line2) {\r\n            frm.set_value('address_line2', formatted_line2);\r\n        }\r\n\r\n        // Validate if punctuation exists (after formatting)\r\n        if (/[.,!?;:']/.test(frm.doc.address_line1) || /[.,!?;:']/.test(frm.doc.address_line2)) {\r\n            frappe.msgprint(__('Punctuation is not allowed in address fields.'));\r\n        }\r\n    },\r\n\r\n    // Before saving the document, validate the fields\r\n    validate: function (frm) {\r\n        const invalidFields = [];\r\n\r\n        // Check if punctuation still exists in address fields\r\n        ['address_line1', 'address_line2'].forEach(field => {\r\n            if (/[.,!?;:']/.test(frm.doc[field])) {\r\n                invalidFields.push(field);\r\n            }\r\n        });\r\n\r\n        // If invalid fields exist, stop the save process and alert the user\r\n        if (invalidFields.length > 0) {\r\n            frappe.msgprint(__('Punctuation is not allowed in address fields: ') + invalidFields.join(', '));\r\n            frappe.validated = false; // Prevent saving\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "QB Export",
  "enabled": 1,
  "modified": "2025-02-04 03:12:39.473331",
  "module": null,
  "name": "QB Export",
  "script": "frappe.ui.form.on('QB Export', {\n\tonload(frm) {\n\t    if (!frm.doc.start_date) frm.doc.start_date = new Date(new Date().getFullYear(), new Date().getMonth()-1)\n        if (!frm.doc.end_date) frm.doc.end_date = new Date(new Date().getFullYear(), new Date().getMonth(), 0)\n    }\n})",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Customer",
  "enabled": 1,
  "modified": "2025-02-05 05:28:54.588303",
  "module": null,
  "name": "Customer - Allow Multiple Addresses To Be Asssigned",
  "script": "frappe.ui.form.on('Custom Customer Address Link', {\r\n    address_name: function(frm, cdt, cdn) {\r\n        let row = locals[cdt][cdn];\r\n        \r\n        if (row.address_name) {\r\n            frappe.call({\r\n                method: \"frappe.client.get\",\r\n                args: {\r\n                    doctype: \"Address\",\r\n                    name: row.address_name\r\n                },\r\n                callback: function(r) {\r\n                    if (r.message) {\r\n                        frappe.model.set_value(cdt, cdn, \"address_line1\", r.message.address_line1);\r\n                        frappe.model.set_value(cdt, cdn, \"city\", r.message.city);\r\n                        frappe.model.set_value(cdt, cdn, \"state\", r.message.state);\r\n                        frappe.model.set_value(cdt, cdn, \"pincode\", r.message.pincode);\r\n                        frappe.model.set_value(cdt, cdn, \"address_type\", r.message.address_type);\r\n                    }\r\n                }\r\n            });\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Sales Order",
  "enabled": 1,
  "modified": "2025-03-05 05:13:03.150519",
  "module": null,
  "name": "Carry Over Installation Address",
  "script": "frappe.ui.form.on(\"Sales Order\", {\r\n    onload: function(frm) {\r\n        if (!frm.doc.custom_installation_address && frm.doc.quotation) {\r\n            frappe.db.get_value(\"Quotation\", frm.doc.quotation, \"custom_installation_address\")\r\n                .then(r => {\r\n                    if (r.message.custom_installation_address) {\r\n                        frm.set_value(\"custom_installation_address\", r.message.custom_installation_address);\r\n                       \r\n                    }\r\n                });\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Sales Invoice",
  "enabled": 1,
  "modified": "2025-03-05 05:13:42.768423",
  "module": null,
  "name": "Quotation to Sales Invoice Carry Over",
  "script": "frappe.ui.form.on(\"Sales Invoice\", {\r\n    onload: function(frm) {\r\n        if (!frm.doc.custom_installation_address && frm.doc.quotation) {\r\n            frappe.db.get_value(\"Quotation\", frm.doc.quotation, \"custom_installation_address\")\r\n                .then(r => {\r\n                    if (r.message && r.message.custom_installation_address) {\r\n                        frm.set_value(\"custom_installation_address\", r.message.custom_installation_address);\r\n                    }\r\n                });\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Work Order",
  "enabled": 1,
  "modified": "2025-03-05 07:09:42.680637",
  "module": null,
  "name": "Sales Order to Work Order Carry Over",
  "script": "frappe.ui.form.on(\"Work Order\", {\r\n    onload: function(frm) {\r\n        if (!frm.doc.custom_installation_address && frm.doc.sales_order) {\r\n            frappe.db.get_value(\"Sales Order\", frm.doc.sales_order, \"custom_installation_address\")\r\n                .then(r => {\r\n                    if (r.message && r.message.custom_installation_address) {\r\n                        frm.set_value(\"custom_installation_address\", r.message.custom_installation_address);\r\n                    }\r\n                });\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Delivery Note",
  "enabled": 1,
  "modified": "2025-03-05 05:15:06.408083",
  "module": null,
  "name": "Sales Order to Delivery Note Carry Over",
  "script": "frappe.ui.form.on(\"Delivery Note\", {\r\n    onload: function(frm) {\r\n        if (!frm.doc.custom_installation_address && frm.doc.against_sales_order) {\r\n            frappe.db.get_value(\"Sales Order\", frm.doc.against_sales_order, \"custom_installation_address\")\r\n                .then(r => {\r\n                    if (r.message && r.message.custom_installation_address) {\r\n                        frm.set_value(\"custom_installation_address\", r.message.custom_installation_address);\r\n                    }\r\n                });\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Payment Entry",
  "enabled": 1,
  "modified": "2025-04-25 04:17:25.626671",
  "module": null,
  "name": "Sales Invoice to Payment Entry Carry Over",
  "script": "frappe.ui.form.on(\"Payment Entry\", {\r\n    onload: function(frm) {\r\n        if (!frm.doc.custom_installation_address && frm.doc.reference_doctype && frm.doc.reference_name) {\r\n            let source_doctype = frm.doc.reference_doctype;\r\n            let source_name = frm.doc.reference_name;\r\n\r\n            frappe.db.get_value(\"Sales Invoice\", frm.doc.sales_invoice, \"custom_installation_address\")\r\n                .then(r => {\r\n                    if (r.message && r.message.custom_installation_address) {\r\n                        frm.set_value(\"custom_installation_address\", r.message.custom_installation_address);\r\n                    }\r\n                });\r\n        }\r\n    }\r\n});\r\n\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Job Card",
  "enabled": 1,
  "modified": "2025-03-05 05:22:37.566028",
  "module": null,
  "name": "Work Order to Job Card Carry Over",
  "script": "frappe.ui.form.on(\"Job Card\", {\r\n    onload: function(frm) {\r\n        if (!frm.doc.custom_installation_address && frm.doc.work_order) {\r\n            frappe.db.get_value(\"Work Order\", frm.doc.work_order, \"custom_installation_address\")\r\n                .then(r => {\r\n                    if (r.message && r.message.custom_installation_address) {\r\n                        frm.set_value(\"custom_jobsite\", r.message.custom_installation_address);\r\n                    }\r\n                });\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Project",
  "enabled": 1,
  "modified": "2025-03-05 05:19:29.828616",
  "module": null,
  "name": "Sales Order to Project Carry Over",
  "script": "frappe.ui.form.on(\"Project\", {\r\n    onload: function(frm) {\r\n        if (!frm.doc.custom_installation_address && frm.doc.sales_order) {\r\n            frappe.db.get_value(\"Sales Order\", frm.doc.sales_order, \"custom_installation_address\")\r\n                .then(r => {\r\n                    if (r.message && r.message.custom_installation_address) {\r\n                        frm.set_value(\"custom_installation_address\", r.message.custom_installation_address);\r\n                    }\r\n                });\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Address",
  "enabled": 1,
  "modified": "2025-09-02 11:38:09.302073",
  "module": null,
  "name": "Buttons on Address Doctype",
  "script": "frappe.ui.form.on(\"Address\", {\r\n    refresh: function (frm) {\r\n        if (!frm.is_new()) {\r\n            frm.add_custom_button(__('Schedule On-Site Meeting'), function () {\r\n                sessionStorage.setItem('on-site-meeting-address', frm.doc.name);\r\n                frappe.set_route(\"List\", \"On-Site Meeting\", \"Calendar\", \"On-Site Meeting Calendar\");\r\n            }, __(\"Create\"));\r\n            \r\n              // Add button to create a Sales Order\r\n            frm.add_custom_button(__('Create Sales Order'), function () {\r\n                frappe.new_doc('Sales Order', {\r\n                    custom_installation_address: frm.doc.name,\r\n                    customer: frm.doc.custom_customer_to_bill\r\n                });\r\n            }, __(\"Create\"));\r\n            \r\n               // Add button to create a Quotation\r\n            frm.add_custom_button(__('Create Quotation'), function () {\r\n                frappe.new_doc('Quotation', {\r\n                    custom_installation_address: frm.doc.name,\r\n                    quotation_to: 'Customer',\r\n                    party_name: frm.doc.custom_customer_to_bill\r\n                });\r\n            }, __(\"Create\"));\r\n            // Add button to create a new Service Appointment\r\n            frm.add_custom_button(__('Create Service Appointment'), function() {\r\n                frappe.new_doc('Service Appointment', {\r\n                   //custom_location_of_meeting: frm.doc.name,\r\n                   contact: frm.doc.custom_customer_to_bill\r\n                });\r\n            }, __(\"Create\"));\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "On-Site Meeting",
  "enabled": 1,
  "modified": "2025-04-17 12:19:42.335880",
  "module": null,
  "name": "On-Site Meeting - autofill address from session Enabled",
  "script": "frappe.ui.form.on('On-Site Meeting', {\n\tonload(frm) {\n\t    if (sessionStorage.getItem('on-site-meeting-address')) {\n\t        frm.set_value('address', sessionStorage.getItem('on-site-meeting-address'));\n\t        console.log('start_time', frm.doc.start_time)\n\t        if (frm.doc.start_time) {\n\t            frm.save();\n\t        }\n\t    }\n\t},\n\tvalidate(frm) {\n\t    let end = new Date(frm.doc.start_time + \" GMT\");\n\t    end.setHours(end.getHours() + 1)\n\t    frm.set_value('end_time', end.toISOString().replace('T', ' ').split('.')[0]);\n\t},\n\tafter_save(frm) {\n\t    console.log('on submit')\n\t    if (frm.doc.address === sessionStorage.getItem('on-site-meeting-address')) {\n\t        frappe.set_route('Form', 'Address', frm.doc.address);\n\t    }\n\t    sessionStorage.removeItem('on-site-meeting-address');\n\t},\n\tafter_cancel(frm) {\n\t    sessionStorage.removeItem('on-site-meeting-address');\n\t},\n\tafter_discard(frm) {\n\t    sessionStorage.removeItem('on-site-meeting-address');\n\t},\n})",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Quotation",
  "enabled": 1,
  "modified": "2025-05-08 13:56:46.345762",
  "module": null,
  "name": "Quick-send quote button (SNW)",
  "script": "frappe.ui.form.on('Quotation', {\n\trefresh(frm) {\n\t    if (frm.doc.status == \"Open\" && frm.doc.company == \"Sprinklers Northwest\") {\n    \t\tfrm.add_custom_button(__('Send Email'), async function () {\n    \t\t    const party = frm.doc.quotation_to && frm.doc.party_name ? await frappe.db.get_doc(frm.doc.quotation_to, frm.doc.party_name) : null;\n    \t\t    const address = frm.doc.custom_installation_address ? await frappe.db.get_doc('Address', frm.doc.custom_installation_address) : null;\n    \t\t    \n    \t\t    let email = null;\n    \t\t    if (party && party.email_id) {\n    \t\t        email = party.email_id;\n    \t\t    } else if (party && party.email_ids && party.email_ids.length) {\n    \t\t        const primary = party.email_ids.find(email => email.is_primary);\n    \t\t        if (primary) email = primary.email_id;\n    \t\t        else email = party.email_ids[0].email_id;\n    \t\t    } else if (address && address.email_id) {\n    \t\t        email = address.email_id;\n    \t\t    }\n    \t\t    \n    \t\t    if (!email) {\n    \t\t        frappe.msgprint(\"No email on customer or address\");\n    \t\t        return;\n    \t\t    }\n    \t\t    \n                frappe.confirm(`Send quote to ${frm.doc.party_name} (${email})?`,\n                    async () => {\n                        const { message: { subject, message } } = await frappe.call({\n                            method: \"frappe.email.doctype.email_template.email_template.get_email_template\",\n                            type: \"POST\",\n                            args: {\n                                template_name: 'Quote with Actions - SNW',\n                                doc: frm.doc\n                            }\n                        });\n                        \n                        await frappe.call({\n                            method: \"frappe.core.doctype.communication.email.make\",\n                            type: 'POST',\n                            args: {\n                                recipients: email, \n                                subject,\n                                content: message,\n                                doctype: 'Quotation',\n                                name: frm.doc.name,\n                                send_email: 1,\n                                send_me_a_copy: 0,\n                                print_format: 'SNW Quotations',\n                                email_template: 'Quote with Actions - SNW',\n                                read_receipt: 1,\n                                print_letterhead: 1\n                            }\n                        });\n                        \n                        frappe.msgprint(\"Email Queued\");\n                    },\n                    null\n                );\n            });\n\t    }\n\t}\n})",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Payment Entry",
  "enabled": 1,
  "modified": "2025-05-02 11:03:34.086145",
  "module": null,
  "name": "Installation Address Carry  Over from Sales Order to Payment Address",
  "script": "frappe.ui.form.on(\"Payment Entry\", {\r\n    onload: function(frm) {\r\n        if (!frm.doc.custom_installation_address &&  frm.doc.against_sales_order) {\r\n            frappe.db.get_value(\"Sales Order\",  frm.doc.against_sales_order, \"custom_installation_address\")\r\n                .then(r => {\r\n                    if (r.message.custom_installation_address) {\r\n                        frm.set_value(\"custom_installation_address\", r.message.custom_installation_address);\r\n                    }\r\n                });\r\n        }\r\n    }\r\n});\r\n\r\n\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Payment Entry",
  "enabled": 1,
  "modified": "2025-05-08 13:20:53.255294",
  "module": null,
  "name": "Fetch billing customer from installation address",
  "script": "frappe.ui.form.on('Payment Entry', {\n\tcustom_installation_address(frm) {\n\t    if (frm.doc.custom_installation_address) {\n\t        frappe.db.get_doc('Address', frm.doc.custom_installation_address).then(doc => {\n                frm.set_value('party_type', 'Customer')\n                frm.set_value('party', doc.custom_customer_to_bill)\n            })\n\t    }\n\t}\n})",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Pre-Built Routes",
  "enabled": 1,
  "modified": "2025-06-17 02:12:53.095227",
  "module": null,
  "name": "Filter Route Technicians by Role",
  "script": "frappe.ui.form.on('Pre-Built Routes', {\r\n    onload: function(frm) {\r\n\r\n        // Filter Crew Leader: Only show employees with designation = \"Crew Lead\"\r\n        frm.set_query('crew_leader', () => {\r\n            return {\r\n                filters: {\r\n                    designation: 'Crew Lead',\r\n                    status: 'Active'\r\n                }\r\n            };\r\n        });\r\n\r\n        // Filter Technicians: Only show active employees who are NOT crew leads\r\n        frm.fields_dict['assigned_technicians'].grid.get_field('employee').get_query = function() {\r\n            return {\r\n                filters: [\r\n                    ['designation', '!=', 'Crew Lead'],\r\n                    ['status', '=', 'Active']\r\n                ]\r\n            };\r\n        };\r\n\r\n    },\r\n\r\n    // Optional: Prevent assigning crew leader as a technician too\r\n    validate: function(frm) {\r\n        let crew_leader = frm.doc.crew_leader;\r\n        let techs = frm.doc.assigned_technicians || [];\r\n\r\n        let duplicate = techs.find(row => row.employee === crew_leader);\r\n        if (duplicate) {\r\n            frappe.msgprint(__('Crew Leader cannot be listed as a Technician.'));\r\n            frappe.validated = false;\r\n        }\r\n    }\r\n});\r\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Pre-Built Routes",
  "enabled": 1,
  "modified": "2025-06-17 02:20:56.937981",
  "module": null,
  "name": "Auto-Fetch Relevant Sales Order",
  "script": "// Trigger this logic whenever the address_name field is changed\nfrappe.ui.form.on('Assigned Address', {\n    address_name: function(frm, cdt, cdn) {\n        let row = locals[cdt][cdn];\n// Exit early if no address is selected\n        if (!row.address_name) return;\n // Call the server to fetch all Sales Orders with this installation address\n        frappe.call({\n            method: \"frappe.client.get_list\",\n            args: {\n                doctype: \"Sales Order\",\n                filters: {\n                    custom_installation_address: row.address_name\n                },\n                fields: [\"name\", \"customer\", \"transaction_date\", \"status\", \"grand_total\"]\n            },\n            callback: function(response) {\n                const orders = response.message;\n                // Case: No Sales Orders found\n                if (!orders || orders.length === 0) {\n                    frappe.msgprint(\"No Sales Orders found for this address.\");\n                    frappe.model.set_value(cdt, cdn, \"linked_sales_order\", \"\");\n                    return;\n                }\n                // Case: Exactly one Sales Order found — auto-select it\n                if (orders.length === 1) {\n                    frappe.model.set_value(cdt, cdn, \"linked_sales_order\", orders[0].name);\n                    return;\n                }\n\n                 // Case: Multiple Sales Orders found — show a dialog to select one\n\n                // Create a user-friendly list of options for the select field\n                const options = orders.map(order => {\n                    return {\n                        label: `${order.name} | ${order.customer} | ${frappe.datetime.str_to_user(order.transaction_date)} | ${order.status} | $${order.grand_total}`,\n                        value: order.name\n                    };\n                });\n                  // Define and show a custom dialog for selecting the appropriate Sales Order\n                const dialog = new frappe.ui.Dialog({\n                    title: \"Select Sales Order for \" + row.address_name,\n                    fields: [\n                        {\n                            fieldname: 'selected_so',\n                            label: 'Sales Order',\n                            fieldtype: 'Select',\n                            options: options,\n                            reqd: 1\n                        }\n                    ],\n                    primary_action_label: 'Select',\n                    primary_action(values) {\n                        frappe.model.set_value(cdt, cdn, \"linked_sales_order\", values.selected_so);\n                        dialog.hide();\n                    }\n                });\n\n                dialog.show();\n            }\n        });\n    }\n});\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Project",
  "enabled": 1,
  "modified": "2025-08-29 17:31:31.663751",
  "module": null,
  "name": "Project Warranty Countdown",
  "script": "frappe.ui.form.on('Project', {\n\trefresh(frm) {\n\t\tif (frm.doc.status == \"Completed\" && frm.doc.project_template == \"SNW Install\") {\n\t\t    let message;\n\t\t    const days = Math.abs(frappe.datetime.get_day_diff(frm.doc.custom_warranty_expiration_date, frm.doc.custom_completion_date));\n\t\t    let dayMessage = days == 1 ? \"day\" : \"days\";\n\t\t    if (frappe.datetime.get_today() <= frm.doc.custom_warranty_expiration_date) {\n\t\t        message = `Warranty is valid for ${days} more ${dayMessage}`;\n\t\t    } else {\n\t\t        message = `Warranty has expired ${days} ${dayMessage} ago.`;\n\t\t    }\n\t\t    frm.set_value(\"custom_warranty_information\", message);\n\t\t}\n\t}\n});\n",
  "view": "Form"
 },
 {
  "docstatus": 0,
  "doctype": "Client Script",
  "dt": "Address",
  "enabled": 1,
  "modified": "2025-05-15 09:21:20.813661",
  "module": null,
  "name": "Address Error Fix",
  "script": "frappe.ui.form.on('Address', {\r\n    validate: function (frm) {\r\n        // Default value for the custom field (if it doesn't exist in Address DocType)\r\n        if (!frm.doc.hasOwnProperty('custom_is_your_company_address')) {\r\n            frm.doc.custom_is_your_company_address = false;\r\n        }\r\n\r\n        // Custom validation logic\r\n        // if (frm.doc.custom_is_your_company_address && !frm.doc.some_other_field) {\r\n        //     frappe.throw(__('Please ensure that the required fields are filled out for Company Address.'));\r\n        // }\r\n    }\r\n});\r\n",
  "view": "Form"
 }
]