Compare commits

..

No commits in common. "test-fixtures" and "main" have entirely different histories.

6 changed files with 49 additions and 53474 deletions

View File

@ -1,519 +0,0 @@
[
{
"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"
}
]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -192,28 +192,68 @@ fixtures = [
{
"dt": "DocType",
"filters": [
["custom", "=", 1]
["name", "in", [
"Quotation Template",
"Quotation Template Item",
"Customer Company Link",
"Customer Address Link",
"Customer Contact Link",
# New link doctypes
"Customer Project Link",
"Customer Quotation Link",
"Customer Sales Order Link",
"Customer On-Site Meeting Link",
"Lead Address Link",
"Lead Contact Link",
"Lead Companies Link",
"Lead Quotation Link",
"Lead On-Site Meeting Link",
"Address Project Link",
"Address Quotation Link",
"Address On-Site Meeting Link",
"Address Sales Order Link",
"Address Contact Link",
"Address Company Link",
"Contact Address Link",
]]
]
},
{
"dt": "Custom Field"
"dt": "Custom Field",
"filters": [
["dt", "=", "Quotation"],
["fieldname", "in", [
"custom_quotation_template",
"custom_project_template"
]]
]
},
{
"dt": "Property Setter"
"dt": "Custom Field",
"filters": [
["dt", "=", "Sales Order"],
["fieldname", "=", "custom_project_template"]
]
},
{
"dt": "Client Script"
"dt": "Custom Field",
"filters": [
["dt", "=", "Lead"],
["fieldname", "=", "custom_customer_name"]
]
},
{
"dt": "Server Script"
"dt": "Custom Field",
"filters": [
["dt", "=", "Project Template"],
["fieldname", "=", "company"]
]
}
]
# Scheduled Tasks
# ---------------