diff --git a/rangeldigital/__pycache__/hooks.cpython-310.pyc b/rangeldigital/__pycache__/hooks.cpython-310.pyc index 496f9b5..3e5ef60 100644 Binary files a/rangeldigital/__pycache__/hooks.cpython-310.pyc and b/rangeldigital/__pycache__/hooks.cpython-310.pyc differ diff --git a/rangeldigital/fixtures/customer_custom_fields.json b/rangeldigital/fixtures/customer_custom_fields.json new file mode 100644 index 0000000..a2cdb7a --- /dev/null +++ b/rangeldigital/fixtures/customer_custom_fields.json @@ -0,0 +1,36 @@ +[ + { + + "doctype": "Custom Field", + "dt": "Customer", + "fieldname": "sb_401", + "fieldtype": "Section Break", + "insert_after": "account_manager", + "label": "Customer Assets", + "name": "customer_sb_401", + "collapsible": 1 + }, + { + + "doctype": "Custom Field", + "dt": "Customer", + "fieldname": "customer_assets", + "fieldtype": "Table", + "insert_after": "sb_401", + "label": "Customer Assets", + "name": "customer_customer_assets", + "options": "Customer Asset" + }, + { + + "doctype": "Custom Field", + "dt": "Customer", + "fieldname": "customer_generate_assets_csv", + "fieldtype": "HTML", + "insert_after": "customer_assets", + "label": "Generate Assets CSV", + "name": "customer_generate_customer_assets_csv", + "options": "" + } + + ] \ No newline at end of file diff --git a/rangeldigital/fixtures/item_custom_fields.json b/rangeldigital/fixtures/item_custom_fields.json index 487bc5f..5a74c77 100644 --- a/rangeldigital/fixtures/item_custom_fields.json +++ b/rangeldigital/fixtures/item_custom_fields.json @@ -8,5 +8,16 @@ "insert_after": "stock_uom", "label": "Manufacturer Item Code", "name": "item_manuf_item_code" + }, + { + + "doctype": "Custom Field", + "dt": "Item", + "fieldname": "manuf_product_url", + "fieldtype": "Data", + "insert_after": "manuf_item_code", + "label": "Manufacturer URL", + "name": "item_manuf_url", + "options": "URL" } ] \ No newline at end of file diff --git a/rangeldigital/fixtures/lead_custom_fields.json b/rangeldigital/fixtures/lead_custom_fields.json index 0773741..0bf90f9 100644 --- a/rangeldigital/fixtures/lead_custom_fields.json +++ b/rangeldigital/fixtures/lead_custom_fields.json @@ -185,7 +185,7 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "auto_creation_of_contact", + "insert_after": "__section_8", "is_system_generated": 1, "is_virtual": 0, "label": "Last Touch Date", diff --git a/rangeldigital/hooks.py b/rangeldigital/hooks.py index 805ef1a..381a170 100644 --- a/rangeldigital/hooks.py +++ b/rangeldigital/hooks.py @@ -15,7 +15,8 @@ override_doctype_class = { # Lead: Add javascript to add "Add to Email Campaign to Action Dropdown" doctype_js = { - "Lead": "public/js/lead.js" + "Lead": "public/js/lead.js", + "Customer": "public/js/customer.js" } # Add cron scheduler for email campaign sending # Send email at 08:15 AM every day diff --git a/rangeldigital/public/css/rangeldigital.css b/rangeldigital/public/css/rangeldigital.css index fe64118..c91ee01 100644 --- a/rangeldigital/public/css/rangeldigital.css +++ b/rangeldigital/public/css/rangeldigital.css @@ -42,6 +42,10 @@ input.form-control { border-radius:0; } +/* Text */ +a {color:white} +.from-markdown b, .from-markdown strong {color:white} + /* ------- FRAPPE ELEMENTS ------- */ /* Text */ body .text-muted {color:#cccccc!important} diff --git a/rangeldigital/public/js/customer.js b/rangeldigital/public/js/customer.js new file mode 100644 index 0000000..de5e857 --- /dev/null +++ b/rangeldigital/public/js/customer.js @@ -0,0 +1,51 @@ +frappe.ui.form.on('Customer', { + refresh: function(frm) { + // Wait for the form and its custom HTML fields to render + frappe.after_ajax(() => { + // Look for a button with the data attribute + const $btn = frm.$wrapper.find('[data-generate-customer-assets-csv]'); + + if ($btn.length) { + // Prevent double-binding + $btn.off('click').on('click', function () { + const assets = frm.doc.customer_assets || []; + + if (!assets.length) { + frappe.msgprint('No customer assets found.'); + return; + } + + const excludedFields = [ + 'owner', 'creation', 'modified', 'modified_by', 'docstatus', 'idx' + ]; + + const fields = Object.keys(assets[0]).filter( + field => !excludedFields.includes(field) + ); + + const csvRows = []; + csvRows.push(fields.join(',')); + + assets.forEach(row => { + const csvRow = fields.map(field => { + let val = row[field] || ''; + return `"${String(val).replace(/"/g, '""')}"`; + }); + csvRows.push(csvRow.join(',')); + }); + + const csvContent = csvRows.join('\n'); + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = `CustomerAssets-${frm.doc.name}.csv`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + }); + } + }); + } +}); diff --git a/rangeldigital/rangel_digital/doctype/__init__.py b/rangeldigital/rangel_digital/doctype/__init__.py new file mode 100644 index 0000000..f102a9c --- /dev/null +++ b/rangeldigital/rangel_digital/doctype/__init__.py @@ -0,0 +1 @@ +__version__ = "0.0.1" diff --git a/rangeldigital/rangel_digital/doctype/__pycache__/__init__.cpython-310.pyc b/rangeldigital/rangel_digital/doctype/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a2bbbab Binary files /dev/null and b/rangeldigital/rangel_digital/doctype/__pycache__/__init__.cpython-310.pyc differ diff --git a/rangeldigital/rangel_digital/doctype/customer_asset/__init__.py b/rangeldigital/rangel_digital/doctype/customer_asset/__init__.py new file mode 100644 index 0000000..f102a9c --- /dev/null +++ b/rangeldigital/rangel_digital/doctype/customer_asset/__init__.py @@ -0,0 +1 @@ +__version__ = "0.0.1" diff --git a/rangeldigital/rangel_digital/doctype/customer_asset/__pycache__/__init__.cpython-310.pyc b/rangeldigital/rangel_digital/doctype/customer_asset/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..3296675 Binary files /dev/null and b/rangeldigital/rangel_digital/doctype/customer_asset/__pycache__/__init__.cpython-310.pyc differ diff --git a/rangeldigital/rangel_digital/doctype/customer_asset/__pycache__/customer_asset.cpython-310.pyc b/rangeldigital/rangel_digital/doctype/customer_asset/__pycache__/customer_asset.cpython-310.pyc new file mode 100644 index 0000000..34e6ab9 Binary files /dev/null and b/rangeldigital/rangel_digital/doctype/customer_asset/__pycache__/customer_asset.cpython-310.pyc differ diff --git a/rangeldigital/rangel_digital/doctype/customer_asset/customer_asset.json b/rangeldigital/rangel_digital/doctype/customer_asset/customer_asset.json new file mode 100644 index 0000000..0be3ef1 --- /dev/null +++ b/rangeldigital/rangel_digital/doctype/customer_asset/customer_asset.json @@ -0,0 +1,103 @@ +{ + "actions": [], + "creation": "2013-02-22 01:27:51", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + + ], + "fields": [ + { + "fieldname": "item", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item", + "options": "Item" + }, + { + "fieldname": "item_brand", + "fieldtype": "Data", + "in_list_view": 0, + "read_only":1, + "label": "Item Brand", + "fetch_from": "item.brand" + }, + { + "fieldname": "brand", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Brand" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "\nIn Service\nStored\nReserved" + }, + { + "fieldname": "serial_no", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Serial Number" + }, + { + "fieldname": "location_address", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Location Address", + "options": "Address" + }, + { + "fieldname": "acquired_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Acquired Date" + }, + { + "fieldname": "eol_date", + "fieldtype": "Date", + "in_list_view": 0, + "label": "End Of Life Date" + }, + { + "fieldname": "warranty_start_date", + "fieldtype": "Date", + "in_list_view": 0, + "label": "Warranty Start Date" + }, + { + "fieldname": "warranty_end_date", + "fieldtype": "Date", + "in_list_view": 0, + "label": "Warranty End Date" + }, + { + "fieldname": "ownership_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Ownership Type", + "options": "Owned\nOperating Lease\nCapital Lease\nOther" + }, + { + "fieldname": "notes", + "fieldtype": "Text", + "in_list_view": 0, + "label": "Notes" + } + ], + "idx": 1, + "istable": 1, + "links": [], + "modified": "2023-11-14 18:35:30.887278", + "modified_by": "Administrator", + "module": "Rangel Digital", + "name": "Customer Asset", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 + } \ No newline at end of file diff --git a/rangeldigital/rangel_digital/doctype/customer_asset/customer_asset.py b/rangeldigital/rangel_digital/doctype/customer_asset/customer_asset.py new file mode 100644 index 0000000..6c182c3 --- /dev/null +++ b/rangeldigital/rangel_digital/doctype/customer_asset/customer_asset.py @@ -0,0 +1,9 @@ + +import frappe + + +from frappe.model.document import Document + + +class CustomerAsset(Document): + pass \ No newline at end of file diff --git a/rangeldigital/rangel_digital/number_card/my_open_tasks/__pycache__/my_open_tasks.cpython-310.pyc b/rangeldigital/rangel_digital/number_card/my_open_tasks/__pycache__/my_open_tasks.cpython-310.pyc new file mode 100644 index 0000000..5db7047 Binary files /dev/null and b/rangeldigital/rangel_digital/number_card/my_open_tasks/__pycache__/my_open_tasks.cpython-310.pyc differ diff --git a/rangeldigital/rangel_digital/number_card/my_open_tasks/my_open_tasks.json b/rangeldigital/rangel_digital/number_card/my_open_tasks/my_open_tasks.json new file mode 100644 index 0000000..8897477 --- /dev/null +++ b/rangeldigital/rangel_digital/number_card/my_open_tasks/my_open_tasks.json @@ -0,0 +1,19 @@ +{ + "creation": "2025-06-25 10:11:15.922486", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Opportunity", + "method": "rangeldigital.rangel_digital.number_card.my_open_tasks.my_open_tasks.get_number_card_data", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "My Open Tasks", + "modified": "2025-06-25 12:19:53.088837", + "modified_by": "Administrator", + "module": "Projects", + "name": "My Open Tasks", + "owner": "Administrator", + "show_percentage_stats": 0, + "stats_time_interval": "", + "type": "Custom" + } \ No newline at end of file diff --git a/rangeldigital/rangel_digital/number_card/my_open_tasks/my_open_tasks.py b/rangeldigital/rangel_digital/number_card/my_open_tasks/my_open_tasks.py new file mode 100644 index 0000000..34a463d --- /dev/null +++ b/rangeldigital/rangel_digital/number_card/my_open_tasks/my_open_tasks.py @@ -0,0 +1,20 @@ +import frappe +from frappe import _ + +@frappe.whitelist() +def get_number_card_data(): + current_user = frappe.session.user + + count = frappe.db.count( + "Task", + filters={ + "status": ["not in", ["Template", "Completed", "Cancelled"]], + "_assign": ["like", f"%{current_user}%"] + } + ) + + return { + "value": count, + "fieldtype": "Int", + "label": _("My Open Tasks") + } diff --git a/rangeldigital/rangel_digital/number_card/quotations_total/quotations_total.json b/rangeldigital/rangel_digital/number_card/quotations_total/quotations_total.json new file mode 100644 index 0000000..caa7ad3 --- /dev/null +++ b/rangeldigital/rangel_digital/number_card/quotations_total/quotations_total.json @@ -0,0 +1,22 @@ +{ + "aggregate_function_based_on": "base_total", + "creation": "2025-06-21 20:17:15.922486", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Quotation", + "dynamic_filters_json": "[[\"Opportunity\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "filters_json": "[[\"Quotation\",\"status\",\"not in\",[\"Draft\",\"Lost\"],false]]", + "function": "Sum", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "label": "Pipeline Total", + "modified": "2025-06-21 12:15:53.088837", + "modified_by": "Administrator", + "module": "CRM", + "name": "Quotation Total", + "owner": "Administrator", + "show_percentage_stats": 0, + "stats_time_interval": "", + "type": "Document Type" + } \ No newline at end of file diff --git a/rangeldigital/utilities/contact/__pycache__/contact_hooks.cpython-310.pyc b/rangeldigital/utilities/contact/__pycache__/contact_hooks.cpython-310.pyc index 029ebda..8b3ed22 100644 Binary files a/rangeldigital/utilities/contact/__pycache__/contact_hooks.cpython-310.pyc and b/rangeldigital/utilities/contact/__pycache__/contact_hooks.cpython-310.pyc differ diff --git a/rangeldigital/utilities/lead/__pycache__/find_duplicates.cpython-310.pyc b/rangeldigital/utilities/lead/__pycache__/find_duplicates.cpython-310.pyc new file mode 100644 index 0000000..6eea627 Binary files /dev/null and b/rangeldigital/utilities/lead/__pycache__/find_duplicates.cpython-310.pyc differ