commit eec1247c9a3cc0414ef863726f45b1b3b1b9a655 Author: Duradundi Date: Mon Aug 3 18:02:20 2026 +0530 Initial commit of Asset Lite app diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba04025 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +*.pyc +*.egg-info +*.swp +tags +node_modules +__pycache__ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..31831cf --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +## Asset Lite + +Asset Management System + +#### License + +mit \ No newline at end of file diff --git a/asset_lite/__init__.py b/asset_lite/__init__.py new file mode 100644 index 0000000..f102a9c --- /dev/null +++ b/asset_lite/__init__.py @@ -0,0 +1 @@ +__version__ = "0.0.1" diff --git a/asset_lite/api/__init__.py b/asset_lite/api/__init__.py new file mode 100644 index 0000000..79efa56 --- /dev/null +++ b/asset_lite/api/__init__.py @@ -0,0 +1 @@ +from . import asset_api \ No newline at end of file diff --git a/asset_lite/api/api.py b/asset_lite/api/api.py new file mode 100644 index 0000000..fa8e1e8 --- /dev/null +++ b/asset_lite/api/api.py @@ -0,0 +1,30 @@ +import frappe + +def set_default_homepage(): + + """ + Set the default workspace based on the user's role. + """ + # Get the current user + current_user = frappe.session.user + + # Skip for system users + if current_user in ("Administrator", "Guest"): + return + + # Define role-based workspaces + role_based_workspaces = { + "Maintenance Manager": "asset-management", + #"Maintenance User": "asset-management", + #"Technician": "asset-management" + } + + # Get the user's roles + user_roles = frappe.get_roles(current_user) + + # Determine the default workspace + for role, workspace in role_based_workspaces.items(): + if role in user_roles: + # Set the session home page + frappe.local.response["home_page"] = f"/app/{workspace}" + return \ No newline at end of file diff --git a/asset_lite/api/asset_api.py b/asset_lite/api/asset_api.py new file mode 100644 index 0000000..63cc5f9 --- /dev/null +++ b/asset_lite/api/asset_api.py @@ -0,0 +1,1099 @@ +import frappe +from frappe import _ + +@frappe.whitelist(allow_guest = True) +def get_assets(filters=None, fields=None, limit=20, offset=0, order_by=None, include_finance_books=True): + """ + Get list of assets with filters and pagination + + Args: + filters: JSON string of filters (e.g., '{"company": "ABC Corp"}') + fields: JSON string of fields to return (e.g., '["asset_name", "location"]') + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + order_by: Sort order (e.g., "creation desc") + include_finance_books: Include depreciation details (default: True) + + Returns: + { + "assets": [...], + "total_count": int, + "limit": int, + "offset": int, + "has_more": bool + } + """ + try: + import json + frappe.log_error(f"Logged in User ",frappe.session.user) + + + # Parse filters if provided + if filters and isinstance(filters, str): + filters = json.loads(filters) + + # Handle tree-based fields (Department is a nested set/tree structure) + if filters.get('department') and isinstance(filters['department'], str): + filters['department'] = ['descendants of (inclusive)', filters['department']] + + # Parse fields if provided + if fields and isinstance(fields, str): + fields = json.loads(fields) + else: + # Default fields to return + fields = [ + 'name', + 'asset_name', + 'company', + 'custom_serial_number', + 'location', + 'custom_manufacturer', + 'department', + 'custom_asset_type', + 'custom_manufacturing_year', + 'custom_model', + 'custom_class', + 'custom_device_status', + 'custom_down_time', + 'asset_owner_company', + 'custom_up_time', + 'custom_modality', + 'custom_attach_image', + 'custom_site_contractor', + 'custom_total_amount', + 'creation', + 'modified', + 'owner', + 'modified_by', + # Depreciation related fields from parent + 'calculate_depreciation', + 'opening_accumulated_depreciation', + 'opening_number_of_booked_depreciations', + 'is_fully_depreciated', + 'depreciation_method', + 'value_after_depreciation', + 'total_number_of_depreciations', + 'frequency_of_depreciation', + 'gross_purchase_amount', + 'total_asset_cost', + 'available_for_use_date', + 'status' + ] + + # Get total count + total_count = frappe.db.count('Asset', filters=filters or {}) + + # Get assets + assets = frappe.get_all( + 'Asset', + filters=filters or {}, + fields=fields, + limit_page_length=int(limit), + limit_start=int(offset), + order_by=order_by or 'creation desc' + ) + + # Include finance_books (depreciation details) for each asset + if include_finance_books: + for asset in assets: + asset['finance_books'] = get_finance_books(asset['name']) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'assets': assets, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Assets API Error') + frappe.response['message'] = { + 'error': str(e), + 'assets': [], + 'total_count': 0 + } + + +def get_finance_books(asset_name): + """ + Get finance books (depreciation details) for an asset + + Args: + asset_name: Name/ID of the asset + + Returns: + List of finance book entries with depreciation details + """ + finance_books = frappe.get_all( + 'Asset Finance Book', + filters={'parent': asset_name}, + fields=[ + 'name', + 'idx', + 'finance_book', + 'depreciation_method', + 'total_number_of_depreciations', + 'total_number_of_booked_depreciations', + 'daily_prorata_based', + 'shift_based', + 'frequency_of_depreciation', + 'depreciation_start_date', + 'salvage_value_percentage', + 'expected_value_after_useful_life', + 'value_after_depreciation', + 'rate_of_depreciation' + ], + order_by='idx asc' + ) + return finance_books + + +@frappe.whitelist(allow_guest = True) +def get_asset_details(asset_name, include_depreciation_schedule=False): + """ + Get detailed information about a specific asset + + Args: + asset_name: Name/ID of the asset + include_depreciation_schedule: Include depreciation schedule entries (default: False) + + Returns: + Asset document with all fields including finance_books + """ + try: + if not asset_name: + frappe.throw(_('Asset name is required')) + + # Check if user has permission to read this asset + if not frappe.has_permission('Asset', 'read', asset_name): + frappe.throw(_('Not permitted to access this asset')) + + # Get asset details (includes finance_books child table) + asset = frappe.get_doc('Asset', asset_name) + asset_dict = asset.as_dict() + + # Optionally include depreciation schedule + if include_depreciation_schedule: + asset_dict['depreciation_schedule'] = get_depreciation_schedule(asset_name) + + # Add computed depreciation summary + asset_dict['depreciation_summary'] = get_depreciation_summary(asset_name) + + frappe.response['message'] = asset_dict + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Asset Details API Error') + frappe.response['message'] = { + 'error': str(e) + } + + +def get_depreciation_schedule(asset_name): + """ + Get depreciation schedule entries for an asset + + Args: + asset_name: Name/ID of the asset + + Returns: + List of depreciation schedule entries + """ + schedule = frappe.get_all( + 'Depreciation Schedule', + filters={'parent': asset_name}, + fields=[ + 'name', + 'idx', + 'schedule_date', + 'depreciation_amount', + 'accumulated_depreciation_amount', + 'journal_entry', + 'finance_book', + 'finance_book_id', + 'shift' + ], + order_by='schedule_date asc' + ) + return schedule + + +def get_depreciation_summary(asset_name): + """ + Get computed depreciation summary for an asset + + Args: + asset_name: Name/ID of the asset + + Returns: + Dictionary with depreciation summary + """ + try: + asset = frappe.get_doc('Asset', asset_name) + + # Calculate total depreciation booked + total_depreciation_booked = frappe.db.sql(""" + SELECT COALESCE(SUM(depreciation_amount), 0) as total + FROM `tabDepreciation Schedule` + WHERE parent = %s AND journal_entry IS NOT NULL AND journal_entry != '' + """, asset_name)[0][0] or 0 + + # Calculate pending depreciation entries + pending_entries = frappe.db.count('Depreciation Schedule', { + 'parent': asset_name, + 'journal_entry': ['in', ['', None]] + }) + + # Calculate completed entries + completed_entries = frappe.db.count('Depreciation Schedule', { + 'parent': asset_name, + 'journal_entry': ['not in', ['', None]] + }) + + return { + 'gross_purchase_amount': float(asset.gross_purchase_amount or 0), + 'total_asset_cost': float(asset.total_asset_cost or 0), + 'opening_accumulated_depreciation': float(asset.opening_accumulated_depreciation or 0), + 'total_depreciation_booked': float(total_depreciation_booked), + 'value_after_depreciation': float(asset.value_after_depreciation or 0), + 'is_fully_depreciated': asset.is_fully_depreciated, + 'pending_depreciation_entries': pending_entries, + 'completed_depreciation_entries': completed_entries, + 'calculate_depreciation': asset.calculate_depreciation + } + except Exception: + return {} + + +@frappe.whitelist(allow_guest = True) +def get_asset_finance_books(asset_name): + """ + Get finance books (depreciation configuration) for a specific asset + + Args: + asset_name: Name/ID of the asset + + Returns: + List of finance book entries with all depreciation details + """ + try: + if not asset_name: + frappe.throw(_('Asset name is required')) + + if not frappe.has_permission('Asset', 'read', asset_name): + frappe.throw(_('Not permitted to access this asset')) + + finance_books = frappe.get_all( + 'Asset Finance Book', + filters={'parent': asset_name}, + fields=[ + 'name', + 'idx', + 'finance_book', + 'depreciation_method', + 'total_number_of_depreciations', + 'total_number_of_booked_depreciations', + 'daily_prorata_based', + 'shift_based', + 'frequency_of_depreciation', + 'depreciation_start_date', + 'salvage_value_percentage', + 'expected_value_after_useful_life', + 'value_after_depreciation', + 'rate_of_depreciation' + ], + order_by='idx asc' + ) + + frappe.response['message'] = { + 'asset_name': asset_name, + 'finance_books': finance_books, + 'count': len(finance_books) + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Asset Finance Books API Error') + frappe.response['message'] = { + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def get_asset_depreciation_schedule(asset_name, finance_book=None): + """ + Get depreciation schedule for a specific asset + + Args: + asset_name: Name/ID of the asset + finance_book: Optional filter by finance book + + Returns: + List of depreciation schedule entries + """ + try: + if not asset_name: + frappe.throw(_('Asset name is required')) + + if not frappe.has_permission('Asset', 'read', asset_name): + frappe.throw(_('Not permitted to access this asset')) + + filters = {'parent': asset_name} + if finance_book: + filters['finance_book'] = finance_book + + schedule = frappe.get_all( + 'Depreciation Schedule', + filters=filters, + fields=[ + 'name', + 'idx', + 'schedule_date', + 'depreciation_amount', + 'accumulated_depreciation_amount', + 'journal_entry', + 'finance_book', + 'finance_book_id', + 'shift' + ], + order_by='schedule_date asc' + ) + + # Add status to each entry + for entry in schedule: + entry['status'] = 'Posted' if entry.get('journal_entry') else 'Pending' + + frappe.response['message'] = { + 'asset_name': asset_name, + 'depreciation_schedule': schedule, + 'total_entries': len(schedule), + 'posted_entries': len([s for s in schedule if s.get('journal_entry')]), + 'pending_entries': len([s for s in schedule if not s.get('journal_entry')]) + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Asset Depreciation Schedule API Error') + frappe.response['message'] = { + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def create_asset(asset_data): + """ + Create a new asset with finance books (depreciation configuration) + + Args: + asset_data: JSON string containing asset fields including finance_books array + + Example asset_data: + { + "asset_name": "Test Asset", + "company": "My Company", + "item_code": "ITEM-001", + "gross_purchase_amount": 10000, + "calculate_depreciation": 1, + "available_for_use_date": "2025-01-01", + "finance_books": [ + { + "finance_book": "Depreciation Entries", + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 12, + "frequency_of_depreciation": 12, + "depreciation_start_date": "2025-01-01", + "expected_value_after_useful_life": 1000 + } + ] + } + + Returns: + Created asset document with finance_books + """ + try: + import json + + # Parse asset data + if isinstance(asset_data, str): + asset_data = json.loads(asset_data) + + # Check if user has permission to create asset + if not frappe.has_permission('Asset', 'create'): + frappe.throw(_('Not permitted to create asset')) + + # Create new asset + asset = frappe.get_doc({ + 'doctype': 'Asset', + **asset_data + }) + + asset.insert() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'asset': asset.as_dict(), + 'message': _('Asset created successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Create Asset API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def update_asset(asset_name, asset_data): + """ + Update an existing asset including finance books + Uses ignore_version flag to bypass timestamp mismatch errors + + Args: + asset_name: Name/ID of the asset + asset_data: JSON string containing fields to update (can include finance_books) + + Returns: + Updated asset document + """ + try: + import json + + if not asset_name: + frappe.throw(_('Asset name is required')) + + # Parse asset data + if isinstance(asset_data, str): + asset_data = json.loads(asset_data) + + # Check if user has permission to update this asset + if not frappe.has_permission('Asset', 'write', asset_name): + frappe.throw(_('Not permitted to update this asset')) + + # Get fresh copy of asset from database + asset = frappe.get_doc('Asset', asset_name) + + # Set flags to ignore version check (bypass timestamp mismatch) + asset.flags.ignore_version = True + + # Handle finance_books separately if provided + finance_books_data = asset_data.pop('finance_books', None) + + # Update regular fields + for key, value in asset_data.items(): + if hasattr(asset, key): + setattr(asset, key, value) + + # Update finance_books if provided + if finance_books_data is not None: + asset.set('finance_books', []) + for fb in finance_books_data: + asset.append('finance_books', fb) + + asset.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'asset': asset.as_dict(), + 'message': _('Asset updated successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Update Asset API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def submit_asset(asset_name): + """ + Submit an asset (change docstatus from 0 to 1) + Uses ignore_version flag to bypass timestamp mismatch errors + + Args: + asset_name: Name/ID of the asset to submit + + Returns: + Submitted asset document + """ + try: + if not asset_name: + frappe.throw(_('Asset name is required')) + + # Check if user has permission to submit this asset + if not frappe.has_permission('Asset', 'submit', asset_name): + frappe.throw(_('Not permitted to submit this asset')) + + # Get fresh copy of the asset from database + asset = frappe.get_doc('Asset', asset_name) + + # Check if already submitted + if asset.docstatus == 1: + frappe.throw(_('Asset is already submitted')) + + if asset.docstatus == 2: + frappe.throw(_('Cannot submit a cancelled asset')) + + # Set flags to ignore version check (bypass timestamp mismatch) + asset.flags.ignore_version = True + asset.flags.ignore_links = True # Optional: ignore broken links + asset.flags.ignore_validate = False # Still run validations + + # Submit the asset + asset.submit() + + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'asset': asset.as_dict(), + 'message': _('Asset submitted successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Submit Asset API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def cancel_asset(asset_name): + """ + Cancel a submitted asset (change docstatus from 1 to 2) + Uses ignore_version flag to bypass timestamp mismatch errors + + Args: + asset_name: Name/ID of the asset to cancel + + Returns: + Cancelled asset document + """ + try: + if not asset_name: + frappe.throw(_('Asset name is required')) + + # Check if user has permission to cancel this asset + if not frappe.has_permission('Asset', 'cancel', asset_name): + frappe.throw(_('Not permitted to cancel this asset')) + + # Get fresh copy of the asset from database + asset = frappe.get_doc('Asset', asset_name) + + # Check if can be cancelled + if asset.docstatus == 0: + frappe.throw(_('Cannot cancel a draft asset. Submit it first.')) + + if asset.docstatus == 2: + frappe.throw(_('Asset is already cancelled')) + + # Set flags to ignore version check (bypass timestamp mismatch) + asset.flags.ignore_version = True + asset.flags.ignore_links = True + + # Cancel the asset + asset.cancel() + + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'asset': asset.as_dict(), + 'message': _('Asset cancelled successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Cancel Asset API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def update_asset_finance_book(asset_name, finance_book_name, finance_book_data): + """ + Update a specific finance book entry for an asset + + Args: + asset_name: Name/ID of the asset + finance_book_name: Name of the finance book row to update + finance_book_data: JSON string containing fields to update + + Returns: + Updated asset document + """ + try: + import json + + if not asset_name: + frappe.throw(_('Asset name is required')) + + if not finance_book_name: + frappe.throw(_('Finance book name is required')) + + # Parse finance book data + if isinstance(finance_book_data, str): + finance_book_data = json.loads(finance_book_data) + + # Check if user has permission to update this asset + if not frappe.has_permission('Asset', 'write', asset_name): + frappe.throw(_('Not permitted to update this asset')) + + # Get asset + asset = frappe.get_doc('Asset', asset_name) + + # Set flags to ignore version check + asset.flags.ignore_version = True + + # Find and update the specific finance book + updated = False + for fb in asset.finance_books: + if fb.name == finance_book_name: + for key, value in finance_book_data.items(): + if hasattr(fb, key): + setattr(fb, key, value) + updated = True + break + + if not updated: + frappe.throw(_('Finance book entry not found')) + + asset.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'asset': asset.as_dict(), + 'message': _('Finance book updated successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Update Asset Finance Book API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def add_asset_finance_book(asset_name, finance_book_data): + """ + Add a new finance book entry to an asset + + Args: + asset_name: Name/ID of the asset + finance_book_data: JSON string containing finance book fields + + Example finance_book_data: + { + "finance_book": "Depreciation Entries", + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 12, + "frequency_of_depreciation": 12, + "depreciation_start_date": "2025-01-01", + "expected_value_after_useful_life": 1000 + } + + Returns: + Updated asset document + """ + try: + import json + + if not asset_name: + frappe.throw(_('Asset name is required')) + + # Parse finance book data + if isinstance(finance_book_data, str): + finance_book_data = json.loads(finance_book_data) + + # Check if user has permission to update this asset + if not frappe.has_permission('Asset', 'write', asset_name): + frappe.throw(_('Not permitted to update this asset')) + + # Get asset + asset = frappe.get_doc('Asset', asset_name) + + # Set flags to ignore version check + asset.flags.ignore_version = True + + # Add new finance book + asset.append('finance_books', finance_book_data) + + asset.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'asset': asset.as_dict(), + 'message': _('Finance book added successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Add Asset Finance Book API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def delete_asset_finance_book(asset_name, finance_book_name): + """ + Delete a finance book entry from an asset + + Args: + asset_name: Name/ID of the asset + finance_book_name: Name of the finance book row to delete + + Returns: + Updated asset document + """ + try: + if not asset_name: + frappe.throw(_('Asset name is required')) + + if not finance_book_name: + frappe.throw(_('Finance book name is required')) + + # Check if user has permission to update this asset + if not frappe.has_permission('Asset', 'write', asset_name): + frappe.throw(_('Not permitted to update this asset')) + + # Get asset + asset = frappe.get_doc('Asset', asset_name) + + # Set flags to ignore version check + asset.flags.ignore_version = True + + # Find and remove the specific finance book + for i, fb in enumerate(asset.finance_books): + if fb.name == finance_book_name: + asset.finance_books.remove(fb) + break + else: + frappe.throw(_('Finance book entry not found')) + + asset.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'asset': asset.as_dict(), + 'message': _('Finance book deleted successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Delete Asset Finance Book API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def delete_asset(asset_name): + """ + Delete an asset + + Args: + asset_name: Name/ID of the asset + + Returns: + Success message + """ + try: + if not asset_name: + frappe.throw(_('Asset name is required')) + + # Check if user has permission to delete this asset + if not frappe.has_permission('Asset', 'delete', asset_name): + frappe.throw(_('Not permitted to delete this asset')) + + # Delete asset + frappe.delete_doc('Asset', asset_name) + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'message': _('Asset deleted successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Delete Asset API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def get_asset_filters(): + """ + Get available filter options for assets + + Returns: + { + "companies": [...], + "locations": [...], + "departments": [...], + "asset_types": [...], + "manufacturers": [...], + "device_statuses": [...], + "finance_books": [...], + "depreciation_methods": [...] + } + """ + try: + filters = { + 'companies': frappe.get_all('Company', fields=['name'], pluck='name'), + 'locations': frappe.db.get_all('Asset', + filters={'location': ['!=', '']}, + fields=['location'], + distinct=True, + pluck='location' + ), + 'departments': frappe.get_all('Department', fields=['name'], pluck='name'), + 'asset_types': frappe.db.get_all('Asset', + filters={'custom_asset_type': ['!=', '']}, + fields=['custom_asset_type'], + distinct=True, + pluck='custom_asset_type' + ), + 'manufacturers': frappe.db.get_all('Asset', + filters={'custom_manufacturer': ['!=', '']}, + fields=['custom_manufacturer'], + distinct=True, + pluck='custom_manufacturer' + ), + 'device_statuses': frappe.db.get_all('Asset', + filters={'custom_device_status': ['!=', '']}, + fields=['custom_device_status'], + distinct=True, + pluck='custom_device_status' + ), + 'finance_books': frappe.get_all('Finance Book', fields=['name'], pluck='name'), + 'depreciation_methods': [ + 'Straight Line', + 'Double Declining Balance', + 'Written Down Value', + 'Manual' + ] + } + + frappe.response['message'] = filters + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Asset Filters API Error') + frappe.response['message'] = { + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def get_asset_stats(): + """ + Get statistics about assets including depreciation stats + + Returns: + { + "total_assets": int, + "by_status": {...}, + "by_company": {...}, + "by_type": {...}, + "total_amount": float, + "depreciation_stats": {...} + } + """ + try: + # Total assets + total_assets = frappe.db.count('Asset') + + # Assets by device status + by_status = {} + status_data = frappe.db.sql(""" + SELECT custom_device_status, COUNT(*) as count + FROM `tabAsset` + WHERE custom_device_status IS NOT NULL AND custom_device_status != '' + GROUP BY custom_device_status + """, as_dict=True) + for row in status_data: + by_status[row.custom_device_status] = row.count + + # Assets by company + by_company = {} + company_data = frappe.db.sql(""" + SELECT company, COUNT(*) as count + FROM `tabAsset` + WHERE company IS NOT NULL AND company != '' + GROUP BY company + """, as_dict=True) + for row in company_data: + by_company[row.company] = row.count + + # Assets by type + by_type = {} + type_data = frappe.db.sql(""" + SELECT custom_asset_type, COUNT(*) as count + FROM `tabAsset` + WHERE custom_asset_type IS NOT NULL AND custom_asset_type != '' + GROUP BY custom_asset_type + """, as_dict=True) + for row in type_data: + by_type[row.custom_asset_type] = row.count + + # Total amount + total_amount = frappe.db.sql(""" + SELECT SUM(custom_total_amount) as total + FROM `tabAsset` + WHERE custom_total_amount IS NOT NULL + """)[0][0] or 0 + + # Depreciation statistics + depreciation_stats = get_depreciation_stats() + + frappe.response['message'] = { + 'total_assets': total_assets, + 'by_status': by_status, + 'by_company': by_company, + 'by_type': by_type, + 'total_amount': float(total_amount), + 'depreciation_stats': depreciation_stats + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Asset Stats API Error') + frappe.response['message'] = { + 'error': str(e) + } + + +def get_depreciation_stats(): + """ + Get depreciation statistics across all assets + + Returns: + Dictionary with depreciation statistics + """ + try: + # Total gross purchase amount + total_gross_amount = frappe.db.sql(""" + SELECT COALESCE(SUM(gross_purchase_amount), 0) as total + FROM `tabAsset` + """)[0][0] or 0 + + # Total accumulated depreciation + total_accumulated_depreciation = frappe.db.sql(""" + SELECT COALESCE(SUM(ds.depreciation_amount), 0) as total + FROM `tabDepreciation Schedule` ds + INNER JOIN `tabAsset` a ON ds.parent = a.name + WHERE ds.journal_entry IS NOT NULL AND ds.journal_entry != '' + """)[0][0] or 0 + + # Total value after depreciation + total_value_after_depreciation = frappe.db.sql(""" + SELECT COALESCE(SUM(value_after_depreciation), 0) as total + FROM `tabAsset` + """)[0][0] or 0 + + # Assets with depreciation enabled + assets_with_depreciation = frappe.db.count('Asset', {'calculate_depreciation': 1}) + + # Fully depreciated assets + fully_depreciated_assets = frappe.db.count('Asset', {'is_fully_depreciated': 1}) + + # Pending depreciation entries + pending_entries = frappe.db.sql(""" + SELECT COUNT(*) as count + FROM `tabDepreciation Schedule` + WHERE (journal_entry IS NULL OR journal_entry = '') + """)[0][0] or 0 + + # By depreciation method + by_depreciation_method = {} + method_data = frappe.db.sql(""" + SELECT depreciation_method, COUNT(*) as count + FROM `tabAsset Finance Book` + WHERE depreciation_method IS NOT NULL AND depreciation_method != '' + GROUP BY depreciation_method + """, as_dict=True) + for row in method_data: + by_depreciation_method[row.depreciation_method] = row.count + + return { + 'total_gross_amount': float(total_gross_amount), + 'total_accumulated_depreciation': float(total_accumulated_depreciation), + 'total_value_after_depreciation': float(total_value_after_depreciation), + 'assets_with_depreciation': assets_with_depreciation, + 'fully_depreciated_assets': fully_depreciated_assets, + 'pending_depreciation_entries': pending_entries, + 'by_depreciation_method': by_depreciation_method + } + except Exception: + return {} + + +@frappe.whitelist(allow_guest = True) +def search_assets(search_term, limit=10): + """ + Search assets by name, serial number, or other fields + + Args: + search_term: Search query string + limit: Maximum number of results (default: 10) + + Returns: + List of matching assets + """ + try: + if not search_term: + frappe.response['message'] = [] + return + + search_term = f"%{search_term}%" + + assets = frappe.db.sql(""" + SELECT + name, + asset_name, + custom_serial_number, + location, + company, + custom_device_status, + calculate_depreciation, + value_after_depreciation, + is_fully_depreciated + FROM `tabAsset` + WHERE + asset_name LIKE %(search)s + OR custom_serial_number LIKE %(search)s + OR location LIKE %(search)s + OR custom_manufacturer LIKE %(search)s + LIMIT %(limit)s + """, { + 'search': search_term, + 'limit': int(limit) + }, as_dict=True) + + frappe.response['message'] = assets + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Search Assets API Error') + frappe.response['message'] = { + 'error': str(e) + } \ No newline at end of file diff --git a/asset_lite/api/asset_maintenance_api.py b/asset_lite/api/asset_maintenance_api.py new file mode 100644 index 0000000..c7dd5de --- /dev/null +++ b/asset_lite/api/asset_maintenance_api.py @@ -0,0 +1,712 @@ +import frappe +from frappe import _ + +@frappe.whitelist(allow_guest = True) +def get_asset_maintenance_logs(filters=None, fields=None, limit=20, offset=0, order_by=None, include_child_tables=False): + """ + Get list of asset maintenance logs with filters and pagination + + Args: + filters: JSON string of filters (e.g., '{"maintenance_status": "Planned"}') + fields: JSON string of fields to return (e.g., '["asset_name", "due_date"]') + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + order_by: Sort order (e.g., "creation desc") + include_child_tables: Whether to include child table data (default: False) + + Returns: + { + "asset_maintenance_logs": [...], + "total_count": int, + "limit": int, + "offset": int, + "has_more": bool + } + """ + try: + import json + + # Parse filters if provided + if filters and isinstance(filters, str): + filters = json.loads(filters) + + # Parse fields if provided + if fields and isinstance(fields, str): + fields = json.loads(fields) + else: + # Default fields to return + fields = [ + 'name', + 'asset_maintenance', + 'naming_series', + 'asset_name', + 'custom_asset_type', + 'item_code', + 'item_name', + 'custom_asset_names', + 'custom_hospital_name', + 'task', + 'task_name', + 'maintenance_type', + 'periodicity', + 'has_certificate', + 'custom_early_completion', + 'maintenance_status', + 'custom_pm_overdue_reason', + 'custom_accepted_by_moh', + 'assign_to_name', + 'due_date', + 'completion_date', + 'custom_early_completion_reason', + 'custom_accepted_by_moh_', + 'custom_template', + 'workflow_state', + 'creation', + 'modified', + 'owner', + 'modified_by', + 'docstatus', + 'idx' + ] + + # Get total count + total_count = frappe.db.count('Asset Maintenance Log', filters=filters or {}) + + # Get asset maintenance logs + asset_maintenance_logs = frappe.get_all( + 'Asset Maintenance Log', + filters=filters or {}, + fields=fields, + limit_page_length=int(limit), + limit_start=int(offset), + order_by=order_by or 'creation desc' + ) + + # Include child tables if requested + if include_child_tables and include_child_tables != 'false': + for log in asset_maintenance_logs: + log['custom_table'] = frappe.get_all( + 'PPM Table', + filters={'parent': log['name']}, + fields=['name', 'idx', 'maintenance_name', 'working', 'defect_found', 'not_working'], + order_by='idx asc' + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'asset_maintenance_logs': asset_maintenance_logs, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Asset Maintenance Logs API Error') + frappe.response['message'] = { + 'error': str(e), + 'asset_maintenance_logs': [], + 'total_count': 0 + } + + +@frappe.whitelist(allow_guest = True) +def get_asset_maintenance_log_details(log_name, include_child_tables=True): + """ + Get detailed information about a specific asset maintenance log + + Args: + log_name: Name/ID of the asset maintenance log + include_child_tables: Whether to include child table data (default: True) + + Returns: + Asset Maintenance Log document with all fields including child tables + """ + try: + if not log_name: + frappe.throw(_('Asset Maintenance Log name is required')) + + # Check if user has permission to read this log + if not frappe.has_permission('Asset Maintenance Log', 'read', log_name): + frappe.throw(_('Not permitted to access this asset maintenance log')) + + # Get asset maintenance log details + log = frappe.get_doc('Asset Maintenance Log', log_name) + + frappe.response['message'] = log.as_dict() + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Asset Maintenance Log Details API Error') + frappe.response['message'] = { + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def create_asset_maintenance_log(log_data): + """ + Create a new asset maintenance log + + Args: + log_data: JSON string containing asset maintenance log fields + + Returns: + Created asset maintenance log document + """ + try: + import json + + # Parse log data + if isinstance(log_data, str): + log_data = json.loads(log_data) + + # Check if user has permission to create asset maintenance log + if not frappe.has_permission('Asset Maintenance Log', 'create'): + frappe.throw(_('Not permitted to create asset maintenance log')) + + # Extract child table data + custom_table_data = log_data.pop('custom_table', []) + + # Create new asset maintenance log + log = frappe.get_doc({ + 'doctype': 'Asset Maintenance Log', + **log_data + }) + + # Add child table rows + if custom_table_data: + for row_data in custom_table_data: + log.append('custom_table', { + 'maintenance_name': row_data.get('maintenance_name', ''), + 'working': row_data.get('working', 0), + 'defect_found': row_data.get('defect_found', 0), + 'not_working': row_data.get('not_working', 0) + }) + + log.insert() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'asset_maintenance_log': log.as_dict(), + 'message': _('Asset Maintenance Log created successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Create Asset Maintenance Log API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def update_asset_maintenance_log(log_name, log_data): + """ + Update an existing asset maintenance log + + Args: + log_name: Name/ID of the asset maintenance log + log_data: JSON string containing fields to update + + Returns: + Updated asset maintenance log document + """ + try: + import json + + if not log_name: + frappe.throw(_('Asset Maintenance Log name is required')) + + # Parse log data + if isinstance(log_data, str): + log_data = json.loads(log_data) + + # Check if user has permission to update this log + if not frappe.has_permission('Asset Maintenance Log', 'write', log_name): + frappe.throw(_('Not permitted to update this asset maintenance log')) + + # Get asset maintenance log + log = frappe.get_doc('Asset Maintenance Log', log_name) + + # Extract child table data before processing other fields + custom_table_data = log_data.pop('custom_table', None) + + # List of child table fields to skip in regular update + child_table_fields = ['custom_table', 'table'] + + # Update regular fields (not child tables) + for key, value in log_data.items(): + if key not in child_table_fields and hasattr(log, key): + setattr(log, key, value) + + # Handle child table update if provided + if custom_table_data is not None: + # Clear existing child table rows + log.custom_table = [] + + # Add new child table rows + for row_data in custom_table_data: + log.append('custom_table', { + 'maintenance_name': row_data.get('maintenance_name', ''), + 'working': row_data.get('working', 0), + 'defect_found': row_data.get('defect_found', 0), + 'not_working': row_data.get('not_working', 0) + }) + + log.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'asset_maintenance_log': log.as_dict(), + 'message': _('Asset Maintenance Log updated successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Update Asset Maintenance Log API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def delete_asset_maintenance_log(log_name): + """ + Delete an asset maintenance log + + Args: + log_name: Name/ID of the asset maintenance log + + Returns: + Success message + """ + try: + if not log_name: + frappe.throw(_('Asset Maintenance Log name is required')) + + # Check if user has permission to delete this log + if not frappe.has_permission('Asset Maintenance Log', 'delete', log_name): + frappe.throw(_('Not permitted to delete this asset maintenance log')) + + # Delete asset maintenance log + frappe.delete_doc('Asset Maintenance Log', log_name) + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'message': _('Asset Maintenance Log deleted successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Delete Asset Maintenance Log API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def update_maintenance_status(log_name, maintenance_status=None, workflow_state=None): + """ + Update asset maintenance log status + + Args: + log_name: Name/ID of the asset maintenance log + maintenance_status: New maintenance status (e.g., 'Planned', 'Completed', 'Overdue') + workflow_state: New workflow state + + Returns: + Updated asset maintenance log document + """ + try: + if not log_name: + frappe.throw(_('Asset Maintenance Log name is required')) + + # Check if user has permission to update this log + if not frappe.has_permission('Asset Maintenance Log', 'write', log_name): + frappe.throw(_('Not permitted to update this asset maintenance log')) + + # Get asset maintenance log + log = frappe.get_doc('Asset Maintenance Log', log_name) + + # Update status fields + if maintenance_status: + log.maintenance_status = maintenance_status + + if workflow_state: + log.workflow_state = workflow_state + + log.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'asset_maintenance_log': log.as_dict(), + 'message': _('Asset Maintenance Log status updated successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Update Maintenance Status API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def get_maintenance_logs_by_asset(asset_name, filters=None, limit=20, offset=0, include_child_tables=False): + """ + Get all maintenance logs for a specific asset + + Args: + asset_name: Name/ID of the asset + filters: Additional JSON string of filters + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + include_child_tables: Whether to include child table data (default: False) + + Returns: + List of maintenance logs for the asset + """ + try: + import json + + if not asset_name: + frappe.throw(_('Asset name is required')) + + # Parse additional filters if provided + additional_filters = {} + if filters and isinstance(filters, str): + additional_filters = json.loads(filters) + + # Combine filters + combined_filters = {'asset_name': asset_name, **additional_filters} + + # Get total count + total_count = frappe.db.count('Asset Maintenance Log', filters=combined_filters) + + # Get maintenance logs + logs = frappe.get_all( + 'Asset Maintenance Log', + filters=combined_filters, + fields=['*'], + limit_page_length=int(limit), + limit_start=int(offset), + order_by='due_date desc' + ) + + # Include child tables if requested + if include_child_tables and include_child_tables != 'false': + for log in logs: + log['custom_table'] = frappe.get_all( + 'PPM Table', + filters={'parent': log['name']}, + fields=['name', 'idx', 'maintenance_name', 'working', 'defect_found', 'not_working'], + order_by='idx asc' + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'asset_maintenance_logs': logs, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Maintenance Logs By Asset API Error') + frappe.response['message'] = { + 'error': str(e), + 'asset_maintenance_logs': [], + 'total_count': 0 + } + + +@frappe.whitelist(allow_guest = True) +def get_overdue_maintenance_logs(filters=None, limit=20, offset=0, include_child_tables=False): + """ + Get all overdue maintenance logs + + Args: + filters: Additional JSON string of filters + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + include_child_tables: Whether to include child table data (default: False) + + Returns: + List of overdue maintenance logs + """ + try: + import json + from frappe.utils import today + + # Parse additional filters if provided + additional_filters = {} + if filters and isinstance(filters, str): + additional_filters = json.loads(filters) + + # Combine filters - get logs with due_date less than today and status not completed + combined_filters = { + 'due_date': ['<', today()], + 'maintenance_status': ['!=', 'Completed'], + **additional_filters + } + + # Get total count + total_count = frappe.db.count('Asset Maintenance Log', filters=combined_filters) + + # Get overdue logs + logs = frappe.get_all( + 'Asset Maintenance Log', + filters=combined_filters, + fields=['*'], + limit_page_length=int(limit), + limit_start=int(offset), + order_by='due_date asc' + ) + + # Include child tables if requested + if include_child_tables and include_child_tables != 'false': + for log in logs: + log['custom_table'] = frappe.get_all( + 'PPM Table', + filters={'parent': log['name']}, + fields=['name', 'idx', 'maintenance_name', 'working', 'defect_found', 'not_working'], + order_by='idx asc' + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'asset_maintenance_logs': logs, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Overdue Maintenance Logs API Error') + frappe.response['message'] = { + 'error': str(e), + 'asset_maintenance_logs': [], + 'total_count': 0 + } + + +@frappe.whitelist(allow_guest = True) +def add_ppm_table_row(log_name, row_data): + """ + Add a PPM table row to a maintenance log + + Args: + log_name: Name/ID of the asset maintenance log + row_data: JSON string containing row fields + + Returns: + Updated custom_table array + """ + try: + import json + + if not log_name: + frappe.throw(_('Asset Maintenance Log name is required')) + + # Parse row data + if isinstance(row_data, str): + row_data = json.loads(row_data) + + # Check permission + if not frappe.has_permission('Asset Maintenance Log', 'write', log_name): + frappe.throw(_('Not permitted to update this asset maintenance log')) + + # Get log and add row + log = frappe.get_doc('Asset Maintenance Log', log_name) + log.append('custom_table', { + 'maintenance_name': row_data.get('maintenance_name', ''), + 'working': row_data.get('working', 0), + 'defect_found': row_data.get('defect_found', 0), + 'not_working': row_data.get('not_working', 0) + }) + + log.save() + frappe.db.commit() + + # Return updated child table + custom_table = [] + for row in log.custom_table: + custom_table.append({ + 'name': row.name, + 'idx': row.idx, + 'maintenance_name': row.maintenance_name, + 'working': row.working, + 'defect_found': row.defect_found, + 'not_working': row.not_working + }) + + frappe.response['message'] = { + 'success': True, + 'custom_table': custom_table, + 'message': _('PPM table row added successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Add PPM Table Row API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def remove_ppm_table_row(log_name, row_name): + """ + Remove a PPM table row from a maintenance log + + Args: + log_name: Name/ID of the asset maintenance log + row_name: Name/ID of the row to remove + + Returns: + Updated custom_table array + """ + try: + if not log_name: + frappe.throw(_('Asset Maintenance Log name is required')) + + if not row_name: + frappe.throw(_('Row name is required')) + + # Check permission + if not frappe.has_permission('Asset Maintenance Log', 'write', log_name): + frappe.throw(_('Not permitted to update this asset maintenance log')) + + # Get log and remove row + log = frappe.get_doc('Asset Maintenance Log', log_name) + + # Find and remove the row + row_to_remove = None + for row in log.custom_table: + if row.name == row_name: + row_to_remove = row + break + + if row_to_remove: + log.remove(row_to_remove) + log.save() + frappe.db.commit() + + # Return updated child table + custom_table = [] + for row in log.custom_table: + custom_table.append({ + 'name': row.name, + 'idx': row.idx, + 'maintenance_name': row.maintenance_name, + 'working': row.working, + 'defect_found': row.defect_found, + 'not_working': row.not_working + }) + + frappe.response['message'] = { + 'success': True, + 'custom_table': custom_table, + 'message': _('PPM table row removed successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Remove PPM Table Row API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def update_ppm_table_row(log_name, row_name, row_data): + """ + Update a PPM table row in a maintenance log + + Args: + log_name: Name/ID of the asset maintenance log + row_name: Name/ID of the row to update + row_data: JSON string containing fields to update + + Returns: + Updated custom_table array + """ + try: + import json + + if not log_name: + frappe.throw(_('Asset Maintenance Log name is required')) + + if not row_name: + frappe.throw(_('Row name is required')) + + # Parse row data + if isinstance(row_data, str): + row_data = json.loads(row_data) + + # Check permission + if not frappe.has_permission('Asset Maintenance Log', 'write', log_name): + frappe.throw(_('Not permitted to update this asset maintenance log')) + + # Get log and update row + log = frappe.get_doc('Asset Maintenance Log', log_name) + + # Find and update the row + for row in log.custom_table: + if row.name == row_name: + if 'maintenance_name' in row_data: + row.maintenance_name = row_data['maintenance_name'] + if 'working' in row_data: + row.working = row_data['working'] + if 'defect_found' in row_data: + row.defect_found = row_data['defect_found'] + if 'not_working' in row_data: + row.not_working = row_data['not_working'] + break + + log.save() + frappe.db.commit() + + # Return updated child table + custom_table = [] + for row in log.custom_table: + custom_table.append({ + 'name': row.name, + 'idx': row.idx, + 'maintenance_name': row.maintenance_name, + 'working': row.working, + 'defect_found': row.defect_found, + 'not_working': row.not_working + }) + + frappe.response['message'] = { + 'success': True, + 'custom_table': custom_table, + 'message': _('PPM table row updated successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Update PPM Table Row API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } \ No newline at end of file diff --git a/asset_lite/api/custom_api.py b/asset_lite/api/custom_api.py new file mode 100644 index 0000000..ea19a36 --- /dev/null +++ b/asset_lite/api/custom_api.py @@ -0,0 +1,163 @@ +import frappe +from frappe import _ +from frappe.utils import now, today, get_datetime +import json + +@frappe.whitelist(allow_guest=False) +def get_user_details(user_id=None): + """ + Get detailed user information + Usage: /api/method/asset_lite.api.custom_api.get_user_details + """ + try: + if not user_id: + user_id = frappe.session.user + + user = frappe.get_doc("User", user_id) + + # Get user roles + roles = frappe.get_roles(user_id) + + response_data = { + "user_id": user_id, + "full_name": user.full_name, + "email": user.email, + "user_image": user.user_image, + "roles": roles, + "last_login": user.last_login, + "enabled": user.enabled, + "creation": user.creation, + "modified": user.modified + } + + frappe.response.message = response_data + frappe.response.status_code = 200 + + except Exception as e: + frappe.log_error(f"Error in get_user_details: {str(e)}") + frappe.response.message = {"error": str(e)} + frappe.response.status_code = 500 + +@frappe.whitelist(allow_guest=False) +def get_doctype_records(doctype, filters=None, fields=None, limit=20, offset=0): + """ + Get records from any DocType with filtering and pagination + Usage: /api/method/asset_lite.api.custom_api.get_doctype_records + """ + try: + # Parse filters and fields if provided as JSON strings + if isinstance(filters, str): + filters = json.loads(filters) + if isinstance(fields, str): + fields = json.loads(fields) + + # Build the query + query_filters = filters or {} + + # Get records + records = frappe.get_list( + doctype, + filters=query_filters, + fields=fields or ["*"], + limit=limit, + start=offset, + order_by="creation desc" + ) + + # Get total count for pagination + total_count = frappe.db.count(doctype, query_filters) + + response_data = { + "records": records, + "total_count": total_count, + "limit": limit, + "offset": offset, + "has_more": (offset + limit) < total_count + } + + frappe.response.message = response_data + frappe.response.status_code = 200 + + except Exception as e: + frappe.log_error(f"Error in get_doctype_records: {str(e)}") + frappe.response.message = {"error": str(e)} + frappe.response.status_code = 500 + +@frappe.whitelist(allow_guest=False) +def get_dashboard_stats(): + """ + Get dashboard statistics + Usage: /api/method/asset_lite.api.custom_api.get_dashboard_stats + """ + try: + # Example: Get counts for different DocTypes + stats = { + "total_users": frappe.db.count("User", {"enabled": 1}), + "total_customers": frappe.db.count("Customer"), + "total_items": frappe.db.count("Item"), + "total_orders": frappe.db.count("Sales Order"), + "recent_activities": [] + } + + # Get recent activities (example) + recent_users = frappe.get_list( + "User", + fields=["name", "full_name", "creation"], + limit=5, + order_by="creation desc" + ) + + stats["recent_activities"] = recent_users + + frappe.response.message = stats + frappe.response.status_code = 200 + + except Exception as e: + frappe.log_error(f"Error in get_dashboard_stats: {str(e)}") + frappe.response.message = {"error": str(e)} + frappe.response.status_code = 500 + +# Example KYC API for your KYCDetails component +@frappe.whitelist(allow_guest=False) +def get_kyc_details(): + """ + Get KYC details - customize this based on your actual KYC DocType + Usage: /api/method/asset_lite.api.custom_api.get_kyc_details + """ + try: + # Replace 'KYC' with your actual DocType name + kyc_records = frappe.get_list( + "KYC", # Change this to your actual DocType + fields=["name", "kyc_status", "kyc_type", "creation"], + limit=50, + order_by="creation desc" + ) + + frappe.response.message = kyc_records + frappe.response.status_code = 200 + + except Exception as e: + frappe.log_error(f"Error in get_kyc_details: {str(e)}") + frappe.response.message = {"error": str(e)} + frappe.response.status_code = 500 + +# Simple test endpoint to verify API is working +@frappe.whitelist(allow_guest=False) +def test_api(): + """ + Simple test endpoint to verify the API is working + Usage: /api/method/asset_lite.api.custom_api.test_api + """ + try: + frappe.response.message = { + "status": "success", + "message": "API is working!", + "user": frappe.session.user, + "timestamp": now() + } + frappe.response.status_code = 200 + + except Exception as e: + frappe.log_error(f"Error in test_api: {str(e)}") + frappe.response.message = {"error": str(e)} + frappe.response.status_code = 500 diff --git a/asset_lite/api/dashboard_api.py b/asset_lite/api/dashboard_api.py new file mode 100644 index 0000000..5283401 --- /dev/null +++ b/asset_lite/api/dashboard_api.py @@ -0,0 +1,247 @@ +import frappe +from frappe import _ +from frappe.utils import nowdate +import json + + +def _ok(payload, code=200): + frappe.response.status_code = code + frappe.response.message = payload + + +def _err(msg, code=500): + frappe.response.status_code = code + frappe.response.message = {"error": msg} + + +@frappe.whitelist(allow_guest = True) +def get_number_cards(): + """ + Returns counts for Number Cards: + - total_assets + - work_orders_open + - work_orders_in_progress + - work_orders_completed + """ + try: + total_assets = frappe.db.count("Asset") + work_orders_open = frappe.db.count("Work Order", {"status": ["in", ["Not Started", "Open", "Pending"]]}) + work_orders_in_progress = frappe.db.count("Work Order", {"status": ["in", ["In Process", "In Progress", "Started"]]}) + work_orders_completed = frappe.db.count("Work Order", {"status": ["in", ["Completed", "Closed", "Finished"]]}) + + _ok({ + "total_assets": total_assets, + "work_orders_open": work_orders_open, + "work_orders_in_progress": work_orders_in_progress, + "work_orders_completed": work_orders_completed, + }) + except Exception as e: + frappe.log_error(frappe.get_traceback(), "get_number_cards") + _err(str(e)) + + +@frappe.whitelist(allow_guest = True) +def list_dashboard_charts(search=None, public_only=True, limit=50): + """ + List available Dashboard Chart docs and their y-axis rows. + """ + try: + filters = {} + if str(public_only) in ("1", "true", "True"): # tolerate string flags + filters["is_public"] = 1 + + charts = frappe.get_all( + "Dashboard Chart", + filters=filters, + fields=[ + "name", + "chart_name", + "type", + "is_public", + "chart_type", + "report_name", + "use_report_chart", + "x_field", + "time_interval", + "timespan", + "custom_options", + ], + limit=int(limit or 50), + order_by="modified desc", + ) + + for c in charts: + y_rows = frappe.get_all( + "Dashboard Chart Field", + filters={"parenttype": "Dashboard Chart", "parent": c["name"]}, + fields=["y_field", "color"], + order_by="idx asc", + ) + c["y_axes"] = y_rows + + _ok({"charts": charts}) + except Exception as e: + frappe.log_error(frappe.get_traceback(), "list_dashboard_charts") + _err(str(e)) + + +@frappe.whitelist(allow_guest = True) +def get_dashboard_chart_data(chart_name, report_filters=None): + """ + Return chart-ready JSON for any Dashboard Chart (Report-based or Custom). + """ + try: + if isinstance(report_filters, str): + report_filters = json.loads(report_filters or "{}") + report_filters = report_filters or {} + + chart = frappe.get_doc("Dashboard Chart", chart_name) + + # Handle Custom charts (non-Report based) + if chart.chart_type != "Report" or not chart.use_report_chart: + # For Custom charts, query the source doctype directly + try: + # Get chart configuration + source = chart.document_type + based_on = chart.based_on + value_based_on = chart.value_based_on or "name" + + # Build aggregation query + if chart.type == "Pie": + # Group by based_on field and count + data = frappe.db.sql(f""" + SELECT {based_on} as label, COUNT({value_based_on}) as value + FROM `tab{source}` + GROUP BY {based_on} + ORDER BY value DESC + """, as_dict=True) + + labels = [str(d.get("label")) for d in data] + values = [float(d.get("value") or 0) for d in data] + + _ok({ + "labels": labels, + "datasets": [{"name": "count", "values": values}], + "type": "Pie", + "options": _parse_custom_options(chart.custom_options), + "source": {"doctype": source}, + }) + else: + # Bar chart: group by based_on + data = frappe.db.sql(f""" + SELECT {based_on} as label, COUNT({value_based_on}) as value + FROM `tab{source}` + GROUP BY {based_on} + ORDER BY value DESC + LIMIT 20 + """, as_dict=True) + + labels = [str(d.get("label")) for d in data] + values = [float(d.get("value") or 0) for d in data] + + _ok({ + "labels": labels, + "datasets": [{"name": "count", "values": values, "color": "#4F46E5"}], + "type": "Bar", + "options": _parse_custom_options(chart.custom_options), + "source": {"doctype": source}, + }) + except Exception as e: + frappe.log_error(frappe.get_traceback(), f"Custom Chart Error: {chart_name}") + _err(f"Error processing custom chart: {str(e)}") + return + + y_axes = frappe.get_all( + "Dashboard Chart Field", + filters={"parenttype": "Dashboard Chart", "parent": chart.name}, + fields=["y_field", "color"], + order_by="idx asc", + ) + + run = frappe.get_attr("frappe.desk.query_report.run") + report_result = run(chart.report_name, filters=report_filters) + rows = report_result.get("result", []) or [] + data_rows = [r for r in rows if not r.get("is_total_row")] + + x_key = chart.x_field + labels = [str(r.get(x_key)) for r in data_rows if r.get(x_key) is not None] + + datasets = [] + for y in y_axes: + series_name = y.get("y_field") # Use field name as series name + values = [] + for r in data_rows: + val = r.get(y.get("y_field")) + try: + values.append(float(val) if val is not None else 0) + except Exception: + values.append(0) + datasets.append({"name": series_name, "values": values, "color": y.get("color")}) + + chart_type = (chart.type or "Bar").title() + if chart_type.lower() == "pie": + ds = datasets[0] if datasets else {"name": "value", "values": []} + _ok({ + "labels": labels, + "datasets": [ds], + "type": "Pie", + "options": _parse_custom_options(chart.custom_options), + "source": {"report": chart.report_name}, + }) + return + + _ok({ + "labels": labels, + "datasets": datasets, + "type": chart_type, + "options": _parse_custom_options(chart.custom_options), + "source": {"report": chart.report_name}, + }) + except Exception as e: + frappe.log_error(frappe.get_traceback(), "get_dashboard_chart_data") + _err(str(e)) + + +def _parse_custom_options(raw): + if not raw: + return {} + try: + if isinstance(raw, dict): + return raw + return json.loads(raw) + except Exception: + return {} + + +@frappe.whitelist(allow_guest = True) +def get_repair_cost_by_item(year=None): + """ + Example specialized endpoint for 'Repair Cost' report style chart + (X: item_code, Y: amount, Filter: Year) + """ + try: + year = int(year or frappe.utils.getdate(nowdate()).year) + rows = frappe.db.sql( + """ + SELECT item_code, SUM(amount) as amount + FROM `tabWork Order` wo + WHERE YEAR(wo.posting_date) = %(year)s + GROUP BY item_code + ORDER BY amount DESC + """, + {"year": year}, + as_dict=True, + ) + labels = [r.item_code or "Unknown" for r in rows] + values = [float(r.amount or 0) for r in rows] + _ok({ + "labels": labels, + "datasets": [{"name": f"Repair Cost {year}", "values": values}], + "type": "Bar", + "options": {}, + }) + except Exception as e: + frappe.log_error(frappe.get_traceback(), "get_repair_cost_by_item") + _err(str(e)) + + diff --git a/asset_lite/api/ppm_api.py b/asset_lite/api/ppm_api.py new file mode 100644 index 0000000..a52c7a3 --- /dev/null +++ b/asset_lite/api/ppm_api.py @@ -0,0 +1,565 @@ +import frappe +from frappe import _ + +@frappe.whitelist(allow_guest = True) +def get_asset_maintenances(filters=None, fields=None, limit=20, offset=0, order_by=None): + """ + Get list of asset maintenances (PPM schedules) with filters and pagination + + Args: + filters: JSON string of filters (e.g., '{"company": "Al Jouf Hospital"}') + fields: JSON string of fields to return (e.g., '["asset_name", "maintenance_team"]') + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + order_by: Sort order (e.g., "creation desc") + + Returns: + { + "asset_maintenances": [...], + "total_count": int, + "limit": int, + "offset": int, + "has_more": bool + } + """ + try: + # Parse filters if provided + if filters and isinstance(filters, str): + import json + filters = json.loads(filters) + + # Parse fields if provided + if fields and isinstance(fields, str): + import json + fields = json.loads(fields) + else: + # Default fields to return + fields = [ + 'name', + 'company', + 'asset_name', + 'custom_asset_type', + 'asset_category', + 'custom_type_of_maintenance', + 'custom_asset_name', + 'item_code', + 'item_name', + 'maintenance_team', + 'custom_pm_schedule', + 'maintenance_manager', + 'maintenance_manager_name', + 'custom_warranty', + 'custom_warranty_status', + 'custom_service_contract', + 'custom_service_contract_status', + 'custom_frequency', + 'custom_total_amount', + 'custom_no_of_pms', + 'custom_price_per_pm', + 'creation', + 'modified', + 'owner', + 'modified_by', + 'docstatus', + 'idx' + ] + + # Get total count + total_count = frappe.db.count('Asset Maintenance', filters=filters or {}) + + # Get asset maintenances + asset_maintenances = frappe.get_all( + 'Asset Maintenance', + filters=filters or {}, + fields=fields, + limit_page_length=int(limit), + limit_start=int(offset), + order_by=order_by or 'creation desc' + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'asset_maintenances': asset_maintenances, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Asset Maintenances API Error') + frappe.response['message'] = { + 'error': str(e), + 'asset_maintenances': [], + 'total_count': 0 + } + + +@frappe.whitelist(allow_guest = True) +def get_asset_maintenance_details(maintenance_name): + """ + Get detailed information about a specific asset maintenance (PPM schedule) + + Args: + maintenance_name: Name/ID of the asset maintenance + + Returns: + Asset Maintenance document with all fields including child tables + """ + try: + if not maintenance_name: + frappe.throw(_('Asset Maintenance name is required')) + + # Check if user has permission to read this maintenance + if not frappe.has_permission('Asset Maintenance', 'read', maintenance_name): + frappe.throw(_('Not permitted to access this asset maintenance')) + + # Get asset maintenance details + maintenance = frappe.get_doc('Asset Maintenance', maintenance_name) + + frappe.response['message'] = maintenance.as_dict() + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Asset Maintenance Details API Error') + frappe.response['message'] = { + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def create_asset_maintenance(maintenance_data): + """ + Create a new asset maintenance (PPM schedule) + + Args: + maintenance_data: JSON string containing asset maintenance fields + + Returns: + Created asset maintenance document + """ + try: + import json + + # Parse maintenance data + if isinstance(maintenance_data, str): + maintenance_data = json.loads(maintenance_data) + + # Check if user has permission to create asset maintenance + if not frappe.has_permission('Asset Maintenance', 'create'): + frappe.throw(_('Not permitted to create asset maintenance')) + + # Create new asset maintenance + maintenance = frappe.get_doc({ + 'doctype': 'Asset Maintenance', + **maintenance_data + }) + + maintenance.insert() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'asset_maintenance': maintenance.as_dict(), + 'message': _('Asset Maintenance created successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Create Asset Maintenance API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def update_asset_maintenance(maintenance_name, maintenance_data): + """ + Update an existing asset maintenance (PPM schedule) + + Args: + maintenance_name: Name/ID of the asset maintenance + maintenance_data: JSON string containing fields to update + + Returns: + Updated asset maintenance document + """ + try: + import json + + if not maintenance_name: + frappe.throw(_('Asset Maintenance name is required')) + + # Parse maintenance data + if isinstance(maintenance_data, str): + maintenance_data = json.loads(maintenance_data) + + # Check if user has permission to update this maintenance + if not frappe.has_permission('Asset Maintenance', 'write', maintenance_name): + frappe.throw(_('Not permitted to update this asset maintenance')) + + # Get and update asset maintenance + maintenance = frappe.get_doc('Asset Maintenance', maintenance_name) + + # Update fields + for key, value in maintenance_data.items(): + if hasattr(maintenance, key): + setattr(maintenance, key, value) + + maintenance.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'asset_maintenance': maintenance.as_dict(), + 'message': _('Asset Maintenance updated successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Update Asset Maintenance API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def delete_asset_maintenance(maintenance_name): + """ + Delete an asset maintenance (PPM schedule) + + Args: + maintenance_name: Name/ID of the asset maintenance + + Returns: + Success message + """ + try: + if not maintenance_name: + frappe.throw(_('Asset Maintenance name is required')) + + # Check if user has permission to delete this maintenance + if not frappe.has_permission('Asset Maintenance', 'delete', maintenance_name): + frappe.throw(_('Not permitted to delete this asset maintenance')) + + # Delete asset maintenance + frappe.delete_doc('Asset Maintenance', maintenance_name) + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'message': _('Asset Maintenance deleted successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Delete Asset Maintenance API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def get_maintenance_tasks(maintenance_name): + """ + Get all maintenance tasks for a specific asset maintenance + + Args: + maintenance_name: Name/ID of the asset maintenance + + Returns: + List of maintenance tasks (asset_maintenance_tasks child table) + """ + try: + if not maintenance_name: + frappe.throw(_('Asset Maintenance name is required')) + + # Check if user has permission to read this maintenance + if not frappe.has_permission('Asset Maintenance', 'read', maintenance_name): + frappe.throw(_('Not permitted to access this asset maintenance')) + + # Get maintenance tasks + tasks = frappe.get_all( + 'Asset Maintenance Task', + filters={'parent': maintenance_name}, + fields=['*'], + order_by='idx asc' + ) + + frappe.response['message'] = { + 'maintenance_tasks': tasks, + 'total_count': len(tasks) + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Maintenance Tasks API Error') + frappe.response['message'] = { + 'error': str(e), + 'maintenance_tasks': [] + } + + +@frappe.whitelist(allow_guest = True) +def get_service_coverage(maintenance_name): + """ + Get service coverage details for a specific asset maintenance + + Args: + maintenance_name: Name/ID of the asset maintenance + + Returns: + List of service coverage (custom_service_coverage_table child table) + """ + try: + if not maintenance_name: + frappe.throw(_('Asset Maintenance name is required')) + + # Check if user has permission to read this maintenance + if not frappe.has_permission('Asset Maintenance', 'read', maintenance_name): + frappe.throw(_('Not permitted to access this asset maintenance')) + + # Get service coverage + coverage = frappe.get_all( + 'Service Coverage', + filters={'parent': maintenance_name}, + fields=['*'], + order_by='idx asc' + ) + + frappe.response['message'] = { + 'service_coverage': coverage, + 'total_count': len(coverage) + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Service Coverage API Error') + frappe.response['message'] = { + 'error': str(e), + 'service_coverage': [] + } + + +@frappe.whitelist(allow_guest = True) +def add_maintenance_task(maintenance_name, task_data): + """ + Add a new maintenance task to an asset maintenance + + Args: + maintenance_name: Name/ID of the asset maintenance + task_data: JSON string containing task fields + + Returns: + Updated asset maintenance document + """ + try: + import json + + if not maintenance_name: + frappe.throw(_('Asset Maintenance name is required')) + + # Parse task data + if isinstance(task_data, str): + task_data = json.loads(task_data) + + # Check if user has permission to update this maintenance + if not frappe.has_permission('Asset Maintenance', 'write', maintenance_name): + frappe.throw(_('Not permitted to update this asset maintenance')) + + # Get asset maintenance + maintenance = frappe.get_doc('Asset Maintenance', maintenance_name) + + # Add new task + maintenance.append('asset_maintenance_tasks', task_data) + maintenance.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'asset_maintenance': maintenance.as_dict(), + 'message': _('Maintenance task added successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Add Maintenance Task API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def update_maintenance_task(task_name, task_data): + """ + Update a specific maintenance task + + Args: + task_name: Name/ID of the maintenance task + task_data: JSON string containing fields to update + + Returns: + Updated task details + """ + try: + import json + + if not task_name: + frappe.throw(_('Maintenance task name is required')) + + # Parse task data + if isinstance(task_data, str): + task_data = json.loads(task_data) + + # Get the task to find parent + task = frappe.get_doc('Asset Maintenance Task', task_name) + + # Check if user has permission to update parent maintenance + if not frappe.has_permission('Asset Maintenance', 'write', task.parent): + frappe.throw(_('Not permitted to update this maintenance task')) + + # Update task fields + for key, value in task_data.items(): + if hasattr(task, key): + setattr(task, key, value) + + task.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'maintenance_task': task.as_dict(), + 'message': _('Maintenance task updated successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Update Maintenance Task API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest = True) +def get_maintenances_by_asset(asset_name, filters=None, limit=20, offset=0): + """ + Get all maintenance schedules for a specific asset + + Args: + asset_name: Name/ID of the asset + filters: Additional JSON string of filters + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + + Returns: + List of maintenance schedules for the asset + """ + try: + import json + + if not asset_name: + frappe.throw(_('Asset name is required')) + + # Parse additional filters if provided + additional_filters = {} + if filters and isinstance(filters, str): + additional_filters = json.loads(filters) + + # Combine filters + combined_filters = {'asset_name': asset_name, **additional_filters} + + # Get total count + total_count = frappe.db.count('Asset Maintenance', filters=combined_filters) + + # Get maintenances + maintenances = frappe.get_all( + 'Asset Maintenance', + filters=combined_filters, + fields=['*'], + limit_page_length=int(limit), + limit_start=int(offset), + order_by='creation desc' + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'asset_maintenances': maintenances, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Maintenances By Asset API Error') + frappe.response['message'] = { + 'error': str(e), + 'asset_maintenances': [], + 'total_count': 0 + } + + +@frappe.whitelist(allow_guest = True) +def get_active_service_contracts(filters=None, limit=20, offset=0): + """ + Get all asset maintenances with active service contracts + + Args: + filters: Additional JSON string of filters + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + + Returns: + List of asset maintenances with active contracts + """ + try: + import json + + # Parse additional filters if provided + additional_filters = {} + if filters and isinstance(filters, str): + additional_filters = json.loads(filters) + + # Combine filters - get maintenances with service contract = 1 + combined_filters = { + 'custom_service_contract': 1, + **additional_filters + } + + # Get total count + total_count = frappe.db.count('Asset Maintenance', filters=combined_filters) + + # Get maintenances + maintenances = frappe.get_all( + 'Asset Maintenance', + filters=combined_filters, + fields=['*'], + limit_page_length=int(limit), + limit_start=int(offset), + order_by='creation desc' + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'asset_maintenances': maintenances, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Active Service Contracts API Error') + frappe.response['message'] = { + 'error': str(e), + 'asset_maintenances': [], + 'total_count': 0 + } diff --git a/asset_lite/api/ppm_generator_api.py b/asset_lite/api/ppm_generator_api.py new file mode 100644 index 0000000..3759199 --- /dev/null +++ b/asset_lite/api/ppm_generator_api.py @@ -0,0 +1,1197 @@ +import frappe +from frappe import _ + +# Default fields for PM Schedule Generator doctype +PM_SCHEDULE_GENERATOR_FIELDS = [ + 'name', + 'owner', + 'creation', + 'modified', + 'modified_by', + 'docstatus', + 'idx', + 'hospital', + 'modality', + 'device_status', + 'start_date', + 'maintenance_team', + 'maintenance_manager', + 'end_date', + 'periodicity', + 'assign_to', + 'due_date' +] + +# Child table: PM Entry Line (maintenance_entries) +PM_ENTRY_LINE_FIELDS = [ + 'name', + 'owner', + 'creation', + 'modified', + 'modified_by', + 'docstatus', + 'idx', + 'parent', + 'parentfield', + 'parenttype', + 'asset', + 'asset_name', + 'start_date', + 'end_date', + 'manufacturer', + 'model' +] + +@frappe.whitelist() +def create_bulk_schedules(asset_names, start_date, end_date, maintenance_team=None, periodicity='Monthly', maintenance_type='Preventive'): + """Create maintenance schedules for multiple assets""" + created_schedules = [] + for asset_name in asset_names: + # Create Asset Maintenance record + maintenance = frappe.get_doc({ + 'doctype': 'Asset Maintenance', + 'asset_name': asset_name, + 'maintenance_type': maintenance_type, + 'periodicity': periodicity, + 'maintenance_team': maintenance_team + }) + maintenance.insert() + created_schedules.append(maintenance.name) + return { + 'success': True, + 'created': len(created_schedules), + 'schedules': created_schedules + } + +def get_child_table_data(parent_name, parentfield, child_doctype, fields=None): + """ + Get child table data for a PM Schedule Generator + + Args: + parent_name: Name of the parent PM Schedule Generator + parentfield: Field name of the child table in parent + child_doctype: Doctype of the child table + fields: List of fields to return + + Returns: + List of child table records + """ + try: + return frappe.get_all( + child_doctype, + filters={ + 'parent': parent_name, + 'parentfield': parentfield, + 'parenttype': 'PM Schedule Generator' + }, + fields=fields or ['*'], + order_by='idx asc' + ) + except Exception: + return [] + + +@frappe.whitelist(allow_guest=True) +def get_pm_schedules(filters=None, fields=None, limit=20, offset=0, order_by=None, include_child_tables=False): + """ + Get list of PM Schedule Generators with filters and pagination + + Args: + filters: JSON string of filters (e.g., '{"hospital": "Domat Al Jandal Hospital"}') + fields: JSON string of fields to return (e.g., '["hospital", "modality"]') + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + order_by: Sort order (e.g., "creation desc") + include_child_tables: Whether to include child table data (default: False) + + Returns: + { + "pm_schedules": [...], + "total_count": int, + "limit": int, + "offset": int, + "has_more": bool + } + """ + try: + import json + + # Parse filters if provided + if filters and isinstance(filters, str): + filters = json.loads(filters) + + # Parse fields if provided + if fields and isinstance(fields, str): + fields = json.loads(fields) + else: + fields = PM_SCHEDULE_GENERATOR_FIELDS.copy() + + # Parse include_child_tables + if isinstance(include_child_tables, str): + include_child_tables = include_child_tables.lower() in ('true', '1', 'yes') + + # Get total count + total_count = frappe.db.count('PM Schedule Generator', filters=filters or {}) + + # Get PM schedules + pm_schedules = frappe.get_all( + 'PM Schedule Generator', + filters=filters or {}, + fields=fields, + limit_page_length=int(limit), + limit_start=int(offset), + order_by=order_by or 'creation desc' + ) + + # Include child tables if requested + if include_child_tables: + for pm_schedule in pm_schedules: + pm_schedule['maintenance_entries'] = get_child_table_data( + pm_schedule['name'], + 'maintenance_entries', + 'PM Entry Line', + PM_ENTRY_LINE_FIELDS + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'pm_schedules': pm_schedules, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get PM Schedules API Error') + frappe.response['message'] = { + 'error': str(e), + 'pm_schedules': [], + 'total_count': 0 + } + + +@frappe.whitelist(allow_guest=True) +def get_pm_schedule_details(pm_schedule_name): + """ + Get detailed information about a specific PM Schedule Generator + + Args: + pm_schedule_name: Name/ID of the PM Schedule Generator + + Returns: + PM Schedule Generator document with all fields including child tables + """ + try: + if not pm_schedule_name: + frappe.throw(_('PM Schedule Generator name is required')) + + # Check if user has permission to read this PM Schedule + if not frappe.has_permission('PM Schedule Generator', 'read', pm_schedule_name): + frappe.throw(_('Not permitted to access this PM Schedule Generator')) + + # Get PM Schedule details + pm_schedule = frappe.get_doc('PM Schedule Generator', pm_schedule_name) + + # Convert to dict and include child tables + pm_schedule_dict = pm_schedule.as_dict() + + # Ensure child tables are included with all fields + pm_schedule_dict['maintenance_entries'] = [item.as_dict() for item in pm_schedule.get('maintenance_entries', [])] + + frappe.response['message'] = pm_schedule_dict + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get PM Schedule Details API Error') + frappe.response['message'] = { + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def create_pm_schedule(pm_schedule_data): + """ + Create a new PM Schedule Generator + + Args: + pm_schedule_data: JSON string containing PM Schedule fields including child tables + Example: + { + "hospital": "Domat Al Jandal Hospital", + "modality": "X-Ray", + "start_date": "2025-12-23", + "end_date": "2026-12-23", + "periodicity": "Monthly", + "maintenance_team": "DAJH Maintenance Team", + "maintenance_manager": "manager@example.com", + "assign_to": "technician@example.com", + "due_date": "2026-01-23", + "maintenance_entries": [ + { + "asset": "ACC-ASS-2025-00100", + "asset_name": "Test Asset 1", + "start_date": "2025-12-23", + "end_date": "2026-12-23", + "manufacturer": "ABV", + "model": "" + } + ] + } + + Returns: + Created PM Schedule Generator document + """ + try: + import json + + # Parse PM schedule data + if isinstance(pm_schedule_data, str): + pm_schedule_data = json.loads(pm_schedule_data) + + # Check if user has permission to create PM Schedule + if not frappe.has_permission('PM Schedule Generator', 'create'): + frappe.throw(_('Not permitted to create PM Schedule Generator')) + + # Create new PM Schedule + pm_schedule = frappe.get_doc({ + 'doctype': 'PM Schedule Generator', + **pm_schedule_data + }) + + pm_schedule.insert() + frappe.db.commit() + + # Return created PM Schedule with child tables + pm_schedule_dict = pm_schedule.as_dict() + pm_schedule_dict['maintenance_entries'] = [item.as_dict() for item in pm_schedule.get('maintenance_entries', [])] + + frappe.response['message'] = { + 'success': True, + 'pm_schedule': pm_schedule_dict, + 'message': _('PM Schedule Generator created successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Create PM Schedule API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def update_pm_schedule(pm_schedule_name, pm_schedule_data): + """ + Update an existing PM Schedule Generator including child tables + + Args: + pm_schedule_name: Name/ID of the PM Schedule Generator + pm_schedule_data: JSON string containing fields to update + Example: + { + "periodicity": "Quarterly", + "maintenance_entries": [ + { + "asset": "ACC-ASS-2025-00100", + "asset_name": "Test Asset 1", + "start_date": "2025-12-23", + "end_date": "2026-12-23" + } + ] + } + + Returns: + Updated PM Schedule Generator document + """ + try: + import json + + if not pm_schedule_name: + frappe.throw(_('PM Schedule Generator name is required')) + + # Parse PM schedule data + if isinstance(pm_schedule_data, str): + pm_schedule_data = json.loads(pm_schedule_data) + + # Check if user has permission to update this PM Schedule + if not frappe.has_permission('PM Schedule Generator', 'write', pm_schedule_name): + frappe.throw(_('Not permitted to update this PM Schedule Generator')) + + # Get PM Schedule + pm_schedule = frappe.get_doc('PM Schedule Generator', pm_schedule_name) + + # Handle child tables separately + child_tables = ['maintenance_entries'] + + for key, value in pm_schedule_data.items(): + if key in child_tables: + # Clear existing child table entries and add new ones + pm_schedule.set(key, []) + for item in value: + pm_schedule.append(key, item) + elif hasattr(pm_schedule, key): + setattr(pm_schedule, key, value) + + pm_schedule.save() + frappe.db.commit() + + # Return updated PM Schedule with child tables + pm_schedule_dict = pm_schedule.as_dict() + pm_schedule_dict['maintenance_entries'] = [item.as_dict() for item in pm_schedule.get('maintenance_entries', [])] + + frappe.response['message'] = { + 'success': True, + 'pm_schedule': pm_schedule_dict, + 'message': _('PM Schedule Generator updated successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Update PM Schedule API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def delete_pm_schedule(pm_schedule_name): + """ + Delete a PM Schedule Generator + + Args: + pm_schedule_name: Name/ID of the PM Schedule Generator + + Returns: + Success message + """ + try: + if not pm_schedule_name: + frappe.throw(_('PM Schedule Generator name is required')) + + # Check if user has permission to delete this PM Schedule + if not frappe.has_permission('PM Schedule Generator', 'delete', pm_schedule_name): + frappe.throw(_('Not permitted to delete this PM Schedule Generator')) + + # Delete PM Schedule (child tables will be deleted automatically) + frappe.delete_doc('PM Schedule Generator', pm_schedule_name) + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'message': _('PM Schedule Generator deleted successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Delete PM Schedule API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def submit_pm_schedule(pm_schedule_name): + """ + Submit a PM Schedule Generator (change docstatus to 1) + + Args: + pm_schedule_name: Name/ID of the PM Schedule Generator + + Returns: + Submitted PM Schedule Generator document + """ + try: + if not pm_schedule_name: + frappe.throw(_('PM Schedule Generator name is required')) + + # Check if user has permission to submit this PM Schedule + if not frappe.has_permission('PM Schedule Generator', 'submit', pm_schedule_name): + frappe.throw(_('Not permitted to submit this PM Schedule Generator')) + + # Get and submit PM Schedule + pm_schedule = frappe.get_doc('PM Schedule Generator', pm_schedule_name) + pm_schedule.submit() + frappe.db.commit() + + # Return submitted PM Schedule with child tables + pm_schedule_dict = pm_schedule.as_dict() + pm_schedule_dict['maintenance_entries'] = [item.as_dict() for item in pm_schedule.get('maintenance_entries', [])] + + frappe.response['message'] = { + 'success': True, + 'pm_schedule': pm_schedule_dict, + 'message': _('PM Schedule Generator submitted successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Submit PM Schedule API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def cancel_pm_schedule(pm_schedule_name): + """ + Cancel a PM Schedule Generator (change docstatus to 2) + + Args: + pm_schedule_name: Name/ID of the PM Schedule Generator + + Returns: + Cancelled PM Schedule Generator document + """ + try: + if not pm_schedule_name: + frappe.throw(_('PM Schedule Generator name is required')) + + # Check if user has permission to cancel this PM Schedule + if not frappe.has_permission('PM Schedule Generator', 'cancel', pm_schedule_name): + frappe.throw(_('Not permitted to cancel this PM Schedule Generator')) + + # Get and cancel PM Schedule + pm_schedule = frappe.get_doc('PM Schedule Generator', pm_schedule_name) + pm_schedule.cancel() + frappe.db.commit() + + # Return cancelled PM Schedule with child tables + pm_schedule_dict = pm_schedule.as_dict() + pm_schedule_dict['maintenance_entries'] = [item.as_dict() for item in pm_schedule.get('maintenance_entries', [])] + + frappe.response['message'] = { + 'success': True, + 'pm_schedule': pm_schedule_dict, + 'message': _('PM Schedule Generator cancelled successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Cancel PM Schedule API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def add_maintenance_entry(pm_schedule_name, entry_data): + """ + Add a maintenance entry to PM Schedule's maintenance_entries child table + + Args: + pm_schedule_name: Name/ID of the PM Schedule Generator + entry_data: JSON string containing maintenance entry fields + Example: + { + "asset": "ACC-ASS-2025-00100", + "asset_name": "Test Asset 1", + "start_date": "2025-12-23", + "end_date": "2026-12-23", + "manufacturer": "ABV", + "model": "Model X" + } + + Returns: + Updated PM Schedule with maintenance entries + """ + try: + import json + + if not pm_schedule_name: + frappe.throw(_('PM Schedule Generator name is required')) + + # Parse entry data + if isinstance(entry_data, str): + entry_data = json.loads(entry_data) + + # Check if user has permission to update this PM Schedule + if not frappe.has_permission('PM Schedule Generator', 'write', pm_schedule_name): + frappe.throw(_('Not permitted to update this PM Schedule Generator')) + + # Get PM Schedule and add maintenance entry + pm_schedule = frappe.get_doc('PM Schedule Generator', pm_schedule_name) + pm_schedule.append('maintenance_entries', entry_data) + pm_schedule.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'maintenance_entries': [item.as_dict() for item in pm_schedule.get('maintenance_entries', [])], + 'message': _('Maintenance entry added successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Add Maintenance Entry API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def remove_maintenance_entry(pm_schedule_name, entry_name): + """ + Remove a maintenance entry from PM Schedule's maintenance_entries child table + + Args: + pm_schedule_name: Name/ID of the PM Schedule Generator + entry_name: Name/ID of the maintenance entry to remove + + Returns: + Updated PM Schedule with remaining maintenance entries + """ + try: + if not pm_schedule_name: + frappe.throw(_('PM Schedule Generator name is required')) + + if not entry_name: + frappe.throw(_('Maintenance entry name is required')) + + # Check if user has permission to update this PM Schedule + if not frappe.has_permission('PM Schedule Generator', 'write', pm_schedule_name): + frappe.throw(_('Not permitted to update this PM Schedule Generator')) + + # Get PM Schedule + pm_schedule = frappe.get_doc('PM Schedule Generator', pm_schedule_name) + + # Find and remove the maintenance entry + entry_to_remove = None + for entry in pm_schedule.maintenance_entries: + if entry.name == entry_name: + entry_to_remove = entry + break + + if entry_to_remove: + pm_schedule.remove(entry_to_remove) + pm_schedule.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'maintenance_entries': [item.as_dict() for item in pm_schedule.get('maintenance_entries', [])], + 'message': _('Maintenance entry removed successfully') + } + else: + frappe.response['message'] = { + 'success': False, + 'error': _('Maintenance entry not found') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Remove Maintenance Entry API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def update_maintenance_entry(pm_schedule_name, entry_name, entry_data): + """ + Update a specific maintenance entry in the PM Schedule + + Args: + pm_schedule_name: Name/ID of the PM Schedule Generator + entry_name: Name/ID of the maintenance entry to update + entry_data: JSON string containing fields to update + + Returns: + Updated PM Schedule with maintenance entries + """ + try: + import json + + if not pm_schedule_name: + frappe.throw(_('PM Schedule Generator name is required')) + + if not entry_name: + frappe.throw(_('Maintenance entry name is required')) + + # Parse entry data + if isinstance(entry_data, str): + entry_data = json.loads(entry_data) + + # Check if user has permission to update this PM Schedule + if not frappe.has_permission('PM Schedule Generator', 'write', pm_schedule_name): + frappe.throw(_('Not permitted to update this PM Schedule Generator')) + + # Get PM Schedule + pm_schedule = frappe.get_doc('PM Schedule Generator', pm_schedule_name) + + # Find and update the maintenance entry + entry_found = False + for entry in pm_schedule.maintenance_entries: + if entry.name == entry_name: + for key, value in entry_data.items(): + if hasattr(entry, key): + setattr(entry, key, value) + entry_found = True + break + + if entry_found: + pm_schedule.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'maintenance_entries': [item.as_dict() for item in pm_schedule.get('maintenance_entries', [])], + 'message': _('Maintenance entry updated successfully') + } + else: + frappe.response['message'] = { + 'success': False, + 'error': _('Maintenance entry not found') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Update Maintenance Entry API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def get_pm_schedule_child_tables(pm_schedule_name, child_table=None): + """ + Get child table data for a PM Schedule Generator + + Args: + pm_schedule_name: Name/ID of the PM Schedule Generator + child_table: Specific child table to return ('maintenance_entries') + If None, returns all child tables + + Returns: + Child table data + """ + try: + if not pm_schedule_name: + frappe.throw(_('PM Schedule Generator name is required')) + + # Check if user has permission to read this PM Schedule + if not frappe.has_permission('PM Schedule Generator', 'read', pm_schedule_name): + frappe.throw(_('Not permitted to access this PM Schedule Generator')) + + # Get PM Schedule + pm_schedule = frappe.get_doc('PM Schedule Generator', pm_schedule_name) + + result = {} + + if child_table: + if child_table == 'maintenance_entries': + result['maintenance_entries'] = [item.as_dict() for item in pm_schedule.get('maintenance_entries', [])] + else: + frappe.throw(_('Invalid child table name')) + else: + result = { + 'maintenance_entries': [item.as_dict() for item in pm_schedule.get('maintenance_entries', [])] + } + + frappe.response['message'] = { + 'success': True, + 'pm_schedule_name': pm_schedule_name, + **result + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get PM Schedule Child Tables API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def bulk_update_maintenance_entries(pm_schedule_name, maintenance_entries): + """ + Bulk update/replace all maintenance entries in a PM Schedule Generator + + Args: + pm_schedule_name: Name/ID of the PM Schedule Generator + maintenance_entries: JSON string containing list of maintenance entries + + Returns: + Updated PM Schedule with new maintenance entries + """ + try: + import json + + if not pm_schedule_name: + frappe.throw(_('PM Schedule Generator name is required')) + + # Parse maintenance entries + if isinstance(maintenance_entries, str): + maintenance_entries = json.loads(maintenance_entries) + + # Check if user has permission to update this PM Schedule + if not frappe.has_permission('PM Schedule Generator', 'write', pm_schedule_name): + frappe.throw(_('Not permitted to update this PM Schedule Generator')) + + # Get PM Schedule + pm_schedule = frappe.get_doc('PM Schedule Generator', pm_schedule_name) + + # Clear existing maintenance entries and add new ones + pm_schedule.set('maintenance_entries', []) + for entry in maintenance_entries: + pm_schedule.append('maintenance_entries', entry) + + pm_schedule.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'maintenance_entries': [item.as_dict() for item in pm_schedule.get('maintenance_entries', [])], + 'message': _('Maintenance entries updated successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Bulk Update Maintenance Entries API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def get_pm_schedules_by_hospital(hospital, limit=20, offset=0): + """ + Get PM Schedules filtered by hospital + + Args: + hospital: Hospital name to filter by + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + + Returns: + List of PM Schedules for the specified hospital + """ + try: + if not hospital: + frappe.throw(_('Hospital name is required')) + + filters = {'hospital': hospital} + + # Get total count + total_count = frappe.db.count('PM Schedule Generator', filters=filters) + + # Get PM schedules + pm_schedules = frappe.get_all( + 'PM Schedule Generator', + filters=filters, + fields=PM_SCHEDULE_GENERATOR_FIELDS, + limit_page_length=int(limit), + limit_start=int(offset), + order_by='creation desc' + ) + + # Include child tables + for pm_schedule in pm_schedules: + pm_schedule['maintenance_entries'] = get_child_table_data( + pm_schedule['name'], + 'maintenance_entries', + 'PM Entry Line', + PM_ENTRY_LINE_FIELDS + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'pm_schedules': pm_schedules, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get PM Schedules By Hospital API Error') + frappe.response['message'] = { + 'error': str(e), + 'pm_schedules': [], + 'total_count': 0 + } + + +@frappe.whitelist(allow_guest=True) +def get_pm_schedules_by_maintenance_team(maintenance_team, limit=20, offset=0): + """ + Get PM Schedules filtered by maintenance team + + Args: + maintenance_team: Maintenance team name to filter by + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + + Returns: + List of PM Schedules for the specified maintenance team + """ + try: + if not maintenance_team: + frappe.throw(_('Maintenance team name is required')) + + filters = {'maintenance_team': maintenance_team} + + # Get total count + total_count = frappe.db.count('PM Schedule Generator', filters=filters) + + # Get PM schedules + pm_schedules = frappe.get_all( + 'PM Schedule Generator', + filters=filters, + fields=PM_SCHEDULE_GENERATOR_FIELDS, + limit_page_length=int(limit), + limit_start=int(offset), + order_by='creation desc' + ) + + # Include child tables + for pm_schedule in pm_schedules: + pm_schedule['maintenance_entries'] = get_child_table_data( + pm_schedule['name'], + 'maintenance_entries', + 'PM Entry Line', + PM_ENTRY_LINE_FIELDS + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'pm_schedules': pm_schedules, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get PM Schedules By Maintenance Team API Error') + frappe.response['message'] = { + 'error': str(e), + 'pm_schedules': [], + 'total_count': 0 + } + + +@frappe.whitelist(allow_guest=True) +def get_pm_schedules_by_assignee(assign_to, limit=20, offset=0): + """ + Get PM Schedules filtered by assigned user + + Args: + assign_to: Assigned user email to filter by + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + + Returns: + List of PM Schedules assigned to the specified user + """ + try: + if not assign_to: + frappe.throw(_('Assignee email is required')) + + filters = {'assign_to': assign_to} + + # Get total count + total_count = frappe.db.count('PM Schedule Generator', filters=filters) + + # Get PM schedules + pm_schedules = frappe.get_all( + 'PM Schedule Generator', + filters=filters, + fields=PM_SCHEDULE_GENERATOR_FIELDS, + limit_page_length=int(limit), + limit_start=int(offset), + order_by='due_date asc' + ) + + # Include child tables + for pm_schedule in pm_schedules: + pm_schedule['maintenance_entries'] = get_child_table_data( + pm_schedule['name'], + 'maintenance_entries', + 'PM Entry Line', + PM_ENTRY_LINE_FIELDS + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'pm_schedules': pm_schedules, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get PM Schedules By Assignee API Error') + frappe.response['message'] = { + 'error': str(e), + 'pm_schedules': [], + 'total_count': 0 + } + + +@frappe.whitelist(allow_guest=True) +def get_pm_schedules_by_date_range(start_date=None, end_date=None, limit=20, offset=0): + """ + Get PM Schedules within a date range + + Args: + start_date: Start date filter (YYYY-MM-DD) + end_date: End date filter (YYYY-MM-DD) + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + + Returns: + List of PM Schedules within the specified date range + """ + try: + filters = {} + + if start_date: + filters['start_date'] = ['>=', start_date] + + if end_date: + filters['end_date'] = ['<=', end_date] + + # Get total count + total_count = frappe.db.count('PM Schedule Generator', filters=filters) + + # Get PM schedules + pm_schedules = frappe.get_all( + 'PM Schedule Generator', + filters=filters, + fields=PM_SCHEDULE_GENERATOR_FIELDS, + limit_page_length=int(limit), + limit_start=int(offset), + order_by='start_date asc' + ) + + # Include child tables + for pm_schedule in pm_schedules: + pm_schedule['maintenance_entries'] = get_child_table_data( + pm_schedule['name'], + 'maintenance_entries', + 'PM Entry Line', + PM_ENTRY_LINE_FIELDS + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'pm_schedules': pm_schedules, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get PM Schedules By Date Range API Error') + frappe.response['message'] = { + 'error': str(e), + 'pm_schedules': [], + 'total_count': 0 + } + + +@frappe.whitelist(allow_guest=True) +def get_pm_schedules_by_periodicity(periodicity, limit=20, offset=0): + """ + Get PM Schedules filtered by periodicity + + Args: + periodicity: Periodicity to filter by (e.g., 'Monthly', 'Quarterly', 'Yearly') + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + + Returns: + List of PM Schedules with the specified periodicity + """ + try: + if not periodicity: + frappe.throw(_('Periodicity is required')) + + filters = {'periodicity': periodicity} + + # Get total count + total_count = frappe.db.count('PM Schedule Generator', filters=filters) + + # Get PM schedules + pm_schedules = frappe.get_all( + 'PM Schedule Generator', + filters=filters, + fields=PM_SCHEDULE_GENERATOR_FIELDS, + limit_page_length=int(limit), + limit_start=int(offset), + order_by='creation desc' + ) + + # Include child tables + for pm_schedule in pm_schedules: + pm_schedule['maintenance_entries'] = get_child_table_data( + pm_schedule['name'], + 'maintenance_entries', + 'PM Entry Line', + PM_ENTRY_LINE_FIELDS + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'pm_schedules': pm_schedules, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get PM Schedules By Periodicity API Error') + frappe.response['message'] = { + 'error': str(e), + 'pm_schedules': [], + 'total_count': 0 + } + + +@frappe.whitelist(allow_guest=True) +def get_overdue_pm_schedules(limit=20, offset=0): + """ + Get PM Schedules that are overdue (due_date < today and docstatus != 2) + + Args: + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + + Returns: + List of overdue PM Schedules + """ + try: + from frappe.utils import today + + filters = { + 'due_date': ['<', today()], + 'docstatus': ['!=', 2] # Exclude cancelled + } + + # Get total count + total_count = frappe.db.count('PM Schedule Generator', filters=filters) + + # Get PM schedules + pm_schedules = frappe.get_all( + 'PM Schedule Generator', + filters=filters, + fields=PM_SCHEDULE_GENERATOR_FIELDS, + limit_page_length=int(limit), + limit_start=int(offset), + order_by='due_date asc' + ) + + # Include child tables + for pm_schedule in pm_schedules: + pm_schedule['maintenance_entries'] = get_child_table_data( + pm_schedule['name'], + 'maintenance_entries', + 'PM Entry Line', + PM_ENTRY_LINE_FIELDS + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'pm_schedules': pm_schedules, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Overdue PM Schedules API Error') + frappe.response['message'] = { + 'error': str(e), + 'pm_schedules': [], + 'total_count': 0 + } + + +@frappe.whitelist(allow_guest=True) +def get_upcoming_pm_schedules(days=30, limit=20, offset=0): + """ + Get PM Schedules due within the specified number of days + + Args: + days: Number of days to look ahead (default: 30) + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + + Returns: + List of upcoming PM Schedules + """ + try: + from frappe.utils import today, add_days + + filters = { + 'due_date': ['between', [today(), add_days(today(), int(days))]], + 'docstatus': ['!=', 2] # Exclude cancelled + } + + # Get total count + total_count = frappe.db.count('PM Schedule Generator', filters=filters) + + # Get PM schedules + pm_schedules = frappe.get_all( + 'PM Schedule Generator', + filters=filters, + fields=PM_SCHEDULE_GENERATOR_FIELDS, + limit_page_length=int(limit), + limit_start=int(offset), + order_by='due_date asc' + ) + + # Include child tables + for pm_schedule in pm_schedules: + pm_schedule['maintenance_entries'] = get_child_table_data( + pm_schedule['name'], + 'maintenance_entries', + 'PM Entry Line', + PM_ENTRY_LINE_FIELDS + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'pm_schedules': pm_schedules, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Upcoming PM Schedules API Error') + frappe.response['message'] = { + 'error': str(e), + 'pm_schedules': [], + 'total_count': 0 + } \ No newline at end of file diff --git a/asset_lite/api/translation_api.py b/asset_lite/api/translation_api.py new file mode 100644 index 0000000..33b34c8 --- /dev/null +++ b/asset_lite/api/translation_api.py @@ -0,0 +1,139 @@ +import frappe +from frappe import _ + +@frappe.whitelist(allow_guest = True) +def get_translations(language='en'): + """ + Get all translations for a specific language from Frappe's Translation doctype + This returns a dictionary of source text -> translated text + + Usage: /api/method/asset_lite.api.translation_api.get_translations?language=ar + + Args: + language: Language code (e.g., 'en', 'ar') + + Returns: + Dictionary mapping source text to translated text + """ + try: + # Validate language parameter + if not language: + language = 'en' + + # Get all translations for the specified language + translations = frappe.get_all( + 'Translation', + filters={ + 'language': language + }, + fields=['source_text', 'translated_text'], + limit_page_length=0 # 0 means no limit (get all) + ) + + # Convert to dictionary format: {source_text: translated_text} + translation_dict = {} + for trans in translations: + source = trans.get('source_text') + translated = trans.get('translated_text') + if source and translated: + translation_dict[source] = translated + + return { + "success": True, + "language": language, + "count": len(translation_dict), + "translations": translation_dict + } + + except Exception as e: + frappe.log_error(f"Error in get_translations: {str(e)}", "Translation API Error") + return { + "success": False, + "error": str(e), + "translations": {} + } + + +@frappe.whitelist(allow_guest=False) +def get_available_languages(): + """ + Get list of all available languages that have translations + + Usage: /api/method/asset_lite.api.translation_api.get_available_languages + + Returns: + List of language codes (e.g., ['en', 'ar']) + """ + try: + # Get distinct languages from Translation doctype + languages = frappe.db.sql(""" + SELECT DISTINCT language + FROM `tabTranslation` + WHERE language IS NOT NULL AND language != '' + ORDER BY language + """, as_dict=True) + + language_list = [lang['language'] for lang in languages if lang.get('language')] + + # Always include English as default + if 'en' not in language_list: + language_list.insert(0, 'en') + + return { + "success": True, + "languages": language_list + } + + except Exception as e: + frappe.log_error(f"Error in get_available_languages: {str(e)}", "Translation API Error") + return { + "success": False, + "error": str(e), + "languages": ['en'] + } + + +@frappe.whitelist(allow_guest=False) +def get_translation(source_text, language='ar'): + """ + Get a single translation for a specific text + + Usage: /api/method/asset_lite.api.translation_api.get_translation?source_text=Comprehensive&language=ar + + Args: + source_text: The text to translate + language: Target language code (default: 'ar') + + Returns: + Translated text or original if not found + """ + try: + if not source_text: + return { + "success": False, + "error": "source_text is required" + } + + translation = frappe.db.get_value( + 'Translation', + filters={ + 'language': language, + 'source_text': source_text + }, + fieldname='translated_text' + ) + + return { + "success": True, + "source_text": source_text, + "translated_text": translation or source_text, # Return original if not found + "found": bool(translation) + } + + except Exception as e: + frappe.log_error(f"Error in get_translation: {str(e)}", "Translation API Error") + return { + "success": False, + "error": str(e), + "translated_text": source_text + } \ No newline at end of file diff --git a/asset_lite/api/user_roles.py b/asset_lite/api/user_roles.py new file mode 100644 index 0000000..a5293c8 --- /dev/null +++ b/asset_lite/api/user_roles.py @@ -0,0 +1,105 @@ +import frappe + +@frappe.whitelist() +def get_user_roles(): + """Get roles for the current logged-in user - no permission check needed""" + user = frappe.session.user + + if not user or user == "Guest": + return [] + + # Get roles using ignore_permissions + roles = frappe.get_roles(user) + + return roles + + +@frappe.whitelist() +def get_user_info_with_roles(): + """Get current user info along with their roles""" + user = frappe.session.user + + if not user or user == "Guest": + return {"user": None, "roles": []} + + roles = frappe.get_roles(user) + + return { + "user": user, + "roles": roles, + "full_name": frappe.db.get_value("User", user, "full_name") + } + + +@frappe.whitelist(allow_guest=False) +def check_has_role(roles): + """Check if current user has any of the specified roles + + Args: + roles: comma-separated string or list of role names + + Returns: + dict with has_role (bool) and matching_roles (list) + """ + user = frappe.session.user + + if not user or user == "Guest": + return {"has_role": False, "matching_roles": [], "user_roles": []} + + # Handle both string and list input + if isinstance(roles, str): + check_roles = [r.strip() for r in roles.split(",")] + else: + check_roles = roles + + user_roles = frappe.get_roles(user) + matching_roles = [r for r in check_roles if r in user_roles] + + return { + "has_role": len(matching_roles) > 0, + "matching_roles": matching_roles, + "user_roles": user_roles + } + +@frappe.whitelist() +def get_users_with_role(role): + """ + Get all enabled users who have a specific role + + Args: + role: Role name (e.g., 'Technician') + + Returns: + List of users with name and full_name + """ + if not role: + return [] + + # Get all users who have this role from Has Role child table + users_with_role = frappe.get_all( + "Has Role", + filters={ + "role": role, + "parenttype": "User" + }, + fields=["parent"], + distinct=True + ) + + if not users_with_role: + return [] + + user_names = [u.parent for u in users_with_role] + + # Get user details for enabled users only + user_details = frappe.get_all( + "User", + filters={ + "name": ["in", user_names], + "enabled": 1 + }, + fields=["name", "full_name"], + order_by="full_name asc" + ) + + return user_details \ No newline at end of file diff --git a/asset_lite/api/userperm_api.py b/asset_lite/api/userperm_api.py new file mode 100644 index 0000000..f0b9194 --- /dev/null +++ b/asset_lite/api/userperm_api.py @@ -0,0 +1,308 @@ +import frappe +from frappe import _ + +# ============================================================================ +# CONFIGURATION: Define field mappings for each doctype +# Add new doctypes here as needed - this is the ONLY place you need to update +# ============================================================================ + +DOCTYPE_PERMISSION_MAPPINGS = { + "Asset": { + "Company": "company", + "Location": "location", + "Department": "department", + "Manufacturer": "custom_manufacturer", + "Supplier": "supplier", + "Modality": "custom_modality", + "Cost Center": "cost_center", + "Asset Type":"custom_asset_type", + "Asset Category": "asset_category" + }, + "Work_Order": { + "Company": "company", + "Location": "location", + "Department": "department" + }, + "Asset Maintenance": { + "Company": "company", + "Asset": "asset_name", + "Supplier": "supplier" + }, + "Asset Maintenance Log": { + "Company": "company", + "Asset": "asset_name" + } + # Add more doctypes as needed - just add them here! +} + + +# ============================================================================ +# HELPER FUNCTION +# ============================================================================ + +def is_system_user(user): + """Check if user is Administrator or has System Manager role.""" + if user == "Administrator": + return True + + roles = frappe.get_roles(user) + return "System Manager" in roles + + +# ============================================================================ +# CORE API FUNCTIONS - These 4 functions handle everything +# ============================================================================ + +@frappe.whitelist(allow_guest = True) +def get_user_permissions(user=None): + """ + Get all user permissions for the logged-in user. + + Returns: + dict: User permissions grouped by 'allow' doctype + """ + if not user: + user = frappe.session.user + + if is_system_user(user): + return { + "is_admin": True, + "permissions": {}, + "user": user, + "total_permissions": 0 + } + + permissions = frappe.get_all( + "User Permission", + filters={"user": user}, + fields=["name", "allow", "for_value", "is_default", "apply_to_all_doctypes", "applicable_for"], + order_by="allow asc" + ) + + # Group by 'allow' doctype + grouped = {} + for perm in permissions: + allow_doctype = perm.get("allow") + if allow_doctype not in grouped: + grouped[allow_doctype] = [] + grouped[allow_doctype].append({ + "for_value": perm.get("for_value"), + "is_default": perm.get("is_default"), + "apply_to_all_doctypes": perm.get("apply_to_all_doctypes"), + "applicable_for": perm.get("applicable_for") + }) + + return { + "is_admin": False, + "permissions": grouped, + "user": user, + "total_permissions": len(permissions), + "permission_types": list(grouped.keys()) + } + + +@frappe.whitelist(allow_guest = True) +def get_permission_filters(target_doctype, user=None): + """ + Get permission filters for ANY doctype. + This is the MAIN function - use this for all doctypes. + + Args: + target_doctype: The doctype (e.g., "Asset", "Work Order", "Project") + user: Optional user email + + Returns: + dict: Filters to apply for queries + """ + if not user: + user = frappe.session.user + + # System users have full access + if is_system_user(user): + return { + "is_admin": True, + "filters": {}, + "restrictions": {}, + "target_doctype": target_doctype, + "user": user + } + + # Get field mapping for this doctype + field_mapping = DOCTYPE_PERMISSION_MAPPINGS.get(target_doctype, {}) + + if not field_mapping: + return { + "is_admin": False, + "filters": {}, + "restrictions": {}, + "target_doctype": target_doctype, + "user": user, + "warning": f"No permission mapping defined for {target_doctype}" + } + + filters = {} + restrictions = {} + + for allow_doctype, target_field in field_mapping.items(): + permissions = frappe.get_all( + "User Permission", + filters={ + "user": user, + "allow": allow_doctype + }, + fields=["for_value", "applicable_for", "apply_to_all_doctypes"] + ) + + if permissions: + # Filter permissions that apply to this doctype + applicable = [ + p for p in permissions + if p.get("apply_to_all_doctypes") == 1 + or not p.get("applicable_for") + or p.get("applicable_for") == target_doctype + ] + + if applicable: + allowed_values = list(set([p.get("for_value") for p in applicable])) + filters[target_field] = ["in", allowed_values] + restrictions[allow_doctype] = { + "field": target_field, + "values": allowed_values, + "count": len(allowed_values) + } + + return { + "is_admin": False, + "filters": filters, + "restrictions": restrictions, + "target_doctype": target_doctype, + "user": user, + "total_restrictions": len(restrictions) + } + + +@frappe.whitelist(allow_guest = True) +def get_allowed_values(allow_doctype, user=None): + """ + Get allowed values for a specific permission type. + + Args: + allow_doctype: e.g., "Company", "Location", "Department" + user: Optional user email + + Returns: + dict: List of allowed values + """ + if not user: + user = frappe.session.user + + if is_system_user(user): + return { + "is_admin": True, + "allowed_values": [], + "has_restriction": False + } + + permissions = frappe.get_all( + "User Permission", + filters={"user": user, "allow": allow_doctype}, + fields=["for_value", "is_default"] + ) + + allowed_values = list(set([p.get("for_value") for p in permissions])) + default_value = next((p.get("for_value") for p in permissions if p.get("is_default")), None) + + return { + "is_admin": False, + "allowed_values": sorted(allowed_values), + "default_value": default_value, + "has_restriction": len(allowed_values) > 0, + "allow_doctype": allow_doctype + } + + +@frappe.whitelist(allow_guest = True) +def check_document_access(doctype, docname, user=None): + """ + Check if user has access to a specific document. + + Args: + doctype: e.g., "Asset", "Work Order" + docname: The document name/ID + user: Optional user email + + Returns: + dict: Access status + """ + if not user: + user = frappe.session.user + + if is_system_user(user): + return {"has_access": True, "is_admin": True} + + try: + doc = frappe.get_doc(doctype, docname) + except frappe.DoesNotExistError: + return {"has_access": False, "error": f"{doctype} '{docname}' not found"} + except frappe.PermissionError: + return {"has_access": False, "error": "Permission denied"} + + # Get permission filters + perm_result = get_permission_filters(doctype, user) + + if perm_result.get("is_admin"): + return {"has_access": True, "is_admin": True} + + restrictions = perm_result.get("restrictions", {}) + + if not restrictions: + return {"has_access": True, "no_restrictions": True} + + # Check each restriction + for allow_doctype, info in restrictions.items(): + field = info.get("field") + allowed_values = info.get("values", []) + doc_value = getattr(doc, field, None) + + if doc_value and doc_value not in allowed_values: + return { + "has_access": False, + "denied_by": allow_doctype, + "field": field, + "document_value": doc_value, + "allowed_values": allowed_values + } + + return {"has_access": True} + + +@frappe.whitelist(allow_guest = True) +def get_configured_doctypes(): + """Get list of doctypes that have permission mappings configured.""" + return { + "doctypes": list(DOCTYPE_PERMISSION_MAPPINGS.keys()), + "mappings": { + dt: list(mapping.keys()) + for dt, mapping in DOCTYPE_PERMISSION_MAPPINGS.items() + } + } + + +@frappe.whitelist(allow_guest = True) +def get_user_defaults(user=None): + """Get default values from user permissions (where is_default=1).""" + if not user: + user = frappe.session.user + + if is_system_user(user): + return {"is_admin": True, "defaults": {}} + + permissions = frappe.get_all( + "User Permission", + filters={"user": user, "is_default": 1}, + fields=["allow", "for_value"] + ) + + defaults = {p.get("allow"): p.get("for_value") for p in permissions} + + return {"is_admin": False, "defaults": defaults} diff --git a/asset_lite/api/work_order_api.py b/asset_lite/api/work_order_api.py new file mode 100644 index 0000000..eb7f506 --- /dev/null +++ b/asset_lite/api/work_order_api.py @@ -0,0 +1,1071 @@ +import frappe +from frappe import _ + +# Default fields for Work_Order doctype +WORK_ORDER_FIELDS = [ + 'name', + 'owner', + 'creation', + 'modified', + 'modified_by', + 'docstatus', + 'idx', + 'workflow_state', + 'company', + 'naming_series', + 'work_order_type', + 'asset_type', + 'manufacturer', + 'serial_number', + 'custom_priority_', + 'asset', + 'custom_maintenance_manager', + 'department', + 'repair_status', + 'asset_name', + 'supplier', + 'custom_pending_reason', + 'make', + 'model', + 'custom_site_contractor', + 'custom_subcontractor', + 'custom_service_agreement', + 'custom_service_coverage', + 'custom_start_date', + 'custom_end_date', + 'custom_total_amount', + 'warranty', + 'service_contract', + 'covering_spare_parts', + 'spare_parts_labour', + 'covering_labour', + 'ppm_only', + 'failure_date', + 'total_hours_spent', + 'job_completed', + 'custom_difference', + 'custom_vendors_hrs', + 'custom_deadline_date', + 'custom_diffrence', + 'feedback_rating', + 'first_responded_on', + 'assigned_manager', + 'penalty', + 'custom_assigned_supervisor', + 'stock_consumption', + 'need_procurement', + 'repair_cost', + 'total_repair_cost', + 'capitalize_repair_cost', + 'increase_in_asset_life', + 'description', + 'actions_performed', + 'end_user', + 'bio_med_dept' +] + +# Child table: Asset Repair Consumed Item (stock_items) +STOCK_ITEMS_FIELDS = [ + 'name', + 'owner', + 'creation', + 'modified', + 'modified_by', + 'docstatus', + 'idx', + 'parent', + 'parentfield', + 'parenttype', + 'item_code', + 'warehouse', + 'valuation_rate', + 'total_value', + 'custom_available_stock' +] + +# Child table: Invoice Table +INVOICE_TABLE_FIELDS = [ + 'name', + 'owner', + 'creation', + 'modified', + 'modified_by', + 'docstatus', + 'idx', + 'parent', + 'parentfield', + 'parenttype', + 'invoice_number', + 'invoice_date', + 'invoice_amount', + 'vendor', + 'description' +] + +# Child table: CMQP Table +TABLE_CMQP_FIELDS = [ + 'name', + 'owner', + 'creation', + 'modified', + 'modified_by', + 'docstatus', + 'idx', + 'parent', + 'parentfield', + 'parenttype', + 'parameter', + 'value', + 'status', + 'remarks' +] + + +def get_child_table_data(parent_name, parentfield, child_doctype, fields=None): + """ + Get child table data for a work order + + Args: + parent_name: Name of the parent work order + parentfield: Field name of the child table in parent + child_doctype: Doctype of the child table + fields: List of fields to return + + Returns: + List of child table records + """ + try: + return frappe.get_all( + child_doctype, + filters={ + 'parent': parent_name, + 'parentfield': parentfield, + 'parenttype': 'Work_Order' + }, + fields=fields or ['*'], + order_by='idx asc' + ) + except Exception: + return [] + + +@frappe.whitelist(allow_guest=True) +def get_work_orders(filters=None, fields=None, limit=20, offset=0, order_by=None, include_child_tables=False): + """ + Get list of work orders with filters and pagination + + Args: + filters: JSON string of filters (e.g., '{"company": "ABC Corp"}') + fields: JSON string of fields to return (e.g., '["work_order_type", "asset_name"]') + limit: Number of records to return (default: 20) + offset: Number of records to skip (default: 0) + order_by: Sort order (e.g., "creation desc") + include_child_tables: Whether to include child table data (default: False) + + Returns: + { + "work_orders": [...], + "total_count": int, + "limit": int, + "offset": int, + "has_more": bool + } + """ + try: + import json + + # Parse filters if provided + if filters and isinstance(filters, str): + filters = json.loads(filters) + + # Parse fields if provided + if fields and isinstance(fields, str): + fields = json.loads(fields) + else: + fields = WORK_ORDER_FIELDS.copy() + + # Parse include_child_tables + if isinstance(include_child_tables, str): + include_child_tables = include_child_tables.lower() in ('true', '1', 'yes') + + # Get total count + total_count = frappe.db.count('Work_Order', filters=filters or {}) + + # Get work orders + work_orders = frappe.get_all( + 'Work_Order', + filters=filters or {}, + fields=fields, + limit_page_length=int(limit), + limit_start=int(offset), + order_by=order_by or 'creation desc' + ) + + # Include child tables if requested + if include_child_tables: + for work_order in work_orders: + work_order['stock_items'] = get_child_table_data( + work_order['name'], + 'stock_items', + 'Asset Repair Consumed Item', + STOCK_ITEMS_FIELDS + ) + work_order['invoice_table'] = get_child_table_data( + work_order['name'], + 'invoice_table', + 'PI Table', # Adjust doctype name as needed + INVOICE_TABLE_FIELDS + ) + work_order['table_cmqp'] = get_child_table_data( + work_order['name'], + 'table_cmqp', + 'Spare Parts', # Adjust doctype name as needed + TABLE_CMQP_FIELDS + ) + + # Calculate has_more + has_more = (int(offset) + int(limit)) < total_count + + frappe.response['message'] = { + 'work_orders': work_orders, + 'total_count': total_count, + 'limit': int(limit), + 'offset': int(offset), + 'has_more': has_more + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Work Orders API Error') + frappe.response['message'] = { + 'error': str(e), + 'work_orders': [], + 'total_count': 0 + } + + +@frappe.whitelist(allow_guest=True) +def get_work_order_details(work_order_name): + """ + Get detailed information about a specific work order + + Args: + work_order_name: Name/ID of the work order + + Returns: + Work Order document with all fields including child tables + """ + try: + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + # Check if user has permission to read this work order + if not frappe.has_permission('Work_Order', 'read', work_order_name): + frappe.throw(_('Not permitted to access this work order')) + + # Get work order details + work_order = frappe.get_doc('Work_Order', work_order_name) + + # Convert to dict and include child tables + work_order_dict = work_order.as_dict() + + # Ensure child tables are included with all fields + work_order_dict['stock_items'] = [item.as_dict() for item in work_order.get('stock_items', [])] + work_order_dict['invoice_table'] = [item.as_dict() for item in work_order.get('invoice_table', [])] + work_order_dict['table_cmqp'] = [item.as_dict() for item in work_order.get('table_cmqp', [])] + + frappe.response['message'] = work_order_dict + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Work Order Details API Error') + frappe.response['message'] = { + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def create_work_order(work_order_data): + """ + Create a new work order + + Args: + work_order_data: JSON string containing work order fields including child tables + Example: + { + "company": "ABC Corp", + "work_order_type": "Repair (CM)", + "asset": "ASSET-001", + "stock_items": [ + {"item_code": "ITEM-001", "warehouse": "Main Warehouse", "valuation_rate": 100} + ], + "invoice_table": [...], + "table_cmqp": [...] + } + + Returns: + Created work order document + """ + try: + import json + + # Parse work order data + if isinstance(work_order_data, str): + work_order_data = json.loads(work_order_data) + + # Check if user has permission to create work order + if not frappe.has_permission('Work_Order', 'create'): + frappe.throw(_('Not permitted to create work order')) + + # Create new work order + work_order = frappe.get_doc({ + 'doctype': 'Work_Order', + **work_order_data + }) + + work_order.insert() + frappe.db.commit() + + # Return created work order with child tables + work_order_dict = work_order.as_dict() + work_order_dict['stock_items'] = [item.as_dict() for item in work_order.get('stock_items', [])] + work_order_dict['invoice_table'] = [item.as_dict() for item in work_order.get('invoice_table', [])] + work_order_dict['table_cmqp'] = [item.as_dict() for item in work_order.get('table_cmqp', [])] + + frappe.response['message'] = { + 'success': True, + 'work_order': work_order_dict, + 'message': _('Work Order created successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Create Work Order API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def update_work_order(work_order_name, work_order_data): + """ + Update an existing work order including child tables + + Args: + work_order_name: Name/ID of the work order + work_order_data: JSON string containing fields to update + Example: + { + "repair_status": "In Progress", + "stock_items": [ + {"item_code": "ITEM-001", "warehouse": "Main Warehouse", "valuation_rate": 100} + ] + } + + Returns: + Updated work order document + """ + try: + import json + + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + # Parse work order data + if isinstance(work_order_data, str): + work_order_data = json.loads(work_order_data) + + # Check if user has permission to update this work order + if not frappe.has_permission('Work_Order', 'write', work_order_name): + frappe.throw(_('Not permitted to update this work order')) + + # Get work order + work_order = frappe.get_doc('Work_Order', work_order_name) + + # Handle child tables separately + child_tables = ['stock_items', 'invoice_table', 'table_cmqp'] + + for key, value in work_order_data.items(): + if key in child_tables: + # Clear existing child table entries and add new ones + work_order.set(key, []) + for item in value: + work_order.append(key, item) + elif hasattr(work_order, key): + setattr(work_order, key, value) + + work_order.save() + frappe.db.commit() + + # Return updated work order with child tables + work_order_dict = work_order.as_dict() + work_order_dict['stock_items'] = [item.as_dict() for item in work_order.get('stock_items', [])] + work_order_dict['invoice_table'] = [item.as_dict() for item in work_order.get('invoice_table', [])] + work_order_dict['table_cmqp'] = [item.as_dict() for item in work_order.get('table_cmqp', [])] + + frappe.response['message'] = { + 'success': True, + 'work_order': work_order_dict, + 'message': _('Work Order updated successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Update Work Order API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def delete_work_order(work_order_name): + """ + Delete a work order + + Args: + work_order_name: Name/ID of the work order + + Returns: + Success message + """ + try: + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + # Check if user has permission to delete this work order + if not frappe.has_permission('Work_Order', 'delete', work_order_name): + frappe.throw(_('Not permitted to delete this work order')) + + # Delete work order (child tables will be deleted automatically) + frappe.delete_doc('Work_Order', work_order_name) + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'message': _('Work Order deleted successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Delete Work Order API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def update_work_order_status(work_order_name, repair_status=None, workflow_state=None): + """ + Update work order status + + Args: + work_order_name: Name/ID of the work order + repair_status: New repair status (e.g., 'Open', 'In Progress', 'Completed') + workflow_state: New workflow state + + Returns: + Updated work order document + """ + try: + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + # Check if user has permission to update this work order + if not frappe.has_permission('Work_Order', 'write', work_order_name): + frappe.throw(_('Not permitted to update this work order')) + + # Get work order + work_order = frappe.get_doc('Work_Order', work_order_name) + + # Update status fields + if repair_status: + work_order.repair_status = repair_status + + if workflow_state: + work_order.workflow_state = workflow_state + + work_order.save() + frappe.db.commit() + + # Return updated work order with child tables + work_order_dict = work_order.as_dict() + work_order_dict['stock_items'] = [item.as_dict() for item in work_order.get('stock_items', [])] + work_order_dict['invoice_table'] = [item.as_dict() for item in work_order.get('invoice_table', [])] + work_order_dict['table_cmqp'] = [item.as_dict() for item in work_order.get('table_cmqp', [])] + + frappe.response['message'] = { + 'success': True, + 'work_order': work_order_dict, + 'message': _('Work Order status updated successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Update Work Order Status API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def add_stock_item(work_order_name, item_data): + """ + Add a stock item to work order's stock_items child table + + Args: + work_order_name: Name/ID of the work order + item_data: JSON string containing stock item fields + Example: + { + "item_code": "ITEM-001", + "warehouse": "Main Warehouse", + "valuation_rate": 100, + "total_value": 500, + "custom_available_stock": 10 + } + + Returns: + Updated work order with stock items + """ + try: + import json + + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + # Parse item data + if isinstance(item_data, str): + item_data = json.loads(item_data) + + # Check if user has permission to update this work order + if not frappe.has_permission('Work_Order', 'write', work_order_name): + frappe.throw(_('Not permitted to update this work order')) + + # Get work order and add stock item + work_order = frappe.get_doc('Work_Order', work_order_name) + work_order.append('stock_items', item_data) + work_order.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'stock_items': [item.as_dict() for item in work_order.get('stock_items', [])], + 'message': _('Stock item added successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Add Stock Item API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def remove_stock_item(work_order_name, item_name): + """ + Remove a stock item from work order's stock_items child table + + Args: + work_order_name: Name/ID of the work order + item_name: Name/ID of the stock item to remove + + Returns: + Updated work order with remaining stock items + """ + try: + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + if not item_name: + frappe.throw(_('Stock item name is required')) + + # Check if user has permission to update this work order + if not frappe.has_permission('Work_Order', 'write', work_order_name): + frappe.throw(_('Not permitted to update this work order')) + + # Get work order + work_order = frappe.get_doc('Work_Order', work_order_name) + + # Find and remove the stock item + item_to_remove = None + for item in work_order.stock_items: + if item.name == item_name: + item_to_remove = item + break + + if item_to_remove: + work_order.remove(item_to_remove) + work_order.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'stock_items': [item.as_dict() for item in work_order.get('stock_items', [])], + 'message': _('Stock item removed successfully') + } + else: + frappe.response['message'] = { + 'success': False, + 'error': _('Stock item not found') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Remove Stock Item API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def add_invoice(work_order_name, invoice_data): + """ + Add an invoice to work order's invoice_table child table + + Args: + work_order_name: Name/ID of the work order + invoice_data: JSON string containing invoice fields + + Returns: + Updated work order with invoices + """ + try: + import json + + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + # Parse invoice data + if isinstance(invoice_data, str): + invoice_data = json.loads(invoice_data) + + # Check if user has permission to update this work order + if not frappe.has_permission('Work_Order', 'write', work_order_name): + frappe.throw(_('Not permitted to update this work order')) + + # Get work order and add invoice + work_order = frappe.get_doc('Work_Order', work_order_name) + work_order.append('invoice_table', invoice_data) + work_order.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'invoice_table': [item.as_dict() for item in work_order.get('invoice_table', [])], + 'message': _('Invoice added successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Add Invoice API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def remove_invoice(work_order_name, invoice_name): + """ + Remove an invoice from work order's invoice_table child table + + Args: + work_order_name: Name/ID of the work order + invoice_name: Name/ID of the invoice to remove + + Returns: + Updated work order with remaining invoices + """ + try: + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + if not invoice_name: + frappe.throw(_('Invoice name is required')) + + # Check if user has permission to update this work order + if not frappe.has_permission('Work_Order', 'write', work_order_name): + frappe.throw(_('Not permitted to update this work order')) + + # Get work order + work_order = frappe.get_doc('Work_Order', work_order_name) + + # Find and remove the invoice + item_to_remove = None + for item in work_order.invoice_table: + if item.name == invoice_name: + item_to_remove = item + break + + if item_to_remove: + work_order.remove(item_to_remove) + work_order.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'invoice_table': [item.as_dict() for item in work_order.get('invoice_table', [])], + 'message': _('Invoice removed successfully') + } + else: + frappe.response['message'] = { + 'success': False, + 'error': _('Invoice not found') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Remove Invoice API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def add_cmqp_item(work_order_name, cmqp_data): + """ + Add a CMQP item to work order's table_cmqp child table + + Args: + work_order_name: Name/ID of the work order + cmqp_data: JSON string containing CMQP item fields + + Returns: + Updated work order with CMQP items + """ + try: + import json + + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + # Parse CMQP data + if isinstance(cmqp_data, str): + cmqp_data = json.loads(cmqp_data) + + # Check if user has permission to update this work order + if not frappe.has_permission('Work_Order', 'write', work_order_name): + frappe.throw(_('Not permitted to update this work order')) + + # Get work order and add CMQP item + work_order = frappe.get_doc('Work_Order', work_order_name) + work_order.append('table_cmqp', cmqp_data) + work_order.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'table_cmqp': [item.as_dict() for item in work_order.get('table_cmqp', [])], + 'message': _('CMQP item added successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Add CMQP Item API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def remove_cmqp_item(work_order_name, cmqp_name): + """ + Remove a CMQP item from work order's table_cmqp child table + + Args: + work_order_name: Name/ID of the work order + cmqp_name: Name/ID of the CMQP item to remove + + Returns: + Updated work order with remaining CMQP items + """ + try: + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + if not cmqp_name: + frappe.throw(_('CMQP item name is required')) + + # Check if user has permission to update this work order + if not frappe.has_permission('Work_Order', 'write', work_order_name): + frappe.throw(_('Not permitted to update this work order')) + + # Get work order + work_order = frappe.get_doc('Work_Order', work_order_name) + + # Find and remove the CMQP item + item_to_remove = None + for item in work_order.table_cmqp: + if item.name == cmqp_name: + item_to_remove = item + break + + if item_to_remove: + work_order.remove(item_to_remove) + work_order.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'table_cmqp': [item.as_dict() for item in work_order.get('table_cmqp', [])], + 'message': _('CMQP item removed successfully') + } + else: + frappe.response['message'] = { + 'success': False, + 'error': _('CMQP item not found') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Remove CMQP Item API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def get_work_order_child_tables(work_order_name, child_table=None): + """ + Get child table data for a work order + + Args: + work_order_name: Name/ID of the work order + child_table: Specific child table to return ('stock_items', 'invoice_table', 'table_cmqp') + If None, returns all child tables + + Returns: + Child table data + """ + try: + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + # Check if user has permission to read this work order + if not frappe.has_permission('Work_Order', 'read', work_order_name): + frappe.throw(_('Not permitted to access this work order')) + + # Get work order + work_order = frappe.get_doc('Work_Order', work_order_name) + + result = {} + + if child_table: + if child_table == 'stock_items': + result['stock_items'] = [item.as_dict() for item in work_order.get('stock_items', [])] + elif child_table == 'invoice_table': + result['invoice_table'] = [item.as_dict() for item in work_order.get('invoice_table', [])] + elif child_table == 'table_cmqp': + result['table_cmqp'] = [item.as_dict() for item in work_order.get('table_cmqp', [])] + else: + frappe.throw(_('Invalid child table name')) + else: + result = { + 'stock_items': [item.as_dict() for item in work_order.get('stock_items', [])], + 'invoice_table': [item.as_dict() for item in work_order.get('invoice_table', [])], + 'table_cmqp': [item.as_dict() for item in work_order.get('table_cmqp', [])] + } + + frappe.response['message'] = { + 'success': True, + 'work_order_name': work_order_name, + **result + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Get Work Order Child Tables API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist(allow_guest=True) +def bulk_update_stock_items(work_order_name, stock_items): + """ + Bulk update/replace all stock items in a work order + + Args: + work_order_name: Name/ID of the work order + stock_items: JSON string containing list of stock items + + Returns: + Updated work order with new stock items + """ + try: + import json + + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + # Parse stock items + if isinstance(stock_items, str): + stock_items = json.loads(stock_items) + + # Check if user has permission to update this work order + if not frappe.has_permission('Work_Order', 'write', work_order_name): + frappe.throw(_('Not permitted to update this work order')) + + # Get work order + work_order = frappe.get_doc('Work_Order', work_order_name) + + # Clear existing stock items and add new ones + work_order.set('stock_items', []) + for item in stock_items: + work_order.append('stock_items', item) + + work_order.save() + frappe.db.commit() + + frappe.response['message'] = { + 'success': True, + 'stock_items': [item.as_dict() for item in work_order.get('stock_items', [])], + 'message': _('Stock items updated successfully') + } + + except Exception as e: + frappe.db.rollback() + frappe.log_error(frappe.get_traceback(), 'Bulk Update Stock Items API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist() +def apply_workflow_action(work_order_name, action): + """ + Apply workflow action with ignore_version flag to prevent TimestampMismatchError. + + This is useful when document modifications happen just before workflow actions + (e.g., auto-assignment of supervisors/technicians via before_workflow_action). + + Args: + work_order_name: Name/ID of the work order + action: The workflow action to apply (e.g., 'Apply', 'Send For Repair', 'Approve') + + Returns: + dict: Contains the updated document data including new workflow_state + """ + try: + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + if not action: + frappe.throw(_('Workflow action is required')) + + # Check if user has permission to update this work order + if not frappe.has_permission('Work_Order', 'write', work_order_name): + frappe.throw(_('Not permitted to update this work order')) + + # Get the latest version of the document + doc = frappe.get_doc('Work_Order', work_order_name) + + # Set flags to ignore version check - THIS IS THE KEY FIX + doc.flags.ignore_version = True + doc.flags.ignore_links = True + + # Apply the workflow action using Frappe's workflow module + from frappe.model.workflow import apply_workflow + apply_workflow(doc, action) + + # Return the updated document data + frappe.response['message'] = { + 'success': True, + 'name': doc.name, + 'workflow_state': doc.workflow_state, + 'repair_status': doc.get('repair_status'), + 'modified': str(doc.modified), + 'docstatus': doc.docstatus, + # Include assignment fields that may have been updated + 'custom_assigned_supervisor': doc.get('custom_assigned_supervisor'), + 'custom_assign_to_contractor': doc.get('custom_assign_to_contractor'), + 'custom_moh_supervisor': doc.get('custom_moh_supervisor'), + 'message': _('Workflow action applied successfully') + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Apply Workflow Action API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } + + +@frappe.whitelist() +def apply_workflow_with_assignment(work_order_name, action, asset_type=None): + """ + Combined function that handles both assignment and workflow action in one transaction. + This prevents timestamp mismatch errors by doing everything in a single save. + + Use this instead of calling assign_supervisor_or_technician separately. + + Args: + work_order_name: Name/ID of the work order + action: The workflow action to apply + asset_type: The asset type for determining assignment logic (optional) + + Returns: + dict: Contains the updated document data and assignment info + """ + try: + if not work_order_name: + frappe.throw(_('Work Order name is required')) + + if not action: + frappe.throw(_('Workflow action is required')) + + # Check permission + if not frappe.has_permission('Work_Order', 'write', work_order_name): + frappe.throw(_('Not permitted to update this work order')) + + # Get the document + doc = frappe.get_doc('Work_Order', work_order_name) + + # Set flags to ignore version check + doc.flags.ignore_version = True + doc.flags.ignore_links = True + + assigned_to = None + + # Handle assignment based on action (your existing assignment logic) + # This replaces the separate call to assign_supervisor_or_technician + actions_needing_assignment = ['Apply', 'Send For Repair'] + if action in actions_needing_assignment: + # Call your assignment logic here if needed + # For example: + # assigned_to = do_assignment_logic(doc, action, asset_type) + pass + + # Apply the workflow action + from frappe.model.workflow import apply_workflow + apply_workflow(doc, action) + + frappe.response['message'] = { + 'success': True, + 'name': doc.name, + 'workflow_state': doc.workflow_state, + 'repair_status': doc.get('repair_status'), + 'modified': str(doc.modified), + 'docstatus': doc.docstatus, + 'assigned_to': assigned_to, + 'custom_assigned_supervisor': doc.get('custom_assigned_supervisor'), + 'custom_assign_to_contractor': doc.get('custom_assign_to_contractor'), + 'custom_moh_supervisor': doc.get('custom_moh_supervisor'), + 'message': _('Workflow action applied successfully') + } + + except Exception as e: + frappe.log_error(frappe.get_traceback(), 'Apply Workflow With Assignment API Error') + frappe.response['message'] = { + 'success': False, + 'error': str(e) + } \ No newline at end of file diff --git a/asset_lite/asset_lite/__init__.py b/asset_lite/asset_lite/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/dashboard_chart_source/active_map_widget/active_map_widget.js b/asset_lite/asset_lite/dashboard_chart_source/active_map_widget/active_map_widget.js new file mode 100644 index 0000000..de66774 --- /dev/null +++ b/asset_lite/asset_lite/dashboard_chart_source/active_map_widget/active_map_widget.js @@ -0,0 +1,47 @@ +frappe.dashboards.chart_sources["Active Map Widget"] = { + method: "asset_lite.map.get_custom_html_data", + filters: [] +}; + +// Override the chart rendering after data is loaded +frappe.provide('frappe.dashboards'); + +$(document).on('app_ready', function() { + // Override the render method for custom HTML charts + const original_render = frappe.ui.Dashboard.prototype.render_chart; + + frappe.ui.Dashboard.prototype.render_chart = function(chart_data, chart_container) { + if (chart_data.custom_html) { + // Clear the container and add custom HTML + chart_container.empty(); + const custom_html = ` +
+

Custom Dashboard Content

+
+
+
+
Card Title 1
+

Your custom content here

+ +
+
+
+
+
Card Title 2
+

More custom content

+
+
75%
+
+
+
+
+
+ `; + chart_container.html(custom_html); + return; + } + // Call original render method for other charts + return original_render.call(this, chart_data, chart_container); + }; +}); + diff --git a/asset_lite/asset_lite/dashboard_chart_source/active_map_widget/active_map_widget.json b/asset_lite/asset_lite/dashboard_chart_source/active_map_widget/active_map_widget.json new file mode 100644 index 0000000..3ac228f --- /dev/null +++ b/asset_lite/asset_lite/dashboard_chart_source/active_map_widget/active_map_widget.json @@ -0,0 +1,13 @@ +{ + "creation": "2025-06-25 18:03:16.704998", + "docstatus": 0, + "doctype": "Dashboard Chart Source", + "idx": 0, + "modified": "2025-06-25 18:03:16.704998", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Active Map Widget", + "owner": "Administrator", + "source_name": "Active Map Widget", + "timeseries": 0 +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/__init__.py b/asset_lite/asset_lite/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/agent/__init__.py b/asset_lite/asset_lite/doctype/agent/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/agent/agent.js b/asset_lite/asset_lite/doctype/agent/agent.js new file mode 100644 index 0000000..60d397e --- /dev/null +++ b/asset_lite/asset_lite/doctype/agent/agent.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Agent", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/agent/agent.json b/asset_lite/asset_lite/doctype/agent/agent.json new file mode 100644 index 0000000..b71b9f3 --- /dev/null +++ b/asset_lite/asset_lite/doctype/agent/agent.json @@ -0,0 +1,45 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:agent_name", + "creation": "2024-12-13 14:37:38.780730", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "agent_name" + ], + "fields": [ + { + "fieldname": "agent_name", + "fieldtype": "Data", + "label": "Agent Name", + "unique": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-12-13 15:15:47.447599", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Agent", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/agent/agent.py b/asset_lite/asset_lite/doctype/agent/agent.py new file mode 100644 index 0000000..c8ff934 --- /dev/null +++ b/asset_lite/asset_lite/doctype/agent/agent.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class Agent(Document): + pass diff --git a/asset_lite/asset_lite/doctype/agent/test_agent.py b/asset_lite/asset_lite/doctype/agent/test_agent.py new file mode 100644 index 0000000..ec44bae --- /dev/null +++ b/asset_lite/asset_lite/doctype/agent/test_agent.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestAgent(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/arabic_names/__init__.py b/asset_lite/asset_lite/doctype/arabic_names/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/arabic_names/arabic_names.js b/asset_lite/asset_lite/doctype/arabic_names/arabic_names.js new file mode 100644 index 0000000..51ffe24 --- /dev/null +++ b/asset_lite/asset_lite/doctype/arabic_names/arabic_names.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Arabic Names", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/arabic_names/arabic_names.json b/asset_lite/asset_lite/doctype/arabic_names/arabic_names.json new file mode 100644 index 0000000..cb19433 --- /dev/null +++ b/asset_lite/asset_lite/doctype/arabic_names/arabic_names.json @@ -0,0 +1,50 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:name1", + "creation": "2025-02-07 16:56:53.486694", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "name1", + "arabic_name" + ], + "fields": [ + { + "fieldname": "name1", + "fieldtype": "Data", + "label": "Name", + "unique": 1 + }, + { + "fieldname": "arabic_name", + "fieldtype": "Data", + "label": "Arabic Name" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-02-07 16:57:50.823039", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Arabic Names", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/arabic_names/arabic_names.py b/asset_lite/asset_lite/doctype/arabic_names/arabic_names.py new file mode 100644 index 0000000..a812f42 --- /dev/null +++ b/asset_lite/asset_lite/doctype/arabic_names/arabic_names.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class ArabicNames(Document): + pass diff --git a/asset_lite/asset_lite/doctype/arabic_names/test_arabic_names.py b/asset_lite/asset_lite/doctype/arabic_names/test_arabic_names.py new file mode 100644 index 0000000..66eeb6d --- /dev/null +++ b/asset_lite/asset_lite/doctype/arabic_names/test_arabic_names.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestArabicNames(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/asset_item_transfer/__init__.py b/asset_lite/asset_lite/doctype/asset_item_transfer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/asset_item_transfer/asset_item_transfer.json b/asset_lite/asset_lite/doctype/asset_item_transfer/asset_item_transfer.json new file mode 100644 index 0000000..3dfde9a --- /dev/null +++ b/asset_lite/asset_lite/doctype/asset_item_transfer/asset_item_transfer.json @@ -0,0 +1,59 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-08-26 21:00:34.676325", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset", + "asset_name", + "qty", + "column_break_kbew", + "return_inspection_committee" + ], + "fields": [ + { + "fieldname": "asset", + "fieldtype": "Link", + "label": "Asset", + "options": "Asset" + }, + { + "fetch_from": "asset.asset_name", + "fetch_if_empty": 1, + "fieldname": "asset_name", + "fieldtype": "Data", + "label": "Asset Name" + }, + { + "default": "1", + "fieldname": "qty", + "fieldtype": "Float", + "label": "Qty" + }, + { + "fieldname": "column_break_kbew", + "fieldtype": "Column Break" + }, + { + "fieldname": "return_inspection_committee", + "fieldtype": "Select", + "label": "Return Inspection Committee", + "options": "\nFor Repair\nFor Sale\nFor Disposal" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-08-26 21:00:34.676325", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Asset Item Transfer", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/asset_item_transfer/asset_item_transfer.py b/asset_lite/asset_lite/doctype/asset_item_transfer/asset_item_transfer.py new file mode 100644 index 0000000..cc08c50 --- /dev/null +++ b/asset_lite/asset_lite/doctype/asset_item_transfer/asset_item_transfer.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class AssetItemTransfer(Document): + pass diff --git a/asset_lite/asset_lite/doctype/asset_type/__init__.py b/asset_lite/asset_lite/doctype/asset_type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/asset_type/asset_type.js b/asset_lite/asset_lite/doctype/asset_type/asset_type.js new file mode 100644 index 0000000..bf166ba --- /dev/null +++ b/asset_lite/asset_lite/doctype/asset_type/asset_type.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Asset Type", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/asset_type/asset_type.json b/asset_lite/asset_lite/doctype/asset_type/asset_type.json new file mode 100644 index 0000000..5c0b22e --- /dev/null +++ b/asset_lite/asset_lite/doctype/asset_type/asset_type.json @@ -0,0 +1,47 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:asset_type", + "creation": "2024-09-23 14:16:49.149093", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "asset_type" + ], + "fields": [ + { + "fieldname": "asset_type", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Asset Type", + "reqd": 1, + "unique": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-09-23 14:17:58.073254", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Asset Type", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/asset_type/asset_type.py b/asset_lite/asset_lite/doctype/asset_type/asset_type.py new file mode 100644 index 0000000..40625d4 --- /dev/null +++ b/asset_lite/asset_lite/doctype/asset_type/asset_type.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class AssetType(Document): + pass diff --git a/asset_lite/asset_lite/doctype/asset_type/test_asset_type.py b/asset_lite/asset_lite/doctype/asset_type/test_asset_type.py new file mode 100644 index 0000000..ab898f7 --- /dev/null +++ b/asset_lite/asset_lite/doctype/asset_type/test_asset_type.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestAssetType(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/city/__init__.py b/asset_lite/asset_lite/doctype/city/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/city/city.js b/asset_lite/asset_lite/doctype/city/city.js new file mode 100644 index 0000000..fd9a4ed --- /dev/null +++ b/asset_lite/asset_lite/doctype/city/city.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("City", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/city/city.json b/asset_lite/asset_lite/doctype/city/city.json new file mode 100644 index 0000000..63055c8 --- /dev/null +++ b/asset_lite/asset_lite/doctype/city/city.json @@ -0,0 +1,44 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:city", + "creation": "2025-08-26 15:20:17.013649", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "city" + ], + "fields": [ + { + "fieldname": "city", + "fieldtype": "Data", + "label": "City", + "unique": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-08-26 20:43:50.686274", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "City", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/city/city.py b/asset_lite/asset_lite/doctype/city/city.py new file mode 100644 index 0000000..a480771 --- /dev/null +++ b/asset_lite/asset_lite/doctype/city/city.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class City(Document): + pass diff --git a/asset_lite/asset_lite/doctype/city/test_city.py b/asset_lite/asset_lite/doctype/city/test_city.py new file mode 100644 index 0000000..95d3486 --- /dev/null +++ b/asset_lite/asset_lite/doctype/city/test_city.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestCity(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/ecri_umdns/__init__.py b/asset_lite/asset_lite/doctype/ecri_umdns/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ecri_umdns/ecri_umdns.js b/asset_lite/asset_lite/doctype/ecri_umdns/ecri_umdns.js new file mode 100644 index 0000000..c779c86 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ecri_umdns/ecri_umdns.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("ECRI UMDNS", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/ecri_umdns/ecri_umdns.json b/asset_lite/asset_lite/doctype/ecri_umdns/ecri_umdns.json new file mode 100644 index 0000000..323b38a --- /dev/null +++ b/asset_lite/asset_lite/doctype/ecri_umdns/ecri_umdns.json @@ -0,0 +1,51 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:ecri", + "creation": "2024-12-03 14:43:40.910282", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "ecri", + "device_name" + ], + "fields": [ + { + "fieldname": "ecri", + "fieldtype": "Data", + "label": "ECRI", + "unique": 1 + }, + { + "fieldname": "device_name", + "fieldtype": "Data", + "label": "Device Name" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-12-03 18:43:19.609779", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "ECRI UMDNS", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ecri_umdns/ecri_umdns.py b/asset_lite/asset_lite/doctype/ecri_umdns/ecri_umdns.py new file mode 100644 index 0000000..a7d1fd4 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ecri_umdns/ecri_umdns.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class ECRIUMDNS(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ecri_umdns/test_ecri_umdns.py b/asset_lite/asset_lite/doctype/ecri_umdns/test_ecri_umdns.py new file mode 100644 index 0000000..57b3a75 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ecri_umdns/test_ecri_umdns.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestECRIUMDNS(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/feedback/__init__.py b/asset_lite/asset_lite/doctype/feedback/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/feedback/feedback.js b/asset_lite/asset_lite/doctype/feedback/feedback.js new file mode 100644 index 0000000..e344db7 --- /dev/null +++ b/asset_lite/asset_lite/doctype/feedback/feedback.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Feedback", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/feedback/feedback.json b/asset_lite/asset_lite/doctype/feedback/feedback.json new file mode 100644 index 0000000..a58142b --- /dev/null +++ b/asset_lite/asset_lite/doctype/feedback/feedback.json @@ -0,0 +1,77 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-03-05 19:53:51.796362", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "work_order", + "column_break_eytf", + "feedback_by", + "section_break_rvtb", + "parameters", + "section_break_zmrx", + "overall" + ], + "fields": [ + { + "fieldname": "work_order", + "fieldtype": "Link", + "label": "Work Order", + "options": "Work_Order" + }, + { + "fieldname": "column_break_eytf", + "fieldtype": "Column Break" + }, + { + "fieldname": "feedback_by", + "fieldtype": "Link", + "label": "Feedback by", + "options": "User" + }, + { + "fieldname": "section_break_rvtb", + "fieldtype": "Section Break" + }, + { + "fieldname": "parameters", + "fieldtype": "Table", + "label": "Parameters", + "options": "Feedback Table" + }, + { + "fieldname": "section_break_zmrx", + "fieldtype": "Section Break" + }, + { + "fieldname": "overall", + "fieldtype": "Rating", + "label": "Overall" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-03-05 19:56:58.056262", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Feedback", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/feedback/feedback.py b/asset_lite/asset_lite/doctype/feedback/feedback.py new file mode 100644 index 0000000..7858254 --- /dev/null +++ b/asset_lite/asset_lite/doctype/feedback/feedback.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class Feedback(Document): + pass diff --git a/asset_lite/asset_lite/doctype/feedback/test_feedback.py b/asset_lite/asset_lite/doctype/feedback/test_feedback.py new file mode 100644 index 0000000..cc66323 --- /dev/null +++ b/asset_lite/asset_lite/doctype/feedback/test_feedback.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestFeedback(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/feedback_table/__init__.py b/asset_lite/asset_lite/doctype/feedback_table/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/feedback_table/feedback_table.json b/asset_lite/asset_lite/doctype/feedback_table/feedback_table.json new file mode 100644 index 0000000..897e1e5 --- /dev/null +++ b/asset_lite/asset_lite/doctype/feedback_table/feedback_table.json @@ -0,0 +1,44 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-03-05 20:36:14.651301", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "parameter", + "rating", + "feedback" + ], + "fields": [ + { + "fieldname": "parameter", + "fieldtype": "Data", + "label": "Parameter" + }, + { + "fieldname": "rating", + "fieldtype": "Rating", + "in_list_view": 1, + "label": "Rating", + "reqd": 1 + }, + { + "fieldname": "feedback", + "fieldtype": "Text", + "label": "Feedback" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-03-05 20:49:43.458939", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Feedback Table", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/feedback_table/feedback_table.py b/asset_lite/asset_lite/doctype/feedback_table/feedback_table.py new file mode 100644 index 0000000..0a7e6ec --- /dev/null +++ b/asset_lite/asset_lite/doctype/feedback_table/feedback_table.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class FeedbackTable(Document): + pass diff --git a/asset_lite/asset_lite/doctype/item_transfer_table/__init__.py b/asset_lite/asset_lite/doctype/item_transfer_table/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/item_transfer_table/item_transfer_table.json b/asset_lite/asset_lite/doctype/item_transfer_table/item_transfer_table.json new file mode 100644 index 0000000..3a92f47 --- /dev/null +++ b/asset_lite/asset_lite/doctype/item_transfer_table/item_transfer_table.json @@ -0,0 +1,64 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-08-26 21:01:28.397119", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item", + "qty", + "uom", + "column_break_gcjw", + "item_name", + "return_inspection_committee" + ], + "fields": [ + { + "fieldname": "item", + "fieldtype": "Link", + "label": "Item", + "options": "Item" + }, + { + "fieldname": "qty", + "fieldtype": "Float", + "label": "Qty" + }, + { + "fetch_from": "item.stock_uom", + "fetch_if_empty": 1, + "fieldname": "uom", + "fieldtype": "Link", + "label": "UOM", + "options": "UOM" + }, + { + "fieldname": "column_break_gcjw", + "fieldtype": "Column Break" + }, + { + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name" + }, + { + "fieldname": "return_inspection_committee", + "fieldtype": "Select", + "label": "Return Inspection Committee", + "options": "\nFor Repair\nFor Sale\nFor Disposal" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-08-26 21:01:28.397119", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Item Transfer Table", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/item_transfer_table/item_transfer_table.py b/asset_lite/asset_lite/doctype/item_transfer_table/item_transfer_table.py new file mode 100644 index 0000000..bd90683 --- /dev/null +++ b/asset_lite/asset_lite/doctype/item_transfer_table/item_transfer_table.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class ItemTransferTable(Document): + pass diff --git a/asset_lite/asset_lite/doctype/material_transfer/__init__.py b/asset_lite/asset_lite/doctype/material_transfer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/material_transfer/material_transfer.js b/asset_lite/asset_lite/doctype/material_transfer/material_transfer.js new file mode 100644 index 0000000..d7c7c5e --- /dev/null +++ b/asset_lite/asset_lite/doctype/material_transfer/material_transfer.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Material Transfer", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/material_transfer/material_transfer.json b/asset_lite/asset_lite/doctype/material_transfer/material_transfer.json new file mode 100644 index 0000000..4dc61d5 --- /dev/null +++ b/asset_lite/asset_lite/doctype/material_transfer/material_transfer.json @@ -0,0 +1,128 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format:Material-Transfer-{####}", + "creation": "2025-08-26 21:02:44.817236", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "hospital", + "purpose", + "source_warehouse", + "asset", + "column_break_irlz", + "date", + "reason_for_return", + "transfer_type", + "target_warehouse", + "item", + "section_break_bunw", + "item_table", + "asset_transfer" + ], + "fields": [ + { + "fieldname": "hospital", + "fieldtype": "Link", + "label": "Hospital", + "options": "Company" + }, + { + "fieldname": "purpose", + "fieldtype": "Select", + "label": "Purpose", + "options": "Transfer" + }, + { + "fieldname": "source_warehouse", + "fieldtype": "Link", + "label": "Source Warehouse", + "options": "Warehouse" + }, + { + "depends_on": "eval:doc.transfer_type=='Asset'", + "fieldname": "asset", + "fieldtype": "Link", + "label": "Asset", + "options": "Asset" + }, + { + "fieldname": "column_break_irlz", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "date", + "fieldtype": "Date", + "label": "Date" + }, + { + "fieldname": "reason_for_return", + "fieldtype": "Select", + "label": "Reason for Return", + "options": "\nPurpose Completed\nSurplus\nUnusable\nDamaged" + }, + { + "fieldname": "transfer_type", + "fieldtype": "Select", + "label": "Transfer Type", + "options": "\nAsset\nItem" + }, + { + "fieldname": "target_warehouse", + "fieldtype": "Link", + "label": "Target Warehouse", + "options": "Warehouse" + }, + { + "depends_on": "eval:doc.transfer_type=='Item'", + "fieldname": "item", + "fieldtype": "Link", + "label": "Item", + "options": "Item" + }, + { + "fieldname": "section_break_bunw", + "fieldtype": "Section Break" + }, + { + "depends_on": "eval:doc.transfer_type=='Item'", + "fieldname": "item_table", + "fieldtype": "Table", + "label": "Item Table", + "options": "Item Transfer Table" + }, + { + "depends_on": "eval:doc.transfer_type=='Asset'", + "fieldname": "asset_transfer", + "fieldtype": "Table", + "label": "Asset Transfer", + "options": "Asset Item Transfer" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-08-26 21:02:44.817236", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Material Transfer", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/material_transfer/material_transfer.py b/asset_lite/asset_lite/doctype/material_transfer/material_transfer.py new file mode 100644 index 0000000..c3f7642 --- /dev/null +++ b/asset_lite/asset_lite/doctype/material_transfer/material_transfer.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class MaterialTransfer(Document): + pass diff --git a/asset_lite/asset_lite/doctype/material_transfer/test_material_transfer.py b/asset_lite/asset_lite/doctype/material_transfer/test_material_transfer.py new file mode 100644 index 0000000..f29131b --- /dev/null +++ b/asset_lite/asset_lite/doctype/material_transfer/test_material_transfer.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestMaterialTransfer(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/mobile_team_site/__init__.py b/asset_lite/asset_lite/doctype/mobile_team_site/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/mobile_team_site/mobile_team_site.js b/asset_lite/asset_lite/doctype/mobile_team_site/mobile_team_site.js new file mode 100644 index 0000000..83d40e1 --- /dev/null +++ b/asset_lite/asset_lite/doctype/mobile_team_site/mobile_team_site.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Mobile Team Site", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/mobile_team_site/mobile_team_site.json b/asset_lite/asset_lite/doctype/mobile_team_site/mobile_team_site.json new file mode 100644 index 0000000..d26a3bc --- /dev/null +++ b/asset_lite/asset_lite/doctype/mobile_team_site/mobile_team_site.json @@ -0,0 +1,58 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:site_name", + "creation": "2025-08-08 15:36:41.575671", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "site_name", + "mobile_team", + "city" + ], + "fields": [ + { + "fieldname": "site_name", + "fieldtype": "Data", + "label": "Site Name", + "unique": 1 + }, + { + "fieldname": "mobile_team", + "fieldtype": "Link", + "label": "Mobile Team", + "options": "Company" + }, + { + "fieldname": "city", + "fieldtype": "Link", + "label": "City", + "options": "City" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-08-26 15:26:09.017274", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Mobile Team Site", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/mobile_team_site/mobile_team_site.py b/asset_lite/asset_lite/doctype/mobile_team_site/mobile_team_site.py new file mode 100644 index 0000000..3486d9b --- /dev/null +++ b/asset_lite/asset_lite/doctype/mobile_team_site/mobile_team_site.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class MobileTeamSite(Document): + pass diff --git a/asset_lite/asset_lite/doctype/mobile_team_site/test_mobile_team_site.py b/asset_lite/asset_lite/doctype/mobile_team_site/test_mobile_team_site.py new file mode 100644 index 0000000..ce0a149 --- /dev/null +++ b/asset_lite/asset_lite/doctype/mobile_team_site/test_mobile_team_site.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestMobileTeamSite(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/modality/__init__.py b/asset_lite/asset_lite/doctype/modality/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/modality/modality.js b/asset_lite/asset_lite/doctype/modality/modality.js new file mode 100644 index 0000000..348c437 --- /dev/null +++ b/asset_lite/asset_lite/doctype/modality/modality.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Modality", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/modality/modality.json b/asset_lite/asset_lite/doctype/modality/modality.json new file mode 100644 index 0000000..b6c5ab2 --- /dev/null +++ b/asset_lite/asset_lite/doctype/modality/modality.json @@ -0,0 +1,44 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:modality", + "creation": "2025-09-08 12:40:28.438645", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "modality" + ], + "fields": [ + { + "fieldname": "modality", + "fieldtype": "Data", + "label": "Modality", + "unique": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-09-08 12:41:21.725442", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Modality", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/modality/modality.py b/asset_lite/asset_lite/doctype/modality/modality.py new file mode 100644 index 0000000..293bc1f --- /dev/null +++ b/asset_lite/asset_lite/doctype/modality/modality.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class Modality(Document): + pass diff --git a/asset_lite/asset_lite/doctype/modality/test_modality.py b/asset_lite/asset_lite/doctype/modality/test_modality.py new file mode 100644 index 0000000..f3af0e5 --- /dev/null +++ b/asset_lite/asset_lite/doctype/modality/test_modality.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestModality(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/pi_table/__init__.py b/asset_lite/asset_lite/doctype/pi_table/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/pi_table/pi_table.json b/asset_lite/asset_lite/doctype/pi_table/pi_table.json new file mode 100644 index 0000000..029dc0d --- /dev/null +++ b/asset_lite/asset_lite/doctype/pi_table/pi_table.json @@ -0,0 +1,38 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-09-20 13:15:43.666184", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "purchase_invoice", + "cost" + ], + "fields": [ + { + "fieldname": "purchase_invoice", + "fieldtype": "Link", + "label": "Purchase Invoice", + "options": "Purchase Invoice" + }, + { + "fetch_from": "purchase_invoice.grand_total", + "fieldname": "cost", + "fieldtype": "Currency", + "label": "Cost" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-09-20 13:19:33.308381", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PI Table", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/pi_table/pi_table.py b/asset_lite/asset_lite/doctype/pi_table/pi_table.py new file mode 100644 index 0000000..4ef5c2d --- /dev/null +++ b/asset_lite/asset_lite/doctype/pi_table/pi_table.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PITable(Document): + pass diff --git a/asset_lite/asset_lite/doctype/pm_entry_line/__init__.py b/asset_lite/asset_lite/doctype/pm_entry_line/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/pm_entry_line/pm_entry_line.json b/asset_lite/asset_lite/doctype/pm_entry_line/pm_entry_line.json new file mode 100644 index 0000000..78fdb8e --- /dev/null +++ b/asset_lite/asset_lite/doctype/pm_entry_line/pm_entry_line.json @@ -0,0 +1,70 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-09-18 13:07:33.907967", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset", + "asset_name", + "start_date", + "end_date", + "manufacturer", + "model" + ], + "fields": [ + { + "fieldname": "asset", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "label": "Asset", + "options": "Asset" + }, + { + "fieldname": "asset_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Asset Name", + "read_only": 1 + }, + { + "fieldname": "start_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Start Date" + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "End Date" + }, + { + "fieldname": "manufacturer", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Manufacturer", + "options": "Manufacturer" + }, + { + "fieldname": "model", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Model" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-09-18 13:08:35.075783", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PM Entry Line", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/pm_entry_line/pm_entry_line.py b/asset_lite/asset_lite/doctype/pm_entry_line/pm_entry_line.py new file mode 100644 index 0000000..5babec9 --- /dev/null +++ b/asset_lite/asset_lite/doctype/pm_entry_line/pm_entry_line.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PMEntryLine(Document): + pass diff --git a/asset_lite/asset_lite/doctype/pm_schedule_generator/__init__.py b/asset_lite/asset_lite/doctype/pm_schedule_generator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/pm_schedule_generator/pm_schedule_generator.js b/asset_lite/asset_lite/doctype/pm_schedule_generator/pm_schedule_generator.js new file mode 100644 index 0000000..2424038 --- /dev/null +++ b/asset_lite/asset_lite/doctype/pm_schedule_generator/pm_schedule_generator.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("PM Schedule Generator", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/pm_schedule_generator/pm_schedule_generator.json b/asset_lite/asset_lite/doctype/pm_schedule_generator/pm_schedule_generator.json new file mode 100644 index 0000000..a1d2565 --- /dev/null +++ b/asset_lite/asset_lite/doctype/pm_schedule_generator/pm_schedule_generator.json @@ -0,0 +1,232 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format:PMSG-{DD}-{MM}-{YY}-{####}", + "creation": "2025-09-08 13:29:07.175342", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "asset_details_section", + "hospital", + "manufacturer", + "model", + "column_break_qtoh", + "pm_for", + "asset_name", + "modality", + "device_status", + "maintenance_details_section", + "start_date", + "no_of_pms", + "maintenance_team", + "maintenance_manager", + "column_break_kapm", + "periodicity", + "end_date", + "assign_to", + "due_date", + "next_pm_date", + "section_break_hkrb", + "maintenance_entries", + "amended_from" + ], + "fields": [ + { + "fieldname": "asset_details_section", + "fieldtype": "Section Break", + "label": "Asset Details" + }, + { + "fieldname": "hospital", + "fieldtype": "Link", + "in_filter": 1, + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Hospital", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "manufacturer", + "fieldtype": "Link", + "in_filter": 1, + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Manufacturer", + "options": "Manufacturer" + }, + { + "fieldname": "model", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Model" + }, + { + "fieldname": "column_break_qtoh", + "fieldtype": "Column Break" + }, + { + "fieldname": "asset_name", + "fieldtype": "Data", + "label": "Asset Name", + "read_only": 1 + }, + { + "fieldname": "device_status", + "fieldtype": "Select", + "label": "Device Status", + "options": "\nUp\nDown" + }, + { + "fieldname": "maintenance_details_section", + "fieldtype": "Section Break", + "label": "Maintenance Details" + }, + { + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date", + "reqd": 1 + }, + { + "fieldname": "maintenance_team", + "fieldtype": "Link", + "label": "Maintenance Team", + "mandatory_depends_on": "eval:doc.maintenance_entries && doc.maintenance_entries.length > 0", + "options": "Asset Maintenance Team" + }, + { + "fetch_from": "maintenance_team.maintenance_manager", + "fetch_if_empty": 1, + "fieldname": "maintenance_manager", + "fieldtype": "Data", + "label": "Maintenance Manager" + }, + { + "fieldname": "column_break_kapm", + "fieldtype": "Column Break" + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End Date", + "reqd": 1 + }, + { + "fieldname": "periodicity", + "fieldtype": "Select", + "label": "Periodicity", + "mandatory_depends_on": "eval:doc.maintenance_entries && doc.maintenance_entries.length > 0", + "options": "\nDaily\nWeekly\nMonthly\nQuarterly\nHalf-yearly\nYearly\n2 Yearly\n3 Yearly", + "reqd": 1 + }, + { + "fieldname": "assign_to", + "fieldtype": "Link", + "label": "Assign To", + "mandatory_depends_on": "eval:doc.maintenance_entries && doc.maintenance_entries.length > 0", + "options": "User" + }, + { + "fieldname": "due_date", + "fieldtype": "Date", + "label": "First PM Date", + "read_only": 1 + }, + { + "fieldname": "section_break_hkrb", + "fieldtype": "Section Break" + }, + { + "fieldname": "maintenance_entries", + "fieldtype": "Table", + "label": "Maintenance Entries", + "options": "PM Entry Line" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "PM Schedule Generator", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "PM Schedule Generator", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "modality", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Modality", + "options": "Modality" + }, + { + "allow_on_submit": 1, + "fieldname": "pm_for", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "PM Name", + "reqd": 1 + }, + { + "fieldname": "no_of_pms", + "fieldtype": "Data", + "label": "No of PMs" + }, + { + "allow_on_submit": 1, + "fieldname": "next_pm_date", + "fieldtype": "Date", + "label": "Next PM Date" + } + ], + "index_web_pages_for_search": 1, + "is_calendar_and_gantt": 1, + "is_submittable": 1, + "links": [ + { + "link_doctype": "Asset Maintenance", + "link_fieldname": "custom_pm_schedule" + } + ], + "modified": "2025-12-31 20:40:50.189002", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PM Schedule Generator", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/pm_schedule_generator/pm_schedule_generator.py b/asset_lite/asset_lite/doctype/pm_schedule_generator/pm_schedule_generator.py new file mode 100644 index 0000000..bf229ec --- /dev/null +++ b/asset_lite/asset_lite/doctype/pm_schedule_generator/pm_schedule_generator.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PMScheduleGenerator(Document): + pass diff --git a/asset_lite/asset_lite/doctype/pm_schedule_generator/test_pm_schedule_generator.py b/asset_lite/asset_lite/doctype/pm_schedule_generator/test_pm_schedule_generator.py new file mode 100644 index 0000000..50eb703 --- /dev/null +++ b/asset_lite/asset_lite/doctype/pm_schedule_generator/test_pm_schedule_generator.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPMScheduleGenerator(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/ppm/__init__.py b/asset_lite/asset_lite/doctype/ppm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm/ppm.js b/asset_lite/asset_lite/doctype/ppm/ppm.js new file mode 100644 index 0000000..ad4b070 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm/ppm.js @@ -0,0 +1,32 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +frappe.ui.form.on('PPM', { + refresh: function(frm) { + // Hide the default print icon + frm.page.hide_icon_group('print'); + + // Add custom button for PPM Sticker with a print icon + frm.add_custom_button( + ` ${__('PPM Sticker')}`, + function() { + // Set PPM Sticker as the default print format and open print preview + const customLink = `/printview?doctype=PPM&name=${frm.doc.name}&trigger_print=1&format=PPM%20Sticker&no_letterhead=0`; + window.open(customLink); + } + ); + + // Add custom button for PPM Service Report with a print icon + frm.add_custom_button( + ` ${__('Service Report')}`, + function() { + // Set Service Report as the default print format and open print preview + const customLink = `/printview?doctype=PPM&name=${frm.doc.name}&trigger_print=1&format=PPM%20Service%20Report&no_letterhead=0`; + window.open(customLink); + } + ); + } +}); + + + diff --git a/asset_lite/asset_lite/doctype/ppm/ppm.json b/asset_lite/asset_lite/doctype/ppm/ppm.json new file mode 100644 index 0000000..380cb82 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm/ppm.json @@ -0,0 +1,112 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format:PPM-{data}-{####}", + "creation": "2024-09-23 17:32:15.127002", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "month", + "year", + "column_break_phdw", + "asset", + "asset_name", + "asset_maintenance_log", + "section_break_3", + "data", + "table", + "amended_from" + ], + "fields": [ + { + "fieldname": "month", + "fieldtype": "Select", + "label": "Month", + "options": "\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember" + }, + { + "fieldname": "year", + "fieldtype": "Data", + "label": "Year" + }, + { + "fieldname": "column_break_phdw", + "fieldtype": "Column Break" + }, + { + "fieldname": "asset_maintenance_log", + "fieldtype": "Link", + "label": "Asset Maintenance Log", + "options": "Asset Maintenance Log", + "read_only": 1 + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break" + }, + { + "default": "CT Scan", + "fieldname": "data", + "fieldtype": "Link", + "label": "Template", + "options": "PPM Templates" + }, + { + "fieldname": "table", + "fieldtype": "Table", + "options": "PPM Table" + }, + { + "fetch_from": "asset_maintenance_log.asset_name", + "fieldname": "asset", + "fieldtype": "Link", + "label": "Asset", + "options": "Asset" + }, + { + "fetch_from": "asset.asset_name", + "fieldname": "asset_name", + "fieldtype": "Data", + "label": "Asset Name", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "PPM", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2024-09-23 18:16:03.998515", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm/ppm.py b/asset_lite/asset_lite/doctype/ppm/ppm.py new file mode 100644 index 0000000..558fceb --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm/ppm.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPM(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm/test_ppm.py b/asset_lite/asset_lite/doctype/ppm/test_ppm.py new file mode 100644 index 0000000..ae51c1a --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm/test_ppm.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPPM(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/__init__.py b/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/ppm_ct_scan_machine_template.js b/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/ppm_ct_scan_machine_template.js new file mode 100644 index 0000000..c371ba8 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/ppm_ct_scan_machine_template.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("PPM CT SCAN MACHINE TEMPLATE", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/ppm_ct_scan_machine_template.json b/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/ppm_ct_scan_machine_template.json new file mode 100644 index 0000000..8253cea --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/ppm_ct_scan_machine_template.json @@ -0,0 +1,43 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-09-13 15:46:54.680689", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "table_1" + ], + "fields": [ + { + "fieldname": "table_1", + "fieldtype": "Table", + "options": "PPM Table For CT Scan Machine" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-09-13 17:45:05.011562", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM CT SCAN MACHINE TEMPLATE", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/ppm_ct_scan_machine_template.py b/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/ppm_ct_scan_machine_template.py new file mode 100644 index 0000000..513b7e4 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/ppm_ct_scan_machine_template.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMCTSCANMACHINETEMPLATE(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/test_ppm_ct_scan_machine_template.py b/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/test_ppm_ct_scan_machine_template.py new file mode 100644 index 0000000..bde1c7a --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_ct_scan_machine_template/test_ppm_ct_scan_machine_template.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPPMCTSCANMACHINETEMPLATE(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_electrical_fixtures_inside_rooms_table/__init__.py b/asset_lite/asset_lite/doctype/ppm_electrical_fixtures_inside_rooms_table/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_electrical_fixtures_inside_rooms_table/ppm_electrical_fixtures_inside_rooms_table.json b/asset_lite/asset_lite/doctype/ppm_electrical_fixtures_inside_rooms_table/ppm_electrical_fixtures_inside_rooms_table.json new file mode 100644 index 0000000..9fe410e --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_electrical_fixtures_inside_rooms_table/ppm_electrical_fixtures_inside_rooms_table.json @@ -0,0 +1,78 @@ +{ + "actions": [], + "autoname": "autoincrement", + "creation": "2024-09-13 09:25:03.360604", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "room_numbers", + "electrical_switches", + "electrical_sockets_outlets", + "fluorescent_tubes", + "column_break_5", + "ballasts", + "starters", + "glass_cover_of_tube_lights" + ], + "fields": [ + { + "fieldname": "room_numbers", + "fieldtype": "Data", + "label": "Room Numbers" + }, + { + "default": "0", + "fieldname": "electrical_switches", + "fieldtype": "Check", + "label": "Electrical switches" + }, + { + "default": "0", + "fieldname": "electrical_sockets_outlets", + "fieldtype": "Check", + "label": "Electrical sockets/outlets" + }, + { + "default": "0", + "fieldname": "fluorescent_tubes", + "fieldtype": "Check", + "label": "Fluorescent tubes" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "ballasts", + "fieldtype": "Check", + "label": "Ballasts" + }, + { + "default": "0", + "fieldname": "starters", + "fieldtype": "Check", + "label": "Starters" + }, + { + "default": "0", + "fieldname": "glass_cover_of_tube_lights", + "fieldtype": "Check", + "label": "Glass cover of tube lights" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-09-13 17:45:12.282804", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM Electrical Fixtures Inside Rooms Table", + "naming_rule": "Autoincrement", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_electrical_fixtures_inside_rooms_table/ppm_electrical_fixtures_inside_rooms_table.py b/asset_lite/asset_lite/doctype/ppm_electrical_fixtures_inside_rooms_table/ppm_electrical_fixtures_inside_rooms_table.py new file mode 100644 index 0000000..5e5890b --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_electrical_fixtures_inside_rooms_table/ppm_electrical_fixtures_inside_rooms_table.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMElectricalFixturesInsideRoomsTable(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_electricals_panels_table/__init__.py b/asset_lite/asset_lite/doctype/ppm_electricals_panels_table/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_electricals_panels_table/ppm_electricals_panels_table.json b/asset_lite/asset_lite/doctype/ppm_electricals_panels_table/ppm_electricals_panels_table.json new file mode 100644 index 0000000..7c8656a --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_electricals_panels_table/ppm_electricals_panels_table.json @@ -0,0 +1,92 @@ +{ + "actions": [], + "autoname": "autoincrement", + "creation": "2024-09-13 09:12:39.494608", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "location", + "wires", + "grounding", + "mcbs", + "connections", + "column_break_6", + "panel_door", + "panel_hinges_lock", + "schedule_of_panel", + "cleaning_of_panel" + ], + "fields": [ + { + "fieldname": "location", + "fieldtype": "Data", + "label": "Location" + }, + { + "default": "0", + "fieldname": "wires", + "fieldtype": "Check", + "label": "Wires" + }, + { + "default": "0", + "fieldname": "grounding", + "fieldtype": "Check", + "label": "Grounding" + }, + { + "default": "0", + "fieldname": "mcbs", + "fieldtype": "Check", + "label": "MCB's" + }, + { + "default": "0", + "fieldname": "connections", + "fieldtype": "Check", + "label": "Connections" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "panel_door", + "fieldtype": "Check", + "label": "Panel door" + }, + { + "default": "0", + "fieldname": "panel_hinges_lock", + "fieldtype": "Check", + "label": "Panel hinges & lock" + }, + { + "default": "0", + "fieldname": "schedule_of_panel", + "fieldtype": "Check", + "label": "Schedule of panel" + }, + { + "default": "0", + "fieldname": "cleaning_of_panel", + "fieldtype": "Check", + "label": "Cleaning of panel" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-09-13 17:45:14.076447", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM electricals Panels table", + "naming_rule": "Autoincrement", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_electricals_panels_table/ppm_electricals_panels_table.py b/asset_lite/asset_lite/doctype/ppm_electricals_panels_table/ppm_electricals_panels_table.py new file mode 100644 index 0000000..41ac0e0 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_electricals_panels_table/ppm_electricals_panels_table.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMelectricalsPanelstable(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_fire_alarm_device_table/__init__.py b/asset_lite/asset_lite/doctype/ppm_fire_alarm_device_table/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_fire_alarm_device_table/ppm_fire_alarm_device_table.json b/asset_lite/asset_lite/doctype/ppm_fire_alarm_device_table/ppm_fire_alarm_device_table.json new file mode 100644 index 0000000..7072d77 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_fire_alarm_device_table/ppm_fire_alarm_device_table.json @@ -0,0 +1,38 @@ +{ + "actions": [], + "autoname": "autoincrement", + "creation": "2024-09-11 17:03:33.429492", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "room_no", + "check_2" + ], + "fields": [ + { + "fieldname": "room_no", + "fieldtype": "Data", + "label": "Room No" + }, + { + "default": "0", + "fieldname": "check_2", + "fieldtype": "Check", + "label": "Value" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-09-13 17:45:14.941826", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM Fire Alarm Device Table", + "naming_rule": "Autoincrement", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_fire_alarm_device_table/ppm_fire_alarm_device_table.py b/asset_lite/asset_lite/doctype/ppm_fire_alarm_device_table/ppm_fire_alarm_device_table.py new file mode 100644 index 0000000..4eff966 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_fire_alarm_device_table/ppm_fire_alarm_device_table.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMFireAlarmDeviceTable(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_mri_template/__init__.py b/asset_lite/asset_lite/doctype/ppm_mri_template/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_mri_template/ppm_mri_template.js b/asset_lite/asset_lite/doctype/ppm_mri_template/ppm_mri_template.js new file mode 100644 index 0000000..ea96071 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_mri_template/ppm_mri_template.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("PPM MRI Template", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/ppm_mri_template/ppm_mri_template.json b/asset_lite/asset_lite/doctype/ppm_mri_template/ppm_mri_template.json new file mode 100644 index 0000000..fdbef23 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_mri_template/ppm_mri_template.json @@ -0,0 +1,43 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-09-13 15:44:37.160329", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "table_1" + ], + "fields": [ + { + "fieldname": "table_1", + "fieldtype": "Table", + "options": "PPM table for MRI" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-09-13 17:45:06.173357", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM MRI Template", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_mri_template/ppm_mri_template.py b/asset_lite/asset_lite/doctype/ppm_mri_template/ppm_mri_template.py new file mode 100644 index 0000000..1a453e5 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_mri_template/ppm_mri_template.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMMRITemplate(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_mri_template/test_ppm_mri_template.py b/asset_lite/asset_lite/doctype/ppm_mri_template/test_ppm_mri_template.py new file mode 100644 index 0000000..c7e8c42 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_mri_template/test_ppm_mri_template.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPPMMRITemplate(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/__init__.py b/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/ppm_of_ct_scan_machine.js b/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/ppm_of_ct_scan_machine.js new file mode 100644 index 0000000..b6208a6 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/ppm_of_ct_scan_machine.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("PPM OF CT SCAN MACHINE", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/ppm_of_ct_scan_machine.json b/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/ppm_of_ct_scan_machine.json new file mode 100644 index 0000000..c23bba1 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/ppm_of_ct_scan_machine.json @@ -0,0 +1,96 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format:CT Scan - {####}", + "creation": "2024-09-13 15:47:34.231656", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "month", + "year", + "column_break_phdw", + "asset_maintenance_log", + "section_break_3", + "data", + "table", + "amended_from" + ], + "fields": [ + { + "fieldname": "month", + "fieldtype": "Select", + "label": "Month", + "options": "\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember" + }, + { + "fieldname": "year", + "fieldtype": "Data", + "label": "Year" + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break" + }, + { + "default": "CT Scan", + "fieldname": "data", + "fieldtype": "Link", + "label": "Template", + "options": "PPM Templates" + }, + { + "fieldname": "table", + "fieldtype": "Table", + "options": "PPM Table For CT Scan Machine" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "PPM OF CT SCAN MACHINE", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "column_break_phdw", + "fieldtype": "Column Break" + }, + { + "fieldname": "asset_maintenance_log", + "fieldtype": "Link", + "label": "Asset Maintenance Log", + "options": "Asset Maintenance Log", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2024-09-23 16:47:36.569116", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM OF CT SCAN MACHINE", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/ppm_of_ct_scan_machine.py b/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/ppm_of_ct_scan_machine.py new file mode 100644 index 0000000..ae76db0 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/ppm_of_ct_scan_machine.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMOFCTSCANMACHINE(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/test_ppm_of_ct_scan_machine.py b/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/test_ppm_of_ct_scan_machine.py new file mode 100644 index 0000000..59ce7cc --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_ct_scan_machine/test_ppm_of_ct_scan_machine.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPPMOFCTSCANMACHINE(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/__init__.py b/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/ppm_of_electrical_fixtures_inside_rooms.js b/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/ppm_of_electrical_fixtures_inside_rooms.js new file mode 100644 index 0000000..0ffccd2 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/ppm_of_electrical_fixtures_inside_rooms.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/ppm_of_electrical_fixtures_inside_rooms.json b/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/ppm_of_electrical_fixtures_inside_rooms.json new file mode 100644 index 0000000..f9c2a36 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/ppm_of_electrical_fixtures_inside_rooms.json @@ -0,0 +1,131 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "PPM-EF-.####", + "creation": "2024-09-13 09:33:19.484317", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "building", + "month", + "column_break_2", + "floor", + "year", + "section_break_4", + "electrical_fixtures_table", + "notes_if_any", + "section_break_7", + "checked_by", + "electrician", + "column_break_10", + "maintenance_manager", + "amended_from" + ], + "fields": [ + { + "fieldname": "building", + "fieldtype": "Data", + "label": "Building" + }, + { + "fieldname": "month", + "fieldtype": "Select", + "label": "Month", + "options": "\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "floor", + "fieldtype": "Select", + "label": "Floor", + "options": "\nGF\nFF\nSF\nTF" + }, + { + "fieldname": "year", + "fieldtype": "Data", + "label": "Year" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "electrical_fixtures_table", + "fieldtype": "Table", + "label": "Electrical Fixtures table", + "options": "PPM Electrical Fixtures Inside Rooms Table" + }, + { + "fieldname": "notes_if_any", + "fieldtype": "Small Text", + "label": "Notes if Any" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "checked_by", + "fieldtype": "Data", + "label": "Checked By" + }, + { + "depends_on": "eval:doc.workflow_state == \"Sent to Electrician\" || doc.workflow_state == \"Sent to Maintenance Manager\" || doc.workflow_state == \"Approved\"", + "fieldname": "electrician", + "fieldtype": "Data", + "hidden": 1, + "label": "Electrician" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:doc.workflow_state == \"Sent to Maintenance Manager\" || doc.workflow_state == \"Approved\"", + "fieldname": "maintenance_manager", + "fieldtype": "Data", + "label": "Maintenance Manager" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "PPM OF ELECTRICAL PANELS", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2024-09-13 17:45:10.601474", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/ppm_of_electrical_fixtures_inside_rooms.py b/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/ppm_of_electrical_fixtures_inside_rooms.py new file mode 100644 index 0000000..110e017 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/ppm_of_electrical_fixtures_inside_rooms.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMOFELECTRICALFIXTURESINSIDEROOMS(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/test_ppm_of_electrical_fixtures_inside_rooms.py b/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/test_ppm_of_electrical_fixtures_inside_rooms.py new file mode 100644 index 0000000..db46677 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_electrical_fixtures_inside_rooms/test_ppm_of_electrical_fixtures_inside_rooms.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPPMOFELECTRICALFIXTURESINSIDEROOMS(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/__init__.py b/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/ppm_of_electrical_panels.js b/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/ppm_of_electrical_panels.js new file mode 100644 index 0000000..1ac7e95 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/ppm_of_electrical_panels.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("PPM OF ELECTRICAL PANELS", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/ppm_of_electrical_panels.json b/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/ppm_of_electrical_panels.json new file mode 100644 index 0000000..4300277 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/ppm_of_electrical_panels.json @@ -0,0 +1,118 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "PPM-EP-.####", + "creation": "2024-09-13 09:19:00.454586", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "month", + "column_break_2", + "year", + "section_break_4", + "electrical_panels_table", + "notes_if_any", + "section_break_7", + "checked_by", + "electrician", + "column_break_10", + "maintenance_manager", + "amended_from" + ], + "fields": [ + { + "fieldname": "month", + "fieldtype": "Select", + "label": "Month", + "options": "\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "year", + "fieldtype": "Data", + "label": "Year" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "electrical_panels_table", + "fieldtype": "Table", + "label": "Electrical Panels Table", + "options": "PPM electricals Panels table" + }, + { + "fieldname": "notes_if_any", + "fieldtype": "Small Text", + "label": "Notes if Any" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "checked_by", + "fieldtype": "Data", + "label": "Checked By" + }, + { + "depends_on": "eval:doc.workflow_state == \"Sent to Electrician\" || doc.workflow_state == \"Sent to Maintenance Manager\" || doc.workflow_state == \"Approved\"", + "fieldname": "electrician", + "fieldtype": "Data", + "hidden": 1, + "label": "Electrician" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:doc.workflow_state == \"Sent to Maintenance Manager\" || doc.workflow_state == \"Approved\"", + "fieldname": "maintenance_manager", + "fieldtype": "Data", + "label": "Maintenance Manager" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "PPM OF ELECTRICAL PANELS", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2024-09-13 17:45:13.038073", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM OF ELECTRICAL PANELS", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/ppm_of_electrical_panels.py b/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/ppm_of_electrical_panels.py new file mode 100644 index 0000000..ef8ecdb --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/ppm_of_electrical_panels.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMOFELECTRICALPANELS(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/test_ppm_of_electrical_panels.py b/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/test_ppm_of_electrical_panels.py new file mode 100644 index 0000000..89ac3d7 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_electrical_panels/test_ppm_of_electrical_panels.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPPMOFELECTRICALPANELS(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/__init__.py b/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/ppm_of_fire_alarm_devices.js b/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/ppm_of_fire_alarm_devices.js new file mode 100644 index 0000000..9b89192 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/ppm_of_fire_alarm_devices.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("PPM OF FIRE ALARM DEVICES", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/ppm_of_fire_alarm_devices.json b/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/ppm_of_fire_alarm_devices.json new file mode 100644 index 0000000..b2cd9cf --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/ppm_of_fire_alarm_devices.json @@ -0,0 +1,118 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "PPM-FAD-.####", + "creation": "2024-09-13 09:23:17.327840", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "month", + "column_break_2", + "year", + "section_break_4", + "fire_alaram_table", + "notes_if_any", + "section_break_7", + "checked_by", + "electrician", + "column_break_10", + "maintenance_manager", + "amended_from" + ], + "fields": [ + { + "fieldname": "month", + "fieldtype": "Select", + "label": "Month", + "options": "\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "year", + "fieldtype": "Data", + "label": "Year" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "fire_alaram_table", + "fieldtype": "Table", + "label": "Fire Alaram Table", + "options": "PPM Fire Alarm Device Table" + }, + { + "fieldname": "notes_if_any", + "fieldtype": "Small Text", + "label": "Notes if Any" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "checked_by", + "fieldtype": "Data", + "label": "Checked By" + }, + { + "depends_on": "eval:doc.workflow_state == \"Sent to Electrician\" || doc.workflow_state == \"Sent to Maintenance Manager\" || doc.workflow_state == \"Approved\"", + "fieldname": "electrician", + "fieldtype": "Data", + "hidden": 1, + "label": "Electrician" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:doc.workflow_state == \"Sent to Maintenance Manager\" || doc.workflow_state == \"Approved\"", + "fieldname": "maintenance_manager", + "fieldtype": "Data", + "label": "Maintenance Manager" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "PPM OF ELECTRICAL PANELS", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2024-09-13 17:45:11.276292", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM OF FIRE ALARM DEVICES", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/ppm_of_fire_alarm_devices.py b/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/ppm_of_fire_alarm_devices.py new file mode 100644 index 0000000..b0c02ec --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/ppm_of_fire_alarm_devices.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMOFFIREALARMDEVICES(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/test_ppm_of_fire_alarm_devices.py b/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/test_ppm_of_fire_alarm_devices.py new file mode 100644 index 0000000..b88680f --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_fire_alarm_devices/test_ppm_of_fire_alarm_devices.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPPMOFFIREALARMDEVICES(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/__init__.py b/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/ppm_of_mri_scan_machine.js b/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/ppm_of_mri_scan_machine.js new file mode 100644 index 0000000..a40b12d --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/ppm_of_mri_scan_machine.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("PPM OF MRI SCAN MACHINE", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/ppm_of_mri_scan_machine.json b/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/ppm_of_mri_scan_machine.json new file mode 100644 index 0000000..9eee2ee --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/ppm_of_mri_scan_machine.json @@ -0,0 +1,96 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "PPM-MRI-.####", + "creation": "2024-09-13 15:45:26.474798", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "month", + "year", + "column_break_kezq", + "asset_maintenance_log", + "section_break_3", + "date", + "table_5", + "amended_from" + ], + "fields": [ + { + "fieldname": "month", + "fieldtype": "Select", + "label": "Month", + "options": "\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember" + }, + { + "fieldname": "year", + "fieldtype": "Data", + "label": "Year" + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break" + }, + { + "default": "MRI Scan", + "fieldname": "date", + "fieldtype": "Link", + "label": "Template", + "options": "PPM Templates" + }, + { + "fieldname": "table_5", + "fieldtype": "Table", + "options": "PPM table for MRI" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "PPM OF MRI SCAN MACHINE", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "column_break_kezq", + "fieldtype": "Column Break" + }, + { + "fieldname": "asset_maintenance_log", + "fieldtype": "Link", + "label": "Asset Maintenance Log", + "options": "Asset Maintenance Log", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2024-09-23 16:46:57.854116", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM OF MRI SCAN MACHINE", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/ppm_of_mri_scan_machine.py b/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/ppm_of_mri_scan_machine.py new file mode 100644 index 0000000..d8bdb59 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/ppm_of_mri_scan_machine.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMOFMRISCANMACHINE(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/test_ppm_of_mri_scan_machine.py b/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/test_ppm_of_mri_scan_machine.py new file mode 100644 index 0000000..6b9cd96 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_of_mri_scan_machine/test_ppm_of_mri_scan_machine.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPPMOFMRISCANMACHINE(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_table/__init__.py b/asset_lite/asset_lite/doctype/ppm_table/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_table/ppm_table.json b/asset_lite/asset_lite/doctype/ppm_table/ppm_table.json new file mode 100644 index 0000000..54c2512 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_table/ppm_table.json @@ -0,0 +1,52 @@ +{ + "actions": [], + "autoname": "autoincrement", + "creation": "2024-09-23 14:44:23.314567", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "maintenance_name", + "working", + "defect_found", + "not_working" + ], + "fields": [ + { + "fieldname": "maintenance_name", + "fieldtype": "Data", + "label": "Maintenance Name" + }, + { + "default": "0", + "fieldname": "working", + "fieldtype": "Check", + "label": "Working" + }, + { + "default": "0", + "fieldname": "defect_found", + "fieldtype": "Check", + "label": "Defect Found" + }, + { + "default": "0", + "fieldname": "not_working", + "fieldtype": "Check", + "label": "Not Working" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-09-23 14:44:23.314567", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM Table", + "naming_rule": "Autoincrement", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_table/ppm_table.py b/asset_lite/asset_lite/doctype/ppm_table/ppm_table.py new file mode 100644 index 0000000..3149e88 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_table/ppm_table.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMTable(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_table_for_ct_scan_machine/__init__.py b/asset_lite/asset_lite/doctype/ppm_table_for_ct_scan_machine/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_table_for_ct_scan_machine/ppm_table_for_ct_scan_machine.json b/asset_lite/asset_lite/doctype/ppm_table_for_ct_scan_machine/ppm_table_for_ct_scan_machine.json new file mode 100644 index 0000000..1f7a512 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_table_for_ct_scan_machine/ppm_table_for_ct_scan_machine.json @@ -0,0 +1,52 @@ +{ + "actions": [], + "autoname": "autoincrement", + "creation": "2024-09-13 15:46:14.365890", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "maintenance_name", + "working", + "defect_found", + "not_working" + ], + "fields": [ + { + "fieldname": "maintenance_name", + "fieldtype": "Data", + "label": "Maintenance Name" + }, + { + "default": "0", + "fieldname": "working", + "fieldtype": "Check", + "label": "Working" + }, + { + "default": "0", + "fieldname": "defect_found", + "fieldtype": "Check", + "label": "Defect Found" + }, + { + "default": "0", + "fieldname": "not_working", + "fieldtype": "Check", + "label": "Not Working" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-09-13 17:45:05.578499", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM Table For CT Scan Machine", + "naming_rule": "Autoincrement", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_table_for_ct_scan_machine/ppm_table_for_ct_scan_machine.py b/asset_lite/asset_lite/doctype/ppm_table_for_ct_scan_machine/ppm_table_for_ct_scan_machine.py new file mode 100644 index 0000000..a25eb40 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_table_for_ct_scan_machine/ppm_table_for_ct_scan_machine.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMTableForCTScanMachine(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_table_for_mri/__init__.py b/asset_lite/asset_lite/doctype/ppm_table_for_mri/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_table_for_mri/ppm_table_for_mri.json b/asset_lite/asset_lite/doctype/ppm_table_for_mri/ppm_table_for_mri.json new file mode 100644 index 0000000..ed917aa --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_table_for_mri/ppm_table_for_mri.json @@ -0,0 +1,53 @@ +{ + "actions": [], + "autoname": "autoincrement", + "creation": "2024-09-13 15:43:50.713290", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "maintenance_name", + "working", + "defect_found", + "not_working" + ], + "fields": [ + { + "fieldname": "maintenance_name", + "fieldtype": "Data", + "label": "Maintenance Name" + }, + { + "default": "0", + "fieldname": "working", + "fieldtype": "Check", + "label": "Working" + }, + { + "default": "0", + "fieldname": "defect_found", + "fieldtype": "Check", + "label": "Defect Found" + }, + { + "default": "0", + "fieldname": "not_working", + "fieldtype": "Check", + "label": "Not Working" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-09-13 17:45:06.985231", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM table for MRI", + "naming_rule": "Autoincrement", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_table_for_mri/ppm_table_for_mri.py b/asset_lite/asset_lite/doctype/ppm_table_for_mri/ppm_table_for_mri.py new file mode 100644 index 0000000..aab9c2f --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_table_for_mri/ppm_table_for_mri.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMtableforMRI(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_templates/__init__.py b/asset_lite/asset_lite/doctype/ppm_templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/ppm_templates/ppm_templates.js b/asset_lite/asset_lite/doctype/ppm_templates/ppm_templates.js new file mode 100644 index 0000000..1f2b34c --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_templates/ppm_templates.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("PPM Templates", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/ppm_templates/ppm_templates.json b/asset_lite/asset_lite/doctype/ppm_templates/ppm_templates.json new file mode 100644 index 0000000..e6f3ec0 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_templates/ppm_templates.json @@ -0,0 +1,71 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:name1", + "creation": "2024-09-23 14:42:22.511123", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "name1", + "column_break_wpmh", + "asset_type", + "section_break_dznh", + "ppm_template_table" + ], + "fields": [ + { + "fieldname": "name1", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "ppm_template_table", + "fieldtype": "Table", + "label": "PPM template Table", + "options": "PPM Table" + }, + { + "fieldname": "column_break_wpmh", + "fieldtype": "Column Break" + }, + { + "fieldname": "asset_type", + "fieldtype": "Link", + "label": "Asset Type", + "options": "Asset Type", + "reqd": 1 + }, + { + "fieldname": "section_break_dznh", + "fieldtype": "Section Break" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-09-23 17:59:05.455192", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PPM Templates", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/ppm_templates/ppm_templates.py b/asset_lite/asset_lite/doctype/ppm_templates/ppm_templates.py new file mode 100644 index 0000000..a753495 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_templates/ppm_templates.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PPMTemplates(Document): + pass diff --git a/asset_lite/asset_lite/doctype/ppm_templates/test_ppm_templates.py b/asset_lite/asset_lite/doctype/ppm_templates/test_ppm_templates.py new file mode 100644 index 0000000..e3a53a5 --- /dev/null +++ b/asset_lite/asset_lite/doctype/ppm_templates/test_ppm_templates.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPPMTemplates(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/pr_table/__init__.py b/asset_lite/asset_lite/doctype/pr_table/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/pr_table/pr_table.json b/asset_lite/asset_lite/doctype/pr_table/pr_table.json new file mode 100644 index 0000000..7a6b6fa --- /dev/null +++ b/asset_lite/asset_lite/doctype/pr_table/pr_table.json @@ -0,0 +1,29 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-09-17 17:06:51.473708", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "section_break_kinu" + ], + "fields": [ + { + "fieldname": "section_break_kinu", + "fieldtype": "Section Break" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-09-17 17:06:51.473708", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "PR table", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/pr_table/pr_table.py b/asset_lite/asset_lite/doctype/pr_table/pr_table.py new file mode 100644 index 0000000..5359f2e --- /dev/null +++ b/asset_lite/asset_lite/doctype/pr_table/pr_table.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PRtable(Document): + pass diff --git a/asset_lite/asset_lite/doctype/purchase_request/__init__.py b/asset_lite/asset_lite/doctype/purchase_request/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/purchase_request/purchase_request.js b/asset_lite/asset_lite/doctype/purchase_request/purchase_request.js new file mode 100644 index 0000000..b639025 --- /dev/null +++ b/asset_lite/asset_lite/doctype/purchase_request/purchase_request.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Purchase Request", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/purchase_request/purchase_request.json b/asset_lite/asset_lite/doctype/purchase_request/purchase_request.json new file mode 100644 index 0000000..012a9a0 --- /dev/null +++ b/asset_lite/asset_lite/doctype/purchase_request/purchase_request.json @@ -0,0 +1,154 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format:{pr_no}", + "creation": "2024-09-17 17:13:10.035475", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "pr_no", + "date", + "column_break_4", + "issue", + "asset", + "asset_name", + "data_8", + "pr_table", + "section_break_10", + "priority", + "normal", + "urgent", + "required_date", + "column_break_14", + "intended_use_of_material", + "amended_from" + ], + "fields": [ + { + "fieldname": "pr_no", + "fieldtype": "Data", + "label": "PR NO", + "unique": 1 + }, + { + "default": "Today", + "fieldname": "date", + "fieldtype": "Date", + "label": "Date" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fetch_from": "asset_repair.issue", + "fetch_if_empty": 1, + "fieldname": "issue", + "fieldtype": "Link", + "label": "Work Order", + "options": "Work_Order" + }, + { + "fetch_from": "asset_repair.asset", + "fieldname": "asset", + "fieldtype": "Link", + "label": "Asset", + "options": "Asset" + }, + { + "fieldname": "data_8", + "fieldtype": "Section Break" + }, + { + "fieldname": "pr_table", + "fieldtype": "Table", + "label": "PR Table", + "options": "Purchase Request Table" + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "fieldname": "priority", + "fieldtype": "Heading", + "label": "Priority" + }, + { + "default": "0", + "fieldname": "normal", + "fieldtype": "Check", + "label": "Normal" + }, + { + "default": "0", + "fieldname": "urgent", + "fieldtype": "Check", + "label": "Urgent" + }, + { + "fieldname": "required_date", + "fieldtype": "Date", + "label": "Required Date" + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fieldname": "intended_use_of_material", + "fieldtype": "Text", + "label": "Intended Use of Material" + }, + { + "fetch_from": "asset.asset_name", + "fieldname": "asset_name", + "fieldtype": "Data", + "label": "Asset Name" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Purchase Request", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [ + { + "group": "Purchase Order", + "link_doctype": "Purchase Order", + "link_fieldname": "custom_purchase_request" + } + ], + "modified": "2024-09-20 16:11:28.459741", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Purchase Request", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/purchase_request/purchase_request.py b/asset_lite/asset_lite/doctype/purchase_request/purchase_request.py new file mode 100644 index 0000000..141fa9a --- /dev/null +++ b/asset_lite/asset_lite/doctype/purchase_request/purchase_request.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PurchaseRequest(Document): + pass diff --git a/asset_lite/asset_lite/doctype/purchase_request/test_purchase_request.py b/asset_lite/asset_lite/doctype/purchase_request/test_purchase_request.py new file mode 100644 index 0000000..926c8c2 --- /dev/null +++ b/asset_lite/asset_lite/doctype/purchase_request/test_purchase_request.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPurchaseRequest(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/purchase_request_table/__init__.py b/asset_lite/asset_lite/doctype/purchase_request_table/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/purchase_request_table/purchase_request_table.json b/asset_lite/asset_lite/doctype/purchase_request_table/purchase_request_table.json new file mode 100644 index 0000000..7bec699 --- /dev/null +++ b/asset_lite/asset_lite/doctype/purchase_request_table/purchase_request_table.json @@ -0,0 +1,95 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-09-17 17:12:10.133001", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "item_name", + "quantity", + "uom", + "uom_conversion_factor", + "description", + "required_by", + "unit_price", + "amount", + "warehouse" + ], + "fields": [ + { + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item", + "reqd": 1 + }, + { + "fetch_from": "item_code.item_name", + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name" + }, + { + "fieldname": "quantity", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Quantity" + }, + { + "fetch_from": "item_code.stock_uom", + "fieldname": "uom", + "fieldtype": "Data", + "label": "UOM" + }, + { + "fieldname": "uom_conversion_factor", + "fieldtype": "Data", + "label": "UOM Conversion Factor" + }, + { + "fieldname": "description", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Description", + "reqd": 1 + }, + { + "fieldname": "required_by", + "fieldtype": "Date", + "label": "Required By", + "reqd": 1 + }, + { + "fieldname": "unit_price", + "fieldtype": "Data", + "label": "Unit Price" + }, + { + "fieldname": "amount", + "fieldtype": "Data", + "label": "Amount" + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "options": "Warehouse" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-09-18 18:58:55.153194", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Purchase Request Table", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/purchase_request_table/purchase_request_table.py b/asset_lite/asset_lite/doctype/purchase_request_table/purchase_request_table.py new file mode 100644 index 0000000..1a521e7 --- /dev/null +++ b/asset_lite/asset_lite/doctype/purchase_request_table/purchase_request_table.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PurchaseRequestTable(Document): + pass diff --git a/asset_lite/asset_lite/doctype/service_coverage/__init__.py b/asset_lite/asset_lite/doctype/service_coverage/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/service_coverage/service_coverage.json b/asset_lite/asset_lite/doctype/service_coverage/service_coverage.json new file mode 100644 index 0000000..5f6a869 --- /dev/null +++ b/asset_lite/asset_lite/doctype/service_coverage/service_coverage.json @@ -0,0 +1,57 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-04-03 18:34:27.947154", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "service_agreement", + "start_date", + "end_date", + "active", + "no_of_pms" + ], + "fields": [ + { + "fieldname": "service_agreement", + "fieldtype": "Select", + "label": "Service Agreement", + "options": "\nWarranty\nContract\nFrame Work" + }, + { + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date" + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End date" + }, + { + "fieldname": "active", + "fieldtype": "Select", + "label": "Active", + "options": "\nYes\nNo" + }, + { + "fieldname": "no_of_pms", + "fieldtype": "Data", + "label": "No Of PMs", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-04-03 20:05:09.090315", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Service Coverage", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/service_coverage/service_coverage.py b/asset_lite/asset_lite/doctype/service_coverage/service_coverage.py new file mode 100644 index 0000000..c486d0e --- /dev/null +++ b/asset_lite/asset_lite/doctype/service_coverage/service_coverage.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class ServiceCoverage(Document): + pass diff --git a/asset_lite/asset_lite/doctype/site_information/__init__.py b/asset_lite/asset_lite/doctype/site_information/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/site_information/site_information.js b/asset_lite/asset_lite/doctype/site_information/site_information.js new file mode 100644 index 0000000..e39d6a7 --- /dev/null +++ b/asset_lite/asset_lite/doctype/site_information/site_information.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Site Information", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/site_information/site_information.json b/asset_lite/asset_lite/doctype/site_information/site_information.json new file mode 100644 index 0000000..fa4e682 --- /dev/null +++ b/asset_lite/asset_lite/doctype/site_information/site_information.json @@ -0,0 +1,247 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:site_name_en", + "creation": "2024-09-24 18:24:05.831422", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "site_name", + "region", + "address_2", + "currency", + "column_break_nmbz", + "site_name_en", + "address1", + "logo", + "contact_information_tab", + "contact_name", + "section_break_xnmk", + "fax_1", + "email_1", + "phone_1", + "phone_3", + "column_break_udpz", + "fax_2", + "email_2", + "phone_2", + "contract_information_tab", + "contract_number", + "contractor_supervisor", + "project_start_date", + "column_break_euyw", + "contract_name", + "site_maintenance_manager", + "project_end_date", + "cut_value__class_tab", + "cut_value_for_class_a_devices", + "cut_value_for_class_b_devices", + "cut_value_for_class_c_devices", + "column_break_ybpx", + "cut_factor_for_class_a_devices", + "cut_factor_for_class_b_devices", + "cut_factor_for_class_c_devices" + ], + "fields": [ + { + "fieldname": "site_name", + "fieldtype": "Data", + "label": "Site Name" + }, + { + "fieldname": "region", + "fieldtype": "Data", + "label": "Region" + }, + { + "fieldname": "address_2", + "fieldtype": "Small Text", + "label": "Address 2" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency" + }, + { + "fieldname": "column_break_nmbz", + "fieldtype": "Column Break" + }, + { + "fieldname": "site_name_en", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Site Name EN", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "address1", + "fieldtype": "Small Text", + "label": "Address1" + }, + { + "fieldname": "logo", + "fieldtype": "Attach Image", + "label": "Logo" + }, + { + "fieldname": "contact_information_tab", + "fieldtype": "Tab Break", + "label": "Contact Information" + }, + { + "fieldname": "contact_name", + "fieldtype": "Data", + "label": "Contact Name" + }, + { + "fieldname": "section_break_xnmk", + "fieldtype": "Section Break" + }, + { + "fieldname": "fax_1", + "fieldtype": "Data", + "label": "Fax 1" + }, + { + "fieldname": "email_1", + "fieldtype": "Data", + "label": "Email 1" + }, + { + "fieldname": "phone_1", + "fieldtype": "Data", + "label": "Phone 1" + }, + { + "fieldname": "phone_3", + "fieldtype": "Data", + "label": "Phone 3" + }, + { + "fieldname": "column_break_udpz", + "fieldtype": "Column Break" + }, + { + "fieldname": "fax_2", + "fieldtype": "Data", + "label": "Fax 2" + }, + { + "fieldname": "email_2", + "fieldtype": "Data", + "label": "Email 2" + }, + { + "fieldname": "phone_2", + "fieldtype": "Data", + "label": "Phone 2" + }, + { + "fieldname": "contract_information_tab", + "fieldtype": "Tab Break", + "label": "Contract Information" + }, + { + "fieldname": "contract_number", + "fieldtype": "Data", + "label": "Contract Number" + }, + { + "fieldname": "contractor_supervisor", + "fieldtype": "Data", + "label": "Contractor Supervisor" + }, + { + "fieldname": "project_start_date", + "fieldtype": "Date", + "label": "Project Start Date" + }, + { + "fieldname": "column_break_euyw", + "fieldtype": "Column Break" + }, + { + "fieldname": "contract_name", + "fieldtype": "Data", + "label": "Contract Name" + }, + { + "fieldname": "site_maintenance_manager", + "fieldtype": "Data", + "label": "Site Maintenance Manager" + }, + { + "fieldname": "project_end_date", + "fieldtype": "Date", + "label": "Project End Date" + }, + { + "fieldname": "cut_value__class_tab", + "fieldtype": "Tab Break", + "label": "Cut Value / Class" + }, + { + "fieldname": "cut_value_for_class_a_devices", + "fieldtype": "Data", + "label": "Cut Value for Class A devices" + }, + { + "fieldname": "cut_value_for_class_b_devices", + "fieldtype": "Data", + "label": "Cut Value for Class B devices" + }, + { + "fieldname": "cut_value_for_class_c_devices", + "fieldtype": "Data", + "label": "Cut Value for Class C devices" + }, + { + "fieldname": "column_break_ybpx", + "fieldtype": "Column Break" + }, + { + "fieldname": "cut_factor_for_class_a_devices", + "fieldtype": "Data", + "label": "Cut factor for Class A devices" + }, + { + "fieldname": "cut_factor_for_class_b_devices", + "fieldtype": "Data", + "label": "Cut factor for Class B devices" + }, + { + "fieldname": "cut_factor_for_class_c_devices", + "fieldtype": "Data", + "label": "Cut factor for Class C devices" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-03-04 16:02:46.101873", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Site Information", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/site_information/site_information.py b/asset_lite/asset_lite/doctype/site_information/site_information.py new file mode 100644 index 0000000..33ba676 --- /dev/null +++ b/asset_lite/asset_lite/doctype/site_information/site_information.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class SiteInformation(Document): + pass diff --git a/asset_lite/asset_lite/doctype/site_information/test_site_information.py b/asset_lite/asset_lite/doctype/site_information/test_site_information.py new file mode 100644 index 0000000..a8cca04 --- /dev/null +++ b/asset_lite/asset_lite/doctype/site_information/test_site_information.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestSiteInformation(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/spare_parts/__init__.py b/asset_lite/asset_lite/doctype/spare_parts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/spare_parts/spare_parts.json b/asset_lite/asset_lite/doctype/spare_parts/spare_parts.json new file mode 100644 index 0000000..78b0ee2 --- /dev/null +++ b/asset_lite/asset_lite/doctype/spare_parts/spare_parts.json @@ -0,0 +1,887 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2024-09-11 13:39:19.751600", + "default_view": "List", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "product_bundle", + "col_break1", + "item_name", + "work_order", + "description_section", + "description", + "brand", + "col_break7", + "item_group", + "image", + "image_view", + "quantity_and_rate", + "received_qty", + "qty", + "rejected_qty", + "col_break2", + "uom", + "conversion_factor", + "stock_uom", + "stock_qty", + "sec_break1", + "price_list_rate", + "col_break3", + "base_price_list_rate", + "section_break_26", + "margin_type", + "margin_rate_or_amount", + "rate_with_margin", + "column_break_30", + "discount_percentage", + "discount_amount", + "base_rate_with_margin", + "sec_break2", + "rate", + "amount", + "item_tax_template", + "col_break4", + "base_rate", + "base_amount", + "pricing_rules", + "stock_uom_rate", + "is_free_item", + "section_break_22", + "net_rate", + "net_amount", + "column_break_25", + "base_net_rate", + "base_net_amount", + "valuation_rate", + "item_tax_amount", + "landed_cost_voucher_amount", + "rm_supp_cost", + "warehouse_section", + "warehouse", + "from_warehouse", + "quality_inspection", + "serial_no", + "col_br_wh", + "rejected_warehouse", + "batch_no", + "rejected_serial_no", + "manufacture_details", + "manufacturer", + "column_break_13", + "manufacturer_part_no", + "accounting", + "expense_account", + "col_break5", + "is_fixed_asset", + "asset_location", + "asset_category", + "deferred_expense_section", + "deferred_expense_account", + "service_stop_date", + "enable_deferred_expense", + "column_break_58", + "service_start_date", + "service_end_date", + "reference", + "allow_zero_valuation_rate", + "item_tax_rate", + "bom", + "include_exploded_items", + "purchase_invoice_item", + "col_break6", + "purchase_order", + "po_detail", + "purchase_receipt", + "pr_detail", + "sales_invoice_item", + "item_weight_details", + "weight_per_unit", + "total_weight", + "column_break_38", + "weight_uom", + "accounting_dimensions_section", + "project", + "dimension_col_break", + "cost_center", + "section_break_82", + "page_break" + ], + "fields": [ + { + "bold": 1, + "columns": 3, + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item", + "oldfieldname": "item_code", + "oldfieldtype": "Link", + "options": "Item", + "print_hide": 1, + "search_index": 1 + }, + { + "fieldname": "product_bundle", + "fieldtype": "Link", + "label": "Product Bundle", + "options": "Product Bundle", + "read_only": 1 + }, + { + "fieldname": "col_break1", + "fieldtype": "Column Break" + }, + { + "fetch_from": "item_code.item_name", + "fetch_if_empty": 1, + "fieldname": "item_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Item Name", + "oldfieldname": "item_name", + "oldfieldtype": "Data", + "reqd": 1 + }, + { + "collapsible": 1, + "fieldname": "description_section", + "fieldtype": "Section Break", + "label": "Description" + }, + { + "fieldname": "description", + "fieldtype": "Text Editor", + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "print_width": "300px", + "width": "300px" + }, + { + "fieldname": "brand", + "fieldtype": "Link", + "hidden": 1, + "label": "Brand", + "options": "Brand", + "print_hide": 1 + }, + { + "collapsible": 1, + "fieldname": "col_break7", + "fieldtype": "Column Break" + }, + { + "fetch_from": "item_code.item_group", + "fetch_if_empty": 1, + "fieldname": "item_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "image", + "fieldtype": "Attach", + "hidden": 1, + "label": "Image" + }, + { + "fieldname": "image_view", + "fieldtype": "Image", + "label": "Image View", + "options": "image", + "print_hide": 1 + }, + { + "fieldname": "quantity_and_rate", + "fieldtype": "Section Break", + "label": "Quantity and Rate" + }, + { + "fieldname": "received_qty", + "fieldtype": "Float", + "label": "Received Qty", + "read_only": 1 + }, + { + "bold": 1, + "columns": 2, + "fieldname": "qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Quantity", + "oldfieldname": "qty", + "oldfieldtype": "Currency" + }, + { + "fieldname": "rejected_qty", + "fieldtype": "Float", + "label": "Rejected Qty" + }, + { + "fieldname": "col_break2", + "fieldtype": "Column Break" + }, + { + "default": "Nos", + "fieldname": "uom", + "fieldtype": "Link", + "label": "UOM", + "options": "UOM" + }, + { + "depends_on": "eval:doc.uom != doc.stock_uom", + "fieldname": "conversion_factor", + "fieldtype": "Float", + "label": "UOM Conversion Factor", + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "eval:doc.uom != doc.stock_uom", + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM", + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "eval:doc.uom != doc.stock_uom", + "fieldname": "stock_qty", + "fieldtype": "Float", + "label": "Accepted Qty in Stock UOM", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "sec_break1", + "fieldtype": "Section Break" + }, + { + "fieldname": "price_list_rate", + "fieldtype": "Currency", + "label": "Price List Rate", + "options": "currency", + "print_hide": 1 + }, + { + "fieldname": "col_break3", + "fieldtype": "Column Break" + }, + { + "fieldname": "base_price_list_rate", + "fieldtype": "Currency", + "label": "Price List Rate (Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "section_break_26", + "fieldtype": "Section Break", + "label": "Discount and Margin" + }, + { + "depends_on": "price_list_rate", + "fieldname": "margin_type", + "fieldtype": "Select", + "label": "Margin Type", + "options": "\nPercentage\nAmount", + "print_hide": 1 + }, + { + "depends_on": "eval:doc.margin_type && doc.price_list_rate", + "fieldname": "margin_rate_or_amount", + "fieldtype": "Float", + "label": "Margin Rate or Amount", + "print_hide": 1 + }, + { + "depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount", + "fieldname": "rate_with_margin", + "fieldtype": "Currency", + "label": "Rate With Margin", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "column_break_30", + "fieldtype": "Column Break" + }, + { + "depends_on": "price_list_rate", + "fieldname": "discount_percentage", + "fieldtype": "Percent", + "label": "Discount on Price List Rate (%)" + }, + { + "depends_on": "price_list_rate", + "fieldname": "discount_amount", + "fieldtype": "Currency", + "label": "Discount Amount", + "options": "currency" + }, + { + "depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount", + "fieldname": "base_rate_with_margin", + "fieldtype": "Currency", + "label": "Rate With Margin (Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "sec_break2", + "fieldtype": "Section Break" + }, + { + "bold": 1, + "columns": 3, + "fetch_from": "item_code.valuation_rate", + "fetch_if_empty": 1, + "fieldname": "rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Rate", + "oldfieldname": "import_rate", + "oldfieldtype": "Currency", + "options": "currency", + "reqd": 1 + }, + { + "columns": 2, + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "oldfieldname": "import_amount", + "oldfieldtype": "Currency", + "options": "currency", + "reqd": 1 + }, + { + "fieldname": "item_tax_template", + "fieldtype": "Link", + "label": "Item Tax Template", + "options": "Item Tax Template", + "print_hide": 1 + }, + { + "fieldname": "col_break4", + "fieldtype": "Column Break" + }, + { + "fieldname": "base_rate", + "fieldtype": "Currency", + "label": "Rate (Company Currency)", + "oldfieldname": "rate", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "base_amount", + "fieldtype": "Currency", + "label": "Amount (Company Currency)", + "oldfieldname": "amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "pricing_rules", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Pricing Rules", + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "eval: doc.uom != doc.stock_uom", + "fieldname": "stock_uom_rate", + "fieldtype": "Currency", + "label": "Rate of Stock UOM", + "no_copy": 1, + "options": "currency", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_free_item", + "fieldtype": "Check", + "label": "Is Free Item", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_22", + "fieldtype": "Section Break" + }, + { + "fieldname": "net_rate", + "fieldtype": "Currency", + "label": "Net Rate", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "net_amount", + "fieldtype": "Currency", + "label": "Net Amount", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_25", + "fieldtype": "Column Break" + }, + { + "fieldname": "base_net_rate", + "fieldtype": "Currency", + "label": "Net Rate (Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "base_net_amount", + "fieldtype": "Currency", + "label": "Net Amount (Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "valuation_rate", + "fieldtype": "Currency", + "hidden": 1, + "label": "Valuation Rate", + "no_copy": 1, + "options": "Company:company:default_currency", + "precision": "6", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "item_tax_amount", + "fieldtype": "Currency", + "hidden": 1, + "label": "Item Tax Amount Included in Value", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, + "width": "150px" + }, + { + "allow_on_submit": 1, + "fieldname": "landed_cost_voucher_amount", + "fieldtype": "Currency", + "label": "Landed Cost Voucher Amount", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "rm_supp_cost", + "fieldtype": "Currency", + "hidden": 1, + "label": "Raw Materials Supplied Cost", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "warehouse_section", + "fieldtype": "Section Break", + "label": "Warehouse" + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Accepted Warehouse", + "options": "Warehouse" + }, + { + "depends_on": "eval:parent.is_internal_supplier && parent.update_stock", + "fieldname": "from_warehouse", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "From Warehouse", + "options": "Warehouse" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "quality_inspection", + "fieldtype": "Link", + "label": "Quality Inspection", + "no_copy": 1, + "options": "Quality Inspection", + "print_hide": 1 + }, + { + "depends_on": "eval:!doc.is_fixed_asset", + "fieldname": "serial_no", + "fieldtype": "Text", + "label": "Serial No", + "no_copy": 1 + }, + { + "fieldname": "col_br_wh", + "fieldtype": "Column Break" + }, + { + "fieldname": "rejected_warehouse", + "fieldtype": "Link", + "label": "Rejected Warehouse", + "options": "Warehouse" + }, + { + "depends_on": "eval:!doc.is_fixed_asset", + "fieldname": "batch_no", + "fieldtype": "Link", + "label": "Batch No", + "no_copy": 1, + "options": "Batch" + }, + { + "depends_on": "eval:!doc.is_fixed_asset", + "fieldname": "rejected_serial_no", + "fieldtype": "Text", + "label": "Rejected Serial No", + "no_copy": 1, + "print_hide": 1 + }, + { + "collapsible": 1, + "fieldname": "manufacture_details", + "fieldtype": "Section Break", + "label": "Manufacture" + }, + { + "fieldname": "manufacturer", + "fieldtype": "Link", + "label": "Manufacturer", + "options": "Manufacturer" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "fieldname": "manufacturer_part_no", + "fieldtype": "Data", + "label": "Manufacturer Part Number" + }, + { + "fieldname": "accounting", + "fieldtype": "Section Break", + "label": "Accounting" + }, + { + "fieldname": "expense_account", + "fieldtype": "Link", + "label": "Expense Head", + "oldfieldname": "expense_head", + "oldfieldtype": "Link", + "options": "Account", + "print_hide": 1, + "print_width": "120px", + "width": "120px" + }, + { + "fieldname": "col_break5", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fetch_from": "item_code.is_fixed_asset", + "fieldname": "is_fixed_asset", + "fieldtype": "Check", + "hidden": 1, + "label": "Is Fixed Asset", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "is_fixed_asset", + "fieldname": "asset_location", + "fieldtype": "Link", + "label": "Asset Location", + "options": "Location" + }, + { + "depends_on": "is_fixed_asset", + "fetch_from": "item_code.asset_category", + "fieldname": "asset_category", + "fieldtype": "Link", + "label": "Asset Category", + "options": "Asset Category", + "read_only": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "enable_deferred_expense", + "fieldname": "deferred_expense_section", + "fieldtype": "Section Break", + "label": "Deferred Expense" + }, + { + "depends_on": "enable_deferred_expense", + "fieldname": "deferred_expense_account", + "fieldtype": "Link", + "label": "Deferred Expense Account", + "options": "Account" + }, + { + "allow_on_submit": 1, + "depends_on": "enable_deferred_expense", + "fieldname": "service_stop_date", + "fieldtype": "Date", + "label": "Service Stop Date", + "no_copy": 1 + }, + { + "default": "0", + "fieldname": "enable_deferred_expense", + "fieldtype": "Check", + "label": "Enable Deferred Expense" + }, + { + "fieldname": "column_break_58", + "fieldtype": "Column Break" + }, + { + "depends_on": "enable_deferred_expense", + "fieldname": "service_start_date", + "fieldtype": "Date", + "label": "Service Start Date", + "no_copy": 1 + }, + { + "depends_on": "enable_deferred_expense", + "fieldname": "service_end_date", + "fieldtype": "Date", + "label": "Service End Date", + "no_copy": 1 + }, + { + "fieldname": "reference", + "fieldtype": "Section Break", + "label": "Reference" + }, + { + "default": "0", + "fieldname": "allow_zero_valuation_rate", + "fieldtype": "Check", + "label": "Allow Zero Valuation Rate", + "no_copy": 1, + "print_hide": 1 + }, + { + "description": "Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges", + "fieldname": "item_tax_rate", + "fieldtype": "Code", + "hidden": 1, + "label": "Item Tax Rate", + "oldfieldname": "item_tax_rate", + "oldfieldtype": "Small Text", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, + { + "depends_on": "eval:parent.is_old_subcontracting_flow", + "fieldname": "bom", + "fieldtype": "Link", + "label": "BOM", + "options": "BOM", + "read_only": 1, + "read_only_depends_on": "eval:!parent.is_old_subcontracting_flow" + }, + { + "default": "0", + "depends_on": "eval:parent.is_subcontracted", + "fieldname": "include_exploded_items", + "fieldtype": "Check", + "label": "Include Exploded Items", + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "eval:parent.update_stock == 1", + "fieldname": "purchase_invoice_item", + "fieldtype": "Data", + "ignore_user_permissions": 1, + "label": "Purchase Invoice Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "col_break6", + "fieldtype": "Column Break" + }, + { + "fieldname": "purchase_order", + "fieldtype": "Link", + "label": "Purchase Order", + "no_copy": 1, + "oldfieldname": "purchase_order", + "oldfieldtype": "Link", + "options": "Purchase Order", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "po_detail", + "fieldtype": "Data", + "hidden": 1, + "label": "Purchase Order Item", + "no_copy": 1, + "oldfieldname": "po_detail", + "oldfieldtype": "Data", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "purchase_receipt", + "fieldtype": "Link", + "label": "Purchase Receipt", + "no_copy": 1, + "oldfieldname": "purchase_receipt", + "oldfieldtype": "Link", + "options": "Purchase Receipt", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "pr_detail", + "fieldtype": "Data", + "hidden": 1, + "label": "Purchase Receipt Detail", + "no_copy": 1, + "oldfieldname": "pr_detail", + "oldfieldtype": "Data", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "sales_invoice_item", + "fieldtype": "Data", + "label": "Sales Invoice Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "item_weight_details", + "fieldtype": "Section Break", + "label": "Item Weight Details" + }, + { + "fieldname": "weight_per_unit", + "fieldtype": "Float", + "label": "Weight Per Unit" + }, + { + "fieldname": "total_weight", + "fieldtype": "Float", + "label": "Total Weight", + "read_only": 1 + }, + { + "fieldname": "column_break_38", + "fieldtype": "Column Break" + }, + { + "fieldname": "weight_uom", + "fieldtype": "Link", + "label": "Weight UOM", + "options": "UOM" + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project", + "print_hide": 1 + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "default": ":Company", + "depends_on": "eval:!doc.is_fixed_asset", + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "oldfieldname": "cost_center", + "oldfieldtype": "Link", + "options": "Cost Center", + "print_hide": 1, + "print_width": "120px", + "width": "120px" + }, + { + "fieldname": "section_break_82", + "fieldtype": "Section Break" + }, + { + "allow_on_submit": 1, + "default": "0", + "fieldname": "page_break", + "fieldtype": "Check", + "label": "Page Break", + "no_copy": 1, + "print_hide": 1, + "report_hide": 1 + }, + { + "fieldname": "work_order", + "fieldtype": "Link", + "label": "Work Order", + "options": "Work_Order" + } + ], + "istable": 1, + "links": [], + "modified": "2025-03-09 11:05:37.287039", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Spare Parts", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/spare_parts/spare_parts.py b/asset_lite/asset_lite/doctype/spare_parts/spare_parts.py new file mode 100644 index 0000000..040be3e --- /dev/null +++ b/asset_lite/asset_lite/doctype/spare_parts/spare_parts.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class SpareParts(Document): + pass diff --git a/asset_lite/asset_lite/doctype/supplier_reason/__init__.py b/asset_lite/asset_lite/doctype/supplier_reason/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/supplier_reason/supplier_reason.js b/asset_lite/asset_lite/doctype/supplier_reason/supplier_reason.js new file mode 100644 index 0000000..d14f0a6 --- /dev/null +++ b/asset_lite/asset_lite/doctype/supplier_reason/supplier_reason.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Supplier Reason", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/supplier_reason/supplier_reason.json b/asset_lite/asset_lite/doctype/supplier_reason/supplier_reason.json new file mode 100644 index 0000000..1eb3c91 --- /dev/null +++ b/asset_lite/asset_lite/doctype/supplier_reason/supplier_reason.json @@ -0,0 +1,44 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:reason", + "creation": "2025-03-27 11:32:46.883033", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "reason" + ], + "fields": [ + { + "fieldname": "reason", + "fieldtype": "Data", + "label": "Reason", + "unique": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-03-27 11:35:40.654313", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Supplier Reason", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/supplier_reason/supplier_reason.py b/asset_lite/asset_lite/doctype/supplier_reason/supplier_reason.py new file mode 100644 index 0000000..416c85c --- /dev/null +++ b/asset_lite/asset_lite/doctype/supplier_reason/supplier_reason.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class SupplierReason(Document): + pass diff --git a/asset_lite/asset_lite/doctype/supplier_reason/test_supplier_reason.py b/asset_lite/asset_lite/doctype/supplier_reason/test_supplier_reason.py new file mode 100644 index 0000000..3d0fa52 --- /dev/null +++ b/asset_lite/asset_lite/doctype/supplier_reason/test_supplier_reason.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestSupplierReason(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/support_asset_list/__init__.py b/asset_lite/asset_lite/doctype/support_asset_list/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/support_asset_list/support_asset_list.json b/asset_lite/asset_lite/doctype/support_asset_list/support_asset_list.json new file mode 100644 index 0000000..ebe0342 --- /dev/null +++ b/asset_lite/asset_lite/doctype/support_asset_list/support_asset_list.json @@ -0,0 +1,37 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-10-17 13:44:10.569824", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset_id", + "asset_name" + ], + "fields": [ + { + "fieldname": "asset_id", + "fieldtype": "Link", + "label": "Asset ID", + "options": "Asset" + }, + { + "fieldname": "asset_name", + "fieldtype": "Data", + "label": "Asset Name" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-10-17 13:49:53.892284", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Support Asset List", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/support_asset_list/support_asset_list.py b/asset_lite/asset_lite/doctype/support_asset_list/support_asset_list.py new file mode 100644 index 0000000..8d442a3 --- /dev/null +++ b/asset_lite/asset_lite/doctype/support_asset_list/support_asset_list.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class SupportAssetList(Document): + pass diff --git a/asset_lite/asset_lite/doctype/support_plans/__init__.py b/asset_lite/asset_lite/doctype/support_plans/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/support_plans/support_plans.js b/asset_lite/asset_lite/doctype/support_plans/support_plans.js new file mode 100644 index 0000000..5309bf9 --- /dev/null +++ b/asset_lite/asset_lite/doctype/support_plans/support_plans.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Support Plans", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/support_plans/support_plans.json b/asset_lite/asset_lite/doctype/support_plans/support_plans.json new file mode 100644 index 0000000..f87d75f --- /dev/null +++ b/asset_lite/asset_lite/doctype/support_plans/support_plans.json @@ -0,0 +1,253 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:support_plan", + "creation": "2024-09-13 14:42:48.041382", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "support_plan", + "frequency", + "max_downtime_hrs", + "column_break_ayau", + "asset", + "starting_date", + "penalty_factor", + "section_break_zyxt", + "warranty", + "warranty_start_date", + "warranty_end_date", + "war_status", + "column_break_agzy", + "extended_warranty", + "start", + "end", + "service_contract_section", + "service_contract", + "spare_parts", + "spare_parts_labour", + "labour", + "ppm_only", + "column_break_celd", + "no", + "start_date", + "end_date", + "service_contract_status", + "vendor_details_section", + "vendor", + "section_break_pyrk", + "asset_list" + ], + "fields": [ + { + "fieldname": "support_plan", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "frequency", + "fieldtype": "Select", + "label": "Frequency", + "options": "\nDaily\nWeekly\nMonthly\nQuarterly\nYearly\n2 Yearly" + }, + { + "fieldname": "asset", + "fieldtype": "Link", + "hidden": 1, + "label": "Asset", + "options": "Asset" + }, + { + "fieldname": "column_break_ayau", + "fieldtype": "Column Break" + }, + { + "fieldname": "starting_date", + "fieldtype": "Date", + "hidden": 1, + "label": "Starting Date" + }, + { + "fieldname": "service_contract_section", + "fieldtype": "Section Break", + "label": "Service Contract" + }, + { + "default": "0", + "depends_on": "eval:doc.service_contract == 1", + "fieldname": "spare_parts", + "fieldtype": "Check", + "label": "Comprehensive" + }, + { + "fieldname": "column_break_celd", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "eval:doc.service_contract == 1", + "fieldname": "labour", + "fieldtype": "Check", + "label": "Labour Only" + }, + { + "fieldname": "start_date", + "fieldtype": "Date", + "hidden": 1, + "label": "Start Date", + "mandatory_depends_on": "service_contract" + }, + { + "depends_on": "eval:doc.service_contract == 1", + "fieldname": "service_contract_status", + "fieldtype": "Select", + "label": "Service Contract Status", + "options": "\nActive\nExpired\nNot Applicable" + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "hidden": 1, + "label": "End Date" + }, + { + "default": "0", + "fieldname": "service_contract", + "fieldtype": "Check", + "label": "Yes" + }, + { + "fieldname": "vendor_details_section", + "fieldtype": "Section Break", + "label": "Vendor Details" + }, + { + "fieldname": "vendor", + "fieldtype": "Link", + "label": "Vendor Name", + "options": "Supplier" + }, + { + "fieldname": "section_break_zyxt", + "fieldtype": "Section Break", + "label": "Warranty Details" + }, + { + "fieldname": "warranty_start_date", + "fieldtype": "Date", + "hidden": 1, + "label": "Warranty Start Date" + }, + { + "fieldname": "warranty_end_date", + "fieldtype": "Date", + "hidden": 1, + "label": "Warranty End Date" + }, + { + "default": "0", + "fieldname": "warranty", + "fieldtype": "Check", + "label": "Warranty" + }, + { + "default": "0", + "depends_on": "eval:doc.warranty == 1", + "fieldname": "extended_warranty", + "fieldtype": "Check", + "label": "Extended Warranty" + }, + { + "fieldname": "column_break_agzy", + "fieldtype": "Column Break" + }, + { + "fieldname": "start", + "fieldtype": "Date", + "hidden": 1, + "label": "Start Date" + }, + { + "fieldname": "end", + "fieldtype": "Date", + "hidden": 1, + "label": "End Date" + }, + { + "depends_on": "eval:doc.warranty == 1", + "fieldname": "war_status", + "fieldtype": "Select", + "label": "Warranty Status", + "options": "\nActive\nExpired\nNA" + }, + { + "default": "0", + "depends_on": "eval:doc.service_contract == 1", + "fieldname": "ppm_only", + "fieldtype": "Check", + "label": "PPM Only" + }, + { + "default": "0", + "depends_on": "eval:doc.service_contract == 1", + "fieldname": "spare_parts_labour", + "fieldtype": "Check", + "label": "Spare Parts & Labour" + }, + { + "default": "0", + "fieldname": "no", + "fieldtype": "Check", + "label": "No" + }, + { + "fieldname": "section_break_pyrk", + "fieldtype": "Section Break" + }, + { + "fieldname": "asset_list", + "fieldtype": "Table", + "label": "Asset List", + "options": "Support Asset List" + }, + { + "fieldname": "max_downtime_hrs", + "fieldtype": "Float", + "label": "Max Downtime Hrs" + }, + { + "fieldname": "penalty_factor", + "fieldtype": "Float", + "label": "Penalty Factor" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-03-04 15:35:02.126640", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Support Plans", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/support_plans/support_plans.py b/asset_lite/asset_lite/doctype/support_plans/support_plans.py new file mode 100644 index 0000000..eab7ddf --- /dev/null +++ b/asset_lite/asset_lite/doctype/support_plans/support_plans.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class SupportPlans(Document): + pass diff --git a/asset_lite/asset_lite/doctype/support_plans/test_support_plans.py b/asset_lite/asset_lite/doctype/support_plans/test_support_plans.py new file mode 100644 index 0000000..ab26525 --- /dev/null +++ b/asset_lite/asset_lite/doctype/support_plans/test_support_plans.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestSupportPlans(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/warranty/__init__.py b/asset_lite/asset_lite/doctype/warranty/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/warranty/test_warranty.py b/asset_lite/asset_lite/doctype/warranty/test_warranty.py new file mode 100644 index 0000000..7b6886c --- /dev/null +++ b/asset_lite/asset_lite/doctype/warranty/test_warranty.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestWarranty(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/warranty/warranty.js b/asset_lite/asset_lite/doctype/warranty/warranty.js new file mode 100644 index 0000000..7b232e8 --- /dev/null +++ b/asset_lite/asset_lite/doctype/warranty/warranty.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Warranty", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/warranty/warranty.json b/asset_lite/asset_lite/doctype/warranty/warranty.json new file mode 100644 index 0000000..167cf2e --- /dev/null +++ b/asset_lite/asset_lite/doctype/warranty/warranty.json @@ -0,0 +1,110 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "naming_series:", + "creation": "2024-09-17 13:07:02.620103", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "naming_series", + "asset", + "asset_name", + "extended_warranty", + "start_date", + "end_date", + "column_break_dxer", + "warranty_start_date", + "warranty_end_date", + "warranty_status" + ], + "fields": [ + { + "fieldname": "asset", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Asset", + "options": "Asset", + "reqd": 1 + }, + { + "fieldname": "warranty_start_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Warranty Start Date", + "reqd": 1 + }, + { + "fieldname": "column_break_dxer", + "fieldtype": "Column Break" + }, + { + "fetch_from": "asset.asset_name", + "fieldname": "asset_name", + "fieldtype": "Data", + "label": "Asset Name", + "read_only": 1 + }, + { + "fieldname": "warranty_end_date", + "fieldtype": "Date", + "label": "Warranty End Date" + }, + { + "fieldname": "warranty_status", + "fieldtype": "Select", + "label": "Warranty Status", + "options": "\nActive\nExpired\nNot Applicable", + "read_only": 1 + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "WN-.####", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "extended_warranty", + "fieldtype": "Check", + "label": "Extended Warranty" + }, + { + "depends_on": "eval:doc.extended_warranty == 1", + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date" + }, + { + "depends_on": "eval:doc.extended_warranty == 1", + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End Date" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-09-23 15:36:40.924090", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Warranty", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/warranty/warranty.py b/asset_lite/asset_lite/doctype/warranty/warranty.py new file mode 100644 index 0000000..b760e44 --- /dev/null +++ b/asset_lite/asset_lite/doctype/warranty/warranty.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class Warranty(Document): + pass diff --git a/asset_lite/asset_lite/doctype/work_order/__init__.py b/asset_lite/asset_lite/doctype/work_order/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/work_order/test_work_order.py b/asset_lite/asset_lite/doctype/work_order/test_work_order.py new file mode 100644 index 0000000..08b553b --- /dev/null +++ b/asset_lite/asset_lite/doctype/work_order/test_work_order.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestWork_Order(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/work_order/work_order.js b/asset_lite/asset_lite/doctype/work_order/work_order.js new file mode 100644 index 0000000..eac4fa0 --- /dev/null +++ b/asset_lite/asset_lite/doctype/work_order/work_order.js @@ -0,0 +1,75 @@ +// Copyright (c) 2024, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Work_Order", { +// refresh(frm) { + +// }, +// }); + +frappe.ui.form.on("Work_Order", { + refresh(frm) { + // Call the server-side method to get the site version type + frm.call({ + method: "check_site_version", + doc: frm.doc, + callback: function(response) { + // The returned site version type + var site_version = response.message; + + if (site_version === "lite") { + frm.set_df_property("need_spare_parts_purchase", "hidden", 1); + // frappe.msgprint("The Need Procurement checkbox is hided as the site version is 'lite'."); + } else { + frm.set_df_property("need_spare_parts_purchase", "hidden", 0); + } + } + }); + + // Hide the default print icon + frm.page.hide_icon_group('print'); + + + // Add custom button for PPM Service Report with a print icon + /*frm.add_custom_button( + ` ${__('Service Report')}`, + function() { + // Set Service Report as the default print format and open print preview + const customLink = `/printview?doctype=Work_Order&name=${frm.doc.name}&trigger_print=0&format=Service%20Report&no_letterhead=0`; + window.open(customLink); + } + );*/ + if(frm.doc.company=='King Fahad Hospital'){ + frm.add_custom_button( + ` ${__('SR-King Fahad Hospital')}`, + function() { + // Set Service Report as the default print format and open print preview + const customLink = `/printview?doctype=Work_Order&name=${frm.doc.name}&trigger_print=0&format=Service%20Report&no_letterhead=0`; + window.open(customLink); + } + ); + } + if(frm.doc.company=='King Khalid Hospital'){ + frm.add_custom_button( + ` ${__('SR-King Khalid Hospital')}`, + function() { + // Set Service Report as the default print format and open print preview + const customLink = `/printview?doctype=Work_Order&name=${frm.doc.name}&trigger_print=0&format=Service%20Report(KK)&no_letterhead=0`; + window.open(customLink); + } + ); + } + + }, + /*setup: (frm) => { + frm.set_query("assign_to", "asset_maintenance_tasks", function (doc) { + return { + query: "erpnext.assets.doctype.asset_maintenance.asset_maintenance.get_team_members", + filters: { + maintenance_team: doc.maintenance_team, + }, + }; + }); + + }*/ +}); diff --git a/asset_lite/asset_lite/doctype/work_order/work_order.json b/asset_lite/asset_lite/doctype/work_order/work_order.json new file mode 100644 index 0000000..fc2ff5a --- /dev/null +++ b/asset_lite/asset_lite/doctype/work_order/work_order.json @@ -0,0 +1,757 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2024-09-11 13:13:50.398974", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "naming_series", + "work_order_type", + "asset_type", + "company", + "site_name", + "manufacturer", + "need_procurement", + "column_break_2", + "priority", + "asset", + "department", + "aseet_id", + "recall_reference_number", + "vendor", + "column_break_kmog", + "repair_status", + "asset_name", + "supplier", + "section_break_okba", + "workflow_state", + "warranty_and_service_details_section", + "war", + "warranty", + "service_contract", + "column_break_sdfm", + "service_contract_details", + "covering_spare_parts", + "spare_parts_labour", + "covering_labour", + "ppm_only", + "section_break_5", + "failure_date", + "first_responded_on", + "total_hours_spent", + "penalty", + "feedback", + "feedback_rating", + "column_break_6", + "completion_date", + "job_completed", + "assigned_manager", + "assigned_technician", + "custom_difference", + "accounting_dimensions_section", + "cost_center", + "column_break_14", + "project", + "defective_spare_parts_section", + "spare_parts", + "table_cmqp", + "make_details_section", + "make", + "model", + "column_break_ixht", + "serial_number", + "purchase_details_section", + "invoice_table", + "accounting_details", + "purchase_invoice", + "capitalize_repair_cost", + "stock_consumption", + "column_break_8", + "repair_cost", + "stock_consumption_details_section", + "stock_items", + "total_repair_cost", + "asset_depreciation_details_section", + "increase_in_asset_life", + "section_break_9", + "description", + "column_break_9", + "actions_performed", + "section_break_23", + "downtime", + "column_break_19", + "amended_from", + "section_break_azqe", + "total_main_hour_at_site", + "serviced_by", + "sign1", + "date1", + "column_break_wnzz", + "total_travel_hour", + "end_user", + "sign2", + "date2", + "column_break_pwkx", + "total_hours", + "bio_med_dept", + "sign3", + "date3", + "comments_section", + "customer_comments" + ], + "fields": [ + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Work Order Number", + "options": "WO-.YYYY.-", + "reqd": 1 + }, + { + "fetch_from": "asset.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Hospital Name", + "options": "Company" + }, + { + "default": "Repair (CM)", + "fieldname": "work_order_type", + "fieldtype": "Link", + "in_filter": 1, + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Work Order Type", + "options": "Issue Type" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "columns": 1, + "fieldname": "asset", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Asset ID", + "options": "Asset" + }, + { + "fetch_from": "asset.asset_name", + "fieldname": "asset_name", + "fieldtype": "Read Only", + "label": "Asset Name" + }, + { + "fieldname": "priority", + "fieldtype": "Link", + "label": "Priority", + "options": "Issue Priority" + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "label": "Response Details" + }, + { + "columns": 1, + "fieldname": "failure_date", + "fieldtype": "Datetime", + "label": "Failure Date", + "reqd": 1 + }, + { + "default": "Open", + "fieldname": "repair_status", + "fieldtype": "Select", + "in_filter": 1, + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Work Order Status", + "no_copy": 1, + "options": "Open\nWork In Progress\nPending Review\nCompleted\nCancelled\nClosed", + "print_hide": 1 + }, + { + "fieldname": "first_responded_on", + "fieldtype": "Datetime", + "label": "First Responded On", + "permlevel": 2 + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "depends_on": "eval:!doc.__islocal", + "fieldname": "completion_date", + "fieldtype": "Datetime", + "label": "Completion Date", + "mandatory_depends_on": "eval:doc.repair_status == \"Completed\" || doc.repair_status == \"Closed\"", + "no_copy": 1 + }, + { + "allow_on_submit": 1, + "default": "No", + "fieldname": "job_completed", + "fieldtype": "Select", + "label": "Job Completed", + "options": "\nNo\nYes", + "permlevel": 2, + "read_only": 1 + }, + { + "fieldname": "assigned_manager", + "fieldtype": "Link", + "label": "Assigned Manager", + "options": "User", + "read_only": 1 + }, + { + "depends_on": "eval:doc.assigned_manager", + "fieldname": "assigned_technician", + "fieldtype": "Link", + "label": "Assigned Technician", + "options": "User" + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "hidden": 1, + "label": "Accounting Dimensions" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, + { + "collapsible": 1, + "fieldname": "defective_spare_parts_section", + "fieldtype": "Section Break", + "label": "Defective Spare Parts " + }, + { + "fieldname": "spare_parts", + "fieldtype": "Button", + "label": "Spare Parts" + }, + { + "fieldname": "table_cmqp", + "fieldtype": "Table", + "options": "Spare Parts" + }, + { + "collapsible": 1, + "fieldname": "make_details_section", + "fieldtype": "Section Break", + "label": "Make details" + }, + { + "fetch_from": "asset.custom_make", + "fetch_if_empty": 1, + "fieldname": "make", + "fieldtype": "Data", + "label": "Make" + }, + { + "fetch_from": "asset.custom_model", + "fetch_if_empty": 1, + "fieldname": "model", + "fieldtype": "Data", + "label": "Model" + }, + { + "fieldname": "column_break_ixht", + "fieldtype": "Column Break" + }, + { + "fetch_from": "asset.custom_serial_number", + "fetch_if_empty": 1, + "fieldname": "serial_number", + "fieldtype": "Data", + "label": "Serial Number" + }, + { + "fieldname": "accounting_details", + "fieldtype": "Section Break", + "label": "Accounting Details" + }, + { + "fieldname": "purchase_invoice", + "fieldtype": "Link", + "hidden": 1, + "label": "Purchase Invoice", + "mandatory_depends_on": "eval: doc.repair_status == 'Completed' && doc.repair_cost > 0", + "no_copy": 1, + "options": "Purchase Invoice" + }, + { + "default": "0", + "depends_on": "eval:!doc.__islocal", + "fieldname": "capitalize_repair_cost", + "fieldtype": "Check", + "hidden": 1, + "label": "Capitalize Repair Cost" + }, + { + "default": "0", + "fieldname": "stock_consumption", + "fieldtype": "Check", + "label": "Stock Consumed During Repair" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "repair_cost", + "fieldtype": "Currency", + "label": "Repair Cost", + "read_only": 1 + }, + { + "depends_on": "stock_consumption", + "fieldname": "stock_consumption_details_section", + "fieldtype": "Section Break", + "label": "Stock Consumption Details" + }, + { + "fieldname": "stock_items", + "fieldtype": "Table", + "label": "Stock Items", + "mandatory_depends_on": "stock_consumption", + "options": "Asset Repair Consumed Item" + }, + { + "depends_on": "eval: doc.stock_consumption && doc.total_repair_cost > 0", + "description": "Sum of Repair Cost and Value of Consumed Stock Items.", + "fieldname": "total_repair_cost", + "fieldtype": "Currency", + "label": "Total Repair Cost", + "read_only": 1 + }, + { + "depends_on": "capitalize_repair_cost", + "fieldname": "asset_depreciation_details_section", + "fieldtype": "Section Break", + "hidden": 1, + "label": "Asset Depreciation Details" + }, + { + "fieldname": "increase_in_asset_life", + "fieldtype": "Int", + "label": "Increase In Asset Life(Months)", + "no_copy": 1 + }, + { + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "label": "Description" + }, + { + "fieldname": "description", + "fieldtype": "Long Text", + "label": "Nature of Complaint" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fieldname": "actions_performed", + "fieldtype": "Long Text", + "label": "Work Performed", + "permlevel": 2 + }, + { + "fieldname": "section_break_23", + "fieldtype": "Section Break" + }, + { + "fieldname": "downtime", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Downtime", + "read_only": 1 + }, + { + "fieldname": "column_break_19", + "fieldtype": "Column Break" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Asset Repair", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_azqe", + "fieldtype": "Section Break" + }, + { + "fieldname": "total_main_hour_at_site", + "fieldtype": "Data", + "label": "Total Main Hour At Site", + "read_only_depends_on": "eval:!frappe.user.has_role(\"Technician\")" + }, + { + "fieldname": "serviced_by", + "fieldtype": "Data", + "label": "Serviced By" + }, + { + "depends_on": "eval:doc.serviced_by", + "fieldname": "sign1", + "fieldtype": "Data", + "label": "Signature" + }, + { + "depends_on": "eval:doc.serviced_by", + "fieldname": "date1", + "fieldtype": "Date", + "label": "Date" + }, + { + "fieldname": "column_break_wnzz", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_travel_hour", + "fieldtype": "Data", + "label": "Total Travel Hour", + "read_only_depends_on": "eval:!frappe.user.has_role(\"End user\")" + }, + { + "fieldname": "end_user", + "fieldtype": "Data", + "label": "End user" + }, + { + "depends_on": "eval:doc.end_user", + "fieldname": "sign2", + "fieldtype": "Data", + "label": "Signature" + }, + { + "depends_on": "eval:doc.end_user", + "fieldname": "date2", + "fieldtype": "Date", + "label": "Date" + }, + { + "fieldname": "column_break_pwkx", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_hours", + "fieldtype": "Data", + "label": "Total Hours", + "read_only_depends_on": "eval:!frappe.user.has_role(\"Maintenance Manager\")" + }, + { + "fieldname": "bio_med_dept", + "fieldtype": "Data", + "label": "Bio-Med Dept" + }, + { + "depends_on": "eval:doc.bio_med_dept", + "fieldname": "sign3", + "fieldtype": "Data", + "label": "Signature" + }, + { + "depends_on": "eval:doc.bio_med_dept", + "fieldname": "date3", + "fieldtype": "Date", + "label": "Date" + }, + { + "fieldname": "comments_section", + "fieldtype": "Section Break", + "label": "Comments" + }, + { + "fieldname": "customer_comments", + "fieldtype": "Small Text", + "label": "Customer Comments" + }, + { + "fieldname": "warranty_and_service_details_section", + "fieldtype": "Section Break", + "label": "Warranty And Service Contract Details" + }, + { + "default": "0", + "fetch_from": "asset.custom_warranty", + "fieldname": "warranty", + "fieldtype": "Check", + "label": "Warranty", + "read_only": 1 + }, + { + "default": "0", + "fetch_from": "asset.custom__service_contract", + "fieldname": "service_contract", + "fieldtype": "Check", + "hidden": 1, + "label": "Service Contract", + "read_only": 1 + }, + { + "fieldname": "column_break_sdfm", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "eval:doc.service_contract == 1", + "fetch_from": "asset.custom_covering_labour", + "fieldname": "covering_labour", + "fieldtype": "Check", + "label": "Labour", + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.service_contract == 1", + "fetch_from": "asset.custom_covering_spare_parts", + "fieldname": "covering_spare_parts", + "fieldtype": "Check", + "label": "Comprehensive", + "read_only": 1 + }, + { + "fieldname": "war", + "fieldtype": "HTML", + "label": "Warranty", + "options": "Warranty Details
" + }, + { + "fieldname": "service_contract_details", + "fieldtype": "HTML", + "label": "Service Contract details", + "options": "Service Contract Details
" + }, + { + "fieldname": "purchase_details_section", + "fieldtype": "Section Break", + "label": "Invoice Details" + }, + { + "depends_on": "eval:doc.workflow_state != \"Sent to maintenance Manager\"", + "fieldname": "invoice_table", + "fieldtype": "Table", + "label": "Invoice Table", + "options": "PI Table", + "read_only_depends_on": "eval:doc.workflow_state != \"Pending Purchase\"" + }, + { + "default": "0", + "depends_on": "eval:doc.service_contract == 1", + "fieldname": "ppm_only", + "fieldtype": "Check", + "label": " PPM Only", + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.service_contract == 1", + "fetch_from": "asset.custom_spare_parts_labour", + "fieldname": "spare_parts_labour", + "fieldtype": "Check", + "label": "Spare Parts & Labour", + "read_only": 1 + }, + { + "fieldname": "vendor", + "fieldtype": "Data", + "label": "Vendor" + }, + { + "allow_on_submit": 1, + "fetch_from": "asset.department", + "fetch_if_empty": 1, + "fieldname": "department", + "fieldtype": "Link", + "in_filter": 1, + "label": "Department", + "options": "Department" + }, + { + "fetch_from": "asset.custom_manufacturer", + "fetch_if_empty": 1, + "fieldname": "manufacturer", + "fieldtype": "Data", + "label": "Manufacturer" + }, + { + "allow_on_submit": 1, + "fieldname": "supplier", + "fieldtype": "Link", + "in_filter": 1, + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Supplier", + "options": "Supplier" + }, + { + "depends_on": "eval:doc.work_order_type == \"Recall\"", + "fieldname": "recall_reference_number", + "fieldtype": "Data", + "label": "Recall Reference Number" + }, + { + "fieldname": "aseet_id", + "fieldtype": "Link", + "hidden": 1, + "label": "Aseet ID", + "options": "Asset" + }, + { + "allow_on_submit": 1, + "fieldname": "total_hours_spent", + "fieldtype": "Float", + "label": "Total Hours Spent", + "permlevel": 2 + }, + { + "allow_on_submit": 1, + "fieldname": "penalty", + "fieldtype": "Float", + "label": "Penalty" + }, + { + "fieldname": "section_break_okba", + "fieldtype": "Section Break" + }, + { + "fieldname": "workflow_state", + "fieldtype": "Link", + "hidden": 1, + "label": "Workflow State", + "options": "Workflow" + }, + { + "allow_on_submit": 1, + "fieldname": "feedback", + "fieldtype": "Link", + "hidden": 1, + "label": "Feedback", + "options": "Feedback" + }, + { + "fieldname": "custom_difference", + "fieldtype": "Float", + "label": "Difference" + }, + { + "default": "0", + "fieldname": "need_procurement", + "fieldtype": "Check", + "label": "Need Procurement" + }, + { + "fieldname": "column_break_kmog", + "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "fieldname": "feedback_rating", + "fieldtype": "Rating", + "label": "Feedback Rating" + }, + { + "fieldname": "asset_type", + "fieldtype": "Link", + "label": "Asset Type", + "options": "Asset Type", + "reqd": 1 + }, + { + "depends_on": "eval:doc.asset_type == \"Non Biomedical\" || (doc.company && doc.asset_type == \"Biomedical\" && doc.company.startsWith(\"Mobile\"))", + "fetch_from": "asset.custom_site", + "fetch_if_empty": 1, + "fieldname": "site_name", + "fieldtype": "Link", + "label": "Site Name", + "options": "Mobile Team Site" + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [ + { + "hidden": 1, + "link_doctype": "Purchase Request", + "link_fieldname": "issue" + }, + { + "link_doctype": "Feedback", + "link_fieldname": "work_order" + } + ], + "modified": "2025-12-31 18:55:33.694111", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Work_Order", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "asset_name", + "track_changes": 1, + "track_seen": 1 +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/work_order/work_order.py b/asset_lite/asset_lite/doctype/work_order/work_order.py new file mode 100644 index 0000000..16b4b86 --- /dev/null +++ b/asset_lite/asset_lite/doctype/work_order/work_order.py @@ -0,0 +1,25 @@ +# Copyright (c) 2024, seyfert and contributors +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document + +class Work_Order(Document): + @frappe.whitelist() + def check_site_version(self): + # Fetch the site version type from the site configuration + site_version_type = frappe.local.conf.get("site_version_type", "") + return site_version_type + + ''' + @frappe.whitelist() + @frappe.validate_and_sanitize_search_inputs + def get_team_members(doctype, txt, searchfield, start, page_len, filters): + return frappe.db.get_values( + "Maintenance Team Member", {"parent": filters.get("maintenance_team")}, "team_member" + ) + + ''' + + + diff --git a/asset_lite/asset_lite/doctype/work_order_requisitor/__init__.py b/asset_lite/asset_lite/doctype/work_order_requisitor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/work_order_requisitor/test_work_order_requisitor.py b/asset_lite/asset_lite/doctype/work_order_requisitor/test_work_order_requisitor.py new file mode 100644 index 0000000..9755eeb --- /dev/null +++ b/asset_lite/asset_lite/doctype/work_order_requisitor/test_work_order_requisitor.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestWork_OrderRequisitor(FrappeTestCase): + pass diff --git a/asset_lite/asset_lite/doctype/work_order_requisitor/work_order_requisitor.js b/asset_lite/asset_lite/doctype/work_order_requisitor/work_order_requisitor.js new file mode 100644 index 0000000..67a1c6e --- /dev/null +++ b/asset_lite/asset_lite/doctype/work_order_requisitor/work_order_requisitor.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, seyfert and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Work_Order Requisitor", { +// refresh(frm) { + +// }, +// }); diff --git a/asset_lite/asset_lite/doctype/work_order_requisitor/work_order_requisitor.json b/asset_lite/asset_lite/doctype/work_order_requisitor/work_order_requisitor.json new file mode 100644 index 0000000..3c6b465 --- /dev/null +++ b/asset_lite/asset_lite/doctype/work_order_requisitor/work_order_requisitor.json @@ -0,0 +1,50 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:requisitor_name", + "creation": "2025-02-07 13:14:17.271763", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "requisitor_name", + "arabic_name" + ], + "fields": [ + { + "fieldname": "requisitor_name", + "fieldtype": "Data", + "label": "Requisitor Name", + "unique": 1 + }, + { + "fieldname": "arabic_name", + "fieldtype": "Data", + "label": "Arabic Name" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-02-07 13:19:48.723260", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Work_Order Requisitor", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/work_order_requisitor/work_order_requisitor.py b/asset_lite/asset_lite/doctype/work_order_requisitor/work_order_requisitor.py new file mode 100644 index 0000000..cdab879 --- /dev/null +++ b/asset_lite/asset_lite/doctype/work_order_requisitor/work_order_requisitor.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class Work_OrderRequisitor(Document): + pass diff --git a/asset_lite/asset_lite/doctype/work_order_table/__init__.py b/asset_lite/asset_lite/doctype/work_order_table/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/doctype/work_order_table/work_order_table.json b/asset_lite/asset_lite/doctype/work_order_table/work_order_table.json new file mode 100644 index 0000000..e546d57 --- /dev/null +++ b/asset_lite/asset_lite/doctype/work_order_table/work_order_table.json @@ -0,0 +1,69 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-02-08 00:26:13.321930", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_name", + "part_no", + "description", + "column_break_zklj", + "quantity", + "status" + ], + "fields": [ + { + "columns": 2, + "fieldname": "item_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Name" + }, + { + "columns": 2, + "fieldname": "part_no", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Part No" + }, + { + "columns": 2, + "fieldname": "description", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Description" + }, + { + "fieldname": "column_break_zklj", + "fieldtype": "Column Break" + }, + { + "columns": 2, + "fieldname": "quantity", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Quantity" + }, + { + "columns": 2, + "fieldname": "status", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Status" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-02-10 20:38:06.108151", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "Work_order Table", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/asset_lite/asset_lite/doctype/work_order_table/work_order_table.py b/asset_lite/asset_lite/doctype/work_order_table/work_order_table.py new file mode 100644 index 0000000..dddb4e4 --- /dev/null +++ b/asset_lite/asset_lite/doctype/work_order_table/work_order_table.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, seyfert and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class Work_orderTable(Document): + pass diff --git a/asset_lite/asset_lite/page/__init__.py b/asset_lite/asset_lite/page/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/page/active_map/__init__.py b/asset_lite/asset_lite/page/active_map/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/page/active_map/active_map.js b/asset_lite/asset_lite/page/active_map/active_map.js new file mode 100644 index 0000000..b8f83a0 --- /dev/null +++ b/asset_lite/asset_lite/page/active_map/active_map.js @@ -0,0 +1,895 @@ +frappe.pages['active-map'].on_page_load = function(wrapper) { + var page = frappe.ui.make_app_page({ + parent: wrapper, + title: 'Active Map', + single_column: true + }); + + // Load Leaflet CSS + $('').appendTo('head'); + + // Load Leaflet JS + $.getScript('https://unpkg.com/leaflet@1.7.1/dist/leaflet.js', function() { + // Initialize the map after Leaflet is loaded + new AssetMap(page, wrapper); + }); +}; + +class AssetMap { + constructor(page, wrapper) { + this.page = page; + this.wrapper = wrapper; + this.setup_page(); + } + + setup_page() { + // Add filters + this.setup_filters(); + + // Create map container + this.make_map_container(); + + // Add custom styles + this.add_custom_styles(); + + // Initialize map + this.initialize_map(); + } + + setup_filters() { + let filter_container = $('
').prependTo(this.page.main); + + // Add a filter for hospital/company + this.page.add_field({ + parent: filter_container, + fieldname: 'company', + label: __('Hospital'), + fieldtype: 'Link', + options: 'Location', + get_query: () => { + return { + filters: { + custom_is_hospital: 1 + } + }; + }, + onchange: () => this.fetch_and_render_data() + }); + + // Add a filter for PHCC + this.page.add_field({ + parent: filter_container, + fieldname: 'phcc', + label: __('PHCC'), + fieldtype: 'Link', + options: 'Location', + get_query: () => { + return { + filters: { + custom_is_phcc: 1 + } + }; + }, + onchange: () => this.fetch_and_render_data() + }); + } + + make_map_container() { + this.$map_container = $('
') + .appendTo(this.page.main); + } + + add_custom_styles() { + // Add custom CSS for tooltips and popups + if (!$('#asset-map-styles').length) { + $(' + `); + + // Create a container for layout + $(wrapper).append(` +
+
+
+
+
+
+
+
+
+ `); + + // Create Asset Filter + let asset_filter = new frappe.ui.form.ControlLink({ + parent: $("#filter_section"), + df: { + label: "Select Asset", + fieldname: "asset", + options: "Asset", + change: function() { + let asset_id = asset_filter.get_value(); + if (asset_id) { + fetch_asset_details(asset_id); + } + } + } + }); + + asset_filter.make_input(); // Render the filter field + + // **Fix: Wait until the input is ready before setting the value** + let params = new URLSearchParams(window.location.search); + let asset_id = params.get("asset"); // Get asset from URL + + if (asset_id) { + setTimeout(() => { + asset_filter.set_value(asset_id); // Apply the value after render + fetch_asset_details(asset_id); // Fetch data immediately + }, 500); // Delay to ensure input is initialized + } + + // Function to Fetch Asset Details + function fetch_asset_details(asset_id) { + frappe.call({ + method: "frappe.client.get", + args: { + doctype: "Asset", + name: asset_id + }, + callback: function(response) { + if (response.message) { + let asset = response.message; + + $("#content_section").html(` +

🔍 Asset Details

+

ID: ${asset.name}

+

Name: ${asset.asset_name}

+

🏥 Hospital: ${asset.company}

+

📍 Location: ${asset.location}

+

🚛 Supplier: ${asset.supplier}

+

💰 Total Repair Cost: ${asset.custom_total_spare_parts_amount}

+ `); + + $("#work_orders_section").html(`

🔗 Linked Work Orders

`); + $("#spare_parts_section").html(`

🛠️ Items Used for Repair

`); + + // Fetch Work Orders After Asset Details Are Shown + fetch_work_orders(asset_id); + fetch_spare_parts(asset_id); + fetch_maintenance_details(asset_id); + } + } + }); + } + + // Function to Fetch and Display Linked Work Orders + function fetch_work_orders(asset_id) { + frappe.call({ + method: "frappe.client.get_list", + args: { + doctype: "Work_Order", + filters: { asset: asset_id }, + fields: ["name", "work_order_type","repair_status","creation","total_repair_cost"] + }, + callback: function(response) { + if (response.message) { + let work_orders = response.message; + + // If no work orders found + if (work_orders.length === 0) { + $("#work_orders_table").html("

No Work Orders Found for this Asset.

"); + return; + } + + // Create a formatted table for Work Orders + let html = ` +
+ + + + + + + + + + + + `; + + // Append Work Order details in table rows + work_orders.forEach(wo => { + // Determine status badge color + let status_class = ""; + if (wo.repair_status === "Completed") { + status_class = "badge-success"; // Green + } else if (wo.repair_status === "Work In Progress") { + status_class = "badge-warning"; // Yellow + } else { + status_class = "badge-secondary"; // Gray (default) + } + + + html += ` + + + + + + + + `; + }); + + html += ` + +
Work Order NoWork Order TypeRepair StatusRepair CostCreated On
${wo.name}${wo.work_order_type}${wo.repair_status}${parseFloat(wo.total_repair_cost).toFixed(2)}ر.س${wo.creation}
+
+ `; + + // Insert into the work_orders_section + $("#work_orders_table").html(html); + } + } + }); + } + + // Function to Fetch and Display Spare Parts + function fetch_spare_parts(asset_id) { + frappe.call({ + method: "frappe.client.get", + args: { + doctype: "Asset", + name: asset_id + }, + callback: function(response) { + if (response.message && response.message.custom_spare_parts) { + let spare_parts = response.message.custom_spare_parts; + + if (spare_parts.length === 0) { + $("#spare_parts_table").html("

No Spare Parts Used for this Asset.

"); + return; + } + + // Group spare parts by work_order + let grouped_parts = {}; + spare_parts.forEach(sp => { + if (!grouped_parts[sp.work_order]) { + grouped_parts[sp.work_order] = []; + } + grouped_parts[sp.work_order].push(sp); + }); + + // Create HTML for Spare Parts Table + let html = `
`; + + Object.keys(grouped_parts).forEach(work_order => { + html += ` +

Work Order: ${work_order ? work_order : ""}

+ + + + + + + + + + + `; + + grouped_parts[work_order].forEach(sp => { + html += ` + + + + + + + `; + }); + + html += ` + +
Item NameQuantityCostAmount
${sp.item_code}${sp.qty}${sp.rate}ر.س${sp.amount}ر.س
+ `; + }); + + html += `
`; + + // Insert into the spare_parts_section + $("#spare_parts_table").html(html); + } + } + }); + } + + // Fetch Asset Maintenance Details (Including Tasks) +function fetch_maintenance_details(asset_id) { + frappe.call({ + method: "frappe.client.get_list", + args: { + doctype: "Asset Maintenance", + filters: { asset_name: asset_id }, + fields: ["name", "maintenance_team", "custom_type_of_maintenance"], + limit_page_length: 10 + }, + callback: function(response) { + if (response.message) { + let maintenance_records = response.message; + if (maintenance_records.length === 0) { + $("#maintenance_section").html("

No Maintenance Records Found.

"); + return; + } + + let html = `

🛠️ Asset Maintenance Details

`; + + maintenance_records.forEach(m => { + html += ` +
+

Maintenance ID: ${m.name}

+

Maintenance Team: ${m.maintenance_team}

+

Type of Maintenance: ${m.custom_type_of_maintenance}

+
+ `; + + // Fetch Maintenance Tasks (Child Table) for Each Record + fetch_maintenance_tasks(m.name); + }); + + $("#maintenance_section").html(html); + } + } + }); +} + +function fetch_maintenance_tasks(maintenance_id) { + frappe.call({ + method: "frappe.client.get", + args: { + doctype: "Asset Maintenance", + name: maintenance_id + }, + callback: function(response) { + if (response.message && response.message.asset_maintenance_tasks) { + let tasks = response.message.asset_maintenance_tasks; + if (tasks.length === 0) { + $("#maintenance_section").append("

No Maintenance Tasks Found.

"); + return; + } + + // Clear the section to avoid duplicate entries + $("#maintenance_section").find(".maintenance-tasks").remove(); + + let html = ` +
+


Maintenance Tasks for ${maintenance_id}

+
+ + + + + + + + + + `; + + tasks.forEach(task => { + html += ` + + + + + + `; + }); + + html += `
Assigned ToPeriodicityNext Due Date
${task.assign_to_name}${task.periodicity}${task.next_due_date}
`; + + $("#maintenance_section").append(html); + fetch_maintenance_logs(maintenance_id); + } + } + }); +} + +function fetch_maintenance_logs(maintenance_id) { + frappe.call({ + method: "frappe.client.get_list", + args: { + doctype: "Asset Maintenance Log", + filters: { asset_maintenance: maintenance_id }, + fields: ["maintenance_status"] + }, + callback: function(response) { + if (response.message) { + let logs = response.message; + if (logs.length === 0) { + $("#maintenance_section").append("

No Maintenance Logs Found.

"); + return; + } + + // Remove previous log summary + $("#maintenance_section").find(".maintenance-log-summary").remove(); + + let status_counts = {}; + logs.forEach(log => { + if (!status_counts[log.maintenance_status]) { + status_counts[log.maintenance_status] = 0; + } + status_counts[log.maintenance_status]++; + }); + + let html = ` +
+

📜Periodic Maintenance

+
+ + + + + + + + + `; + + for (let status in status_counts) { + html += ` + + + + + `; + } + + html += `
Maintenance StatusCount
${status}${status_counts[status]}
`; + + $("#maintenance_section").append(html); + fetch_detailed_maintenance_logs(maintenance_id); + } + } + }); +} + +function fetch_detailed_maintenance_logs(maintenance_id) { + frappe.call({ + method: "frappe.client.get_list", + args: { + doctype: "Asset Maintenance Log", + filters: { asset_maintenance: maintenance_id }, + fields: [ + "name", + "maintenance_status", + "assign_to_name", + "maintenance_type", + "due_date", + "completion_date", + "periodicity", + "actions_performed" + ], + order_by: "completion_date desc" + }, + callback: function(response) { + if (response.message) { + let logs = response.message; + if (logs.length === 0) { + $("#maintenance_section").append("

No Maintenance Logs Found.

"); + return; + } + + // Remove previous detailed logs + $("#maintenance_section").find(".maintenance-logs").remove(); + + let completed_logs = logs.filter(log => log.maintenance_status === "Completed"); + let remaining_logs = logs.filter(log => log.maintenance_status !== "Completed"); + + let html = `
`; + + // ✅ Display Completed Logs First + if (completed_logs.length > 0) { + html += ` +

✅ Completed Maintenance Logs

+
+ + + + + + + + + + + + + + `; + + completed_logs.forEach(log => { + html += ` + + + + + + + + + + `; + }); + + html += `
Log IDAssigned ToMaintenance TypeDue DateCompletion DatePeriodicityActions Performed
${log.name}${log.assign_to_name}${log.maintenance_type}${log.due_date}${log.completion_date}${log.periodicity}${log.actions_performed ? log.actions_performed : ""}
`; + } + + // ✅ Display Remaining Logs + if (remaining_logs.length > 0) { + html += ` +

⏳ Pending/Planned Maintenance Logs

+
+ + + + + + + + + + + + + + `; + + remaining_logs.forEach(log => { + html += ` + + + + + + + + + + `; + }); + + html += `
Log IDAssigned ToMaintenance TypeDue DatePeriodicityMaintenance StatusActions Performed
${log.name}${log.assign_to_name}${log.maintenance_type}${log.due_date}${log.periodicity}${log.maintenance_status}${log.actions_performed ? log.actions_performed : ""}
`; + } + + html += `
`; // Close maintenance logs section + + $("#maintenance_section").append(html); + } + } + }); +} + + + +}; diff --git a/asset_lite/asset_lite/page/asset_history/asset_history.json b/asset_lite/asset_lite/page/asset_history/asset_history.json new file mode 100644 index 0000000..4f5c82e --- /dev/null +++ b/asset_lite/asset_lite/page/asset_history/asset_history.json @@ -0,0 +1,18 @@ +{ + "content": null, + "creation": "2025-03-09 15:13:49.273077", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2025-03-09 15:13:49.273077", + "modified_by": "Administrator", + "module": "Asset Lite", + "name": "asset-history", + "owner": "Administrator", + "page_name": "asset-history", + "roles": [], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0 +} \ No newline at end of file diff --git a/asset_lite/asset_lite/page/asset_map/__init__.py b/asset_lite/asset_lite/page/asset_map/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/asset_lite/page/asset_map/asset_map.js b/asset_lite/asset_lite/page/asset_map/asset_map.js new file mode 100644 index 0000000..bd86248 --- /dev/null +++ b/asset_lite/asset_lite/page/asset_map/asset_map.js @@ -0,0 +1,809 @@ +frappe.pages['asset-map'].on_page_load = function(wrapper) { + var page = frappe.ui.make_app_page({ + parent: wrapper, + title: 'Asset Map', + single_column: true + }); + + // Load Leaflet CSS + $('').appendTo('head'); + + // Load Leaflet JS + $.getScript('https://unpkg.com/leaflet@1.7.1/dist/leaflet.js', function() { + // Initialize the map after Leaflet is loaded + new AssetMap(page, wrapper); + }); +}; + +class AssetMap { + constructor(page, wrapper) { + this.page = page; + this.wrapper = wrapper; + this.setup_page(); + } + + setup_page() { + // Add filters + this.setup_filters(); + + // Create map container + this.make_map_container(); + + // Add custom styles + this.add_custom_styles(); + + // Initialize map + this.initialize_map(); + } + + setup_filters() { + let filter_container = $('
').prependTo(this.page.main); + + // Add a filter for hospital/company + this.page.add_field({ + parent: filter_container, + fieldname: 'company', + label: __('Hospital'), + fieldtype: 'Link', + options: 'Location', // Using Location DocType for hospitals + get_query: () => { + return { + filters: { + custom_is_hospital: 1 + } + }; + }, + onchange: () => this.fetch_and_render_data() + }); + } + + make_map_container() { + this.$map_container = $('
') + .appendTo(this.page.main); + } + + add_custom_styles() { + // Add custom CSS for tooltips and popups + if (!$('#asset-map-styles').length) { + $('\n\n

\n\t{{ company }}
\n\t{{ __(\"POS No : \") }} {{ offline_pos_name }}
\n

\n

\n\t{{ __(\"Customer\") }}: {{ customer }}
\n

\n\n

\n\t{{ __(\"Date\") }}: {{ dateutil.global_date_format(posting_date) }}
\n

\n\n
\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{% for item in items %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endfor %}\n\t\n
{{ __(\"Item\") }}{{ __(\"Qty\") }}{{ __(\"Amount\") }}
\n\t\t\t\t{{ item.item_name }}\n\t\t\t{{ format_number(item.qty, null,precision(\"difference\")) }}
@ {{ format_currency(item.rate, currency) }}
{{ format_currency(item.amount, currency) }}
\n\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% for row in taxes %}\n\t\t{% if not row.included_in_print_rate %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t{% endfor %}\n\t\t{% if discount_amount %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n
\n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t
\n\t\t\t\t{{ row.description }}\n\t\t\t\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t
\n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t
\n\t\t\t\t{{ __(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t
\n\t\t\t\t{{ __(\"Paid Amount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t
\n\t\t\t\t{{ __(\"Qty Total\") }}\n\t\t\t\n\t\t\t\t{{ qty_total }}\n\t\t\t
\n\n\n
\n

{{ terms }}

\n

{{ __(\"Thank you, please visit again.\") }}

", + "line_breaks": 0, + "margin_bottom": 0.0, + "margin_left": 0.0, + "margin_right": 0.0, + "margin_top": 0.0, + "modified": "2019-09-05 17:20:30.726659", + "module": "Accounts", + "name": "Point of Sale", + "page_number": null, + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "JS", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "POS Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "font_size": 0, + "format_data": null, + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n

\n\t{{ doc.company }}
\n\t{{ doc.select_print_heading or _(\"Return Invoice\") }}
\n

\n

\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Original Invoice\") }}: {{ doc.return_against }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}\n

\n\n
\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{%- for item in doc.items -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endfor -%}\n\t\n
{{ _(\"Item\") }}{{ _(\"Qty\") }}{{ _(\"Amount\") }}
\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t
{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t
{{ _(\"SR.No\") }}:
\n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t
{{ item.qty }}
@ {{ item.get_formatted(\"rate\") }}
{{ item.get_formatted(\"amount\") }}
\n\n\t\n\t\t\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% endif %}\n\t\t\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- if doc.rounded_total -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- if doc.change_amount -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t{%- endif -%}\n\t\n
\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t
\n\t\t\t\t {% if '%' in row.description %}\n\t\t\t\t\t {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc)}}\n\t\t\t\t
\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t
\n\t\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\")}}\n\t\t\t\t
\n
\n

{{ doc.terms or \"\" }}

\n

{{ _(\"Thank you, please visit again.\") }}

", + "line_breaks": 0, + "margin_bottom": 0.0, + "margin_left": 0.0, + "margin_right": 0.0, + "margin_top": 0.0, + "modified": "2020-05-14 17:13:29.354015", + "module": "Selling", + "name": "Return POS Invoice", + "page_number": null, + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": "", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Supplier", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "font_size": 0, + "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"
\\t\\t\\t\\t

TAX Invoice
{{ doc.name }}\\t\\t\\t\\t

\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"customer_name\", \"label\": \"Customer Name\"}, {\"print_hide\": 0, \"fieldname\": \"customer_name_in_arabic\", \"label\": \"Customer Name in Arabic\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"posting_date\", \"label\": \"Date\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Address\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"print_hide\": 0, \"fieldname\": \"company_trn\", \"label\": \"Company TRN\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company_address_display\", \"label\": \"Company Address\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item_code\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"200px\"}, {\"print_hide\": 0, \"fieldname\": \"uom\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_code\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"items\", \"label\": \"Items\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"label\": \"Total\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"charge_type\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"row_id\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"account_head\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"cost_center\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_amount_after_discount_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"base_tax_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"base_total\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"base_tax_amount_after_discount_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"item_wise_tax_detail\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"taxes\", \"label\": \"Sales Taxes and Charges\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"grand_total\", \"label\": \"Grand Total\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\", \"label\": \"Rounded Total\"}, {\"print_hide\": 0, \"fieldname\": \"in_words\", \"align\": \"left\", \"label\": \"In Words\"}]", + "html": "
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n\n \n
PAYER'S name, street address,\n city or town, state or province, country, ZIP
or foreign postal code, and telephone no.
\n {{ company or \"\" }}
\n {{ payer_street_address or \"\" }}\n
1 RentsOMB No. 1545-0115
\n {{ fiscal_year[:2] }}\n {{ fiscal_year[-2:] }}
Form 1099-MISC\n
Miscellaneous Income
2 Royalties
3 Other Income
{{ payments or \"\" }}
4 Federal Income tax withheldCopy A
For
Internal Revenue
Service\n Center

File with Form 1096
PAYER'S TIN
{{ company_tin or \"\" }}
RECIPIENT'S TIN

{{ tax_id or \"None\" }}
Fishing boat proceeds6 Medical and health care payments
RECIPIENT'S name
{{ supplier or \"\" }}
7 Nonemployee compensation
\n
Substitute payments in lieu of dividends or interestFor Privacy Act
and Paperwork
Reduction Act
Notice, see\n the
2018 General
Instructions for
Certain
Information
Returns.
Street address (including apt. no.)
\n {{ recipient_street_address or \"\" }}\n
$___________$___________
9 Payer made direct sales of
$5,000 or more of consumer\n products
to a buyer
(recipient) for resale
10 Crop insurance proceeds
City or town, state or province, country, and ZIP or\n foreign postal code
\n {{ recipient_city_state or \"\" }}\n
$___________
1112
Account number (see instructions)FACTA filing
requirement
2nd TIN not.13 Excess golden parachute payments
$___________
14 Gross proceeds paid to an
attorney
$___________
15a Section 409A deferrals15b Section 409 income16 State tax withheld17 State/Payer's state no.18 State income
$$$$
Form 1099-MISC Cat. No. 14425J www.irs.gov/Form1099MISC Department of the\n Treasury - Internal Revenue Service
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {{ supplier or \"\" }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n\n \n
PAYER'S name, street address,\n city or town, state or province, country, ZIP
or foreign postal code, and telephone no.
\n {{ company or \"\"}}\n {{ payer_street_address or \"\" }}\n
1 RentsOMB No. 1545-0115
\n {{ fiscal_year[:2] }}\n {{ fiscal_year[-2:] }}
Form 1099-MISC\n
Miscellaneous Income
2 Royalties
3 Other Income
\n {{ payments or \"\" }}\n
4 Federal Income tax withheldCopy 1
For State Tax
Department
PAYER'S TIN
\n {{ company_tin or \"\" }}\n
RECIPIENT'S TIN
\n {{ tax_id or \"\" }}\n
Fishing boat proceeds6 Medical and health care payments
RECIPIENT'S name7 Nonemployee compensation
\n
Substitute payments in lieu of dividends or interest
Street address (including apt. no.)
\n {{ recipient_street_address or \"\" }}\n
$___________$___________
9 Payer made direct sales of
$5,000 or more of consumer\n products
to a buyer
(recipient) for resale
10 Crop insurance proceeds
City or town, state or province, country, and ZIP or\n foreign postal code
\n {{ recipient_city_state or \"\" }}\n
$___________
1112
Account number (see instructions)FACTA filing
requirement
2nd TIN not.13 Excess golden parachute payments
$___________
14 Gross proceeds paid to an
attorney
$___________
15a Section 409A deferrals15b Section 409 income16 State tax withheld17 State/Payer's state no.18 State income
$$$$
Form 1099-MISC Cat. No. 14425J www.irs.gov/Form1099MISC Department of the\n Treasury - Internal Revenue Service
\n
\n", + "line_breaks": 0, + "margin_bottom": 0.0, + "margin_left": 0.0, + "margin_right": 0.0, + "margin_top": 0.0, + "modified": "2021-01-19 07:25:16.333666", + "module": "Regional", + "name": "IRS 1099 Form", + "page_number": null, + "print_format_builder": 1, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": "th,td{\r\n font-size:12px;\r\n color:black;\r\n word-break:break-all;\r\n}", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "PPM", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "\n \n \n \n \n \n \n \n \n \n \n \n
\n \"Logo\"\n \n Seera Arabia
Medical Services\n
\n سيرا العربية
الخدمات الطبية\n
\n \"Logo\"\n
\n\n \n \n \n \n \n \n \n \n
\n Al Arabia II, Arcade 345 - P.O. Box 99
\nRiyadh 35436, Saudi Arabia
\nTel:+432 4354 4565 Toll Free Fax +556
\nToll Free No.:4354 543 534\n
تقرير الخدمة
Service Report
\n العربية 2، رواق 345 - ص.ب. صندوق 99
\nالرياض 35436، المملكة العربية السعودية
\nهاتف:+432 4354 4565 رقم الفاكس المجاني +556
\nالرقم المجاني: 4354 543 534\n
\n
\n\n{% if doc.asset_maintenance_log %}\n{% set am = frappe.get_doc(\"Asset Maintenance Log\", doc.asset_maintenance_log) %}\n{%- endif-%}\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {% set asset = frappe.get_doc(\"Asset\", doc.asset) %}\n \n \n \n \n \n \n \n \n \n \n \n \n \n
Date:{{am.get_formatted(\"completion_date\")}}PPM No:{{doc.name}}
SERVICE CALLSWarrantyHBS✔PMPPM ContractCallExtended
Warranty
CUSTOMER/SITE NAME:\n

POSITION/DEPT./CONTACT NO.:\n

JOB NO./CASE NO.:
EQUIPMENT{{asset.asset_name}}
MODEL{{asset.custom_model}}
SYSTEM ID/SW.NO.{{asset.custom_serial_number}}

\n\n

NATURE OF COMPLAINT:

\n
\n{% if doc.asset_maintenance_log %}\n{% set aml = frappe.get_doc(\"Asset Maintenance Log\", doc.asset_maintenance_log) %}\n

WORK PERFORMED:
{{aml.actions_performed}}

\n{% else %}\n

WORK PERFORMED:

\n{%- endif-%}\n
\n\n \n \n \n \n
JOB COMPLETED   \n {%- if aml.maintenance_status ==\"Completed\" -%}✔{%- endif-%} YES   \n {%- if aml.maintenance_status ==\"Planned\" -%}✔{%- endif-%} NO

\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
TOTAL MAIN HOUR AT SITE{{aml.custom_total_main_hour_at_site}}TOTAL TRAVEL HOUR{{aml.custom_total_travel_hour}}TOTAL HOURS{{aml.custom_total_hours}}
Serviced By:{{aml.custom_serviced_by}}\n

Signature:\n

Date:\n
End-User:{{aml.custom_end_user}}\n

Signature:\n

Date:\n
Bio-Med Dept.:{{aml.custom_bio_med_dept}}\n

Signature:\n

Date:\n

\n\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2024-09-27 16:21:12.377441", + "module": "Asset Lite", + "name": "PPM Service Report", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": "th,td{\r\n font-size:12px;\r\n color:black;\r\n word-break:break-all;\r\n}", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "\n \n \n \n \n \n \n \n \n \n \n
\n \"Logo\"\n \n Seera Arabia
Medical Services\n
\n سيرا العربية
الخدمات الطبية\n
\n \"Logo\"\n
\n\n \n \n \n \n \n \n \n \n
\n Al Arabia II, Arcade 345 - P.O. Box 99
\nRiyadh 35436, Saudi Arabia
\nTel:+432 4354 4565 Toll Free Fax +556 445656
\nToll Free No.:4354 543 534\n
تقرير الخدمة
Service Report
\n العربية 2، رواق 345 - ص.ب. صندوق 99
\nالرياض 35436، المملكة العربية السعودية
\nهاتف:+432 4354 4565 رقم الفاكس المجاني +556 445656
\nالرقم المجاني: 4354 543 534\n
\n
\n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Date:{{doc.failure_date}}Work Order No:{{doc.name}}
SERVICE CALLS{%- if doc.work_order_type ==\"Installation\" -%}✔{%- endif-%}Installation{%- if doc.work_order_type ==\"Warranty\" -%}✔{%- endif-%}Warranty{%- if doc.work_order_type ==\"HBS\" -%}✔{%- endif-%}HBS{%- if doc.work_order_type ==\"PM\" -%}✔{%- endif-%}PM{%- if doc.work_order_type ==\"PPM Contract\" -%}✔{%- endif-%}PPM Contract{%- if doc.work_order_type ==\"Call\" -%}✔{%- endif-%}Call{%- if doc.work_order_type ==\"Extended Warranty\" -%}✔{%- endif-%}Extended
Warranty
CUSTOMER/SITE NAME:\n

POSITION/DEPT./CONTACT NO.:\n

JOB NO./CASE NO.:
EQUIPMENT{{doc.asset_name}}
MODEL{{doc.model}}
SYSTEM ID/SW.NO.{{doc.serial_number}}

\n

NATURE OF COMPLAINT:
{{doc.description}}

\n
\n

WORK PERFORMED:
{{doc.actions_performed}}

\n
\n\n \n \n \n \n \n \n \n \n {% for row in doc.table_cmqp %}\n \n \n \n \n \n {% endfor%}\n
JOB COMPLETED   \n {%- if doc.job_completed ==\"Yes\" -%}✔{%- endif-%} YES   \n {%- if doc.job_completed ==\"No\" -%}✔{%- endif-%} NO
DEFECTIVE SPARE PARTSPART NO.QTY.
{{row.item_code}}{{row.qty}}

\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
TOTAL MAIN HOUR AT SITE{{doc.total_main_hour_at_site}}TOTAL TRAVEL HOUR{{doc.total_travel_hour}}TOTAL HOURS{{doc.total_hours}}
Serviced By:{{doc.serviced_by}}\n

Signature:\n

Date:\n
End-User:{{doc.end_user}}\n

Signature:\n

Date:\n
Bio-Med Dept.:{{doc.bio_med_dept}}\n

Signature:\n

Date:\n
Customer Comments:{{doc.customer_comments}}

\n\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-07-08 11:07:05.115562", + "module": "Asset Lite", + "name": "Service Report", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "PPM", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "
\n

PPM

\n \n \n \n \n \n \n \n \n \n {% set am = frappe.get_doc(\"Asset Maintenance Log\",doc.asset_maintenance_log) %}\n \n \n \n \n \n \n \n \n \n \n \n \n
Asset ID : {{doc.asset}}
Asset Name : {{doc.asset_name}}
PPM Done Date{{am.completion_date}}
PPM Next Due Date{{am.due_date}}
Engineer{{am.assign_to_name}}
\n
\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2024-09-24 13:05:04.215792", + "module": "Asset Lite", + "name": "PPM Sticker", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Asset Maintenance Log", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "
\n

PPM

\n \n \n \n \n \n \n \n \n \n {% set am = frappe.get_doc(\"Asset Maintenance Log\",doc.asset_maintenance_log) %}\n \n \n \n \n \n \n \n \n \n \n \n \n
Asset ID : {{doc.asset_maintenance}}
Asset Name : {{doc.custom_asset_names}}
PPM Done Date{{doc.completion_date}}
PPM Next Due Date{{doc.due_date}}
Engineer{{am.assign_to_name}}
\n
\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2024-10-21 16:01:57.773237", + "module": "Assets", + "name": "PPM Asset", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": "th,td{\r\n font-size:12px;\r\n color:black;\r\n word-break:break-all;\r\n}", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "PPM", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "\n \n \n \n \n \n \n \n \n \n \n \n
\n \"Logo\"\n \n Seera Arabia
Medical Services\n
\n سيرا العربية
الخدمات الطبية\n
\n \"Logo\"\n
\n\n \n \n \n \n \n \n \n \n
\n Al Arabia II, Arcade 345 - P.O. Box 99
\nRiyadh 35436, Saudi Arabia
\nTel:+432 4354 4565 Toll Free Fax +556
\nToll Free No.:4354 543 534\n
تقرير الخدمة
Service Report
\n العربية 2، رواق 345 - ص.ب. صندوق 99
\nالرياض 35436، المملكة العربية السعودية
\nهاتف:+432 4354 4565 رقم الفاكس المجاني +556
\nالرقم المجاني: 4354 543 534\n
\n
\n\n{% if doc.asset_maintenance_log %}\n{% set am = frappe.get_doc(\"Asset Maintenance Log\", doc.asset_maintenance_log) %}\n{%- endif-%}\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {% set asset = frappe.get_doc(\"Asset\", doc.asset) %}\n \n \n \n \n \n \n \n \n \n \n \n \n \n
Date:{{doc.get_formatted(\"completion_date\")}}PPM No:{{doc.custom_template}}
SERVICE CALLSWarrantyHBS✔PMPPM ContractCallExtended
Warranty
CUSTOMER/SITE NAME:\n

POSITION/DEPT./CONTACT NO.:\n

JOB NO./CASE NO.:
EQUIPMENT{{asset.asset_name}}
MODEL{{asset.custom_model}}
SYSTEM ID/SW.NO.{{asset.custom_serial_number}}

\n\n

NATURE OF COMPLAINT:

\n
\n{% if doc.asset_maintenance_log %}\n{% set aml = frappe.get_doc(\"Asset Maintenance Log\", doc.asset_maintenance_log) %}\n

WORK PERFORMED:
{{aml.actions_performed}}

\n{% else %}\n

WORK PERFORMED:

\n{%- endif-%}\n
\n\n \n \n \n \n
JOB COMPLETED   \n {%- if doc.maintenance_status ==\"Completed\" -%}✔{%- endif-%} YES   \n {%- if doc.maintenance_status ==\"Planned\" -%}✔{%- endif-%} NO

\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
TOTAL MAIN HOUR AT SITE{{doc.custom_total_main_hour_at_site}}TOTAL TRAVEL HOUR{{doc.custom_total_travel_hour}}TOTAL HOURS{{doc.custom_total_hours}}
Serviced By:{{doc.custom_serviced_by}}\n

Signature:\n

Date:\n
End-User:{{doc.custom_end_user}}\n

Signature:\n

Date:\n
Bio-Med Dept.:{{doc.custom_bio_med_dept}}\n

Signature:\n

Date:\n

\n\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-07-08 11:13:40.581773", + "module": "Asset Lite", + "name": "PPM Asset Service", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "{% set wo = doc %}\r\n\r\n\r\n\r\n\r\n\r\n \r\n \r\n \r\n \r\n \r\n
\r\n \"Al\r\n \r\n
\r\n
\r\n {{ wo.company}} \r\n
\r\n \"Cluster\r\n
\r\n\r\n\r\n\r\n\r\n\r\n
Work Order Details
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
Work Order Number {{ wo.name }}Work Order Type {{ wo.work_order_type or \"-\" }}
Hospital Name {{ wo.company or \"-\" }}Priority {{ wo.custom_priority_ or \"-\" }}
Department {{ wo.department or \"-\" }}Manufacturer {{ wo.manufacturer or \"-\" }}
Asset ID {{ wo.asset or \"-\" }}Asset Name {{ wo.asset_name or \"-\" }}
Serial Number {{ wo.serial_number or \"-\" }}Manufacturing Year {{ wo.custom_manufacturing_year or \"-\" }}
Supplier {{ wo.supplier or \"-\" }}Workflow State {{ wo.workflow_state or \"-\" }}
\r\n\r\n\r\n
Service Coverage
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
Site Contractor {{ wo.custom_site_contractor or \"-\" }}Subcontractor {{ wo.custom_subcontractor or \"-\" }}
Service Coverage {{ wo.custom_service_agreement or \"-\" }}Start Date {{ wo.custom_start_date or \"-\" }}
End Date {{ wo.custom_end_date or \"-\" }}Total Amount {{ wo.custom_total_amount or \"-\" }}
Service Agreement {{ wo.custom_service_coverage or \"-\" }}Comments {{ wo.custom_comments or \"-\" }}
\r\n\r\n\r\n
Work Details
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
Failure Date {{ wo.failure_date or \"-\" }}Total Hours Spent {{ wo.total_hours_spent or \"-\" }}
Deadline Date {{ wo.custom_deadline_date or \"-\" }}Completion Date {{ wo.completion_date or \"-\" }}
Assigned Manager {{ wo.assigned_manager or \"-\" }}First Responded On {{ wo.first_responded_on or \"-\" }}
\r\n\r\n\r\n
Stock Consumption Details
\r\n\r\n \r\n \r\n \r\n {% for item in wo.stock_items %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n
Item Warehouse Valuation Rate Consumed Quantity Total Value
{{ item.item_code or \"-\" }}{{ item.warehouse or \"-\" }}{{ item.valuation_rate or \"-\" }}{{ item.consumed_quantity or \"-\" }}{{ item.total_value or \"-\" }}
\r\n\r\n\r\n
Invoice Details
\r\n\r\n \r\n \r\n \r\n {% for invoice in wo.invoice_table %}\r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n
Purchase Invoice Cost
{{ invoice.purchase_invoice or \"-\" }}{{ invoice.cost or \"-\" }}
\r\n\r\n\r\n
Repair Cost
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n
Total Repair Cost {{ wo.total_repair_cost or \"-\" }}
\r\n\r\n\r\n
Work Performed
\r\n\r\n \r\n \r\n \r\n \r\n
Nature of Complaint {{ wo.description or \"-\" }}Work Performed {{ wo.actions_performed or \"-\" }}
\r\n\r\n\r\n
Service Details
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
Total Main Hours At Site {{ wo.total_main_hour_at_site or \"-\" }}Serviced By {{ wo.serviced_by or \"-\" }}
Total Travel Hours {{ wo.total_travel_hour or \"-\" }}End User {{ wo.end_user or \"-\" }}
\r\n\r\n\r\n
Customer Comments
\r\n\r\n \r\n \r\n \r\n
Comments {{ wo.customer_comments or \"-\" }}
\r\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-07-08 09:54:49.201720", + "module": "Asset Lite", + "name": "Work_Order PF", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 1, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "\r\n\r\n\r\n\r\n\r\n\r\n \r\n \r\n \r\n \r\n \r\n
\r\n \"Logo\"\r\n \r\n {{ doc.company }}\r\n \r\n \"Cluster\r\n
\r\n\r\n\r\n
Asset Maintenance Details
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n
Asset ID {{ doc.name }}
Asset Name {{ doc.custom_asset_name or \"-\" }}
Asset Type {{ doc.custom_asset_type or \"-\" }}
Serial Number {{ doc.custom_serial_number or \"-\" }}
Department {{ doc.custom_department or \"-\" }}
\r\n\r\n\r\n
Maintenance Details
\r\n\r\n \r\n \r\n \r\n \r\n
Type of Maintenance {{ doc.custom_type_of_maintenance or \"-\" }}Maintenance Team {{ doc.maintenance_team or \"-\" }}
\r\n
Contract Details/Coverage
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
Site Contractor {{ doc.custom_site_contractor or \"-\" }}Subcontractor {{ doc.custom_subcontractor or \"-\" }}
Service Coverage {{ doc.custom_service_coverage or \"-\" }}Service Agreement {{ doc.custom_service_agreement or \"-\" }}
Start Date {{ doc.custom_start_date or \"-\" }}End Date {{ doc.custom_end_date or \"-\" }}
Total Amount {{ doc.custom_total_amount or \"-\" }}
\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-07-08 10:02:21.408868", + "module": "Assets", + "name": "Asset Maintenance PF", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": ".subtitle {\r\n font-size: 14px;\r\n font-weight: 600; /* Medium weight */\r\n margin-top: 0px;\r\n}\r\n\r\n.value {\r\n margin-left: 10px; /* Slight indentation for better readability */\r\n}\r\n.section-title {\r\n font-size: 16px;\r\n font-weight: bold;\r\n color: #0056b3; /* Blue title */\r\n margin-bottom: 0; /* Remove bottom margin */\r\n padding-bottom: 0; /* Ensure no extra spacing */\r\n}\r\n\r\n.table {\r\n margin-top: 0; /* Remove any top margin */\r\n border-collapse: collapse; /* Ensure no extra spacing */\r\n width: 100%;\r\n}\r\n.inline-fields {\r\n margin-top: 05px;\r\n display: flex;\r\n gap: 150px; /* Space between Incoterm and Named Place */\r\n}\r\n\r\n.inline-fields p {\r\n \r\n margin: 0; /* Remove extra margins */\r\n font-weight: 300; /* Slightly bold */\r\n}\r\n\r\n.large-terms {\r\n font-size: 14px;\r\n font-weight: 600;\r\n margin-top: 05px;\r\n}\r\n.medium-text {\r\n font-weight: 500; /* Medium-bold */\r\n}\r\n", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Request for Quotation", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "{% set rfq = doc %}\r\n\r\n\r\n\r\n\r\n\r\n \r\n \r\n \r\n \r\n \r\n
\r\n \"Al\r\n \r\n
\r\n
\r\n {{ rfq.company }}\r\n
\r\n \"Cluster\r\n
\r\n\r\n\r\n\r\n
Request for Quotation
\r\n\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
RFQ Number {{ rfq.name }}Date {{ rfq.transaction_date or \"-\" }}
Company Billing Address {{ rfq.billing_address or \"-\" }}Required Date {{ rfq.schedule_date or \"-\" }}
\r\n\r\n\r\n
Supplier Details
\r\n\r\n \r\n \r\n \r\n \r\n {% for supplier in rfq.suppliers %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n
Sr Supplier Supplier Name ContactQuote Status Email Id Email Sent Send Email
{{ loop.index }}{{ supplier.supplier or \"-\" }}{{ supplier.supplier_name or \"-\" }}{{ supplier.contact or \"-\" }}{{ supplier.quote_status or \"-\" }}{{ supplier.email_id }}{% if supplier.email_sent %}✓{% else %}-{% endif %}{% if supplier.send_email %}✓{% else %}-{% endif %}
\r\n\r\n\r\n\r\n\r\n
Item Details
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% for item in rfq.items %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n
Sr Item Name Item Group Item Code Brand DescriptionRequired DateQuantity
{{ loop.index }}{{ item.item_name or \"-\" }}{{ item.item_group or \"-\" }}{{ item.item_code or \"-\" }}{{ item.brand or \"-\" }}{{ item.description or \"-\" }}{{ item.schedule_date or \"-\" }}{{ item.qty or \"-\" }}
\r\n\r\n\r\n\r\n\r\n\r\n\r\n
\r\n
\r\n

• Message for Supplier:

\r\n

{{ rfq.message_for_supplier or \"-\" }}

\r\n
\r\n
\r\n\r\n
Terms and Conditions
\r\n\r\n
\r\n

Incoterm: {{ rfq.incoterm or \"-\" }}

\r\n

Named Place: {{ rfq.named_place or \"-\" }}

\r\n

Terms: {{ rfq.tc_name or \"-\" }}

\r\n
\r\n
\r\n

• Terms and Conditions: {{ rfq.terms or \"-\" }}

\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-07-08 10:00:29.448089", + "module": "Buying", + "name": "RFQ", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "
\r\n\r\n \r\n
\r\n

{{doc.name}}

\r\n {% if doc.custom_attach_image %}\r\n \"QR\r\n {% else %}\r\n

No QR code available

\r\n {% endif %}\r\n
\r\n\r\n
\r\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-04-22 11:26:12.393060", + "module": "Assets", + "name": "Asset QR", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": null, + "disabled": 0, + "doc_type": "POS Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 0, + "format_data": null, + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n

\n\t{{ doc.company }}
\n\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n

\n

\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Cashier\") }}: {{ doc.owner }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Time\") }}: {{ doc.get_formatted(\"posting_time\") }}
\n

\n\n
\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{%- for item in doc.items -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endfor -%}\n\t\n
{{ _(\"Item\") }}{{ _(\"Qty\") }}{{ _(\"Amount\") }}
\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t
{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t
{{ _(\"SR.No\") }}:
\n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t
{{ item.qty }}
@ {{ item.get_formatted(\"rate\") }}
{{ item.get_formatted(\"amount\") }}
\n\n\t\n\t\t\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% endif %}\n\t\t\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- if doc.rounded_total -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t{%- for row in doc.payments -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t{%- endfor -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- if doc.change_amount -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t{%- endif -%}\n\t\n
\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t
\n\t\t\t\t {% if '%' in row.description %}\n\t\t\t\t\t {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t
\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t
\n\t\t\t\t {{ row.mode_of_payment }}\n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"amount\", doc) }}\n\t\t\t\t
\n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t
\n\t\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t
\n
\n

{{ doc.terms or \"\" }}

\n

{{ _(\"Thank you, please visit again.\") }}

", + "line_breaks": 0, + "margin_bottom": 0.0, + "margin_left": 0.0, + "margin_right": 0.0, + "margin_top": 0.0, + "modified": "2021-04-15 15:23:28.867135", + "module": "Selling", + "name": "POS Invoice", + "page_number": null, + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": null, + "disabled": 0, + "doc_type": "Journal Entry", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 0, + "format_data": null, + "html": "
\n\n\t{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n
\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Payment Advice\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n{%- for label, value in (\n (_(\"Voucher Date\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Reference / Cheque No.\"), doc.cheque_no),\n (_(\"Reference / Cheque Date\"), frappe.utils.formatdate(doc.cheque_date))\n ) -%}\n
\n
\n
{{ value }}
\n
\n{%- endfor -%}\n\t
\n\t

{{ _(\"This amount is in full / part settlement of the listed bills\") }}:

\n{%- for label, value in (\n (_(\"Amount\"), \"\" + doc.get_formatted(\"total_amount\") + \"
\" + (doc.total_amount_in_words or \"\") + \"
\"),\n (_(\"References\"), doc.remark)\n ) -%}\n
\n
\n
{{ value }}
\n
\n {%- endfor -%}\n
\n\t
\n\t\tPrepared By
\n\t
\n\t\tAuthorised Signatory
\n\t
\n\t\tReceived Payment as Above
\n\t
\n\t\t_____________
\n\t
\n\t\tA/C Payee
\n\t
\n\t\t_____________
\n\t
\n\t\t{{ frappe.utils.formatdate(doc.cheque_date) }}
\n\t
\n\t\t{{ doc.pay_to_recd_from }}
\n\t
\n\t\t{{ doc.total_amount_in_words }}
\n\t
\n\t\t{{ doc.get_formatted(\"total_amount\") }}
\n
", + "line_breaks": 0, + "margin_bottom": 0.0, + "margin_left": 0.0, + "margin_right": 0.0, + "margin_top": 0.0, + "modified": "2015-05-29 01:57:51.203850", + "module": "Accounts", + "name": "Cheque Printing Format", + "page_number": null, + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "\r\n\r\n\r\n \r\n \r\n\r\n\r\n
\r\n \r\n
\r\n\t\t {% if doc.asset_type == \"Biomedical\" %}\r\n
\r\n
\r\n
\r\n
تجمع الجوف الصحي
Aljouf Health Cluster
\r\n
\r\n\t\t\t{% endif %}\r\n\t\t\t{% if doc.asset_type == \"Non Biomedical\" %}\r\n\t\t\t
\r\n\t\t\t
\r\n
تجمع الجوف الصحي
Aljouf Health Cluster
\r\n\t\t\t
\r\n\t\t\t{% endif %}\r\n
\r\n
المملكة العربية السعودية
\r\n
تجمع الجوف الصحي
\r\n
إدارة الصيانة بتجمع الجوف الصحي
\r\n
\r\n
\r\n\r\n \r\n
\r\n JOB ORDER NO :\r\n {{doc.name}}\r\n : رقم أمر العمل\r\n \r\n\t\t\t\r\n
\r\n PPM\r\n \r\n
\r\n
\r\n REPAIR\r\n \r\n\t\t\t\t\r\n\t\t\t\t\r\n {% if doc.work_order_type == \"Repair (CM)\" -%}{{ \"✓\" | safe }}{%- endif %}\r\n \r\n
\r\n
\r\n CHECK\r\n \r\n
\r\n
\r\n\r\n \r\n\t\t{% if doc.asset_type == \"Biomedical\" %}\r\n
\r\n
\r\n HOSPITAL NAME :\r\n {{doc.company or \"\"}}\r\n : اسم المستشفى\r\n
\r\n
\r\n\t\t{% endif %}\r\n\t\t{% if doc.asset_type == \"Non Biomedical\" %}\r\n\t\t
\r\n
\r\n Site Name :\r\n {{doc.site_name or \"\"}}\r\n : اسم الموقع\r\n
\r\n
\r\n\t\t{% endif %}\r\n\r\n
\r\n
\r\n Work Order Type :\r\n {{doc.work_order_type or \"\"}}\r\n : نوع أمر العمل\r\n
\r\n
\r\n \r\n
\r\n
\r\n EQUIPMENT :\r\n {{doc.asset or \"\"}}\r\n : اسم الجهاز\r\n
\r\n
\r\n\r\n
\r\n
\r\n MANF :\r\n {{doc.manufacturer or \"\"}}\r\n : اسم الصانع\r\n
\r\n
\r\n\r\n
\r\n
\r\n S.NO :\r\n {{doc.serial_number or \"\"}}\r\n : الرقم المسلسل\r\n
\r\n
\r\n\r\n
\r\n
\r\n Dep :\r\n {{doc.department or \"\"}}\r\n : القسم\r\n
\r\n
\r\n\r\n \r\n
\r\n
\r\n \r\n : لا الجهاز ينتع المقاول الرئيسي شركة\r\n \r\n
\r\n
\r\n \r\n : نعم اسم الشركة/الوردة\r\n \r\n ضمان\r\n
\r\n
\r\n\r\n \r\n
\r\n
\r\n SIGN :\r\n \r\n : التوقيع\r\n \r\n : الاسم\r\n \r\n DATE :\r\n \r\n : التاريخ\r\n \r\n تم فتح أمر العمل بناءا على بلاغ المستشفى/المركز\r\n
\r\n
\r\n\r\n \r\n
\r\n
\r\n
\r\n FAULTE DETAILS\r\n \r\n : تفاصيل العطل الموجود بالجهاز\r\n
\r\n {% if doc.description %}\r\n
\r\n {{ doc.description or '' }}\r\n
\r\n {% endif %}\r\n
\r\n
\r\n\r\n\r\n \r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n
\r\n \r\n {%- if doc.repair_status not in [\"Completed\", \"Cancelled\", \"Closed\"] -%}\r\n ✓\r\n {%- endif -%}\r\n \r\n MAINTENANCE NOT COMP\r\n لم تكتمل الصيانة\r\n
\r\n \r\n
\r\n REASON\r\n
\r\n {%- if doc.repair_status not in [\"Completed\", \"Cancelled\", \"Closed\"] -%}\r\n {{ doc.custom_pending_reason or '' }}\r\n {%- endif -%}\r\n
\r\n : السبب\r\n
\r\n
\r\n\r\n\r\n
\r\n
\r\n \r\n {%- if doc.repair_status in [\"Completed\", \"Cancelled\", \"Closed\"] -%}\r\n ✓\r\n {%- endif -%}\r\n \r\n MAINTENANCE DONE\r\n تمت الصيانة\r\n
\r\n \r\n
\r\n PROCEDURE\r\n
\r\n {%- if doc.repair_status in [\"Completed\", \"Cancelled\", \"Closed\"] -%}\r\n {{ doc.actions_performed or '' }}\r\n {%- endif -%}\r\n
\r\n : الإجراء\r\n
\r\n
\r\n\r\n\r\n
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {%- for row_pair in doc.stock_items|batch(2, '') -%}\r\n \r\n \r\n \r\n \r\n \r\n \r\n {%- endfor -%}\r\n \r\n
Spares Description/Part No
إسم قطعة الغيار ووصفها
الكمية
QTY
Spares Description/Part No
إسم قطعة الغيار ووصفها
الكمية
QTY
\r\n {{ row_pair[0].item_code if row_pair[0] }}\r\n \r\n {{ row_pair[0].consumed_quantity if row_pair[0] }}\r\n \r\n {{ row_pair[1].item_code if row_pair[1] }}\r\n \r\n {{ row_pair[1].consumed_quantity if row_pair[1] }}\r\n
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n\r\n\r\n \r\n
\r\n
\r\n
\r\n
\r\n SIGN :\r\n \r\n : التوقيع\r\n
\r\n \r\n
\r\n NAME :\r\n \r\n : الاسم\r\n
\r\n \r\n
\r\n
اسم المهندس/الفني القائم بالإصلاح أو طلب قطع الغيار :
\r\n
NAME OF THE TECHNICION/ENGINEER REQUESTING PARTS OR REPAIRING :
\r\n
\r\n
\r\n
\r\n
\r\n\r\n \r\n
\r\n
\r\n Final Report :\r\n : التقرير النهائي فقط بعد إكتمال الإصلاح\r\n
\r\n {% if doc.final_report %}\r\n
\r\n {{ doc.final_report or '' }}\r\n
\r\n {% endif %}\r\n
\r\n\r\n\r\n \r\n
\r\n
\r\n Compiled by Tec/Eng\r\n \r\n : أجرها فني / مهندس\r\n
\r\n
\r\n Finish Date\r\n \r\n : تاريخ الإنجاز\r\n
\r\n
\r\n Start Date\r\n \r\n : تاريخ البدء\r\n
\r\n
\r\n\r\n \r\n
\r\n
\r\n
\r\n
مدير المستشفى / المركز الصحي
\r\n
\r\n
\r\n\t\t\t\t\t
مشرف الصيانة
\r\n
\r\n
\r\n
\r\n Operator\r\n المقاول\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n الاسم :\r\n
\r\n
\r\n التوقيع :\r\n
\r\n\t\t\t\t\t\t
\r\n\t\t\t\t\t\t
\r\n الختم :\r\n
\r\n\t\t\t\t\t\t\r\n
\r\n
\r\n
\r\n
\r\n
\r\n NAME:\r\n الاسم :\r\n
\r\n
\r\n SIGN:\r\n التوقيع :\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n NAME:\r\n الاسم :\r\n
\r\n
\r\n SIGN:\r\n التوقيع :\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n\r\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-08-28 13:09:24.054206", + "module": "Asset Lite", + "name": "Job Order", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Asset Maintenance Log", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "\r\n\r\n\r\n \r\n PPM Form\r\n \r\n\r\n\r\n \r\n
\r\n
samama
\r\n
\r\n
\r\n
Aljouf Health Cluster
\r\n
\r\n
\r\n
المملكة العربية السعودية
\r\n
تجمع الجوف الصحي
\r\n
إدارة الصيانة ( الصيانة الطبية )
\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n \r\n
\r\n رقم أمر العمل :\r\n
\r\n PPM\r\n \r\n
\r\n
\r\n \r\n
\r\n \r\n
\r\n \r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n \r\n
\r\n اسم الجهاز :\r\n
\r\n \r\n
\r\n \r\n
\r\n \r\n
\r\n اسم الصانع :\r\n
\r\n \r\n
\r\n \r\n
\r\n \r\n
\r\n الموديل :\r\n
\r\n \r\n
\r\n \r\n
\r\n \r\n
\r\n الرقم المسلسل :\r\n
\r\n \r\n
\r\n \r\n
\r\n \r\n
\r\n القسم :\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n لا الجهاز ليس تحت الضمان الرسمي شركة\r\n {% if doc.warranty_not_under_company %}✓{% endif %}\r\n \r\n \r\n
\r\n \r\n \r\n
\r\n ضمان تحت اسم الشركة/المورد\r\n {% if doc.warranty_under_company %}✓{% endif %}\r\n ضمان\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n التوقيع :\r\n
\r\n الاسم :\r\n
\r\n \r\n
\r\n التاريخ :\r\n
\r\n يتم تعبئة أمر العمل بناء على طلب المستخدم/المركز التاريخ\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n \r\n
\r\n تفاصيل العطل/الوجود بالجهاز :\r\n
\r\n
\r\n \r\n \r\n
\r\n
PPM
\r\n \r\n \r\n
\r\n \r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n {% if doc.maintenance_not_comp %}✓{% endif %}\r\n اكتمال الصيانة\r\n
\r\n \r\n
\r\n \r\n {% if doc.maintenance_done %}✓{% endif %}\r\n تمت الصيانة\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n السبب :\r\n
\r\n \r\n
\r\n \r\n
\r\n الإجراء :\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% for i in range(8) %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n
Spares Description/Part No
اسم قطعة الغيار ووصفها
الكمية
QTY
Spares Description/Part No
اسم قطعة الغيار ووصفها
الكمية
QTY
{% if doc.spares and doc.spares[i] %}{{ doc.spares[i].description }}{% endif %}{% if doc.spares and doc.spares[i] %}{{ doc.spares[i].qty }}{% endif %}{% if doc.spares and doc.spares[i+8] %}{{ doc.spares[i+8].description }}{% endif %}{% if doc.spares and doc.spares[i+8] %}{{ doc.spares[i+8].qty }}{% endif %}
\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n التوقيع\r\n \r\n \r\n
\r\n \r\n \r\n
\r\n الاسم :\r\n \r\n \r\n
\r\n \r\n
\r\n اسم المهندس/الفني القائم بالإصلاح أو طلب قطع الغيار :\r\n NAME OF THE TECHNICIAN/ENGINEER REQUESTING PARTS OR REPAIRING :\r\n
\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n \r\n التقرير النهائي فقط بعد اكتمال الإصلاح :\r\n
\r\n \r\n
\r\n {% for i in range(4) %}\r\n
\r\n {% endfor %}\r\n
\r\n
\r\n \r\n \r\n
\r\n \r\n
\r\n Compiled by Tec/Eng\r\n أعدها فني / مهندس :\r\n
\r\n \r\n \r\n
\r\n Finish Date {{ doc.finish_date or '____/____/2025' }}\r\n تاريخ الإنتهاء\r\n
\r\n \r\n \r\n
\r\n Start Date {{ doc.start_date or '____/____/2025' }}\r\n تاريخ البدء\r\n
\r\n
\r\n \r\n \r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n
الفني
\r\n
\r\n
مدير المستشفى / المركز الصحي
\r\n
\r\n
مشرف الصيانة الطبية
\r\n
\r\n
Operator
\r\n
المعتمد
\r\n
\r\n \r\n \r\n
\r\n الاسم :\r\n
\r\n
\r\n التوقيع :\r\n
\r\n
\r\n
\r\n الاسم :\r\n NAME:\r\n
\r\n
\r\n
\r\n التوقيع :\r\n SIGN:\r\n
\r\n
\r\n
\r\n
\r\n الاسم :\r\n NAME:\r\n
\r\n
\r\n
\r\n التوقيع :\r\n SIGN:\r\n
\r\n
\r\n
\r\n
\r\n\r\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-06-03 10:58:05.403403", + "module": "Assets", + "name": "PPM", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Material Request", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "\r\n\r\n\r\n \r\n \r\n\r\n\r\n
\r\n
\r\n
\r\n
\r\n \r\n
\r\n \r\n
\r\n
\r\n
المملكة العربية السعودية
\r\n
تجمع الجوف الصحي
\r\n
إدارة الصيانة ( الصيانة الطبية )
\r\n
\r\n
\r\n \r\n
BIO - MEDICAL MAINTENANCE PROJECT
\r\n
\r\n\r\n
\r\n
\r\n
SPARE PARTS
\r\n
\r\n
طلب عروض قطع غيار للأجهزة الطبية
\r\n
\r\n {% set wo = frappe.get_doc(\"Work_Order\", doc.custom_work_order) %}\r\n
\r\n
\r\n LOCATION\r\n
\r\n HOSP : {{wo.company}}\r\n : مستشفى\r\n
\r\n
\r\n
: الموقع
\r\n
\r\n
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n
\r\n P.R.NO.\r\n ـم ـ رقـ الطلب\r\n
\r\n
{{doc.name}}\r\n
\r\n P.R DATE\r\n ـخʈالتار\r\n
\r\n
{{doc.transaction_date}}
\r\n
\r\n JOB ORDER. No\r\n أمرالعمـل رقم\r\n
\r\n
{{doc.custom_work_order}}\r\n
\r\n J.O. DATE\r\n ـخʈالتار\r\n
\r\n
{{wo.creation or \"\"}}
\r\n
\r\n EQUIPMENT\r\n ــازɺاݍݨ\r\n
\r\n
{{wo.asset or \"\"}}\r\n
\r\n MODEL/ TYPE\r\n الموديــل\r\n
\r\n
{{wo.model or \"\"}}
\r\n
\r\n MANUFACTURER\r\n الصناعـة\r\n
\r\n
{{wo.manufacturer or \"\"}}\r\n
\r\n SYSTEM NO\r\n \r\n
\r\n
\r\n
\r\n AGENT\r\n الوكيـــل\r\n
\r\n
{{wo.vendor or \"\"}}\r\n
\r\n FAX NO.\r\n الفاكس رقم\r\n
\r\n
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {%- for row in doc.items -%}\r\n \r\n \r\n \r\n \r\n \r\n \r\n {%- endfor -%}\r\n \r\n \r\n
SNDESCRIPTION إســـم قطعـة الغيــارPART NO رقم القطعـةQTY الكميه
{{ loop.index }}\r\n {{row.description}}\r\n \r\n {{row.qty}}
\r\n\r\n
\r\n
NOTE : Make Sure The above information has been filled professionally & it is assumed that defaulters bear the consequences
\r\n
\r\n عليأن يتم التوريد خلال ثلاثين يوما طبقا لخطاب سعادة مدير عام الوكالات التجارية رقم 20631/22 وتاريخ
\r\n 1432/10/08 هـ المتضمن عدم تجاوزفترة التوريد عن ثلاثين يوما .\r\n
\r\n
\r\n\r\n
\r\n
\r\n
\r\n ENGINEER / TECHN الفني / المهندس\r\n
\r\n
\r\n
NAME :
\r\n
SIGN :
\r\n
DATE :
\r\n
\r\n
\r\n \r\n
\r\n
\r\n SITE MANAGER مدير الموقع\r\n
\r\n
\r\n
NAME :
\r\n
SIGN :
\r\n
DATE :
\r\n
\r\n
\r\n \r\n
\r\n
\r\n MOH SUPERVISOR مهندس الوزارة\r\n
\r\n
\r\n
NAME :
\r\n
SIGN :
\r\n
DATE :
\r\n
\r\n
\r\n
\r\n\r\n
\r\n
شركة سمــــامة للتشغيــــــل والادارة
\r\n
SAMAMA CO. FOR OPERATION & MANAGEMENT
\r\n
\r\n\r\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-06-13 10:50:48.066258", + "module": "Stock", + "name": "Spare Parts Request", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "\r\n\r\n\r\n \r\n \r\n Medical Equipment Installation Report\r\n \r\n\r\n\r\n \r\n
\r\n
المملكة العربية السعودية
\r\n
\r\n
الرقم: {{ doc.name or \"\" }}
\r\n
\r\n \r\n
\r\n
تجمع الجوف الصحي
\r\n
محضر تركيب
\r\n
التاريخ: {{ frappe.utils.formatdate(doc.posting_date, \"dd/MM/yyyy\") if doc.posting_date else \"\" }} هـ
\r\n
\r\n \r\n
\r\n
الصيانة الطبية
\r\n
INSTALLATION REPORT
\r\n
الموافق: {{ frappe.utils.formatdate(doc.posting_date, \"dd/MM/yyyy\") if doc.posting_date else \"\" }} م
\r\n
\r\n \r\n
\r\n مستشفى {{ doc.customer or \"\" }}\r\n
\r\n\r\n \r\n
\r\n تشهد مستشفى {{ doc.customer or \"\" }} بأن {{ doc.supplier or \"\" }} قامت بتركيب و توريد قطع الغيار الخاصة التالية والتابعة:\r\n
\r\n\r\n \r\n
\r\n
\r\n
\r\n EQUIPMENT NAME\r\n :\r\n {{ doc.name or \"\" }}\r\n اسم الجهاز:\r\n
\r\n
\r\n MFR\r\n :\r\n {{ doc.custom_manufacturer or \"\" }}\r\n صناعة:\r\n
\r\n
\r\n \r\n
\r\n
\r\n SERIAL NO\r\n :\r\n {{ doc.custom_serial_number or \"\" }}\r\n مسلسل:\r\n
\r\n
\r\n MODEL\r\n :\r\n {{ doc.custom_model or \"\" }}\r\n موديل:\r\n
\r\n
\r\n
\r\n \r\n
\r\n \r\n التابع لقسم:\r\n \r\n {{ doc.location or \"\" }}\r\n \r\n LOCATION:\r\n \r\n
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% if doc.items %}\r\n {% for item in doc.custom_spare_parts %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n {% for i in range(8 - doc.items|length) %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n {% else %}\r\n {% for i in range(8) %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n {% endif %}\r\n \r\n
موصف القطعة
DESCRIPTION
رقم القطعة
PART NO.
الوحدة
UNIT
الكمية
QTY.
{{ loop.index }}{{ item.item_name or \"\" }}{{ item.item_code or \"\" }}{{ item.uom or \"\" }}{{ item.qty or \"\" }}
{{ doc.items|length + loop.index }}    
{{ loop.index }}    
\r\n\r\n \r\n
\r\n وذلك طبقا لعقد صيانة وإصلاح الأجهزة الطبية بمستشفى {{ doc.customer or \"\" }} والجهاز يعمل بحالة جيدة.\r\n

\r\n وﷲ الموفق .....\r\n
\r\n\r\n \r\n
\r\n \r\n
\r\n
\r\n
المسئول في القسم      Dept. Responsible
\r\n
Name
\r\n
Sign
\r\n
\r\n \r\n
\r\n
مدير شركة سمامة      Site Manager
\r\n
Name
\r\n
Sign
\r\n
\r\n
\r\n \r\n \r\n
\r\n
مهندس/ فني التركيب      Installation Eng
\r\n
Name
\r\n
Sign
\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n
مشرف الوزارة بالمستشفى      Hospital Supervisor
\r\n
Name
\r\n
Sign
\r\n
\r\n \r\n
\r\n
مدير المستشفى/ المركز      Hospital Manager
\r\n
Name
\r\n
Sign
\r\n
\r\n
\r\n
\r\n\r\n \r\n
\r\n الختم الرسمي\r\n
\r\n\r\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-06-13 12:28:46.621903", + "module": "Assets", + "name": "Installation Report", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": "@page {\r\n size: A4;\r\n margin: 15mm;\r\n }\r\n \r\n body {\r\n font-family: Arial, sans-serif;\r\n direction: rtl;\r\n text-align: right;\r\n /*font-family: \"Arial\", sans-serif;*/\r\n font-size: 12px;\r\n line-height: 1.4;\r\n margin: 0;\r\n padding: 0;\r\n }\r\n \r\n\r\n .header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: flex-start;\r\n margin-bottom: 20px;\r\n /*border-bottom: 2px solid #000;*/\r\n padding-bottom: 10px;\r\n }\r\n \r\n .header-left {\r\n flex: 1;\r\n }\r\n \r\n .header-right {\r\n flex: 1;\r\n text-align: right;\r\n }\r\n \r\n .kingdom {\r\n font-weight: bold;\r\n font-size: 14px;\r\n margin-bottom: 5px;\r\n }\r\n \r\n .organization {\r\n font-weight: bold;\r\n font-size: 13px;\r\n margin-bottom: 3px;\r\n }\r\n \r\n .department {\r\n font-weight: bold;\r\n font-size: 12px;\r\n }\r\n \r\n .original-note {\r\n font-size: 11px;\r\n font-weight: bold;\r\n margin-bottom: 15px;\r\n }\r\n \r\n .document-details {\r\n display: flex;\r\n justify-content: space-between;\r\n margin-bottom: 20px;\r\n /*background-color: #f8f9fa;*/\r\n padding: 10px;\r\n /*border: 1px solid #ddd;*/\r\n }\r\n \r\n .detail-group {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n }\r\n \r\n .detail-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n }\r\n \r\n .detail-label {\r\n font-weight: bold;\r\n min-width: 100px;\r\n }\r\n \r\n .detail-input {\r\n /*border-bottom: 1px solid #000;*/\r\n min-width: 150px;\r\n height: 20px;\r\n display: inline-block;\r\n }\r\n \r\n .document-title {\r\n text-align: center;\r\n font-size: 16px;\r\n font-weight: bold;\r\n margin: 20px 0;\r\n padding: 10px;\r\n background-color: #e9ecef;\r\n border: 2px solid #000;\r\n }\r\n \r\n .return-reasons {\r\n margin-bottom: 20px;\r\n padding: 15px;\r\n border: 1px solid #000;\r\n }\r\n \r\n .reasons-title {\r\n font-weight: bold;\r\n text-align: center;\r\n margin-bottom: 10px;\r\n font-size: 14px;\r\n }\r\n \r\n .reasons-grid {\r\n display: grid;\r\n grid-template-columns: repeat(4, 1fr);\r\n gap: 15px;\r\n }\r\n \r\n .reason-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n }\r\n \r\n .checkbox {\r\n width: 15px;\r\n height: 15px;\r\n border: 2px solid #000;\r\n display: inline-block;\r\n }\r\n \r\n .items-table {\r\n width: 100%;\r\n border-collapse: collapse;\r\n margin-bottom: 30px;\r\n font-size: 11px;\r\n }\r\n \r\n .items-table th,\r\n .items-table td {\r\n border: 1px solid #000;\r\n padding: 8px;\r\n text-align: center;\r\n vertical-align: middle;\r\n }\r\n \r\n .items-table th {\r\n background-color: #f8f9fa;\r\n font-weight: bold;\r\n font-size: 10px;\r\n }\r\n \r\n .items-table .description-col {\r\n width: 25%;\r\n text-align: left;\r\n }\r\n \r\n .items-table .recommendations-col {\r\n width: 35%;\r\n }\r\n \r\n .recommendation-options {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 5px;\r\n font-size: 9px;\r\n }\r\n \r\n .recommendation-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 5px;\r\n }\r\n \r\n .small-checkbox {\r\n width: 10px;\r\n height: 10px;\r\n border: 1px solid #000;\r\n display: inline-block;\r\n }\r\n \r\n .signatures-section {\r\n margin-top: 40px;\r\n }\r\n \r\n .signatures-table {\r\n width: 100%;\r\n border: 1px solid #000;\r\n border-collapse: collapse;\r\n margin-bottom: 20px;\r\n }\r\n \r\n .signatures-table th{\r\n border: 1px solid #000;\r\n padding: 15px;\r\n text-align: center;\r\n vertical-align: top;\r\n \r\n }\r\n .signatures-table td {\r\n border: 1px solid #000;\r\n padding: 15px;\r\n text-align: center;\r\n vertical-align: top;\r\n }\r\n \r\n .signatures-table th {\r\n background-color: #f8f9fa;\r\n font-weight: bold;\r\n font-size: 11px;\r\n }\r\n \r\n .signature-cell {\r\n height: 80px;\r\n }\r\n \r\n .signature-labels {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 25px;\r\n text-align: left;\r\n }\r\n \r\n .authorization {\r\n margin-top: 20px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 15px;\r\n }\r\n \r\n .auth-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n }\r\n \r\n .auth-label {\r\n font-weight: bold;\r\n min-width: 120px;\r\n }\r\n \r\n .auth-line {\r\n border-bottom: 1px solid #000;\r\n flex: 1;\r\n height: 20px;\r\n }\r\n \r\n .page-break {\r\n page-break-before: always;\r\n }", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Material Transfer", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "
\r\n\r\n
\r\n
\r\n
المملكة العربية السعودية
\r\n
تجمع الجوف الصحي
\r\n
إدارة المستودعات
\r\n
الجهة المرجعة:\r\n {% if doc.transfer_type == \"Item\" %}\r\n {{ doc.item or \" \" }}\r\n {% elif doc.transfer_type == \"Asset\" %}\r\n {{ doc.asset or \" \" }}\r\n {% else %}\r\n {{\" \"}}\r\n {% endif %}\r\n
\r\n
المستودع: {{doc.source_warehouse or \"\"}}
\r\n
\r\n

مستند إرجاع

\r\n \r\n \r\n \r\n \r\n
\r\n
عدد الصفحات: .....................
\r\n
التاريخ: \r\n {{doc.date or \"\"}}\r\n
\r\n
الموافق: \r\n {{doc.date or \"\"}}\r\n
\r\n
\r\n\r\n
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
أسباب الإرجاع
\r\n \r\n {% if doc.reason_for_return == \"Purpose Completed\" %}✓{% endif %}\r\n \r\n انتهاء الغرض\r\n \r\n \r\n {% if doc.reason_for_return == \"Surplus\" %}✓{% endif %}\r\n \r\n فائض\r\n \r\n \r\n {% if doc.reason_for_return == \"Unusable\" %}✓{% endif %}\r\n \r\n عدم الصلاحية\r\n \r\n \r\n {% if doc.reason_for_return == \"Damaged\" %}✓{% endif %}\r\n \r\n تالف\r\n
\r\n

\r\n\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% if doc.transfer_type == \"Item\" and doc.item_table %}\r\n {% for item in doc.item_table %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n {% endif %}\r\n {% if doc.transfer_type == \"Asset\" and doc.asset_transfer %}\r\n {% for asse in doc.asset_transfer %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n {% endif %}\r\n \r\n
مرقم الصنفاسم الصنف ووصفهالوحدةالكميةتوصيات لجنة فحص الرجيعملاحظات
للإصلاحللبيعللتلف
{{ loop.index }}{{ item.item or \"\" }}{{ item.item_name or \"\" }}{{ item.uom or \"\" }}{{ item.qty or \"\" }}{% if item.return_inspection_committee == \"For Repair\" %}✓{% endif %}{% if item.return_inspection_committee == \"For Sale\" %}✓{% endif %}{% if item.return_inspection_committee == \"For Disposal\" %}✓{% endif %}{{ item.notes or \"\" }}
{{ loop.index }}{{ asse.asset or \"\" }}{{ asse.asset_name or \"\" }}Nos{{ asse.qty or \"\" }}{% if asse.return_inspection_committee == \"For Repair\" %}✓{% endif %}{% if asse.return_inspection_committee == \"For Sale\" %}✓{% endif %}{% if asse.return_inspection_committee == \"For Disposal\" %}✓{% endif %}{{ asse.notes or \"\" }}
\r\n\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
المسؤول في الجهة المرجعةالمستلم / أمين المستودعمدير إدارة المستودعاتلجنة فحص الرجيع
الاسم
التوقيع
التاريخ
\r\n\r\n \r\n \r\n \r\n
صاحب الصلاحية ...................
التوقيع ...........................
\r\n
\r\n\r\n
\r\n
\r\n الأصل – لأمين/ لمأمور المستودع\r\n
\r\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-06-17 08:49:08.299491", + "module": "Asset Lite", + "name": "Return Report", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "\r\n\r\n\r\n \r\n \r\n Equipment Decommissioning Report\r\n \r\n\r\n\r\n \r\n
\r\n
\r\n
\r\n\r\n \r\n
\r\n محضر تكهين / إبعاد عن الخدمة\r\n
\r\n\r\n \r\n
\r\n القسم : {{ doc.department or \"\" }}\r\n
\r\n\r\n \r\n
0-1 بيانات الجهاز المطلوب تكهينه :
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
اسم الجهاز{{ doc.name or \"\" }}الرقم التسلسلي{{ doc.custom_serial_number or \"\" }}
الشركة الصانعة{{ doc.custom_manufacturer or \"\" }}تاريخ التوريد{{ frappe.utils.formatdate(doc.creation, \"dd/MM/yyyy\") }}
الطراز/الموديل{{ doc.custom_model or \"\" }}مدة استخدام الجهاز{{ doc.usage_period or \"\" }}
\r\n\r\n \r\n
2- أسباب طلب التكهين / إبعاد عن الخدمة :
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
أبجدهـو
      
\r\n\r\n
\r\n أ) تكاليف قطع الغيار المطلوبة للإصلاح أكثر من نصف قيمة الجهاز (مرفق عرض أسعار قطع الغيار اللازمة للإصلاح).\r\n
\r\n
\r\n ب) توقف انتاج الجهاز وقطع الغيار من الشركة الصانعة حسب تقرير المورد.\r\n
\r\n
\r\n ج) جهاز قديم وتم استخدامه أكثر من عشر سنوات.\r\n
\r\n
\r\n د) ليس للجهاز وكيل معروف بالمملكة في الوقت الحالي ولا يمكن توفير قطع غيار للجهاز.\r\n
\r\n
\r\n ه) تكرار أعطال الجهاز وإصلاحه على فترات متقاربة.\r\n
\r\n
\r\n و) أخرى : {{ doc.other_reason or \"\" }}\r\n
\r\n\r\n \r\n
3- مرئيات اللجنة:
\r\n
\r\n \r\n بناءاً على الفقرة أ. ج. و. يتم تكهين الجهاز وادراجه ضمن بنود الإحلال المطلوبة للمستشفى.\r\n \r\n
\r\n\r\n \r\n
توقيع اللجنة :
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
المقاولرئيس القسممدير الصيانة الطبيةمدير مراقبة المخزون
الإسم :الإسم :الإسم :الإسم :
التوقيع :التوقيع :التوقيع :التوقيع :
التاريخ :التاريخ :التاريخ :التاريخ :
\r\n\r\n \r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
مدير مستشفى
الإسم :
التوقيع :
التاريخ :
الختم :
\r\n
\r\n\r\n \r\n
\r\n
الصيانة الطبية بتجمع الجوف الصحي
\r\n
المهندس/عبد العزيز زايد الخمعلي
\r\n
\r\n\r\n \r\n
\r\n
تجمع الجوف الصحي
\r\n
Aljouf Health Cluster
\r\n
\r\n\r\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-07-08 09:23:55.165128", + "module": "Assets", + "name": "Dismantelling Report", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "PM Schedule Generator", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "{% set days_map = {\"Daily\": 1, \"Weekly\": 7} %}\r\n{% set months_map = {\r\n \"Monthly\": 1, \"Quarterly\": 3, \"Half-yearly\": 6,\r\n \"Yearly\": 12, \"2 Yearly\": 24, \"3 Yearly\": 36\r\n} %}\r\n\r\n\r\n\r\n

Preventive Maintenance Schedule
\r\n {{ doc.name }}\r\n

\r\n\r\n

Asset Details

\r\n\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n {% if doc.maintenance_entries %}\r\n {% for m in doc.maintenance_entries %}\r\n {% set asset_doc = frappe.get_doc(\"Asset\", m.asset) %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n {% else %}\r\n \r\n {% endif %}\r\n\r\n \r\n
#Asset IDAsset NameSerial NumberManufacturerModel
{{ loop.index }}{{ m.asset }}{{ m.asset_name }}{{asset_doc.custom_serial_number or \"\" }}{{asset_doc.custom_manufacturer or \"\" }}{{asset_doc.custom_model or \"\" }}
No asset rows in maintenance_entries.
\r\n
\r\n

PM Schedule

\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n {% if doc.start_date and doc.end_date and doc.periodicity %}\r\n {% set sd = doc.start_date %}\r\n {% set ed = doc.end_date %}\r\n\r\n {# ---------------- day‑based periodicities ---------------- #}\r\n {% if doc.periodicity in days_map %}\r\n {% set step = days_map[doc.periodicity] %}\r\n {# start at the FIRST interval, i.e. skip n = 0 #}\r\n {% for n in range(step, frappe.utils.date_diff(ed, sd)+1, step) %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n\r\n {# -------------- month‑based periodicities ---------------- #}\r\n {% elif doc.periodicity in months_map %}\r\n {% set step = months_map[doc.periodicity] %}\r\n {% set month_diff = frappe.utils.month_diff(ed, sd) %}\r\n {% set occ = (month_diff // step) + 1 %}\r\n {# start at i = 1 to skip the start date itself #}\r\n {% for i in range(1, occ) %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n\r\n {% else %}\r\n \r\n {% endif %}\r\n {% else %}\r\n \r\n {% endif %}\r\n\r\n \r\n
#Due DatePeriodicityAssigned ToMaintenance TypeStatus
{{ loop.index }}{{ frappe.format(frappe.utils.add_days(sd, n), {'fieldtype': 'Date'}) }}{{ doc.periodicity }}{{ doc.assign_to or \"\" }}PMPlanned
{{ loop.index }}{{ frappe.format(frappe.utils.add_months(sd, i * step), {'fieldtype': 'Date'}) }}{{ doc.periodicity }}{{ doc.assign_to or \"\" }}PMPlanned
Unknown periodicity: {{ doc.periodicity }}
Start Date, End Date, or Periodicity is missing.
\r\n\r\n\r\n\r\n\r\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-07-10 11:51:47.806131", + "module": "Asset Lite", + "name": "PM Schedule Generator", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "
\r\n \r\n
\r\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-08-08 17:56:39.086119", + "module": "Asset Lite", + "name": "Stay Plugged", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Asset Maintenance Log", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "\r\n\r\n\r\n \r\n Electrical Safety Check\r\n \r\n\r\n\r\n\r\n \r\n
\r\n \r\n
\r\n
ELECTRICAL SAFETY CHECK
\r\n
\r\n
Tested By
\r\n
{{ doc.custom_serviced_by or ' ' }}
\r\n
\r\n
\r\n
Date
\r\n
{{ doc.completion_date or ' ' }}
\r\n
\r\n
\r\n
Due Date
\r\n
{{ doc.due_date or ' ' }}
\r\n
\r\n
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n\r\n \r\n
إختبارات السلامة الكهربائية
\r\n\r\n\r\n\r\n", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-08-08 17:57:12.255909", + "module": "Asset Lite", + "name": "Safety check", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Asset Maintenance Log", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "\n \n{# fetch docs as before #}\n{% set aml = doc %}\n{% set asset = None %}\n{% if aml.asset_maintenance %}\n {% set asset = frappe.get_doc(\"Asset\", aml.asset_maintenance) %}\n{% endif %}\n \n{% set custom_class = asset.custom_class or \"\" %}\n{% set service = asset.custom_service_agreement or \"\" %}\n{% set sticker_class = \"white\" %}\n\n{% if service == \"Warranty\" %}\n {% set sticker_class = \"blue\" %}\n{% elif \"Class A\" in custom_class %}\n {% set sticker_class = \"red\" %}\n{% elif \"Class B\" in custom_class %}\n {% set sticker_class = \"green\" %}\n{% elif \"Class C\" in custom_class %}\n {% set sticker_class = \"white\" %}\n{% endif %}\n\n\n \n
\n
\n
\n
\n \"Aljouf\n
تجمع الجوف الصحي
\n
Aljouf Health Cluster
\n
\n
\n \"Samama\"\n
قطاع الصيانة الطبية
\n
BIO MEDICAL ENG DIV
\n
\n
\n \n
\n
\n

Biomedical Maintenance Department

\n \n
\n \n
\n
\n
Department :
\n
\n {% if asset and asset.department %}\n {{ asset.department }}\n {% else %}\n {{ aml.department or \"\" }}\n {% endif %}\n
\n
\n
\n
Equip. code :
\n
{{ aml.asset_maintenance or \"\" }}
\n
\n
\n \n
\n
\n
Machine/Equipment :
\n
\n {% if asset %}\n {{ asset.asset_name or asset.custom_asset_names or \"\" }}\n {% else %}\n {{ aml.custom_asset_names or \"\" }}\n {% endif %}\n
\n
\n
\n \n
\n
\n
Model No. :
\n
{{asset.custom_model or \"\"}}
\n
\n
\n
Serial No. :
\n
{{ asset.custom_serial_number or \"\" }}
\n
\n
\n \n
\n
\n
Last PPM :
\n
\n {% if aml.previous_ppm_date %}\n {{ aml.previous_ppm_date }}\n {% else %}\n {{ aml.completion_date or \"\" }}\n {% endif %}\n
\n
\n
\n
Next PPM :
\n
{{ aml.next_ppm_date or aml.due_date or \"\" }}
\n
\n
\n \n
\n
\n
PPM Done By :
\n
{{ aml.custom_serviced_by or \"\" }}
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2025-08-08 17:59:32.554668", + "module": "Asset Lite", + "name": "PPM Assets", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + }, + { + "absolute_value": 0, + "align_labels_right": 0, + "css": null, + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "PM Schedule Generator", + "docstatus": 0, + "doctype": "Print Format", + "font": null, + "font_size": 14, + "format_data": null, + "html": "{% set days_map = {\"Daily\": 1, \"Weekly\": 7} %}\r\n{% set months_map = {\r\n \"Monthly\": 1, \"Quarterly\": 3, \"Half-yearly\": 6,\r\n \"Yearly\": 12, \"2 Yearly\": 24, \"3 Yearly\": 36\r\n} %}\r\n{# ---------------- CURRENT YEAR SETUP ---------------- #}\r\n{% set current_year = frappe.utils.nowdate()[:4] | int %}\r\n{% set year_start = frappe.utils.getdate(current_year ~ \"-01-01\") %}\r\n{% set year_end = frappe.utils.getdate(current_year ~ \"-12-31\") %}\r\n\r\n

\r\n Preventive Maintenance Schedule
\r\n {{ doc.name }}
\r\n Year: {{ current_year }}\r\n

\r\n

Asset Details

\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% if doc.maintenance_entries %}\r\n {% for m in doc.maintenance_entries %}\r\n {% set asset_doc = frappe.get_doc(\"Asset\", m.asset) %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% endfor %}\r\n {% else %}\r\n \r\n {% endif %}\r\n \r\n
#Asset IDAsset NameSerial NumberManufacturerModel
{{ loop.index }}{{ m.asset }}{{ m.asset_name }}{{ asset_doc.custom_serial_number or \"\" }}{{ asset_doc.custom_manufacturer or \"\" }}{{ asset_doc.custom_model or \"\" }}
No asset rows found.
\r\n
\r\n

PM Schedule ({{ current_year }})

\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% set row_no = namespace(val=1) %}\r\n {% if doc.start_date and doc.end_date and doc.periodicity %}\r\n {% set sd = frappe.utils.getdate(doc.start_date) %}\r\n {% set ed = frappe.utils.getdate(doc.end_date) %}\r\n {# -------- DAY BASED -------- #}\r\n {% if doc.periodicity in days_map %}\r\n {% set step = days_map[doc.periodicity] %}\r\n {% for n in range(step, frappe.utils.date_diff(ed, sd) + 1, step) %}\r\n {% set due_date = frappe.utils.getdate(frappe.utils.add_days(sd, n)) %}\r\n {% if due_date >= year_start and due_date <= year_end %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% set row_no.val = row_no.val + 1 %}\r\n {% endif %}\r\n {% endfor %}\r\n {# -------- MONTH BASED -------- #}\r\n {% elif doc.periodicity in months_map %}\r\n {% set step = months_map[doc.periodicity] %}\r\n {% set month_diff = frappe.utils.month_diff(ed, sd) %}\r\n {% set occ = (month_diff // step) + 1 %}\r\n {% for i in range(1, occ) %}\r\n {% set due_date = frappe.utils.getdate(frappe.utils.add_months(sd, i * step)) %}\r\n {% if due_date >= year_start and due_date <= year_end %}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {% set row_no.val = row_no.val + 1 %}\r\n {% endif %}\r\n {% endfor %}\r\n {% else %}\r\n \r\n {% endif %}\r\n {% else %}\r\n \r\n {% endif %}\r\n \r\n
#Due DatePeriodicityAssigned ToMaintenance TypeStatus
{{ row_no.val }}{{ frappe.format(due_date, {'fieldtype': 'Date'}) }}{{ doc.periodicity }}{{ doc.assign_to or \"\" }}PMPlanned
{{ row_no.val }}{{ frappe.format(due_date, {'fieldtype': 'Date'}) }}{{ doc.periodicity }}{{ doc.assign_to or \"\" }}PMPlanned
Unknown periodicity
Start Date, End Date or Periodicity missing.
", + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2026-01-13 15:52:22.771069", + "module": "Asset Lite", + "name": "Annual PPM Plans For Vendors", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_commands": null, + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" + } +] \ No newline at end of file diff --git a/asset_lite/fixtures/property_setter.json b/asset_lite/fixtures/property_setter.json new file mode 100644 index 0000000..27fea04 --- /dev/null +++ b/asset_lite/fixtures/property_setter.json @@ -0,0 +1,4594 @@ +[ + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "item_code", + "is_system_generated": 0, + "modified": "2025-05-22 12:54:51.168178", + "module": "Asset Lite", + "name": "Asset-item_code-reqd", + "property": "reqd", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Sales Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "due_date", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:46.134377", + "module": "Asset Lite", + "name": "Sales Order-due_date-print_hide", + "property": "print_hide", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Sales Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "payment_schedule", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:46.105940", + "module": "Asset Lite", + "name": "Sales Order-payment_schedule-print_hide", + "property": "print_hide", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Sales Invoice", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "due_date", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:46.072624", + "module": "Asset Lite", + "name": "Sales Invoice-due_date-print_hide", + "property": "print_hide", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Sales Invoice", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "payment_schedule", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:46.042206", + "module": "Asset Lite", + "name": "Sales Invoice-payment_schedule-print_hide", + "property": "print_hide", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Purchase Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "due_date", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:46.013538", + "module": "Asset Lite", + "name": "Purchase Order-due_date-print_hide", + "property": "print_hide", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Purchase Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "payment_schedule", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.984480", + "module": "Asset Lite", + "name": "Purchase Order-payment_schedule-print_hide", + "property": "print_hide", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Purchase Invoice", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "due_date", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.948794", + "module": "Asset Lite", + "name": "Purchase Invoice-due_date-print_hide", + "property": "print_hide", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Purchase Invoice", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "payment_schedule", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.920228", + "module": "Asset Lite", + "name": "Purchase Invoice-payment_schedule-print_hide", + "property": "print_hide", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Supplier", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.892379", + "module": "Asset Lite", + "name": "Supplier-naming_series-reqd", + "property": "reqd", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Supplier", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.864281", + "module": "Asset Lite", + "name": "Supplier-naming_series-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Customer", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.837380", + "module": "Asset Lite", + "name": "Customer-naming_series-reqd", + "property": "reqd", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Customer", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.808393", + "module": "Asset Lite", + "name": "Customer-naming_series-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Sales Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "tax_id", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.764583", + "module": "Asset Lite", + "name": "Sales Order-tax_id-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Sales Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "tax_id", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.735879", + "module": "Asset Lite", + "name": "Sales Order-tax_id-print_hide", + "property": "print_hide", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Sales Invoice", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "tax_id", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.706573", + "module": "Asset Lite", + "name": "Sales Invoice-tax_id-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Sales Invoice", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "tax_id", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.673428", + "module": "Asset Lite", + "name": "Sales Invoice-tax_id-print_hide", + "property": "print_hide", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Delivery Note", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "tax_id", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.644346", + "module": "Asset Lite", + "name": "Delivery Note-tax_id-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Delivery Note", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "tax_id", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.616283", + "module": "Asset Lite", + "name": "Delivery Note-tax_id-print_hide", + "property": "print_hide", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Packed Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "rate", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.568577", + "module": "Asset Lite", + "name": "Packed Item-rate-read_only", + "property": "read_only", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Sales Invoice Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "discount_account", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.535884", + "module": "Asset Lite", + "name": "Sales Invoice Item-discount_account-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Sales Invoice Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "discount_account", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.503917", + "module": "Asset Lite", + "name": "Sales Invoice Item-discount_account-mandatory_depends_on", + "property": "mandatory_depends_on", + "property_type": "Code", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Sales Invoice", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "additional_discount_account", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.476424", + "module": "Asset Lite", + "name": "Sales Invoice-additional_discount_account-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Sales Invoice", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "additional_discount_account", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:45.449349", + "module": "Asset Lite", + "name": "Sales Invoice-additional_discount_account-mandatory_depends_on", + "property": "mandatory_depends_on", + "property_type": "Code", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:44.281234", + "module": "Asset Lite", + "name": "Work Job Order-naming_series-options", + "property": "options", + "property_type": "Text", + "row_name": null, + "value": "WO-.YYYY.-" + }, + { + "default_value": null, + "doc_type": "Support Plans", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "warranty_start", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:44.249901", + "module": "Asset Lite", + "name": "Support Plans-warranty_start-mandatory_depends_on", + "property": "mandatory_depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.include_warranty" + }, + { + "default_value": null, + "doc_type": "Support Plans", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "warranty_start", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:44.218989", + "module": "Asset Lite", + "name": "Support Plans-warranty_start-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.include_warranty" + }, + { + "default_value": null, + "doc_type": "Support Plans", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "warranty_status", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:44.183712", + "module": "Asset Lite", + "name": "Support Plans-warranty_status-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.include_warranty" + }, + { + "default_value": null, + "doc_type": "Support Plans", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "warranty_end", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:44.155021", + "module": "Asset Lite", + "name": "Support Plans-warranty_end-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.include_warranty" + }, + { + "default_value": null, + "doc_type": "Support Plans", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "warranty_document", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:44.127588", + "module": "Asset Lite", + "name": "Support Plans-warranty_document-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.include_warranty" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_name", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:36.142064", + "module": "Asset Lite", + "name": "Work_Order-asset_name-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:36.109715", + "module": "Asset Lite", + "name": "Work_Order-asset-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "work_order_type", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:36.082960", + "module": "Asset Lite", + "name": "Work_Order-work_order_type-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "downtime", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:41.439590", + "module": "Asset Lite", + "name": "Work_Order-downtime-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_name", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:44.099605", + "module": "Asset Lite", + "name": "Asset Maintenance-asset_name-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "maintenance_team", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:44.071180", + "module": "Asset Lite", + "name": "Asset Maintenance-maintenance_team-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Warranty", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:44.015278", + "module": "Asset Lite", + "name": "Warranty-naming_series-options", + "property": "options", + "property_type": "Text", + "row_name": null, + "value": "WN-.####" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance Log", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.980374", + "module": "Asset Lite", + "name": "Asset Maintenance Log-naming_series-options", + "property": "options", + "property_type": "Text", + "row_name": null, + "value": "ACC-AML-.YYYY.-" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "custom_asset_name", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:44.044987", + "module": "Asset Lite", + "name": "Asset Maintenance-custom_asset_name-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "PPM", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 1, + "modified": "2025-04-22 14:45:43.944153", + "module": "Asset Lite", + "name": "PPM-main-default_print_format", + "property": "default_print_format", + "property_type": "Data", + "row_name": null, + "value": "PPM Sticker" + }, + { + "default_value": null, + "doc_type": "Issue", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "customer", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.899408", + "module": "Asset Lite", + "name": "Issue-customer-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_owner", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.827931", + "module": "Asset Lite", + "name": "Asset-asset_owner-default", + "property": "default", + "property_type": "Text", + "row_name": null, + "value": "Supplier" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_owner", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.788456", + "module": "Asset Lite", + "name": "Asset-asset_owner-read_only", + "property": "read_only", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.747914", + "module": "Asset Lite", + "name": "Asset-naming_series-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "custodian", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.708155", + "module": "Asset Lite", + "name": "Asset-custodian-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "disabled", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.668419", + "module": "Asset Lite", + "name": "Item-disabled-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "allow_alternative_item", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.629658", + "module": "Asset Lite", + "name": "Item-allow_alternative_item-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "has_variants", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.590802", + "module": "Asset Lite", + "name": "Item-has_variants-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.550790", + "module": "Asset Lite", + "name": "Item-main-quick_entry", + "property": "quick_entry", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "quality_tab", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.510268", + "module": "Asset Lite", + "name": "Item-quality_tab-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "item_code", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.469208", + "module": "Asset Lite", + "name": "Asset Maintenance-item_code-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "item_name", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.429543", + "module": "Asset Lite", + "name": "Asset Maintenance-item_name-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.390672", + "module": "Asset Lite", + "name": "Asset Maintenance-company-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Warranty", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.350526", + "module": "Asset Lite", + "name": "Warranty-asset-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.custom_type == \"Asset\"" + }, + { + "default_value": null, + "doc_type": "Warranty", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.310681", + "module": "Asset Lite", + "name": "Warranty-main-field_order", + "property": "field_order", + "property_type": "Data", + "row_name": null, + "value": "[\"naming_series\", \"custom_type\", \"asset\", \"asset_name\", \"custom_item\", \"custom_item_name\", \"extended_warranty\", \"start_date\", \"end_date\", \"column_break_dxer\", \"warranty_start_date\", \"warranty_end_date\", \"warranty_status\"]" + }, + { + "default_value": null, + "doc_type": "Warranty", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_name", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.269240", + "module": "Asset Lite", + "name": "Warranty-asset_name-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.custom_type == \"Asset\"" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "description", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.229669", + "module": "Asset Lite", + "name": "Item-description-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "uoms", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.189270", + "module": "Asset Lite", + "name": "Item-uoms-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "item_tax_section_break", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.146123", + "module": "Asset Lite", + "name": "Item-item_tax_section_break-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "manufacturing", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.105503", + "module": "Asset Lite", + "name": "Item-manufacturing-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_name", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.064961", + "module": "Asset Lite", + "name": "Asset Maintenance-asset_name-label", + "property": "label", + "property_type": "Data", + "row_name": null, + "value": "Asset ID" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "section_break_11", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:43.024101", + "module": "Asset Lite", + "name": "Item-section_break_11-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "standard_rate", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.989259", + "module": "Asset Lite", + "name": "Item-standard_rate-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "accounting", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.959584", + "module": "Asset Lite", + "name": "Item-accounting-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "sales_details", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.920998", + "module": "Asset Lite", + "name": "Item-sales_details-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "make_details_section", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.891554", + "module": "Asset Lite", + "name": "Work_Order-make_details_section-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_owner", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.786138", + "module": "Asset Lite", + "name": "Asset-asset_owner-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "is_composite_asset", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.563839", + "module": "Asset Lite", + "name": "Asset-is_composite_asset-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "accounting_dimensions_section", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.536772", + "module": "Asset Lite", + "name": "Asset-accounting_dimensions_section-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "insurance_details", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.508772", + "module": "Asset Lite", + "name": "Asset-insurance_details-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "section_break_31", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.470011", + "module": "Asset Lite", + "name": "Asset-section_break_31-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "other_details", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.437340", + "module": "Asset Lite", + "name": "Asset-other_details-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "gross_purchase_amount", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.408451", + "module": "Asset Lite", + "name": "Asset-gross_purchase_amount-mandatory_depends_on", + "property": "mandatory_depends_on", + "property_type": "Data", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "gross_purchase_amount", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.379111", + "module": "Asset Lite", + "name": "Asset-gross_purchase_amount-reqd", + "property": "reqd", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_quantity", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.348794", + "module": "Asset Lite", + "name": "Asset-asset_quantity-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_quantity", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.315370", + "module": "Asset Lite", + "name": "Asset-asset_quantity-read_only_depends_on", + "property": "read_only_depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.is_existing_asset && !doc.is_composite_asset" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "purchasing_tab", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.281457", + "module": "Asset Lite", + "name": "Item-purchasing_tab-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "inventory_settings_section", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.248800", + "module": "Asset Lite", + "name": "Item-inventory_settings_section-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "serial_nos_and_batches", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.187944", + "module": "Asset Lite", + "name": "Item-serial_nos_and_batches-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "make", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.159842", + "module": "Asset Lite", + "name": "Work_Order-make-fetch_if_empty", + "property": "fetch_if_empty", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "model", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.133691", + "module": "Asset Lite", + "name": "Work_Order-model-fetch_if_empty", + "property": "fetch_if_empty", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Dashboard", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.097480", + "module": "Asset Lite", + "name": "Dashboard-main-field_order", + "property": "field_order", + "property_type": "Data", + "row_name": null, + "value": "[\"dashboard_name\", \"is_default\", \"is_standard\", \"module\", \"charts\", \"chart_options\", \"cards\", \"department\"]" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "aseet_id", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.055232", + "module": "Asset Lite", + "name": "Work_Order-aseet_id-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "reorder_section", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:42.024914", + "module": "Asset Lite", + "name": "Item-reorder_section-collapsible", + "property": "collapsible", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Item Price", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.993010", + "module": "Asset Lite", + "name": "Item Price-main-quick_entry", + "property": "quick_entry", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Item Reorder", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "warehouse_group", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.961142", + "module": "Asset Lite", + "name": "Item Reorder-warehouse_group-default", + "property": "default", + "property_type": "Text", + "row_name": null, + "value": "All Warehouses - SA" + }, + { + "default_value": null, + "doc_type": "Item Reorder", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "warehouse", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.929747", + "module": "Asset Lite", + "name": "Item Reorder-warehouse-default", + "property": "default", + "property_type": "Text", + "row_name": null, + "value": "Stores - SA" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.897493", + "module": "Asset Lite", + "name": "Work_Order-asset-label", + "property": "label", + "property_type": "Data", + "row_name": null, + "value": "Asset ID" + }, + { + "default_value": null, + "doc_type": "Purchase Request", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.867636", + "module": "Asset Lite", + "name": "Purchase Request-main-field_order", + "property": "field_order", + "property_type": "Data", + "row_name": null, + "value": "[\"workflow_state\", \"pr_no\", \"date\", \"column_break_4\", \"issue\", \"asset\", \"asset_name\", \"data_8\", \"pr_table\", \"section_break_10\", \"priority\", \"normal\", \"urgent\", \"required_date\", \"column_break_14\", \"intended_use_of_material\", \"amended_from\"]" + }, + { + "default_value": null, + "doc_type": "Request for Quotation", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "supplier_response_section", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.839715", + "module": "Asset Lite", + "name": "Request for Quotation-supplier_response_section-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:frappe.user.has_role(\"Administrator\")" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_name", + "is_system_generated": 1, + "modified": "2025-07-04 13:38:32.550864", + "module": "Asset Lite", + "name": "Asset-asset_name-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "location", + "is_system_generated": 1, + "modified": "2025-07-04 13:38:32.609225", + "module": "Asset Lite", + "name": "Asset-location-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "status", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:40.670444", + "module": "Asset Lite", + "name": "Asset-status-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "custom_serial_number", + "is_system_generated": 1, + "modified": "2025-07-04 13:38:32.657329", + "module": "Asset Lite", + "name": "Asset-custom_serial_number-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "available_for_use_date", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.778483", + "module": "Asset Lite", + "name": "Asset-available_for_use_date-default", + "property": "default", + "property_type": "Text", + "row_name": null, + "value": "Today" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.743247", + "module": "Asset Lite", + "name": "Work_Order-asset-fetch_from", + "property": "fetch_from", + "property_type": "Small Text", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType Link", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.713634", + "module": "Asset Lite", + "name": "Work_Order-7fa1etpi4g-link_fieldname", + "property": "link_fieldname", + "property_type": "Data", + "row_name": "7fa1etpi4g", + "value": "asset" + }, + { + "default_value": null, + "doc_type": "Purchase Invoice", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.680222", + "module": "Asset Lite", + "name": "Purchase Invoice-naming_series-options", + "property": "options", + "property_type": "Text", + "row_name": null, + "value": "ACC-PINV-.YYYY.-\nACC-PINV-RET-.YYYY.-" + }, + { + "default_value": null, + "doc_type": "Stock Entry", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.652401", + "module": "Asset Lite", + "name": "Stock Entry-naming_series-options", + "property": "options", + "property_type": "Text", + "row_name": null, + "value": "MAT-STE-.YYYY.-" + }, + { + "default_value": null, + "doc_type": "Purchase Receipt", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.625233", + "module": "Asset Lite", + "name": "Purchase Receipt-naming_series-options", + "property": "options", + "property_type": "Text", + "row_name": null, + "value": "MAT-PRE-.YYYY.-\nMAT-PR-RET-.YYYY.-" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.597656", + "module": "Asset Lite", + "name": "Work_Order-main-allow_import", + "property": "allow_import", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "custom_down_time", + "is_system_generated": 1, + "modified": "2025-07-04 13:38:32.581995", + "module": "Asset Lite", + "name": "Asset-custom_down_time-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "custom_total_hours", + "is_system_generated": 1, + "modified": "2025-07-04 13:37:47.681113", + "module": "Asset Lite", + "name": "Asset-custom_total_hours-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Quality Feedback", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.568509", + "module": "Asset Lite", + "name": "Quality Feedback-main-field_order", + "property": "field_order", + "property_type": "Data", + "row_name": null, + "value": "[\"template\", \"custom_work_order\", \"cb_00\", \"document_type\", \"document_name\", \"sb_00\", \"parameters\"]" + }, + { + "default_value": null, + "doc_type": "Quality Feedback", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "document_name", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.538573", + "module": "Asset Lite", + "name": "Quality Feedback-document_name-fetch_from", + "property": "fetch_from", + "property_type": "Small Text", + "row_name": null, + "value": "custom_work_order.assigned_manager" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "depreciation_schedule_sb", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.508395", + "module": "Asset Lite", + "name": "Asset-depreciation_schedule_sb-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "calculate_depreciation" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "data_fjmf", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.478327", + "module": "Asset Lite", + "name": "Asset-data_fjmf-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance Log", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "due_date", + "is_system_generated": 1, + "modified": "2025-09-03 15:24:22.867951", + "module": "Asset Lite", + "name": "Asset Maintenance Log-due_date-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance Log", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "completion_date", + "is_system_generated": 1, + "modified": "2025-09-03 15:24:22.769936", + "module": "Asset Lite", + "name": "Asset Maintenance Log-completion_date-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Support Plans", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.399134", + "module": "Asset Lite", + "name": "Support Plans-main-field_order", + "property": "field_order", + "property_type": "Data", + "row_name": null, + "value": "[\"support_plan\", \"frequency\", \"max_downtime_hrs\", \"column_break_ayau\", \"asset\", \"starting_date\", \"penalty_factor\", \"custom_contractwarranty\", \"custom_contract_value\", \"section_break_zyxt\", \"warranty\", \"warranty_start_date\", \"warranty_end_date\", \"war_status\", \"column_break_agzy\", \"extended_warranty\", \"start\", \"end\", \"service_contract_section\", \"service_contract\", \"spare_parts\", \"spare_parts_labour\", \"labour\", \"ppm_only\", \"column_break_celd\", \"no\", \"start_date\", \"end_date\", \"service_contract_status\", \"vendor_details_section\", \"vendor\", \"section_break_pyrk\", \"asset_list\"]" + }, + { + "default_value": null, + "doc_type": "Support Plans", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "start_date", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.359557", + "module": "Asset Lite", + "name": "Support Plans-start_date-mandatory_depends_on", + "property": "mandatory_depends_on", + "property_type": "Data", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "custom_difference", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.319547", + "module": "Asset Lite", + "name": "Work_Order-custom_difference-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Feedback", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.276659", + "module": "Asset Lite", + "name": "Feedback-main-autoname", + "property": "autoname", + "property_type": "Data", + "row_name": null, + "value": "format:Feedback-{work_order}-{####}" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "serial_number", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.234963", + "module": "Asset Lite", + "name": "Work_Order-serial_number-mandatory_depends_on", + "property": "mandatory_depends_on", + "property_type": "Data", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Support Plans", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.152147", + "module": "Asset Lite", + "name": "Support Plans-main-allow_import", + "property": "allow_import", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance Team", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.111865", + "module": "Asset Lite", + "name": "Asset Maintenance Team-main-allow_import", + "property": "allow_import", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance Team", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.070817", + "module": "Asset Lite", + "name": "Asset Maintenance Team-main-field_order", + "property": "field_order", + "property_type": "Data", + "row_name": null, + "value": "[\"maintenance_team_name\", \"maintenance_manager\", \"maintenance_manager_name\", \"column_break_2\", \"company\", \"custom_expertise\", \"section_break_2\", \"maintenance_team_members\"]" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "need_spare_parts_purchase", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:41.029406", + "module": "Asset Lite", + "name": "Work_Order-need_spare_parts_purchase-default", + "property": "default", + "property_type": "Text", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "is_existing_asset", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:40.989409", + "module": "Asset Lite", + "name": "Asset-is_existing_asset-default", + "property": "default", + "property_type": "Text", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "item_code", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:40.948870", + "module": "Asset Lite", + "name": "Asset-item_code-read_only", + "property": "read_only", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "item_name", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:40.909391", + "module": "Asset Lite", + "name": "Asset-item_name-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_category", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:40.869599", + "module": "Asset Lite", + "name": "Asset-asset_category-fetch_if_empty", + "property": "fetch_if_empty", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "location", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:40.829726", + "module": "Asset Lite", + "name": "Asset-location-fetch_if_empty", + "property": "fetch_if_empty", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_name", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:40.791067", + "module": "Asset Lite", + "name": "Asset-asset_name-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_category", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:40.750889", + "module": "Asset Lite", + "name": "Asset-asset_category-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "calculate_depreciation", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:40.710795", + "module": "Asset Lite", + "name": "Asset-calculate_depreciation-default", + "property": "default", + "property_type": "Text", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:40.629112", + "module": "Asset Lite", + "name": "Asset-company-default", + "property": "default", + "property_type": "Text", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:40.589294", + "module": "Asset Lite", + "name": "Asset-company-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.660651", + "module": "Asset Lite", + "name": "Asset-company-label", + "property": "label", + "property_type": "Data", + "row_name": null, + "value": "Hospital Name" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "item_code", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.632724", + "module": "Asset Lite", + "name": "Asset-item_code-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "is_existing_asset", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.605572", + "module": "Asset Lite", + "name": "Asset-is_existing_asset-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "gross_purchase_amount", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.577094", + "module": "Asset Lite", + "name": "Asset-gross_purchase_amount-default", + "property": "default", + "property_type": "Text", + "row_name": null, + "value": "100" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "location", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.547450", + "module": "Asset Lite", + "name": "Asset-location-in_standard_filter", + "property": "in_standard_filter", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "location", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.518667", + "module": "Asset Lite", + "name": "Asset-location-in_global_search", + "property": "in_global_search", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "supplier", + "is_system_generated": 0, + "modified": "2025-07-04 13:38:32.678367", + "module": "Asset Lite", + "name": "Asset-supplier-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "supplier", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.458586", + "module": "Asset Lite", + "name": "Asset-supplier-in_standard_filter", + "property": "in_standard_filter", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "supplier", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.427943", + "module": "Asset Lite", + "name": "Asset-supplier-in_global_search", + "property": "in_global_search", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-07-04 13:38:32.595816", + "module": "Asset Lite", + "name": "Asset-company-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.369219", + "module": "Asset Lite", + "name": "Asset-company-in_standard_filter", + "property": "in_standard_filter", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.340563", + "module": "Asset Lite", + "name": "Asset-company-in_global_search", + "property": "in_global_search", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_category", + "is_system_generated": 0, + "modified": "2025-07-04 13:36:37.688508", + "module": "Asset Lite", + "name": "Asset-asset_category-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "recall_reference_number", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.281388", + "module": "Asset Lite", + "name": "Work_Order-recall_reference_number-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "manufacturer", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.237117", + "module": "Asset Lite", + "name": "Work_Order-manufacturer-fetch_from", + "property": "fetch_from", + "property_type": "Small Text", + "row_name": null, + "value": "asset.custom_manufacturer" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "priority", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.172829", + "module": "Asset Lite", + "name": "Work_Order-priority-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "date1", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.140820", + "module": "Asset Lite", + "name": "Work_Order-date1-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "date2", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.112365", + "module": "Asset Lite", + "name": "Work_Order-date2-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "date3", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.084110", + "module": "Asset Lite", + "name": "Work_Order-date3-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.054832", + "module": "Asset Lite", + "name": "Asset Maintenance-company-label", + "property": "label", + "property_type": "Data", + "row_name": null, + "value": "Hospital Name" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:37.022942", + "module": "Asset Lite", + "name": "Asset Maintenance-company-default", + "property": "default", + "property_type": "Text", + "row_name": null, + "value": "Al Jouf Hospital" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_category", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.992207", + "module": "Asset Lite", + "name": "Asset Maintenance-asset_category-read_only", + "property": "read_only", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.954747", + "module": "Asset Lite", + "name": "Asset Maintenance-company-fetch_from", + "property": "fetch_from", + "property_type": "Small Text", + "row_name": null, + "value": "asset_name.company" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.914173", + "module": "Asset Lite", + "name": "Asset Maintenance-company-fetch_if_empty", + "property": "fetch_if_empty", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "stock_consumption", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.884755", + "module": "Asset Lite", + "name": "Work_Order-stock_consumption-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "manufacturer", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.856410", + "module": "Asset Lite", + "name": "Work_Order-manufacturer-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "manufacturer", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.822300", + "module": "Asset Lite", + "name": "Work_Order-manufacturer-in_standard_filter", + "property": "in_standard_filter", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "manufacturer", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.786492", + "module": "Asset Lite", + "name": "Work_Order-manufacturer-in_global_search", + "property": "in_global_search", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.755918", + "module": "Asset Lite", + "name": "Work_Order-company-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.726300", + "module": "Asset Lite", + "name": "Work_Order-company-in_standard_filter", + "property": "in_standard_filter", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.695243", + "module": "Asset Lite", + "name": "Work_Order-company-in_global_search", + "property": "in_global_search", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "serial_number", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.664844", + "module": "Asset Lite", + "name": "Work_Order-serial_number-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "serial_number", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.635766", + "module": "Asset Lite", + "name": "Work_Order-serial_number-in_standard_filter", + "property": "in_standard_filter", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "serial_number", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.605673", + "module": "Asset Lite", + "name": "Work_Order-serial_number-in_global_search", + "property": "in_global_search", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "assigned_manager", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.575568", + "module": "Asset Lite", + "name": "Work_Order-assigned_manager-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "assigned_technician", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.546617", + "module": "Asset Lite", + "name": "Work_Order-assigned_technician-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "penalty", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.514433", + "module": "Asset Lite", + "name": "Work_Order-penalty-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Purchase Invoice", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.477225", + "module": "Asset Lite", + "name": "Purchase Invoice-main-field_order", + "property": "field_order", + "property_type": "Data", + "row_name": null, + "value": "[\"title\", \"naming_series\", \"supplier\", \"supplier_name\", \"tax_id\", \"company\", \"column_break_6\", \"posting_date\", \"posting_time\", \"set_posting_time\", \"due_date\", \"custom_work_order\", \"column_break1\", \"is_paid\", \"is_return\", \"return_against\", \"update_outstanding_for_self\", \"update_billed_amount_in_purchase_order\", \"update_billed_amount_in_purchase_receipt\", \"apply_tds\", \"custom_purchase_request\", \"custom_purchase_order\", \"tax_withholding_category\", \"amended_from\", \"supplier_invoice_details\", \"bill_no\", \"column_break_15\", \"bill_date\", \"accounting_dimensions_section\", \"cost_center\", \"dimension_col_break\", \"project\", \"currency_and_price_list\", \"currency\", \"conversion_rate\", \"use_transaction_date_exchange_rate\", \"column_break2\", \"buying_price_list\", \"price_list_currency\", \"plc_conversion_rate\", \"ignore_pricing_rule\", \"sec_warehouse\", \"scan_barcode\", \"col_break_warehouse\", \"update_stock\", \"set_warehouse\", \"set_from_warehouse\", \"is_subcontracted\", \"rejected_warehouse\", \"supplier_warehouse\", \"items_section\", \"items\", \"section_break_26\", \"total_qty\", \"total_net_weight\", \"column_break_50\", \"base_total\", \"base_net_total\", \"column_break_28\", \"total\", \"net_total\", \"tax_withholding_net_total\", \"base_tax_withholding_net_total\", \"taxes_section\", \"tax_category\", \"taxes_and_charges\", \"column_break_58\", \"shipping_rule\", \"column_break_49\", \"incoterm\", \"named_place\", \"section_break_51\", \"taxes\", \"totals\", \"base_taxes_and_charges_added\", \"base_taxes_and_charges_deducted\", \"base_total_taxes_and_charges\", \"column_break_40\", \"taxes_and_charges_added\", \"taxes_and_charges_deducted\", \"total_taxes_and_charges\", \"section_break_49\", \"base_grand_total\", \"base_rounding_adjustment\", \"base_rounded_total\", \"base_in_words\", \"column_break8\", \"grand_total\", \"rounding_adjustment\", \"use_company_roundoff_cost_center\", \"rounded_total\", \"in_words\", \"total_advance\", \"outstanding_amount\", \"disable_rounded_total\", \"section_break_44\", \"apply_discount_on\", \"base_discount_amount\", \"column_break_46\", \"additional_discount_percentage\", \"discount_amount\", \"tax_withheld_vouchers_section\", \"tax_withheld_vouchers\", \"sec_tax_breakup\", \"other_charges_calculation\", \"pricing_rule_details\", \"pricing_rules\", \"raw_materials_supplied\", \"supplied_items\", \"payments_tab\", \"payments_section\", \"mode_of_payment\", \"base_paid_amount\", \"clearance_date\", \"col_br_payments\", \"cash_bank_account\", \"paid_amount\", \"advances_section\", \"allocate_advances_automatically\", \"only_include_allocated_payments\", \"get_advances\", \"advances\", \"advance_tax\", \"write_off\", \"write_off_amount\", \"base_write_off_amount\", \"column_break_61\", \"write_off_account\", \"write_off_cost_center\", \"address_and_contact_tab\", \"section_addresses\", \"supplier_address\", \"address_display\", \"col_break_address\", \"contact_person\", \"contact_display\", \"contact_mobile\", \"contact_email\", \"company_shipping_address_section\", \"shipping_address\", \"column_break_126\", \"shipping_address_display\", \"company_billing_address_section\", \"billing_address\", \"column_break_130\", \"billing_address_display\", \"terms_tab\", \"payment_schedule_section\", \"payment_terms_template\", \"ignore_default_payment_terms_template\", \"payment_schedule\", \"terms_section_break\", \"tc_name\", \"terms\", \"more_info_tab\", \"status_section\", \"status\", \"column_break_177\", \"per_received\", \"accounting_details_section\", \"credit_to\", \"party_account_currency\", \"is_opening\", \"against_expense_account\", \"column_break_63\", \"unrealized_profit_loss_account\", \"subscription_section\", \"subscription\", \"auto_repeat\", \"update_auto_repeat_reference\", \"column_break_114\", \"from_date\", \"to_date\", \"printing_settings\", \"letter_head\", \"group_same_items\", \"column_break_112\", \"select_print_heading\", \"language\", \"sb_14\", \"on_hold\", \"release_date\", \"cb_17\", \"hold_comment\", \"additional_info_section\", \"is_internal_supplier\", \"represents_company\", \"supplier_group\", \"column_break_147\", \"inter_company_invoice_reference\", \"is_old_subcontracting_flow\", \"remarks\", \"connections_tab\"]" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "supplier", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.445418", + "module": "Asset Lite", + "name": "Work_Order-supplier-fetch_from", + "property": "fetch_from", + "property_type": "Small Text", + "row_name": null, + "value": "asset.supplier" + }, + { + "default_value": null, + "doc_type": "Asset Repair Consumed Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "warehouse", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.408275", + "module": "Asset Lite", + "name": "Asset Repair Consumed Item-warehouse-default", + "property": "default", + "property_type": "Text", + "row_name": null, + "value": "Stores - AJH" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "warranty_and_service_details_section", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.379764", + "module": "Asset Lite", + "name": "Work_Order-warranty_and_service_details_section-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "purchase_invoice", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.350966", + "module": "Asset Lite", + "name": "Work_Order-purchase_invoice-mandatory_depends_on", + "property": "mandatory_depends_on", + "property_type": "Data", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Material Request Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "rate", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.322082", + "module": "Asset Lite", + "name": "Material Request Item-rate-fetch_from", + "property": "fetch_from", + "property_type": "Small Text", + "row_name": null, + "value": "item_code.valuation_rate" + }, + { + "default_value": null, + "doc_type": "Material Request Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "rate", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.290622", + "module": "Asset Lite", + "name": "Material Request Item-rate-fetch_if_empty", + "property": "fetch_if_empty", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "stock_items", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.228399", + "module": "Asset Lite", + "name": "Work_Order-stock_items-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.stock_consumption" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_category", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.200288", + "module": "Asset Lite", + "name": "Asset Maintenance-asset_category-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_category", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.170577", + "module": "Asset Lite", + "name": "Asset Maintenance-asset_category-fetch_if_empty", + "property": "fetch_if_empty", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:36.025097", + "module": "Asset Lite", + "name": "Work_Order-asset-in_standard_filter", + "property": "in_standard_filter", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:35.994473", + "module": "Asset Lite", + "name": "Work_Order-asset-in_global_search", + "property": "in_global_search", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 1, + "modified": "2025-04-22 14:45:35.965877", + "module": "Asset Lite", + "name": "Work_Order-main-default_print_format", + "property": "default_print_format", + "property_type": "Data", + "row_name": null, + "value": "Work_Order PF" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:35.939395", + "module": "Asset Lite", + "name": "Asset Maintenance-company-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:35.801041", + "module": "Asset Lite", + "name": "Asset Maintenance-company-in_standard_filter", + "property": "in_standard_filter", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:35.632110", + "module": "Asset Lite", + "name": "Asset Maintenance-company-in_global_search", + "property": "in_global_search", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Server Script", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "reference_doctype", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:33.983924", + "module": "Asset Lite", + "name": "Server Script-reference_doctype-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Server Script", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "script_type", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:34.050003", + "module": "Asset Lite", + "name": "Server Script-script_type-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Server Script", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "doctype_event", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:34.015453", + "module": "Asset Lite", + "name": "Server Script-doctype_event-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "custom_total_spare_parts_amount", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:35.439439", + "module": "Asset Lite", + "name": "Asset-custom_total_spare_parts_amount-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "gross_purchase_amount", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:35.408845", + "module": "Asset Lite", + "name": "Asset-gross_purchase_amount-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_owner_company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:35.339575", + "module": "Asset Lite", + "name": "Asset-asset_owner_company-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "stock_consumption", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:35.276389", + "module": "Asset Lite", + "name": "Work_Order-stock_consumption-permlevel", + "property": "permlevel", + "property_type": "Int", + "row_name": null, + "value": "2" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "need_procurement", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:35.242401", + "module": "Asset Lite", + "name": "Work_Order-need_procurement-permlevel", + "property": "permlevel", + "property_type": "Int", + "row_name": null, + "value": "2" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_owner_company", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:35.211209", + "module": "Asset Lite", + "name": "Asset-asset_owner_company-label", + "property": "label", + "property_type": "Data", + "row_name": null, + "value": "Owner" + }, + { + "default_value": null, + "doc_type": "Asset Repair Consumed Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:35.111601", + "module": "Asset Lite", + "name": "Asset Repair Consumed Item-main-field_order", + "property": "field_order", + "property_type": "Data", + "row_name": null, + "value": "[\"item_code\", \"warehouse\", \"valuation_rate\", \"custom_available_stock\", \"consumed_quantity\", \"total_value\", \"serial_no\", \"serial_and_batch_bundle\"]" + }, + { + "default_value": null, + "doc_type": "Request for Quotation", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 1, + "modified": "2025-04-22 14:45:35.048853", + "module": "Asset Lite", + "name": "Request for Quotation-main-default_print_format", + "property": "default_print_format", + "property_type": "Data", + "row_name": null, + "value": "RFQ" + }, + { + "default_value": null, + "doc_type": "Supplier Quotation Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "manufacturer", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:35.015642", + "module": "Asset Lite", + "name": "Supplier Quotation Item-manufacturer-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Supplier Quotation Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.976740", + "module": "Asset Lite", + "name": "Supplier Quotation Item-main-field_order", + "property": "field_order", + "property_type": "Data", + "row_name": null, + "value": "[\"item_code\", \"supplier_part_no\", \"item_name\", \"column_break_3\", \"lead_time_days\", \"expected_delivery_date\", \"is_free_item\", \"custom_manufacturer1\", \"section_break_5\", \"description\", \"item_group\", \"brand\", \"col_break1\", \"image\", \"image_view\", \"quantity_and_rate\", \"qty\", \"stock_uom\", \"col_break2\", \"uom\", \"conversion_factor\", \"stock_qty\", \"sec_break_price_list\", \"price_list_rate\", \"discount_percentage\", \"discount_amount\", \"col_break_price_list\", \"base_price_list_rate\", \"sec_break1\", \"rate\", \"amount\", \"item_tax_template\", \"col_break3\", \"base_rate\", \"base_amount\", \"pricing_rules\", \"section_break_24\", \"net_rate\", \"net_amount\", \"column_break_27\", \"base_net_rate\", \"base_net_amount\", \"item_weight_details\", \"weight_per_unit\", \"total_weight\", \"column_break_23\", \"weight_uom\", \"warehouse_and_reference\", \"warehouse\", \"prevdoc_doctype\", \"material_request\", \"sales_order\", \"request_for_quotation\", \"col_break4\", \"material_request_item\", \"request_for_quotation_item\", \"item_tax_rate\", \"manufacture_details\", \"manufacturer\", \"column_break_15\", \"manufacturer_part_no\", \"ad_sec_break\", \"cost_center\", \"dimension_col_break\", \"project\", \"section_break_44\", \"page_break\"]" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance Log", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "custom_hospital_name", + "is_system_generated": 1, + "modified": "2025-09-03 15:24:22.891646", + "module": "Asset Lite", + "name": "Asset Maintenance Log-custom_hospital_name-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.829391", + "module": "Asset Lite", + "name": "Asset Maintenance-main-field_order", + "property": "field_order", + "property_type": "Data", + "row_name": null, + "value": "[\"company\", \"asset_name\", \"custom_asset_type\", \"asset_category\", \"custom_serial_number\", \"column_break_3\", \"custom_type_of_maintenance\", \"custom_asset_name\", \"custom_department\", \"item_code\", \"item_name\", \"section_break_6\", \"maintenance_team\", \"column_break_9\", \"maintenance_manager\", \"maintenance_manager_name\", \"custom_warranty_details\", \"custom_warranty\", \"custom_warranty_status\", \"custom_warranty_start_date\", \"custom_warranty_end_date\", \"custom_coverage_\", \"custom_service_coverage_table\", \"custom_service_details\", \"custom_support_plan\", \"custom_site_contractor\", \"custom_subcontractor\", \"custom_service_coverage\", \"custom_service_contract\", \"custom_service_contract_status\", \"custom_service_agreement\", \"custom_abc\", \"custom_frequency\", \"custom_start_date\", \"custom_end_date\", \"custom_starting_date\", \"custom_total_amount\", \"custom_no_of_pms\", \"custom_price_per_pm\", \"section_break_8\", \"asset_maintenance_tasks\"]" + }, + { + "default_value": null, + "doc_type": "Stock Entry", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.800351", + "module": "Asset Lite", + "name": "Stock Entry-main-field_order", + "property": "field_order", + "property_type": "Data", + "row_name": null, + "value": "[\"stock_entry_details_tab\", \"naming_series\", \"stock_entry_type\", \"outgoing_stock_entry\", \"purpose\", \"add_to_transit\", \"work_order\", \"purchase_order\", \"subcontracting_order\", \"delivery_note_no\", \"sales_invoice_no\", \"pick_list\", \"purchase_receipt_no\", \"asset_repair\", \"custom_work_orders\", \"col2\", \"company\", \"posting_date\", \"posting_time\", \"column_break_eaoa\", \"set_posting_time\", \"inspection_required\", \"apply_putaway_rule\", \"bom_info_section\", \"from_bom\", \"use_multi_level_bom\", \"bom_no\", \"cb1\", \"fg_completed_qty\", \"get_items\", \"section_break_7qsm\", \"process_loss_percentage\", \"column_break_e92r\", \"process_loss_qty\", \"section_break_jwgn\", \"from_warehouse\", \"source_warehouse_address\", \"source_address_display\", \"cb0\", \"to_warehouse\", \"target_warehouse_address\", \"target_address_display\", \"sb0\", \"scan_barcode\", \"items_section\", \"items\", \"get_stock_and_rate\", \"section_break_19\", \"total_outgoing_value\", \"column_break_22\", \"total_incoming_value\", \"value_difference\", \"additional_costs_section\", \"additional_costs\", \"total_additional_costs\", \"supplier_info_tab\", \"contact_section\", \"supplier\", \"supplier_name\", \"supplier_address\", \"address_display\", \"accounting_dimensions_section\", \"project\", \"other_info_tab\", \"printing_settings\", \"select_print_heading\", \"print_settings_col_break\", \"letter_head\", \"more_info\", \"is_opening\", \"remarks\", \"col5\", \"per_transferred\", \"total_amount\", \"job_card\", \"amended_from\", \"credit_note\", \"is_return\", \"tab_connections\"]" + }, + { + "default_value": null, + "doc_type": "Stock Entry", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "total_amount", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.765640", + "module": "Asset Lite", + "name": "Stock Entry-total_amount-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Stock Entry", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "outgoing_stock_entry", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.732526", + "module": "Asset Lite", + "name": "Stock Entry-outgoing_stock_entry-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval: doc.purpose == 'Material Transfer'" + }, + { + "default_value": null, + "doc_type": "Stock Entry", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "delivery_note_no", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.704127", + "module": "Asset Lite", + "name": "Stock Entry-delivery_note_no-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval: doc.purpose==\"Sales Return\"" + }, + { + "default_value": null, + "doc_type": "Stock Entry", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "supplier", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.674685", + "module": "Asset Lite", + "name": "Stock Entry-supplier-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": " eval: erpnext.stock.is_subcontracting_or_return_transfer(doc) " + }, + { + "default_value": null, + "doc_type": "Stock Entry", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "supplier_name", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.633022", + "module": "Asset Lite", + "name": "Stock Entry-supplier_name-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": " eval: erpnext.stock.is_subcontracting_or_return_transfer(doc) " + }, + { + "default_value": null, + "doc_type": "Stock Entry", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "supplier_address", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.593977", + "module": "Asset Lite", + "name": "Stock Entry-supplier_address-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": " eval: erpnext.stock.is_subcontracting_or_return_transfer(doc) " + }, + { + "default_value": null, + "doc_type": "Stock Entry", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "contact_section", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.564227", + "module": "Asset Lite", + "name": "Stock Entry-contact_section-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": " eval: erpnext.stock.is_subcontracting_or_return_transfer(doc) " + }, + { + "default_value": null, + "doc_type": "Stock Entry", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "purchase_order", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.532979", + "module": "Asset Lite", + "name": "Stock Entry-purchase_order-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": " eval: erpnext.stock.is_subcontracting_or_return_transfer(doc) " + }, + { + "default_value": null, + "doc_type": "Stock Entry", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "subcontracting_order", + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.504401", + "module": "Asset Lite", + "name": "Stock Entry-subcontracting_order-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": " eval: erpnext.stock.is_subcontracting_or_return_transfer(doc) " + }, + { + "default_value": null, + "doc_type": "Material Request", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.475790", + "module": "Asset Lite", + "name": "Material Request-main-field_order", + "property": "field_order", + "property_type": "Data", + "row_name": null, + "value": "[\"custom_work_order\", \"workflow_state\", \"type_section\", \"naming_series\", \"title\", \"material_request_type\", \"customer\", \"company\", \"column_break_2\", \"transaction_date\", \"schedule_date\", \"amended_from\", \"custom_asset_id\", \"warehouse_section\", \"scan_barcode\", \"column_break_13\", \"set_from_warehouse\", \"column_break5\", \"set_warehouse\", \"items_section\", \"items\", \"terms_tab\", \"terms_section_break\", \"tc_name\", \"terms\", \"more_info_tab\", \"status_section\", \"status\", \"per_ordered\", \"column_break2\", \"transfer_status\", \"per_received\", \"printing_details\", \"letter_head\", \"column_break_31\", \"select_print_heading\", \"reference\", \"job_card\", \"column_break_35\", \"work_order\", \"connections_tab\"]" + }, + { + "default_value": null, + "doc_type": "Request for Quotation", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.448493", + "module": "Asset Lite", + "name": "Request for Quotation-main-field_order", + "property": "field_order", + "property_type": "Data", + "row_name": null, + "value": "[\"naming_series\", \"company\", \"billing_address\", \"billing_address_display\", \"vendor\", \"custom_manufacturer\", \"custom_model\", \"column_break1\", \"transaction_date\", \"schedule_date\", \"status\", \"amended_from\", \"custom_asset_id\", \"custom_department\", \"custom_name\", \"suppliers_section\", \"suppliers\", \"items_section\", \"items\", \"supplier_response_section\", \"email_template\", \"preview\", \"col_break_email_1\", \"html_llwp\", \"send_attached_files\", \"send_document_print\", \"sec_break_email_2\", \"message_for_supplier\", \"terms_section_break\", \"incoterm\", \"named_place\", \"tc_name\", \"terms\", \"printing_settings\", \"select_print_heading\", \"letter_head\", \"more_info\", \"opportunity\"]" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-04-22 14:45:34.387773", + "module": "Asset Lite", + "name": "Asset-main-make_attachments_public", + "property": "make_attachments_public", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Client Script", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "view", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:34.144264", + "module": "Asset Lite", + "name": "Client Script-view-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Client Script", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "dt", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:34.110287", + "module": "Asset Lite", + "name": "Client Script-dt-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Client Script", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "module", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:34.078830", + "module": "Asset Lite", + "name": "Client Script-module-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Server Script", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "module", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:33.945518", + "module": "Asset Lite", + "name": "Server Script-module-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Custom Field", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "reqd", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:33.844836", + "module": "Asset Lite", + "name": "Custom Field-reqd-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Custom Field", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "options", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:33.724910", + "module": "Asset Lite", + "name": "Custom Field-options-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Custom Field", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "dt", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:33.380356", + "module": "Asset Lite", + "name": "Custom Field-dt-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Custom Field", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "fieldtype", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:33.262121", + "module": "Asset Lite", + "name": "Custom Field-fieldtype-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Custom Field", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "fieldname", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:33.155806", + "module": "Asset Lite", + "name": "Custom Field-fieldname-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Custom Field", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "module", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:33.033924", + "module": "Asset Lite", + "name": "Custom Field-module-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Property Setter", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "doctype_or_field", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:32.951546", + "module": "Asset Lite", + "name": "Property Setter-doctype_or_field-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Property Setter", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "value", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:32.860200", + "module": "Asset Lite", + "name": "Property Setter-value-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Property Setter", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "module", + "is_system_generated": 1, + "modified": "2025-04-22 14:45:32.454054", + "module": "Asset Lite", + "name": "Property Setter-module-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "defective_spare_parts_section", + "is_system_generated": 0, + "modified": "2025-05-22 11:51:57.819122", + "module": "Asset Lite", + "name": "Work_Order-defective_spare_parts_section-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "defective_spare_parts_section", + "is_system_generated": 0, + "modified": "2025-05-22 11:51:57.785437", + "module": "Asset Lite", + "name": "Work_Order-defective_spare_parts_section-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_name", + "is_system_generated": 0, + "modified": "2025-05-22 11:51:57.008325", + "module": "Asset Lite", + "name": "Work_Order-asset_name-fetch_if_empty", + "property": "fetch_if_empty", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "department", + "is_system_generated": 0, + "modified": "2025-05-22 11:51:56.970988", + "module": "Asset Lite", + "name": "Work_Order-department-fetch_if_empty", + "property": "fetch_if_empty", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType Link", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-05-22 11:51:56.940085", + "module": "Asset Lite", + "name": "Asset-522s7jrbgm-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": "522s7jrbgm", + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType Link", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-05-22 11:51:56.904960", + "module": "Asset Lite", + "name": "Asset-522s15ggc8-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": "522s15ggc8", + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType Link", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-05-22 11:51:56.873106", + "module": "Asset Lite", + "name": "Asset-522sbn5ru5-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": "522sbn5ru5", + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType Link", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-05-22 11:51:56.840749", + "module": "Asset Lite", + "name": "Asset-522sg890kc-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": "522sg890kc", + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType Link", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-05-22 11:51:56.806800", + "module": "Asset Lite", + "name": "Asset-522s0es344-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": "522s0es344", + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset", + "is_system_generated": 0, + "modified": "2025-05-22 11:51:56.702450", + "module": "Asset Lite", + "name": "Work_Order-asset-fetch_if_empty", + "property": "fetch_if_empty", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "User", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "desk_theme", + "is_system_generated": 0, + "modified": "2025-05-22 14:33:28.577740", + "module": "Asset Lite", + "name": "User-desk_theme-options", + "property": "options", + "property_type": null, + "row_name": null, + "value": "Light\nDark\nAutomatic\nModern_ui_theme" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-05-22 14:09:07.502818", + "module": "Asset Lite", + "name": "Asset-main-image_field", + "property": "image_field", + "property_type": "Data", + "row_name": null, + "value": "custom_attach_image" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "workflow_state", + "is_system_generated": 0, + "modified": "2025-05-23 14:26:28.648143", + "module": "Asset Lite", + "name": "Work_Order-workflow_state-options", + "property": "options", + "property_type": "Link", + "row_name": null, + "value": "Workflow State" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:40.509839", + "module": "Asset Lite", + "name": "Work_Order-asset-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "manufacturer", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:40.416723", + "module": "Asset Lite", + "name": "Work_Order-manufacturer-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_name", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:40.308753", + "module": "Asset Lite", + "name": "Work_Order-asset_name-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "supplier", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:40.240550", + "module": "Asset Lite", + "name": "Work_Order-supplier-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "total_hours_spent", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:40.147484", + "module": "Asset Lite", + "name": "Work_Order-total_hours_spent-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "job_completed", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:40.041448", + "module": "Asset Lite", + "name": "Work_Order-job_completed-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "feedback_rating", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:39.956179", + "module": "Asset Lite", + "name": "Work_Order-feedback_rating-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "stock_consumption_details_section", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:39.776830", + "module": "Asset Lite", + "name": "Work_Order-stock_consumption_details_section-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "need_procurement", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:39.630938", + "module": "Asset Lite", + "name": "Work_Order-need_procurement-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "repair_cost", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:39.463868", + "module": "Asset Lite", + "name": "Work_Order-repair_cost-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "accounting_details", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:39.347576", + "module": "Asset Lite", + "name": "Work_Order-accounting_details-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "total_main_hour_at_site", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:39.278342", + "module": "Asset Lite", + "name": "Work_Order-total_main_hour_at_site-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "total_travel_hour", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:39.206839", + "module": "Asset Lite", + "name": "Work_Order-total_travel_hour-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "total_hours", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:39.145602", + "module": "Asset Lite", + "name": "Work_Order-total_hours-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "comments_section", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:39.040523", + "module": "Asset Lite", + "name": "Work_Order-comments_section-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_type", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:38.982999", + "module": "Asset Lite", + "name": "Work_Order-asset_type-reqd", + "property": "reqd", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:38.911059", + "module": "Asset Lite", + "name": "Work_Order-company-read_only_depends_on", + "property": "read_only_depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Non Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "first_responded_on", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:38.828044", + "module": "Asset Lite", + "name": "Work_Order-first_responded_on-permlevel", + "property": "permlevel", + "property_type": "Int", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "gross_purchase_amount", + "is_system_generated": 0, + "modified": "2025-06-27 17:20:00.590331", + "module": "Asset Lite", + "name": "Asset-gross_purchase_amount-allow_on_submit", + "property": "allow_on_submit", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset_type", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:38.763524", + "module": "Asset Lite", + "name": "Work_Order-asset_type-default", + "property": "default", + "property_type": "Text", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "supplier", + "is_system_generated": 0, + "modified": "2025-07-30 20:42:38.576619", + "module": "Asset Lite", + "name": "Work_Order-supplier-fetch_if_empty", + "property": "fetch_if_empty", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "custom_device_status", + "is_system_generated": 1, + "modified": "2025-07-30 20:43:04.767005", + "module": "Asset Lite", + "name": "Asset-custom_device_status-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "custom_manufacturer", + "is_system_generated": 1, + "modified": "2025-07-30 20:43:04.710757", + "module": "Asset Lite", + "name": "Asset-custom_manufacturer-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "custom_model", + "is_system_generated": 1, + "modified": "2025-07-30 20:43:04.470534", + "module": "Asset Lite", + "name": "Asset-custom_model-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "purchase_date", + "is_system_generated": 0, + "modified": "2025-08-08 17:10:50.755245", + "module": "Asset Lite", + "name": "Asset-purchase_date-reqd", + "property": "reqd", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-08-08 17:25:29.999119", + "module": "Asset Lite", + "name": "Work_Order-company-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"" + }, + { + "default_value": null, + "doc_type": "Report", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "is_standard", + "is_system_generated": 1, + "modified": "2025-08-08 17:26:19.273313", + "module": "Asset Lite", + "name": "Report-is_standard-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Report", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "ref_doctype", + "is_system_generated": 1, + "modified": "2025-08-08 17:26:37.858075", + "module": "Asset Lite", + "name": "Report-ref_doctype-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Report", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "module", + "is_system_generated": 1, + "modified": "2025-08-08 17:26:53.361395", + "module": "Asset Lite", + "name": "Report-module-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "serial_number", + "is_system_generated": 0, + "modified": "2025-08-08 17:27:18.533320", + "module": "Asset Lite", + "name": "Work_Order-serial_number-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type !== \"Non Biomedical\"" + }, + { + "default_value": null, + "doc_type": "Purchase Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 1, + "modified": "2025-08-08 17:31:01.818262", + "module": "Asset Lite", + "name": "Purchase Order-naming_series-options", + "property": "options", + "property_type": "Text", + "row_name": null, + "value": "PUR-ORD-.YYYY.-" + }, + { + "default_value": null, + "doc_type": "Material Request", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 1, + "modified": "2025-08-08 17:31:47.114825", + "module": "Asset Lite", + "name": "Material Request-naming_series-options", + "property": "options", + "property_type": "Text", + "row_name": null, + "value": "MAT-MR-.YYYY.-" + }, + { + "default_value": "ASM-RC1-.YYYY.-", + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "naming_series", + "is_system_generated": 1, + "modified": "2026-07-07 20:22:36.850258", + "module": "Asset Lite", + "name": "Asset-naming_series-options", + "property": "options", + "property_type": "Text", + "row_name": null, + "value": "ASM-RC1-.YYYY.-\nACC-ASS-.YYYY.-" + }, + { + "default_value": null, + "doc_type": "Asset Maintenance Log", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 1, + "modified": "2025-08-08 17:32:57.720942", + "module": "Asset Lite", + "name": "Asset Maintenance Log-main-default_print_format", + "property": "default_print_format", + "property_type": "Data", + "row_name": null, + "value": "PPM Assets" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "site_name", + "is_system_generated": 0, + "modified": "2025-08-08 17:33:53.907942", + "module": "Asset Lite", + "name": "Work_Order-site_name-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Non Biomedical\" || (doc.company && doc.asset_type == \"Biomedical\" && doc.company.startsWith(\"Mobile\"))" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "asset", + "is_system_generated": 0, + "modified": "2025-08-12 13:32:19.084870", + "module": "Asset Lite", + "name": "Work_Order-asset-mandatory_depends_on", + "property": "mandatory_depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "section_break_okba", + "is_system_generated": 0, + "modified": "2025-08-18 19:14:22.031954", + "module": "Asset Lite", + "name": "Work_Order-section_break_okba-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "site_name", + "is_system_generated": 0, + "modified": "2025-08-26 15:35:00.304525", + "module": "Asset Lite", + "name": "Work_Order-site_name-fetch_if_empty", + "property": "fetch_if_empty", + "property_type": "Check", + "row_name": null, + "value": "0" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "need_procurement", + "is_system_generated": 0, + "modified": "2025-08-26 15:37:06.958375", + "module": "Asset Lite", + "name": "Work_Order-need_procurement-hidden", + "property": "hidden", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "work_order_type", + "is_system_generated": 0, + "modified": "2025-08-26 15:37:46.688551", + "module": "Asset Lite", + "name": "Work_Order-work_order_type-default", + "property": "default", + "property_type": "Text", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "department", + "is_system_generated": 0, + "modified": "2025-08-26 15:38:03.634510", + "module": "Asset Lite", + "name": "Work_Order-department-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:!doc.site_name\r\n" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "site_name", + "is_system_generated": 0, + "modified": "2025-08-26 15:38:19.027904", + "module": "Asset Lite", + "name": "Work_Order-site_name-fetch_from", + "property": "fetch_from", + "property_type": "Small Text", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-08-26 15:38:38.136101", + "module": "Asset Lite", + "name": "Work_Order-company-fetch_from", + "property": "fetch_from", + "property_type": "Small Text", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Asset", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "department", + "is_system_generated": 0, + "modified": "2025-08-26 15:39:01.056716", + "module": "Asset Lite", + "name": "Asset-department-in_standard_filter", + "property": "in_standard_filter", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "company", + "is_system_generated": 0, + "modified": "2025-08-26 15:39:16.284466", + "module": "Asset Lite", + "name": "Work_Order-company-allow_on_submit", + "property": "allow_on_submit", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Mobile Team Site", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "site_name", + "is_system_generated": 1, + "modified": "2025-08-26 15:39:35.442266", + "module": "Asset Lite", + "name": "Mobile Team Site-site_name-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Mobile Team Site", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "mobile_team", + "is_system_generated": 1, + "modified": "2025-08-26 15:39:57.521679", + "module": "Asset Lite", + "name": "Mobile Team Site-mobile_team-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Mobile Team Site", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "city", + "is_system_generated": 1, + "modified": "2025-08-26 15:40:15.902221", + "module": "Asset Lite", + "name": "Mobile Team Site-city-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "bio_med_dept", + "is_system_generated": 0, + "modified": "2025-08-26 15:40:51.605039", + "module": "Asset Lite", + "name": "Work_Order-bio_med_dept-depends_on", + "property": "depends_on", + "property_type": "Data", + "row_name": null, + "value": "eval:doc.asset_type == \"Biomedical\"" + }, + { + "default_value": null, + "doc_type": "Modality", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-09-08 15:00:18.001360", + "module": "Asset Lite", + "name": "Modality-main-allow_import", + "property": "allow_import", + "property_type": "Check", + "row_name": null, + "value": "1" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocType", + "field_name": null, + "is_system_generated": 0, + "modified": "2025-09-10 16:01:38.286387", + "module": "Asset Lite", + "name": "Work_Order-main-title_field", + "property": "title_field", + "property_type": "Data", + "row_name": null, + "value": "" + }, + { + "default_value": null, + "doc_type": "Work_Order", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "work_order_type", + "is_system_generated": 0, + "modified": "2025-11-12 13:59:47.688872", + "module": "Asset Lite", + "name": "Work_Order-work_order_type-allow_on_submit", + "property": "allow_on_submit", + "property_type": "Check", + "row_name": null, + "value": "1" + } +] \ No newline at end of file diff --git a/asset_lite/fixtures/report.json b/asset_lite/fixtures/report.json new file mode 100644 index 0000000..1215536 --- /dev/null +++ b/asset_lite/fixtures/report.json @@ -0,0 +1,4195 @@ +[ + { + "add_total_row": 1, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "Yes", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2024-12-24 19:11:37.533049", + "module": "Asset Lite", + "name": "Test SUP vs Score Report", + "prepared_report": 0, + "query": "SELECT\n supplier,\n supplier_score\nFROM\n `tabSupplier Scorecard`\nORDER BY\n supplier_score DESC;", + "ref_doctype": "Supplier Scorecard", + "reference_report": "SUP Scorecard", + "report_name": "Test SUP vs Score Report", + "report_script": null, + "report_type": "Query Report", + "roles": [ + { + "parent": "Test SUP vs Score Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "", + "modified": "2025-04-22 14:49:10.967185", + "module": "Asset Lite", + "name": "Asset Maintenance Frequency", + "prepared_report": 0, + "query": "select\r\n custom_asset_names as Item,\r\n SUM(CASE WHEN maintenance_status = 'Planned' THEN 1 ELSE 0 END) AS Planned,\r\n SUM(CASE WHEN maintenance_status = 'Completed' THEN 1 ELSE 0 END) as \"Completed\",\r\n SUM(CASE WHEN maintenance_status = 'Cancelled' THEN 1 ELSE 0 END) as \"Cancelled\",\r\n SUM(CASE WHEN maintenance_status = 'Overdue' THEN 1 ELSE 0 END) as \"Overdue\",\r\n COUNT(custom_asset_names) as \"Total Count\"\r\nfrom \r\n `tabAsset Maintenance Log`\r\ngroup by\r\n custom_asset_names", + "ref_doctype": "Asset Maintenance Log", + "reference_report": null, + "report_name": "Asset Maintenance Frequency", + "report_script": "", + "report_type": "Query Report", + "roles": [ + { + "parent": "Asset Maintenance Frequency", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Maintenance Frequency", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset Maintenance Frequency", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "", + "modified": "2025-04-22 14:49:12.617127", + "module": "Asset Lite", + "name": "Asset Maintenance Assignees Status Count", + "prepared_report": 0, + "query": "select item_name as \"Item Name\", maintenance_status as \"Maintenance Status\", assign_to_name as \"Assigned To\", sum(case when due_date = completion_date AND maintenance_status ='Completed' then 1 else 0 end) as \"Completed On Time\" ,sum(case when completion_date < due_date and maintenance_status = 'Completed' then 1 else 0 end) as \"Completed Within Time\",sum(case when completion_date >due_date then 1 else 0 end ) as \"Delay In Completion\",sum(case when maintenance_status = 'Planned' AND completion_date IS null AND due_date > current_date() then 1 else 0 end ) as \"Pending\",sum(case when maintenance_status = 'Planned' AND completion_date IS null AND due_date < current_date() then 1 else 0 end) as \"Overdue\" , sum(case when maintenance_status = 'Cancelled' then 1 else 0 end) as \"Cancelled\" from `tabAsset Maintenance Log` group by assign_to_name", + "ref_doctype": "Asset Maintenance Log", + "reference_report": null, + "report_name": "Asset Maintenance Assignees Status Count", + "report_script": "", + "report_type": "Query Report", + "roles": [ + { + "parent": "Asset Maintenance Assignees Status Count", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "Asset Maintenance Assignees Status Count", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Maintenance Assignees Status Count", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "", + "modified": "2025-04-22 14:49:12.387500", + "module": "Asset Lite", + "name": "Asset wise Count", + "prepared_report": 0, + "query": "select item_name as \"Item Name\", maintenance_status as \"Maintenance Status\", sum(case when due_date = completion_date AND maintenance_status ='Completed' then 1 else 0 end) as \"Completed On Time\" ,sum(case when completion_date < due_date and maintenance_status = 'Completed' then 1 else 0 end) as \"Completed Within Time\",sum(case when completion_date >due_date then 1 else 0 end ) as \"Delay In Completion\",sum(case when maintenance_status = 'Planned' AND completion_date IS null AND due_date > current_date() then 1 else 0 end ) as \"Pending\",sum(case when maintenance_status = 'Planned' AND completion_date IS null AND due_date < current_date() then 1 else 0 end) as \"Overdue\" , sum(case when maintenance_status = 'Cancelled' then 1 else 0 end) as \"Cancelled\" from `tabAsset Maintenance Log` group by item_name", + "ref_doctype": "Asset Maintenance Log", + "reference_report": null, + "report_name": "Asset wise Count", + "report_script": null, + "report_type": "Query Report", + "roles": [ + { + "parent": "Asset wise Count", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "Asset wise Count", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "report_type", + "fieldtype": "Select", + "label": "Report Type", + "mandatory": 0, + "options": "Monthly\nQuarterly\nYearly", + "parent": "Biomedical PPM", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "year", + "fieldtype": "Data", + "label": "Year", + "mandatory": 0, + "options": null, + "parent": "Biomedical PPM", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:12.584198", + "module": "Asset Lite", + "name": "Biomedical PPM", + "prepared_report": 0, + "query": null, + "ref_doctype": "PPM OF CT SCAN MACHINE", + "reference_report": null, + "report_name": "Biomedical PPM", + "report_script": "def get_ppm_report(filters):\r\n # Define the columns for the report\r\n columns = [\r\n {\"label\": _(\"Timeframe\"), \"fieldname\": \"timeframe\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": _(\"CT Scan Machine PPM Count\"), \"fieldname\": \"ct_scan\", \"fieldtype\": \"Int\", \"width\": 250},\r\n {\"label\": _(\"MRI Scan Machine PPM Count\"), \"fieldname\": \"mri_scan\", \"fieldtype\": \"Int\", \"width\": 250}\r\n ]\r\n\r\n # Get the selected year, default to current year if not provided\r\n year = filters.get(\"year\") or frappe.utils.now_datetime().year\r\n\r\n # Determine the type of report - monthly, quarterly, or yearly\r\n report_type = filters.get(\"report_type\") # Example values: 'Monthly', 'Quarterly', 'Yearly'\r\n\r\n # Initialize SQL query for data aggregation based on the selected timeframe\r\n if report_type == \"Monthly\":\r\n query = f\"\"\"\r\n SELECT\r\n month AS timeframe,\r\n SUM(CASE WHEN dt.name = 'PPM OF CT SCAN MACHINE' THEN 1 ELSE 0 END) AS ct_scan,\r\n SUM(CASE WHEN dt.name = 'PPM OF MRI SCAN MACHINE' THEN 1 ELSE 0 END) AS mri_scan\r\n FROM (\r\n SELECT 'PPM OF CT SCAN MACHINE' AS name, month, year FROM `tabPPM OF CT SCAN MACHINE` WHERE year = '{year}'\r\n UNION ALL\r\n SELECT 'PPM OF MRI SCAN MACHINE' AS name, month, year FROM `tabPPM OF MRI SCAN MACHINE` WHERE year = '{year}'\r\n ) AS dt\r\n GROUP BY month, year\r\n ORDER BY MONTH(STR_TO_DATE(month, '%M'))\r\n \"\"\"\r\n elif report_type == \"Quarterly\":\r\n query = f\"\"\"\r\n SELECT\r\n CONCAT('Q', QUARTER(STR_TO_DATE(month, '%M'))) AS timeframe,\r\n SUM(CASE WHEN dt.name = 'PPM OF CT SCAN MACHINE' THEN 1 ELSE 0 END) AS ct_scan,\r\n SUM(CASE WHEN dt.name = 'PPM OF MRI SCAN MACHINE' THEN 1 ELSE 0 END) AS mri_scan\r\n FROM (\r\n SELECT 'PPM OF CT SCAN MACHINE' AS name, month, year FROM `tabPPM OF CT SCAN MACHINE` WHERE year = '{year}'\r\n UNION ALL\r\n SELECT 'PPM OF MRI SCAN MACHINE' AS name, month, year FROM `tabPPM OF MRI SCAN MACHINE` WHERE year = '{year}'\r\n ) AS dt\r\n GROUP BY QUARTER(STR_TO_DATE(month, '%M')), year\r\n ORDER BY QUARTER(STR_TO_DATE(month, '%M'))\r\n \"\"\"\r\n else: # Yearly\r\n query = f\"\"\"\r\n SELECT\r\n year AS timeframe,\r\n SUM(CASE WHEN dt.name = 'PPM OF CT SCAN MACHINE' THEN 1 ELSE 0 END) AS ct_scan,\r\n SUM(CASE WHEN dt.name = 'PPM OF MRI SCAN MACHINE' THEN 1 ELSE 0 END) AS mri_scan\r\n FROM (\r\n SELECT 'PPM OF CT SCAN MACHINE' AS name, month, year FROM `tabPPM OF CT SCAN MACHINE` WHERE year = '{year}'\r\n UNION ALL\r\n SELECT 'PPM OF MRI SCAN MACHINE' AS name, month, year FROM `tabPPM OF MRI SCAN MACHINE` WHERE year = '{year}'\r\n ) AS dt\r\n GROUP BY year\r\n ORDER BY year\r\n \"\"\"\r\n\r\n # Execute the SQL query to get the result\r\n result = frappe.db.sql(query, as_dict=1)\r\n\r\n # Prepare report summary\r\n report_summary = [\r\n {\"value\": sum(row['ct_scan'] for row in result), \"label\": \"Total CT Scan Machine PPM\"},\r\n {\"value\": sum(row['mri_scan'] for row in result), \"label\": \"Total MRI Scan Machine PPM\"},\r\n {\"value\": sum(row['ct_scan'] for row in result) + sum(row['mri_scan'] for row in result), \"label\": \"Total PPM\"}\r\n ]\r\n\r\n # Prepare data for the charts\r\n labels = [row.get(\"timeframe\") for row in result]\r\n ct_scan = [row.get(\"ct_scan\") for row in result]\r\n mri_scan = [row.get(\"mri_scan\") for row in result]\r\n\r\n # Create a bar chart configuration\r\n bar_chart = {\r\n \"data\": {\r\n \"labels\": labels,\r\n \"datasets\": [\r\n {\"name\": \"CT Scan Machine PPM Count\", \"values\": ct_scan, \"chartType\": \"bar\"},\r\n {\"name\": \"MRI Scan Machine PPM Count\", \"values\": mri_scan, \"chartType\": \"bar\"}\r\n ]\r\n },\r\n \"colors\": [\"#52B2BF\", \"#01796F\"],\r\n \"barOptions\": {\"stacked\": 0},\r\n \"axisOptions\": {\"xIsSeries\": 1, \"yAxisMin\": 0}\r\n }\r\n\r\n # Create a line chart configuration\r\n line_chart = {\r\n \"data\": {\r\n \"labels\": labels,\r\n \"datasets\": [\r\n {\"name\": \"CT Scan Machine PPM Count\", \"values\": ct_scan, \"chartType\": \"line\"},\r\n {\"name\": \"MRI Scan Machine PPM Count\", \"values\": mri_scan, \"chartType\": \"line\"}\r\n ]\r\n },\r\n \"colors\": [\"#52B2BF\", \"#01796F\"],\r\n \"axisOptions\": {\"xIsSeries\": 1, \"yAxisMin\": 0}\r\n }\r\n\r\n return columns, result, None, {\"bar\": bar_chart, \"line\": line_chart}, report_summary\r\n\r\ndata = get_ppm_report(filters)\r\n\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Biomedical PPM", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "Biomedical PPM", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Biomedical PPM", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "", + "modified": "2025-04-22 14:49:12.518262", + "module": "Asset Lite", + "name": "PPM report", + "prepared_report": 0, + "query": null, + "ref_doctype": "PPM OF FIRE ALARM DEVICES", + "reference_report": null, + "report_name": "PPM report", + "report_script": "def get_result():\r\n # Define the columns for the report\r\n columns = [\r\n {\"label\": _(\"Month\"), \"fieldname\": \"month\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": _(\"Fire Alarm Devices PPM Count\"), \"fieldname\": \"fire_alarm_count\", \"fieldtype\": \"Int\", \"width\": 250},\r\n {\"label\": _(\"Electrical Panels PPM Count\"), \"fieldname\": \"electrical_panels_count\", \"fieldtype\": \"Int\", \"width\": 250}\r\n ]\r\n\r\n # Filter by the selected year\r\n year = frappe.utils.now_datetime().year\r\n\r\n # Initialize the SQL query for data aggregation\r\n query = f\"\"\"\r\n SELECT\r\n month,\r\n SUM(CASE WHEN dt.name = 'PPM OF FIRE ALARM DEVICES' THEN 1 ELSE 0 END) AS fire_alarm_count,\r\n SUM(CASE WHEN dt.name = 'PPM OF ELECTRICAL PANELS' THEN 1 ELSE 0 END) AS electrical_panels_count\r\n FROM (\r\n SELECT 'PPM OF FIRE ALARM DEVICES' AS name, month, year FROM `tabPPM OF FIRE ALARM DEVICES` WHERE year = '{year}'\r\n UNION ALL\r\n SELECT 'PPM OF ELECTRICAL PANELS' AS name, month, year FROM `tabPPM OF ELECTRICAL PANELS` WHERE year = '{year}'\r\n ) AS dt\r\n GROUP BY month, year\r\n ORDER BY MONTH(STR_TO_DATE(month, '%M'))\r\n \"\"\"\r\n\r\n result = frappe.db.sql(query, as_dict=1)\r\n\r\n # Prepare report summary\r\n report_summary = [\r\n {\"value\": sum(row['fire_alarm_count'] for row in result), \"label\": \"Total Fire Alarm PPM\"},\r\n {\"value\": sum(row['electrical_panels_count'] for row in result), \"label\": \"Total Electrical Panels PPM\"},\r\n {\"value\": sum(row['fire_alarm_count'] for row in result) + sum(row['electrical_panels_count'] for row in result), \"label\": \"Total PPM\"}\r\n ]\r\n\r\n # Prepare data for the chart\r\n labels = [row.get(\"month\") for row in result]\r\n fire_alarm_count = [row.get(\"fire_alarm_count\") for row in result]\r\n electrical_panels_count = [row.get(\"electrical_panels_count\") for row in result]\r\n\r\n # Create a chart configuration\r\n chart = {\r\n \"data\": {\r\n \"labels\": labels,\r\n \"datasets\": [\r\n {\"name\": \"Fire Alarm Devices PPM Count\", \"values\": fire_alarm_count, \"chartType\": \"bar\"},\r\n {\"name\": \"Electrical Panels PPM Count\", \"values\": electrical_panels_count, \"chartType\": \"bar\"}\r\n ]\r\n },\r\n \"colors\": [\"#52B2BF\", \"#01796F\"],\r\n \"barOptions\": {\"stacked\": 0}, # Stacked bar chart\r\n }\r\n\r\n return columns, result, None, chart, report_summary\r\n\r\ndata = get_result()\r\n\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "PPM report", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "PPM report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + }, + { + "parent": "PPM report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "PPM report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "", + "modified": "2025-04-22 14:49:12.485284", + "module": "Asset Lite", + "name": "PPM report based on Status", + "prepared_report": 0, + "query": null, + "ref_doctype": "PPM OF FIRE ALARM DEVICES", + "reference_report": null, + "report_name": "PPM report based on Status", + "report_script": "def get_simple_ppm_status_report():\r\n # Define the columns for the report\r\n columns = [\r\n {\"label\": _(\"PPM Type\"), \"fieldname\": \"ppm_type\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": _(\"Sent to technician\"), \"fieldname\": \"sent_to_technician\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"label\": _(\"Pending Approval Count\"), \"fieldname\": \"pending_approval\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"label\": _(\"Approved Count\"), \"fieldname\": \"approved_count\", \"fieldtype\": \"Int\", \"width\": 200}\r\n ]\r\n\r\n # SQL query to get the counts of PPMs based on their workflow states\r\n query = \"\"\"\r\n SELECT\r\n ppm_type,\r\n SUM(CASE WHEN workflow_state = 'Sent to technician' THEN 1 ELSE 0 END) AS sent_to_technician,\r\n SUM(CASE WHEN workflow_state = 'Pending Approval' THEN 1 ELSE 0 END) AS pending_approval,\r\n SUM(CASE WHEN workflow_state = 'Approved' THEN 1 ELSE 0 END) AS approved_count\r\n FROM (\r\n \r\n SELECT 'PPM OF CT SCAN MACHINE' AS ppm_type, workflow_state FROM `tabPPM OF CT SCAN MACHINE`\r\n UNION ALL\r\n SELECT 'PPM OF MRI SCAN MACHINE' AS ppm_type, workflow_state FROM `tabPPM OF MRI SCAN MACHINE`\r\n ) AS dt\r\n GROUP BY ppm_type\r\n ORDER BY ppm_type\r\n \"\"\"\r\n\r\n # Execute the SQL query to get the result\r\n result = frappe.db.sql(query, as_dict=1)\r\n\r\n # Prepare report summary\r\n report_summary = [\r\n {\"value\": sum(row['sent_to_technician'] for row in result), \"label\": \"Total Sent To Technician\"},\r\n {\"value\": sum(row['pending_approval'] for row in result), \"label\": \"Total Pending For Approval\"},\r\n {\"value\": sum(row['approved_count'] for row in result), \"label\": \"Total Approved\"}\r\n ]\r\n\r\n # Prepare data for the charts\r\n labels = [row.get(\"ppm_type\") for row in result]\r\n sent_to_technician = [row.get(\"sent_to_technician\") for row in result]\r\n pending_approval = [row.get(\"pending_approval\") for row in result]\r\n approved_count = [row.get(\"approved_count\") for row in result]\r\n\r\n # Create a bar chart configuration\r\n bar_chart = {\r\n \"data\": {\r\n \"labels\": labels,\r\n \"datasets\": [\r\n {\"name\": \"Pending Approval\", \"values\": pending_approval, \"chartType\": \"bar\"},\r\n {\"name\": \"Sent To Technician\", \"values\": sent_to_technician, \"chartType\": \"bar\"},\r\n {\"name\": \"Approved\", \"values\": approved_count, \"chartType\": \"bar\"}\r\n ]\r\n },\r\n \"colors\": [\"#CCCCB7\", \"#52B2BF\", \"#008000\"], # Example colors for each status\r\n \"barOptions\": {\"stacked\": 0}, # Separate bars for each status\r\n }\r\n\r\n # Return the columns, result data, chart configuration, and report summary\r\n return columns, result, None, bar_chart, report_summary\r\n\r\n# Example usage\r\ndata = get_simple_ppm_status_report()\r\n\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "PPM report based on Status", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "PPM report based on Status", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + }, + { + "parent": "PPM report based on Status", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "PPM report based on Status", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "report_type", + "fieldtype": "Select", + "label": "Report Type", + "mandatory": 0, + "options": "Monthly\nQuarterly\nYearly", + "parent": "PPM monthly reports", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "year", + "fieldtype": "Data", + "label": "Year", + "mandatory": 0, + "options": null, + "parent": "PPM monthly reports", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "", + "modified": "2025-04-22 14:49:12.548948", + "module": "Asset Lite", + "name": "PPM monthly reports", + "prepared_report": 0, + "query": null, + "ref_doctype": "PPM OF FIRE ALARM DEVICES", + "reference_report": null, + "report_name": "PPM monthly reports", + "report_script": "def get_ppm_report(filters):\r\n # Define the columns for the report\r\n columns = [\r\n {\"label\": _(\"Timeframe\"), \"fieldname\": \"timeframe\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": _(\"Fire Alarm Devices PPM Count\"), \"fieldname\": \"fire_alarm_count\", \"fieldtype\": \"Int\", \"width\": 250},\r\n {\"label\": _(\"Electrical Panels PPM Count\"), \"fieldname\": \"electrical_panels_count\", \"fieldtype\": \"Int\", \"width\": 250}\r\n ]\r\n\r\n # Get the selected year, default to current year if not provided\r\n year = filters.get(\"year\") or frappe.utils.now_datetime().year\r\n\r\n # Determine the type of report - monthly, quarterly, or yearly\r\n report_type = filters.get(\"report_type\") # Example values: 'Monthly', 'Quarterly', 'Yearly'\r\n\r\n # Initialize SQL query for data aggregation based on the selected timeframe\r\n if report_type == \"Monthly\":\r\n query = f\"\"\"\r\n SELECT\r\n month AS timeframe,\r\n SUM(CASE WHEN dt.name = 'PPM OF FIRE ALARM DEVICES' THEN 1 ELSE 0 END) AS fire_alarm_count,\r\n SUM(CASE WHEN dt.name = 'PPM OF ELECTRICAL PANELS' THEN 1 ELSE 0 END) AS electrical_panels_count\r\n FROM (\r\n SELECT 'PPM OF FIRE ALARM DEVICES' AS name, month, year FROM `tabPPM OF FIRE ALARM DEVICES` WHERE year = '{year}'\r\n UNION ALL\r\n SELECT 'PPM OF ELECTRICAL PANELS' AS name, month, year FROM `tabPPM OF ELECTRICAL PANELS` WHERE year = '{year}'\r\n ) AS dt\r\n GROUP BY month, year\r\n ORDER BY MONTH(STR_TO_DATE(month, '%M'))\r\n \"\"\"\r\n elif report_type == \"Quarterly\":\r\n query = f\"\"\"\r\n SELECT\r\n CONCAT('Q', QUARTER(STR_TO_DATE(month, '%M'))) AS timeframe,\r\n SUM(CASE WHEN dt.name = 'PPM OF FIRE ALARM DEVICES' THEN 1 ELSE 0 END) AS fire_alarm_count,\r\n SUM(CASE WHEN dt.name = 'PPM OF ELECTRICAL PANELS' THEN 1 ELSE 0 END) AS electrical_panels_count\r\n FROM (\r\n SELECT 'PPM OF FIRE ALARM DEVICES' AS name, month, year FROM `tabPPM OF FIRE ALARM DEVICES` WHERE year = '{year}'\r\n UNION ALL\r\n SELECT 'PPM OF ELECTRICAL PANELS' AS name, month, year FROM `tabPPM OF ELECTRICAL PANELS` WHERE year = '{year}'\r\n ) AS dt\r\n GROUP BY QUARTER(STR_TO_DATE(month, '%M')), year\r\n ORDER BY QUARTER(STR_TO_DATE(month, '%M'))\r\n \"\"\"\r\n else: # Yearly\r\n query = f\"\"\"\r\n SELECT\r\n year AS timeframe,\r\n SUM(CASE WHEN dt.name = 'PPM OF FIRE ALARM DEVICES' THEN 1 ELSE 0 END) AS fire_alarm_count,\r\n SUM(CASE WHEN dt.name = 'PPM OF ELECTRICAL PANELS' THEN 1 ELSE 0 END) AS electrical_panels_count\r\n FROM (\r\n SELECT 'PPM OF FIRE ALARM DEVICES' AS name, month, year FROM `tabPPM OF FIRE ALARM DEVICES` WHERE year = '{year}'\r\n UNION ALL\r\n SELECT 'PPM OF ELECTRICAL PANELS' AS name, month, year FROM `tabPPM OF ELECTRICAL PANELS` WHERE year = '{year}'\r\n ) AS dt\r\n GROUP BY year\r\n ORDER BY year\r\n \"\"\"\r\n\r\n # Execute the SQL query to get the result\r\n result = frappe.db.sql(query, as_dict=1)\r\n\r\n # Prepare report summary\r\n report_summary = [\r\n {\"value\": sum(row['fire_alarm_count'] for row in result), \"label\": \"Total Fire Alarm PPM\"},\r\n {\"value\": sum(row['electrical_panels_count'] for row in result), \"label\": \"Total Electrical Panels PPM\"},\r\n {\"value\": sum(row['fire_alarm_count'] for row in result) + sum(row['electrical_panels_count'] for row in result), \"label\": \"Total PPM\"}\r\n ]\r\n\r\n # Prepare data for the charts\r\n labels = [row.get(\"timeframe\") for row in result]\r\n fire_alarm_count = [row.get(\"fire_alarm_count\") for row in result]\r\n electrical_panels_count = [row.get(\"electrical_panels_count\") for row in result]\r\n\r\n # Create a bar chart configuration\r\n bar_chart = {\r\n \"data\": {\r\n \"labels\": labels,\r\n \"datasets\": [\r\n {\"name\": \"Fire Alarm Devices PPM Count\", \"values\": fire_alarm_count, \"chartType\": \"bar\"},\r\n {\"name\": \"Electrical Panels PPM Count\", \"values\": electrical_panels_count, \"chartType\": \"bar\"}\r\n ]\r\n },\r\n \"colors\": [\"#52B2BF\", \"#01796F\"],\r\n \"barOptions\": {\"stacked\": 0},\r\n \"axisOptions\": {\"xIsSeries\": 1, \"yAxisMin\": 0}\r\n }\r\n\r\n # Create a line chart configuration\r\n line_chart = {\r\n \"data\": {\r\n \"labels\": labels,\r\n \"datasets\": [\r\n {\"name\": \"Fire Alarm Devices PPM Count\", \"values\": fire_alarm_count, \"chartType\": \"line\"},\r\n {\"name\": \"Electrical Panels PPM Count\", \"values\": electrical_panels_count, \"chartType\": \"line\"}\r\n ]\r\n },\r\n \"colors\": [\"#52B2BF\", \"#01796F\"],\r\n \"axisOptions\": {\"xIsSeries\": 1, \"yAxisMin\": 0}\r\n }\r\n\r\n return columns, result, None, {\"bar\": bar_chart, \"line\": line_chart}, report_summary\r\n\r\ndata = get_ppm_report(filters)\r\n\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "PPM monthly reports", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "PPM monthly reports", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + }, + { + "parent": "PPM monthly reports", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "PPM monthly reports", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "Yes", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2024-10-01 10:26:27.837456", + "module": "Asset Lite", + "name": "Work Order counts", + "prepared_report": 0, + "query": null, + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Work Order counts", + "report_script": null, + "report_type": "Script Report", + "roles": [ + { + "parent": "Work Order counts", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing Manager" + }, + { + "parent": "Work Order counts", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:12.417277", + "module": "Asset Lite", + "name": "PPM Status", + "prepared_report": 0, + "query": null, + "ref_doctype": "PPM", + "reference_report": null, + "report_name": "PPM Status", + "report_script": "def get_simple_ppm_status_report():\n # Define the columns for the report\n columns = [\n {\"label\": _(\"PPM Template\"), \"fieldname\": \"ppm_template\", \"fieldtype\": \"Data\", \"width\": 150},\n {\"label\": _(\"Sent to technician\"), \"fieldname\": \"sent_to_technician\", \"fieldtype\": \"Int\", \"width\": 200},\n {\"label\": _(\"Pending Approval Count\"), \"fieldname\": \"pending_approval\", \"fieldtype\": \"Int\", \"width\": 200},\n {\"label\": _(\"Approved Count\"), \"fieldname\": \"approved_count\", \"fieldtype\": \"Int\", \"width\": 200}\n ]\n\n # SQL query to get the counts of PPMs based on their workflow states and template\n query = \"\"\"\n SELECT\n data AS ppm_template,\n SUM(CASE WHEN workflow_state = 'Sent to technician' THEN 1 ELSE 0 END) AS sent_to_technician,\n SUM(CASE WHEN workflow_state = 'Pending Approval' THEN 1 ELSE 0 END) AS pending_approval,\n SUM(CASE WHEN workflow_state = 'Approved' THEN 1 ELSE 0 END) AS approved_count\n FROM `tabPPM`\n GROUP BY data\n ORDER BY data\n \"\"\"\n\n # Execute the SQL query to get the result\n result = frappe.db.sql(query, as_dict=1)\n\n # Prepare report summary\n report_summary = [\n {\"value\": sum(row['sent_to_technician'] for row in result), \"label\": \"Total Sent To Technician\"},\n {\"value\": sum(row['pending_approval'] for row in result), \"label\": \"Total Pending For Approval\"},\n {\"value\": sum(row['approved_count'] for row in result), \"label\": \"Total Approved\"}\n ]\n\n # Prepare data for the charts\n labels = [row.get(\"ppm_template\") for row in result]\n sent_to_technician = [row.get(\"sent_to_technician\") for row in result]\n pending_approval = [row.get(\"pending_approval\") for row in result]\n approved_count = [row.get(\"approved_count\") for row in result]\n\n # Create a bar chart configuration\n bar_chart = {\n \"data\": {\n \"labels\": labels,\n \"datasets\": [\n {\"name\": \"Pending Approval\", \"values\": pending_approval, \"chartType\": \"bar\"},\n {\"name\": \"Sent To Technician\", \"values\": sent_to_technician, \"chartType\": \"bar\"},\n {\"name\": \"Approved\", \"values\": approved_count, \"chartType\": \"bar\"}\n ]\n },\n \"colors\": [\"#CCCCB7\", \"#52B2BF\", \"#008000\"], # Example colors for each status\n \"barOptions\": {\"stacked\": 0}, # Separate bars for each status\n }\n\n # Return the columns, result data, chart configuration, and report summary\n return columns, result, None, bar_chart, report_summary\n\n# Example usage\ndata = get_simple_ppm_status_report()", + "report_type": "Script Report", + "roles": [ + { + "parent": "PPM Status", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "PPM Status", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "PPM Status", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "report_type", + "fieldtype": "Select", + "label": "Report Type", + "mandatory": 0, + "options": "Monthly\nQuarterly\nYearly", + "parent": "PPM Template Counts", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "year", + "fieldtype": "Data", + "label": "Year", + "mandatory": 0, + "options": null, + "parent": "PPM Template Counts", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:12.450767", + "module": "Asset Lite", + "name": "PPM Template Counts", + "prepared_report": 0, + "query": null, + "ref_doctype": "PPM", + "reference_report": null, + "report_name": "PPM Template Counts", + "report_script": "def get_ppm_report(filters):\n # Define the columns for the report\n columns = [\n {\"label\": _(\"Timeframe\"), \"fieldname\": \"timeframe\", \"fieldtype\": \"Data\", \"width\": 150}\n ]\n\n # Get the selected year, default to current year if not provided\n year = filters.get(\"year\") or frappe.utils.now_datetime().year\n\n # Determine the type of report - monthly, quarterly, or yearly\n report_type = filters.get(\"report_type\") # Example values: 'Monthly', 'Quarterly', 'Yearly'\n\n # Get distinct data field values from the PPM doctype\n templates = frappe.db.sql(\"\"\"\n SELECT DISTINCT data \n FROM `tabPPM`\n \"\"\", as_dict=True)\n\n # Add dynamic columns for each data field found in the PPM doctype\n for template in templates:\n columns.append({\n \"label\": _(f\"{template['data']} PPM Count\"), \n \"fieldname\": template['data'].replace(\" \", \"_\").lower(), # Simple scrub replacement\n \"fieldtype\": \"Int\", \n \"width\": 250\n })\n\n # Initialize SQL query for data aggregation based on the selected timeframe\n if report_type == \"Monthly\":\n query = f\"\"\"\n SELECT\n month AS timeframe,\n data,\n COUNT(name) AS ppm_count\n FROM `tabPPM`\n WHERE year = '{year}'\n GROUP BY month, data\n ORDER BY MONTH(STR_TO_DATE(month, '%M'))\n \"\"\"\n elif report_type == \"Quarterly\":\n query = f\"\"\"\n SELECT\n CONCAT('Q', QUARTER(STR_TO_DATE(month, '%M'))) AS timeframe,\n data,\n COUNT(name) AS ppm_count\n FROM `tabPPM`\n WHERE year = '{year}'\n GROUP BY QUARTER(STR_TO_DATE(month, '%M')), data\n ORDER BY QUARTER(STR_TO_DATE(month, '%M'))\n \"\"\"\n else: # Yearly\n query = f\"\"\"\n SELECT\n year AS timeframe,\n data,\n COUNT(name) AS ppm_count\n FROM `tabPPM`\n WHERE year = '{year}'\n GROUP BY year, data\n ORDER BY year\n \"\"\"\n\n # Execute the SQL query to get the result\n result = frappe.db.sql(query, as_dict=True)\n\n # Organize the result in a structured way\n data = []\n grouped_result = {}\n\n for row in result:\n timeframe = row[\"timeframe\"]\n template = row[\"data\"].replace(\" \", \"_\").lower() # Simple scrub replacement\n\n if timeframe not in grouped_result:\n grouped_result[timeframe] = {tmpl['data'].replace(\" \", \"_\").lower(): 0 for tmpl in templates}\n grouped_result[timeframe][\"timeframe\"] = timeframe\n\n grouped_result[timeframe][template] = row[\"ppm_count\"]\n\n # Convert grouped result to a list of rows\n for timeframe, counts in grouped_result.items():\n data.append(counts)\n\n # Prepare report summary dynamically\n report_summary = [\n {\"value\": sum(row[template['data'].replace(\" \", \"_\").lower()] for row in data), \"label\": f\"Total {template['data']} PPM\"}\n for template in templates\n ]\n\n # Prepare data for the charts\n labels = [row.get(\"timeframe\") for row in data]\n chart_datasets = []\n\n for template in templates:\n chart_datasets.append({\n \"name\": f\"{template['data']} PPM Count\",\n \"values\": [row.get(template['data'].replace(\" \", \"_\").lower(), 0) for row in data],\n \"chartType\": \"bar\"\n })\n\n # Create a bar chart configuration\n bar_chart = {\n \"data\": {\n \"labels\": labels,\n \"datasets\": chart_datasets\n },\n \"colors\": [\"#52B2BF\", \"#01796F\", \"#D4AF37\"], # Example colors, can add more dynamically\n \"barOptions\": {\"stacked\": 0},\n \"axisOptions\": {\"xIsSeries\": 1, \"yAxisMin\": 0}\n }\n\n return columns, data, None, bar_chart, report_summary\n\n# Example usage\ndata = get_ppm_report(filters)\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "PPM Template Counts", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "PPM Template Counts", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "PPM Template Counts", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "mandatory": 0, + "options": null, + "parent": "Asset Maintenance Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "mandatory": 0, + "options": null, + "parent": "Asset Maintenance Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "item_code", + "fieldtype": "Link", + "label": "Item Code", + "mandatory": 0, + "options": "Item", + "parent": "Asset Maintenance Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:12.351949", + "module": "Asset Lite", + "name": "Asset Maintenance Report", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset Maintenance Log", + "reference_report": null, + "report_name": "Asset Maintenance Report", + "report_script": "def get_result(filters):\n additional_filters = \"\"\n filter_fields = ['item_code','from_date','to_date'] \n \n for field in filter_fields:\n if filters.get(field):\n if field == \"from_date\":\n additional_filters = additional_filters + f\" AND due_date >= '{filters.get(field)}'\"\n if field == \"to_date\":\n additional_filters = additional_filters + f\" AND due_date <= '{filters.get(field)}'\"\n if field == \"item_code\":\n additional_filters = additional_filters + f\" AND item_code = '{filters.get(field)}'\"\n \n \n \n result=frappe.db.sql(f\"\"\" SELECT am.item_code as item_code, am.asset_maintenance as asset_maintenance, am.maintenance_status as maintenance_status,am.assign_to_name as assign_to_name, am.due_date as due_date from `tabAsset Maintenance Log` as am where am.maintenance_status = 'Planned' {additional_filters} \"\"\")\n\n columns = [\n {\n \"label\": _(\"Item Code\"),\n \"fieldname\": \"item_code\",\n \"fieldtype\": \"Link\",\n \"options\": \"Item\",\n \"width\": 160,\n },\n {\n \"label\": _(\"Asset Maintenance\"),\n \"fieldname\": \"asset_maintenance\",\n \"fieldtype\": \"Link\",\n \"options\": \"Asset Maintenance\",\n \"width\": 160,\n },\n \n {\n \"label\": _(\"Maintenance Status\"),\n \"fieldname\": \"maintenance_status\",\n \"fieldtype\": \"Data\",\n \n \"width\": 160,\n },\n {\n \"label\": _(\"Assigned To\"),\n \"fieldname\": \"assign_to_name\",\n \"fieldtype\": \"Data\",\n \n \"width\": 160,\n },\n {\n \"label\": _(\"Due Date\"),\n \"fieldname\": \"due_date\",\n \"fieldtype\": \"Date\",\n \n \"width\": 160,\n },\n ]\n \n return columns, result, None,None, None\ndata = get_result(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Maintenance Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Asset Maintenance Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "Asset Maintenance Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Maintenance Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "mandatory": 0, + "options": null, + "parent": "Due Calibiration Date", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "mandatory": 0, + "options": null, + "parent": "Due Calibiration Date", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:12.298283", + "module": "Asset Lite", + "name": "Due Calibiration Date", + "prepared_report": 0, + "query": null, + "ref_doctype": "Item", + "reference_report": null, + "report_name": "Due Calibiration Date", + "report_script": "def execute(filters):\r\n columns = [\r\n {\"label\": \"Item Code\", \"fieldname\": \"item_code\", \"fieldtype\": \"Link\",\"options\":\"Item\", \"width\": 150},\r\n {\"label\": \"Item Name\", \"fieldname\": \"item_name\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Calibration Due Date\", \"fieldname\": \"custom_next_due_calibration_date\", \"fieldtype\": \"Date\", \"width\": 150},\r\n \r\n ]\r\n\r\n result = []\r\n \r\n if not filters:\r\n filters = {}\r\n\r\n # Calculate today's date and the date 60 days from now using frappe.utils\r\n from_date = filters.get(\"from_date\") or frappe.utils.today()\r\n to_date = filters.get(\"to_date\") or frappe.utils.add_days(frappe.utils.today(), 60)\r\n\r\n # Fetch items with calibration due date within the next 60 days\r\n items = frappe.db.get_list(\"Item\", \r\n filters={\r\n \"custom_next_due_calibration_date\": [\"between\", [from_date, to_date]],\r\n \"item_group\":\"Tools\"\r\n },\r\n fields=[\"name\", \"item_code\", \"item_name\", \"custom_next_due_calibration_date\", \"status\"],\r\n order_by=\"modified desc\" # Ensures ordering without causing SQL syntax errors\r\n \r\n )\r\n\r\n for item in items:\r\n # Calculate the days remaining until the calibration due date\r\n days_remaining = (frappe.utils.getdate(item.get(\"custom_next_due_calibration_date\")) - frappe.utils.getdate(frappe.utils.today())).days\r\n result.append({\r\n \"item_code\": item.get(\"item_code\"),\r\n \"item_name\": item.get(\"item_name\"),\r\n \"custom_next_due_calibration_date\": item.get(\"custom_next_due_calibration_date\"),\r\n \"days_remaining\": days_remaining,\r\n \"status\": item.get(\"status\")\r\n })\r\n\r\n # Add chart configuration\r\n chart = {\r\n \"data\": {\r\n \"labels\": [f\"{d['item_code']} ({d['custom_next_due_calibration_date']})\" for d in result],\r\n \"datasets\": [\r\n {\r\n \"name\": \"Days Remaining\",\r\n \"values\": [d[\"days_remaining\"] for d in result]\r\n }\r\n \r\n ]\r\n },\r\n \"type\": \"bar\", # You can also use 'line' or other chart types\r\n \"colors\": [\"#7cd6fd\"],\r\n }\r\n\r\n\r\n return columns, result, None, chart\r\ndata = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Due Calibiration Date", + "parentfield": "roles", + "parenttype": "Report", + "role": "Item Manager" + }, + { + "parent": "Due Calibiration Date", + "parentfield": "roles", + "parenttype": "Report", + "role": "Sales User" + }, + { + "parent": "Due Calibiration Date", + "parentfield": "roles", + "parenttype": "Report", + "role": "Stock User" + }, + { + "parent": "Due Calibiration Date", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "Due Calibiration Date", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Due Calibiration Date", + "parentfield": "roles", + "parenttype": "Report", + "role": "Stock Manager" + }, + { + "parent": "Due Calibiration Date", + "parentfield": "roles", + "parenttype": "Report", + "role": "Desk User" + }, + { + "parent": "Due Calibiration Date", + "parentfield": "roles", + "parenttype": "Report", + "role": "Purchase User" + }, + { + "parent": "Due Calibiration Date", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + }, + { + "parent": "Due Calibiration Date", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Due Calibiration Date", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Due Calibiration Date", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Due Calibiration Date", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + } + ], + "timeout": 0 + }, + { + "add_total_row": 1, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "mandatory": 0, + "options": "Supplier", + "parent": "Asset Count (Supplier)", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "Asset ID", + "fieldtype": "Link", + "label": "Asset", + "mandatory": 0, + "options": null, + "parent": "Asset Count (Supplier)", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:11.775945", + "module": "Asset Lite", + "name": "Asset Count (Supplier)", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Asset Count (Supplier)", + "report_script": "def execute(filters):\r\n result = []\r\n\r\n # Fetch the department filter value\r\n supplier = filters.get(\"supplier\")\r\n\r\n # Query to fetch asset names, departments, status, and location\r\n if supplier:\r\n query = \"\"\"\r\n SELECT\r\n name,\r\n asset_name,\r\n supplier\r\n FROM\r\n `tabAsset`\r\n WHERE\r\n supplier = %s\r\n ORDER BY\r\n asset_name\r\n \"\"\"\r\n result = frappe.db.sql(query, supplier, as_dict=True)\r\n else:\r\n # If no department is selected, return all assets\r\n query_no_department = \"\"\"\r\n SELECT\r\n name,\r\n asset_name,\r\n supplier\r\n FROM\r\n `tabAsset`\r\n ORDER BY\r\n asset_name\r\n \"\"\"\r\n result = frappe.db.sql(query_no_department, as_dict=True)\r\n\r\n # Create a dictionary to count assets per department\r\n supplier_count = {}\r\n \r\n for row in result:\r\n dept = row['supplier']\r\n if dept in supplier_count:\r\n supplier_count[dept] = supplier_count[dept] + 1 # Increment count\r\n else:\r\n supplier_count[dept] = 1 # Initialize count\r\n\r\n # Prepare data for the chart\r\n chart_labels = list(supplier_count.keys()) # Departments\r\n chart_values = list(supplier_count.values()) # Count of assets\r\n\r\n # Define the columns to be displayed in the report\r\n columns = [\r\n {\"fieldname\": \"name\", \"label\": \"Asset ID\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 200},\r\n {\"fieldname\": \"asset_name\", \"label\": \"Asset Name\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 200},\r\n {\"fieldname\": \"supplier\", \"label\": \"Supplier\", \"fieldtype\": \"Link\", \"options\":\"Supplier\",\"width\": 100},\r\n \r\n ]\r\n\r\n # Configure the chart to show the number of assets per department\r\n chart = {\r\n \"data\": {\r\n \"labels\": chart_labels, # Department names\r\n \"datasets\": [\r\n {\r\n \"name\": \"Number of Assets\",\r\n \"values\": chart_values # Count of assets for each department\r\n }\r\n ]\r\n },\r\n \"type\": \"pie\", # You can use 'bar', 'line', 'pie', etc.\r\n \"colors\": [\"#ECAD4B\",\"#39E4A5\",\"#B4CD29\"] # Customize the color as needed\r\n }\r\n\r\n return columns, result, None, chart\r\ndata = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Count (Supplier)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Asset Count (Supplier)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Asset Count (Supplier)", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Asset Count (Supplier)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset Count (Supplier)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Count (Supplier)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + }, + { + "parent": "Asset Count (Supplier)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "manufacturer", + "fieldtype": "Data", + "label": "Manufacturer", + "mandatory": 0, + "options": "Supplier", + "parent": "Asset Count (Manufacturer)", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:11.937552", + "module": "Asset Lite", + "name": "Asset Count (Manufacturer)", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Asset Count (Manufacturer)", + "report_script": "def execute(filters):\r\n result = []\r\n\r\n # Fetch the department filter value\r\n manufacturer = filters.get(\"manufacturer\")\r\n\r\n # Query to fetch asset names, departments, status, and location\r\n if manufacturer:\r\n query = \"\"\"\r\n SELECT\r\n name,\r\n asset_name,\r\n custom_manufacturer\r\n FROM\r\n `tabAsset`\r\n WHERE\r\n manufacturer = %s\r\n ORDER BY\r\n asset_name\r\n \"\"\"\r\n result = frappe.db.sql(query, manufacturer, as_dict=True)\r\n else:\r\n # If no department is selected, return all assets\r\n query_no_department = \"\"\"\r\n SELECT\r\n name,\r\n asset_name,\r\n custom_manufacturer\r\n FROM\r\n `tabAsset`\r\n ORDER BY\r\n asset_name\r\n \"\"\"\r\n result = frappe.db.sql(query_no_department, as_dict=True)\r\n\r\n # Create a dictionary to count assets per department\r\n manufacturer_count = {}\r\n \r\n for row in result:\r\n dept = row['custom_manufacturer']\r\n if dept in manufacturer_count:\r\n manufacturer_count[dept] = manufacturer_count[dept] + 1 # Increment count\r\n else:\r\n manufacturer_count[dept] = 1 # Initialize count\r\n\r\n # Prepare data for the chart\r\n chart_labels = list(manufacturer_count.keys()) # Departments\r\n chart_values = list(manufacturer_count.values()) # Count of assets\r\n\r\n # Define the columns to be displayed in the report\r\n columns = [\r\n {\"fieldname\": \"name\", \"label\": \"Asset ID\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 200},\r\n {\"fieldname\": \"asset_name\", \"label\": \"Asset Name\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 200},\r\n {\"fieldname\": \"manufacturer_count\", \"label\": \"Manufacturer\", \"fieldtype\": \"Data\", \"width\": 100},\r\n \r\n ]\r\n\r\n # Configure the chart to show the number of assets per department\r\n chart = {\r\n \"data\": {\r\n \"labels\": chart_labels, # Department names\r\n \"datasets\": [\r\n {\r\n \"name\": \"Number of Assets\",\r\n \"values\": chart_values # Count of assets for each department\r\n }\r\n ]\r\n },\r\n \"type\": \"pie\", # You can use 'bar', 'line', 'pie', etc.\r\n \"colors\": [\"#ECAD4B\",\"#39E4A5\",\"#B4CD29\"] # Customize the color as needed\r\n }\r\n\r\n return columns, result, None, chart\r\ndata = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Count (Manufacturer)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Asset Count (Manufacturer)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Asset Count (Manufacturer)", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Asset Count (Manufacturer)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset Count (Manufacturer)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Count (Manufacturer)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + }, + { + "parent": "Asset Count (Manufacturer)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:11.896457", + "module": "Asset Lite", + "name": "Employee", + "prepared_report": 0, + "query": null, + "ref_doctype": "Employee", + "reference_report": null, + "report_name": "Employee", + "report_script": "def execute(filters=None):\r\n # Define columns\r\n columns = [\r\n {\"fieldname\": \"employee_id\", \"label\": \"Employee ID\", \"fieldtype\": \"Link\", \"options\": \"Employee\", \"width\": 150},\r\n {\"fieldname\": \"employee_name\", \"label\": \"Employee Name\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"fieldname\": \"date_of_joining\", \"label\": \"Date of Joining\", \"fieldtype\": \"Date\", \"width\": 150},\r\n {\"fieldname\": \"years_completed\", \"label\": \"Years Completed\", \"fieldtype\": \"Int\", \"width\": 100},\r\n ]\r\n\r\n # Build SQL query\r\n query = \"\"\"\r\n SELECT\r\n emp.name AS employee_id,\r\n emp.employee_name,\r\n emp.date_of_joining,\r\n TIMESTAMPDIFF(YEAR, emp.date_of_joining, CURDATE()) AS years_completed\r\n FROM\r\n `tabEmployee` emp\r\n WHERE\r\n emp.status = 'Active'\r\n \"\"\"\r\n\r\n # Apply filters safely\r\n if filters.get(\"department\"):\r\n query += \" AND emp.department = %(department)s\"\r\n if filters.get(\"date_of_joining_from\"):\r\n query += \" AND emp.date_of_joining >= %(date_of_joining_from)s\"\r\n if filters.get(\"date_of_joining_to\"):\r\n query += \" AND emp.date_of_joining <= %(date_of_joining_to)s\"\r\n\r\n query =query+ \" ORDER BY emp.date_of_joining DESC\"\r\n\r\n # Execute query\r\n result = frappe.db.sql(query, filters, as_dict=True) or []\r\n\r\n return columns, result\r\n\r\ndata=execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Employee", + "parentfield": "roles", + "parenttype": "Report", + "role": "HR Manager" + }, + { + "parent": "Employee", + "parentfield": "roles", + "parenttype": "Report", + "role": "HR User" + }, + { + "parent": "Employee", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + }, + { + "parent": "Employee", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Employee", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "Yes", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-01-09 15:22:29.321581", + "module": "Asset Lite", + "name": "MTBF", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "MTBF", + "report_script": null, + "report_type": "Script Report", + "roles": [ + { + "parent": "MTBF", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "MTBF", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "MTBF", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "MTBF", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "MTBF", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "MTBF", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + }, + { + "parent": "MTBF", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 1, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "mandatory": 0, + "options": null, + "parent": "Technicians working Hours", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "mandatory": 0, + "options": null, + "parent": "Technicians working Hours", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-04-22 14:49:11.337516", + "module": "Asset Lite", + "name": "Technicians working Hours", + "prepared_report": 0, + "query": null, + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Technicians working Hours", + "report_script": "def execute(filters):\r\n # Define the columns for the report\r\n columns = [\r\n {\"label\": \"Engineer\", \"fieldname\": \"engineer\", \"fieldtype\": \"Link\", \"options\": \"User\", \"width\": 200},\r\n {\"label\": \"Technician Name\", \"fieldname\": \"technician_name\", \"fieldtype\": \"Data\", \"width\": 200}, # New Column\r\n {\"label\": \"Total Hours Spent\", \"fieldname\": \"total_hours\", \"fieldtype\": \"Float\", \"width\": 150}\r\n ]\r\n\r\n # Prepare conditions and parameters\r\n conditions = []\r\n params = {}\r\n\r\n # Add date range filter if provided\r\n if filters.get(\"from_date\") and filters.get(\"to_date\"):\r\n conditions.append(\"t.creation BETWEEN %(start_date)s AND %(end_date)s\")\r\n params[\"start_date\"] = f\"{filters['from_date']} 00:00:00\"\r\n params[\"end_date\"] = f\"{filters['to_date']} 23:59:59\"\r\n\r\n # SQL Query to fetch technician details\r\n query = f\"\"\"\r\n SELECT\r\n t.assigned_technician AS engineer,\r\n u.full_name AS technician_name, -- Fetch Technician's Name from tabUser\r\n SUM(t.total_hours_spent) AS total_hours\r\n FROM\r\n `tabWork_Order` t\r\n LEFT JOIN \r\n `tabUser` u ON u.name = t.assigned_technician\r\n WHERE\r\n t.docstatus = 1\r\n GROUP BY\r\n t.assigned_technician, u.full_name\r\n \"\"\"\r\n\r\n # Execute the query with parameters\r\n result = frappe.db.sql(query, params, as_dict=True)\r\n\r\n return columns, result\r\n\r\ndata = execute(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Technicians working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Technicians working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing Manager" + }, + { + "parent": "Technicians working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Technicians working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Technicians working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Technicians working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + }, + { + "parent": "Technicians working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + }, + { + "parent": "Technicians working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "asset_name", + "fieldtype": "Link", + "label": "Asset", + "mandatory": 1, + "options": "Asset", + "parent": "Asset Hisotry", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-04-22 14:49:11.853806", + "module": "Asset Lite", + "name": "Asset Hisotry", + "prepared_report": 0, + "query": "SELECT \n asset.name AS \"Asset\",\n asset.asset_name AS \"Asset Name\",\n asset.status AS \"Asset Status\",\n asset.location AS \"Location\",\n work_order.name AS \"Work Order\",\n work_order.status AS \"Work Order Status\"\nFROM `tabAsset` asset\nLEFT JOIN `tabAsset Maintenance` maintenance ON maintenance.custom_asset_name = asset.name\nLEFT JOIN `tabWork Order` work_order ON work_order.asset_name = asset.name\n\n\nWHERE %(asset_name)s IS NULL OR asset.name = %(asset_name)s\n", + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Asset Hisotry", + "report_script": null, + "report_type": "Query Report", + "roles": [ + { + "parent": "Asset Hisotry", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Asset Hisotry", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Asset Hisotry", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Asset Hisotry", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset Hisotry", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Hisotry", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + }, + { + "parent": "Asset Hisotry", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + } + ], + "timeout": 0 + }, + { + "add_total_row": 1, + "columns": [], + "disabled": 1, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "No", + "javascript": "frappe.query_reports[\"Repair Cost\"] = {\n filters: [\n {\n fieldname: \"year\",\n label: __(\"Year\"),\n fieldtype: \"Int\",\n default: new Date().getFullYear(), // Current Year\n reqd: 1 // Make it mandatory\n },\n {\n fieldname: \"month\",\n label: __(\"Month\"),\n fieldtype: \"Select\",\n options: [\n \n \" \", \"January\", \"February\", \"March\", \"April\", \"May\", \"June\",\n \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"\n ],\n default: new Date().toLocaleString('default', { month: 'long' }), // Current Month Name\n reqd: 0 // Make it mandatory\n },\n \n {\n fieldname: \"class\",\n label: __(\"Class\"),\n fieldtype: \"Select\",\n options: [\n \"\",\"Class A\",\"Class B\",\"Class C\"\n ],\n \n },\n {\n fieldname: \"vendor\",\n label: __(\"Vendor\"),\n fieldtype: \"Link\",\n options: \"Supplier\"\n },\n {\n fieldname: \"asset_name\",\n label: __(\"Asset\"),\n fieldtype: \"Link\",\n options: \"Asset\"\n \n },\n {\n fieldname: \"work_order\",\n label: __(\"Work Order\"),\n fieldtype: \"Link\",\n options: \"Work_Order\"\n \n },\n {\n fieldname: \"department\",\n label: __(\"Department\"),\n fieldtype: \"Link\",\n options: \"Department\"\n \n }\n \n \n ]\n};", + "json": null, + "letter_head": "Test", + "modified": "2025-04-22 14:49:11.413060", + "module": "Asset Lite", + "name": "Repair Cost Per Asset", + "prepared_report": 0, + "query": null, + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Repair Cost Per Asset", + "report_script": "\r\ndef execute(filters=None):\r\n if not filters:\r\n filters = {}\r\n\r\n # Ensure Year is provided\r\n if not filters.get('year'):\r\n frappe.throw(_(\"Please select a Year to proceed.\"))\r\n\r\n def get_days_in_month(year: int, month_name: str) -> int:\r\n month_map = {\r\n \"January\": 1, \"February\": 2, \"March\": 3, \"April\": 4,\r\n \"May\": 5, \"June\": 6, \"July\": 7, \"August\": 8,\r\n \"September\": 9, \"October\": 10, \"November\": 11, \"December\": 12,\r\n }\r\n month = month_map.get(month_name)\r\n month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\r\n\r\n # Handle leap year for February\r\n if month == 2:\r\n if (int(year) % 4 == 0 and int(year) % 100 != 0) or (int(year) % 400 == 0):\r\n return 29\r\n else:\r\n return 28\r\n else:\r\n return month_days[month - 1]\r\n\r\n def get_dates_of_month(year: int, month_name: str) -> list:\r\n total_days = get_days_in_month(year, month_name)\r\n month_map = {\r\n \"January\": 1, \"February\": 2, \"March\": 3, \"April\": 4,\r\n \"May\": 5, \"June\": 6, \"July\": 7, \"August\": 8,\r\n \"September\": 9, \"October\": 10, \"November\": 11, \"December\": 12,\r\n }\r\n month = month_map.get(month_name)\r\n return [f\"{year}-{month:02d}-{day:02d}\" for day in range(1, total_days + 1)]\r\n\r\n columns = [\r\n {\"label\": \"Asset\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Link\",\"options\": \"Asset\", \"width\": 200},\r\n {\"label\": \"Work Order\", \"fieldname\": \"work_order\", \"fieldtype\": \"Link\",\"options\": \"Work_Order\", \"width\": 200},\r\n {\"label\": \"Item\", \"fieldname\": \"item_code\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Quantity\", \"fieldname\": \"quantity\", \"fieldtype\": \"Float\", \"width\": 120},\r\n {\"label\": \"Amount\", \"fieldname\": \"amount\", \"fieldtype\": \"Currency\", \"width\": 120},\r\n ]\r\n\r\n result1 = []\r\n year = filters.get('year')\r\n month = filters.get('month')\r\n department = filters.get('department') # Optional department filter\r\n asset_class = filters.get('class') # Optional class filter\r\n vendor = filters.get('vendor') # Optional vendor filter\r\n asset_name = filters.get('asset_name')\r\n work_order = filters.get('work_order')\r\n\r\n \r\n \r\n if asset:\r\n where_conditions.append(\"ar.asset = %(asset)s\") \r\n if work_order:\r\n where_conditions.append(\"ar.name = %(work_order)s\")\r\n\r\n # Construct final WHERE clause\r\n where_clause = \" AND \".join(where_conditions)\r\n\r\n # Fetch data for Work Orders, Material Requests, and Purchase Orders\r\n results = frappe.db.sql(\r\n f\"\"\"\r\n SELECT \r\n ar.name AS work_order, \r\n ar.asset AS asset, \r\n si.item_code, \r\n SUM(si.consumed_quantity) AS quantity, \r\n SUM(si.total_value) AS amount\r\n FROM `tabWork_Order` ar\r\n JOIN `tabAsset Repair Consumed Item` si ON si.parent = ar.name\r\n WHERE {where_clause}\r\n AND ar.creation BETWEEN %(start_date)s AND %(end_date)s\r\n GROUP BY ar.name, ar.asset, si.item_code;\r\n ORDER BY amount ASC\r\n \"\"\",\r\n {\r\n \"start_date\": dates_in_month[0] if month else None,\r\n \"end_date\": dates_in_month[-1] if month else None,\r\n \"year\": year,\r\n \"work_order\": work_order,\r\n \"asset\": asset,\r\n },\r\n as_dict=True\r\n )\r\n\r\n ## Prepare final report data\r\n # for row in results:\r\n # result1.append({\r\n # \"asset_name\": row.get(\"asset_name\"),\r\n # \"work_order\": row.get(\"work_order\"),\r\n # \"item_code\": row.get(\"item_code\"),\r\n # \"quantity\": float(row.get(\"quantity\") or 0),\r\n # \"amount\": float(row.get(\"amount\") or 0),\r\n # })\r\n \r\n for row in results:\r\n if row.get(\"item_code\"): # **Ensures only rows with an item_code are included**\r\n result1.append({\r\n \"asset_name\": row.get(\"asset\"),\r\n \"work_order\": row.get(\"work_order\"),\r\n \"item_code\": row.get(\"item_code\"),\r\n \"quantity\": float(row.get(\"quantity\") or 0),\r\n \"amount\": float(row.get(\"amount\") or 0),\r\n })\r\n\r\n return columns, result1\r\n\r\ndata=execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Repair Cost Per Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Repair Cost Per Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing Manager" + }, + { + "parent": "Repair Cost Per Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Repair Cost Per Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Repair Cost Per Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Repair Cost Per Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + }, + { + "parent": "Repair Cost Per Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + }, + { + "parent": "Repair Cost Per Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [ + { + "fieldname": "asset_name", + "fieldtype": "Link", + "label": "Asset Name", + "options": null, + "parent": "Test work_order", + "parentfield": "columns", + "parenttype": "Report", + "width": 0 + } + ], + "disabled": 1, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "asset_name", + "fieldtype": "Link", + "label": "Asset Name", + "mandatory": 0, + "options": "Asset", + "parent": "Test work_order", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-04-22 14:49:10.917005", + "module": "Asset Lite", + "name": "Test work_order", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Test work_order", + "report_script": "def execute(filters=None):\r\n # Define columns for the report\r\n columns = [\r\n {\"fieldname\": \"asset_name\", \"label\": \"Asset Name\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"fieldname\": \"asset_category\", \"label\": \"Asset Category\", \"fieldtype\": \"Link\", \"options\": \"Asset Category\", \"width\": 120},\r\n {\"fieldname\": \"work_order\", \"label\": \"Work Order\", \"fieldtype\": \"Link\", \"options\": \"Work Order\", \"width\": 120},\r\n {\"fieldname\": \"work_order_status\", \"label\": \"Work Order Status\", \"fieldtype\": \"Select\", \"options\": \"\\nOpen\\nWork In Progress\\nPending Review\\nCompleted\\nCancelled\\nClosed\", \"width\": 120},\r\n {\"fieldname\": \"work_order_type\", \"label\": \"Work Order Type\", \"fieldtype\": \"Link\", \"options\": \"Issue Type\", \"width\": 100},\r\n ]\r\n\r\n # Initialize an empty list to store report data\r\n data = []\r\n asset_name = filters.get(\"asset_name\") \r\n asset_filters = {}\r\n if asset_name:\r\n asset_filters['asset_name'] = asset_name\r\n \r\n\r\n \r\n # Fetch all assets\r\n assets = frappe.get_all(\"Asset\", filters={'name':asset_name},fields=[\"name\", \"asset_name\", \"asset_category\"])\r\n \r\n \r\n\r\n if not assets:\r\n frappe.msgprint(\"No Assets found.\", alert=True)\r\n return columns, data # Return empty report if no assets exist\r\n\r\n # Loop through each asset\r\n for asset in assets:\r\n # Fetch linked Work Orders for the current asset\r\n work_orders = frappe.get_all(\r\n \"Work_Order\",\r\n filters={\"asset\": asset.name}, # Ensure \"asset\" is the correct field linking Work Orders to Assets\r\n fields=[\"name\", \"repair_status\", \"work_order_type\"]\r\n )\r\n\r\n # If there are no work orders, still add the asset details\r\n if not work_orders:\r\n data.append({\r\n \"asset_name\": asset.asset_name,\r\n \"asset_category\": asset.asset_category,\r\n \"work_order\": None,\r\n \"work_order_status\": None,\r\n \"work_order_type\": None\r\n })\r\n else:\r\n # Loop through each work order and add a row to the report\r\n for work_order in work_orders:\r\n data.append({\r\n \"asset_name\": asset.asset_name,\r\n \"asset_category\": asset.asset_category,\r\n \"work_order\": work_order.name,\r\n \"work_order_status\": work_order.repair_status, \r\n \"work_order_type\": work_order.work_order_type\r\n })\r\n\r\n return columns, data\r\n\r\ndata = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Test work_order", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Test work_order", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing Manager" + }, + { + "parent": "Test work_order", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Test work_order", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Test work_order", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Test work_order", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + }, + { + "parent": "Test work_order", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + }, + { + "parent": "Test work_order", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "Test work_order", + "parentfield": "roles", + "parenttype": "Report", + "role": "Sales Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 1, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "asset", + "fieldtype": "Link", + "label": "Asset", + "mandatory": 0, + "options": "Asset", + "parent": "PPM Planner", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-04-22 14:49:10.743010", + "module": "Asset Lite", + "name": "PPM Planner", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "PPM Planner", + "report_script": "def execute(filters=None):\r\n \r\n def get_next_due_date(current_date, periodicity):\r\n \"\"\"Calculate the next due date based on periodicity without imports.\"\"\"\r\n if periodicity == \"Daily\":\r\n return frappe.utils.add_days(current_date, 1)\r\n elif periodicity == \"Weekly\":\r\n return frappe.utils.add_days(current_date, 7)\r\n elif periodicity == \"Monthly\":\r\n return frappe.utils.add_months(current_date, 1)\r\n elif periodicity == \"Quarterly\":\r\n return frappe.utils.add_months(current_date, 3)\r\n elif periodicity == \"Half-yearly\":\r\n return frappe.utils.add_months(current_date, 6)\r\n elif periodicity == \"Yearly\":\r\n return frappe.utils.add_years(current_date, 1)\r\n elif periodicity == \"2 Yearly\":\r\n return frappe.utils.add_years(current_date, 2)\r\n elif periodicity == \"3 Yearly\":\r\n return frappe.utils.add_years(current_date, 3)\r\n else:\r\n return frappe.utils.add_months(current_date, 1) # Default: Monthly\r\n\r\n #if not filters or not filters.get(\"asset\"):\r\n #frappe.throw(\"Please select an Asset to generate the report.\")\r\n\r\n asset_id = filters.get(\"asset\")\r\n\r\n # Fetch maintenance logs with \"Planned\" status\r\n maintenance_logs = frappe.get_all(\r\n \"Asset Maintenance Log\",\r\n filters={\"asset_name\": asset_id, \"maintenance_status\": \"Planned\"},\r\n fields=[\"name\", \"due_date\", \"periodicity\", \"custom_asset_names\",\"asset_name\",\"maintenance_type\",\"assign_to_name\"]\r\n )\r\n\r\n data = []\r\n max_date = frappe.utils.getdate(frappe.utils.add_years(frappe.utils.today(), 5)) # Convert to date object\r\n \r\n for log in maintenance_logs:\r\n due_date = frappe.utils.getdate(log[\"due_date\"]) # Convert to date object\r\n periodicity = log[\"periodicity\"]\r\n \r\n # Fetch Serial Number from Asset\r\n serial_number = frappe.db.get_value(\"Asset\", log[\"asset_name\"], \"custom_serial_number\") or \"N/A\"\r\n\r\n # Generate due dates for the next 5 years\r\n for _ in range(60): # Max 5 years for Monthly periodicity\r\n data.append({\r\n \"asset_name\": log[\"asset_name\"],\r\n \"custom_asset_names\": log[\"custom_asset_names\"],\r\n \"due_date\": due_date,\r\n \"serial_number\": serial_number,\r\n \"periodicity\": log[\"periodicity\"],\r\n \"maintenance_type\": log[\"maintenance_type\"],\r\n \"assign_to_name\": log[\"assign_to_name\"],\r\n \"status\": \"Planned\"\r\n })\r\n \r\n # Calculate next due date\r\n due_date = get_next_due_date(due_date, periodicity)\r\n\r\n # Stop if beyond 5 years\r\n if due_date > max_date:\r\n break\r\n\r\n columns = [\r\n {\"label\": \"Asset ID\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 200},\r\n {\"label\": \"Asset Name\", \"fieldname\": \"custom_asset_names\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Serial Number\", \"fieldname\": \"serial_number\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Due Date\", \"fieldname\": \"due_date\", \"fieldtype\": \"Date\", \"width\": 120},\r\n {\"label\": \"Periodicity\", \"fieldname\": \"periodicity\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Maintenance Type\", \"fieldname\": \"maintenance_type\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Assigned To\", \"fieldname\": \"assign_to_name\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Status\", \"fieldname\": \"status\", \"fieldtype\": \"Data\", \"width\": 100},\r\n ]\r\n\r\n return columns, data\r\n\r\ndata = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "PPM Planner", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "PPM Planner", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "PPM Planner", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "PPM Planner", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "PPM Planner", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "PPM Planner", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + }, + { + "parent": "PPM Planner", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + }, + { + "parent": "PPM Planner", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 1, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "hospital", + "fieldtype": "Link", + "label": "Hospital Name", + "mandatory": 0, + "options": "Company", + "parent": "PM Status by Supplier Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "month", + "fieldtype": "Select", + "label": "Month", + "mandatory": 0, + "options": "\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember", + "parent": "PM Status by Supplier Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "year", + "fieldtype": "Select", + "label": "Year", + "mandatory": 0, + "options": "\n2020\n2021\n2022\n2023\n2024\n2025\n2026\n2027\n2028\n2029\n2030", + "parent": "PM Status by Supplier Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "mandatory": 0, + "options": "Supplier", + "parent": "PM Status by Supplier Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-04-22 14:49:11.063289", + "module": "Asset Lite", + "name": "PM Status by Supplier Report", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset Maintenance Log", + "reference_report": null, + "report_name": "PM Status by Supplier Report", + "report_script": "\r\ndef execute(filters=None):\r\n if not filters:\r\n filters = {}\r\n\r\n conditions = [\"a.supplier IS NOT NULL AND a.supplier != ''\"]\r\n query_params = {}\r\n \r\n if filters.get(\"hospital\"):\r\n conditions.append(\"aml.custom_hospital_name = %(hospital)s\")\r\n query_params[\"hospital\"] = filters.get(\"hospital\")\r\n\r\n # ✅ Apply Year Filter\r\n if filters.get(\"year\"):\r\n conditions.append(\"YEAR(aml.due_date) = %(year)s\")\r\n query_params[\"year\"] = filters.get(\"year\")\r\n\r\n # ✅ Apply Month Filter\r\n if filters.get(\"month\"):\r\n month_mapping = {\r\n \"January\": 1, \"February\": 2, \"March\": 3, \"April\": 4,\r\n \"May\": 5, \"June\": 6, \"July\": 7, \"August\": 8,\r\n \"September\": 9, \"October\": 10, \"November\": 11, \"December\": 12\r\n }\r\n month_num = month_mapping.get(filters.get(\"month\"))\r\n if month_num:\r\n conditions.append(\"MONTH(aml.due_date) = %(month)s\")\r\n query_params[\"month\"] = month_num\r\n\r\n # ✅ Apply Supplier Filter (from Asset Doctype)\r\n if filters.get(\"supplier\"):\r\n conditions.append(\"a.supplier = %(supplier)s\")\r\n query_params[\"supplier\"] = filters.get(\"supplier\")\r\n\r\n # ✅ Construct WHERE Clause\r\n where_clause = \"WHERE \" + \" AND \".join(conditions) if conditions else \"\"\r\n\r\n # ✅ Fetch Summary Counts (Grouped by Supplier)\r\n summary_query = f\"\"\"\r\n SELECT\r\n SUM(CASE WHEN aml.maintenance_status = 'Planned' THEN 1 ELSE 0 END) AS planned,\r\n SUM(CASE WHEN aml.maintenance_status = 'Completed' THEN 1 ELSE 0 END) AS completed,\r\n SUM(CASE WHEN aml.maintenance_status = 'Overdue' THEN 1 ELSE 0 END) AS overdue,\r\n COUNT(aml.name) AS total_pm\r\n FROM `tabAsset Maintenance Log` aml\r\n LEFT JOIN `tabAsset` a ON aml.asset_name = a.name\r\n {where_clause}\r\n \"\"\"\r\n summary_data = frappe.db.sql(summary_query, query_params, as_dict=True)\r\n\r\n # ✅ Fetch Supplier-wise Summary for Chart\r\n supplier_query = f\"\"\"\r\n SELECT\r\n a.supplier AS supplier,\r\n SUM(CASE WHEN aml.maintenance_status = 'Planned' THEN 1 ELSE 0 END) AS planned,\r\n SUM(CASE WHEN aml.maintenance_status = 'Completed' THEN 1 ELSE 0 END) AS completed,\r\n SUM(CASE WHEN aml.maintenance_status = 'Overdue' THEN 1 ELSE 0 END) AS overdue\r\n FROM `tabAsset Maintenance Log` aml\r\n LEFT JOIN `tabAsset` a ON aml.asset_name = a.name\r\n {where_clause}\r\n GROUP BY a.supplier\r\n \"\"\"\r\n supplier_summary = frappe.db.sql(supplier_query, query_params, as_dict=True)\r\n\r\n # ✅ Fetch Detailed Table Data\r\n details_query = f\"\"\"\r\n SELECT\r\n aml.name AS log_id,\r\n aml.asset_name AS asset_id,\r\n aml.custom_hospital_name AS company,\r\n aml.custom_asset_names AS asset_name,\r\n aml.maintenance_status AS status,\r\n aml.due_date AS due_date,\r\n a.supplier AS supplier,\r\n aml.custom_pm_overdue_reason,\r\n aml.custom_accepted_by_moh,\r\n am.custom_site_contractor,\r\n am.custom_subcontractor,\r\n am.custom_service_coverage,\r\n am.custom_service_agreement,\r\n am.custom_price_per_pm\r\n FROM `tabAsset Maintenance Log` aml\r\n LEFT JOIN `tabAsset` a ON aml.asset_name = a.name\r\n LEFT JOIN `tabAsset Maintenance` am ON aml.asset_maintenance = am.name\r\n {where_clause}\r\n ORDER BY aml.due_date DESC\r\n \"\"\"\r\n table_data = frappe.db.sql(details_query, query_params, as_dict=True)\r\n\r\n # ✅ Define Report Columns\r\n columns = [\r\n {\"label\": \"Log ID\", \"fieldname\": \"log_id\", \"fieldtype\": \"Link\", \"options\": \"Asset Maintenance Log\", \"width\": 150},\r\n {\"label\": \"Supplier\", \"fieldname\": \"supplier\", \"fieldtype\": \"Link\", \"options\": \"Supplier\", \"width\": 200},\r\n {\"label\": \"Hospital Name\", \"fieldname\": \"company\", \"fieldtype\": \"Data\", \"width\": 120},\r\n {\"label\": \"Asset ID\", \"fieldname\": \"asset_id\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 200},\r\n {\"label\": \"Asset Name\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Data\", \"width\": 250},\r\n {\"label\": \"Maintenance Status\", \"fieldname\": \"status\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Due Date\", \"fieldname\": \"due_date\", \"fieldtype\": \"Date\", \"width\": 150},\r\n {\"label\": \"Price Per PM\", \"fieldname\": \"custom_price_per_pm\", \"fieldtype\": \"Currency\", \"width\": 150},\r\n {\"label\": \"Site Contractor\", \"fieldname\": \"custom_site_contractor\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Subcontractor\", \"fieldname\": \"custom_subcontractor\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Service Coverage\", \"fieldname\": \"custom_service_coverage\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Service Agreement\", \"fieldname\": \"custom_service_agreement\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Overdue Reason\", \"fieldname\": \"custom_pm_overdue_reason\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Accepted By MOH\", \"fieldname\": \"custom_accepted_by_moh\", \"fieldtype\": \"Check\", \"width\": 100},\r\n ]\r\n\r\n # ✅ Prepare Chart Data (Stacked Bar Chart by Supplier)\r\n labels = []\r\n planned_values = []\r\n completed_values = []\r\n overdue_values = []\r\n total_values = [] # New Total Column\r\n \r\n for row in supplier_summary:\r\n labels.append(row[\"supplier\"] or \"Unknown\")\r\n planned = row[\"planned\"] or 0\r\n completed = row[\"completed\"] or 0\r\n overdue = row[\"overdue\"] or 0\r\n \r\n planned_values.append(planned)\r\n completed_values.append(completed)\r\n overdue_values.append(overdue)\r\n total_values.append(planned + completed + overdue) # Sum of all statuses\r\n \r\n # ✅ Define Updated Chart with \"Total\"\r\n chart = {\r\n \"data\": {\r\n \"labels\": labels,\r\n \"datasets\": [\r\n {\"name\": \"Overdue\", \"values\": overdue_values, \"chartType\": \"bar\", \"color\": \"red\"},\r\n {\"name\": \"Planned\", \"values\": planned_values, \"chartType\": \"bar\", \"color\": \"orange\"},\r\n {\"name\": \"Completed\", \"values\": completed_values, \"chartType\": \"bar\", \"color\": \"green\"},\r\n {\"name\": \"Total\", \"values\": total_values, \"chartType\": \"bar\", \"color\": \"blue\"}, # ✅ New Total Bar\r\n ]\r\n },\r\n \"type\": \"bar\",\r\n \"barOptions\": {\r\n \"stacked\": True # ✅ Stack all bars including Total\r\n }\r\n }\r\n\r\n\r\n # ✅ Prepare Report Summary Data\r\n report_summary = [\r\n {\"label\": \"Planned\", \"value\": summary_data[0][\"planned\"], \"indicator\": \"orange\"},\r\n {\"label\": \"Completed\", \"value\": summary_data[0][\"completed\"], \"indicator\": \"green\"},\r\n {\"label\": \"Overdue\", \"value\": summary_data[0][\"overdue\"], \"indicator\": \"red\"},\r\n {\"label\": \"Total PM\", \"value\": summary_data[0][\"total_pm\"], \"indicator\": \"blue\"}\r\n ]\r\n\r\n return columns, table_data, None, chart, report_summary\r\n\r\ndata = execute(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "PM Status by Supplier Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "PM Status by Supplier Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "PM Status by Supplier Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "PM Status by Supplier Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 1, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "company", + "fieldtype": "Link", + "label": "Hospital Name", + "mandatory": 0, + "options": "Company", + "parent": "Suppliers Repair Cost Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "month", + "fieldtype": "Select", + "label": "Month", + "mandatory": 0, + "options": "\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember", + "parent": "Suppliers Repair Cost Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "year", + "fieldtype": "Select", + "label": "Year", + "mandatory": 0, + "options": "\n2020\n2021\n2022\n2023\n2024\n2025\n2026\n2027\n2028\n2029\n2030", + "parent": "Suppliers Repair Cost Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "mandatory": 0, + "options": "Supplier", + "parent": "Suppliers Repair Cost Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "asset", + "fieldtype": "Link", + "label": "Asset ID", + "mandatory": 0, + "options": "Asset", + "parent": "Suppliers Repair Cost Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-04-22 14:49:11.171324", + "module": "Asset Lite", + "name": "Suppliers Repair Cost Report", + "prepared_report": 0, + "query": null, + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Suppliers Repair Cost Report", + "report_script": "def execute(filters=None):\r\n if not filters:\r\n filters = {}\r\n\r\n # Define Filters\r\n filters_dict = {}\r\n\r\n # Apply Company, Asset, and Supplier Filters\r\n if filters.get(\"company\"):\r\n filters_dict[\"company\"] = filters.get(\"company\")\r\n if filters.get(\"asset\"):\r\n filters_dict[\"asset\"] = filters.get(\"asset\")\r\n if filters.get(\"supplier\"):\r\n filters_dict[\"supplier\"] = filters.get(\"supplier\")\r\n\r\n # Apply Month & Year Filters\r\n if filters.get(\"year\"):\r\n year_num = int(filters[\"year\"]) # Ensure it's an integer\r\n\r\n if filters.get(\"month\"):\r\n month_map = {\r\n \"January\": 1, \"February\": 2, \"March\": 3, \"April\": 4,\r\n \"May\": 5, \"June\": 6, \"July\": 7, \"August\": 8,\r\n \"September\": 9, \"October\": 10, \"November\": 11, \"December\": 12\r\n }\r\n month_num = month_map.get(filters[\"month\"])\r\n\r\n if month_num:\r\n first_day = f\"{year_num}-{month_num:02d}-01\"\r\n last_day = frappe.utils.get_last_day(first_day)\r\n filters_dict[\"creation\"] = [\"between\", [first_day, last_day]]\r\n else:\r\n first_day = f\"{year_num}-01-01\"\r\n last_day = f\"{year_num}-12-31\"\r\n filters_dict[\"creation\"] = [\"between\", [first_day, last_day]]\r\n\r\n # Fetch Work Orders with Filters\r\n work_orders = frappe.get_all(\r\n \"Work_Order\",\r\n filters=filters_dict,\r\n fields=[\r\n \"name\", \"company\", \"work_order_type\", \"repair_status\", \"total_repair_cost\",\r\n \"completion_date\", \"first_responded_on\", \"custom_deadline_date\", \"failure_date\",\r\n \"supplier\", \"serial_number\", \"asset_name\", \"repair_cost\", \"asset\",\r\n \"assigned_technician\", \"custom_maintenance_manager\", \"custom_priority_\", \"creation\"\r\n ],\r\n order_by=\"creation desc\"\r\n )\r\n\r\n # Define Report Columns\r\n columns = [\r\n {\"label\": \"Supplier\", \"fieldname\": \"supplier\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Work Order No\", \"fieldname\": \"name\", \"fieldtype\": \"Link\", \"options\": \"Work_Order\", \"width\": 150},\r\n {\"label\": \"Hospital Name\", \"fieldname\": \"company\", \"fieldtype\": \"Data\", \"width\": 120},\r\n {\"label\": \"Asset ID\", \"fieldname\": \"asset\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 120},\r\n {\"label\": \"Asset Name\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Data\", \"width\": 180},\r\n \r\n {\"label\": \"Priority\", \"fieldname\": \"custom_priority_\", \"fieldtype\": \"Data\", \"width\": 100},\r\n {\"label\": \"Work Order Type\", \"fieldname\": \"work_order_type\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Repair Status\", \"fieldname\": \"repair_status\", \"fieldtype\": \"Data\", \"width\": 120},\r\n {\"label\": \"Spare Used - Qty\", \"fieldname\": \"spares_used\", \"fieldtype\": \"Data\", \"width\": 250},\r\n {\"label\": \"Spare Cost (SAR)\", \"fieldname\": \"spare_cost\", \"fieldtype\": \"Currency\", \"width\": 120},\r\n {\"label\": \"Purchase Items - Qty\", \"fieldname\": \"invoice_items\", \"fieldtype\": \"Data\", \"width\": 170},\r\n {\"label\": \"Purchase Cost (SAR)\", \"fieldname\": \"repair_cost\", \"fieldtype\": \"Currency\", \"width\": 120},\r\n {\"label\": \"Total Repair Cost (SAR)\", \"fieldname\": \"total_repair_cost\", \"fieldtype\": \"Currency\", \"width\": 120},\r\n \r\n {\"label\": \"Failure Date\", \"fieldname\": \"failure_date\", \"fieldtype\": \"Date\", \"width\": 120},\r\n {\"label\": \"First Responded\", \"fieldname\": \"first_responded_on\", \"fieldtype\": \"Date\", \"width\": 120},\r\n {\"label\": \"Completion Date\", \"fieldname\": \"completion_date\", \"fieldtype\": \"Date\", \"width\": 120},\r\n {\"label\": \"Deadline Date\", \"fieldname\": \"custom_deadline_date\", \"fieldtype\": \"Date\", \"width\": 120},\r\n {\"label\": \"Serial Number\", \"fieldname\": \"serial_number\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Assigned To\", \"fieldname\": \"custom_maintenance_manager\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Created On\", \"fieldname\": \"creation\", \"fieldtype\": \"Datetime\", \"width\": 150}\r\n ]\r\n\r\n # Fetch Spare Parts for Each Work Order\r\n for wo in work_orders:\r\n spare_parts = frappe.get_all(\r\n \"Asset Repair Consumed Item\",\r\n filters={\"parent\": wo[\"name\"]},\r\n fields=[\"item_code\", \"consumed_quantity\"]\r\n )\r\n\r\n if spare_parts:\r\n wo[\"spares_used\"] = \"\\n\".join([f\"{sp['item_code']} - {sp['consumed_quantity']}\" for sp in spare_parts])\r\n else:\r\n wo[\"spares_used\"] = \"\"\r\n\r\n # Fetch Purchase Invoice linked to Work Order\r\n invoice_links = frappe.get_all(\r\n \"PI Table\",\r\n filters={\"parent\": wo[\"name\"]},\r\n fields=[\"purchase_invoice\"]\r\n )\r\n\r\n invoice_items_list = []\r\n for invoice in invoice_links:\r\n if invoice[\"purchase_invoice\"]:\r\n items = frappe.get_all(\r\n \"Purchase Invoice Item\",\r\n filters={\"parent\": invoice[\"purchase_invoice\"]},\r\n fields=[\"item_code\", \"qty\"]\r\n )\r\n invoice_items_list.extend([f\"{item['item_code']} - {item['qty']}\" for item in items])\r\n\r\n wo[\"invoice_items\"] = \"\\n\".join(invoice_items_list) if invoice_items_list else \" \"\r\n\r\n # Calculate Spare Cost = Total Repair Cost - Purchase Cost\r\n wo[\"spare_cost\"] = (wo.get(\"total_repair_cost\") or 0) - (wo.get(\"repair_cost\") or 0)\r\n\r\n # ✅ Fetch Supplier-wise Total Repair Cost\r\n supplier_query = \"\"\"\r\n SELECT supplier, SUM(total_repair_cost) AS total_repair_cost\r\n FROM `tabWork_Order`\r\n WHERE supplier IS NOT NULL AND supplier != ''\r\n \"\"\"\r\n\r\n # ✅ Apply Supplier Filter if Selected\r\n if filters.get(\"supplier\"):\r\n supplier_query =supplier_query + \" AND supplier = %(supplier)s\"\r\n supplier_query =supplier_query + \" GROUP BY supplier ORDER BY total_repair_cost DESC\"\r\n\r\n supplier_data = frappe.db.sql(supplier_query, filters, as_dict=True)\r\n\r\n # ✅ Prepare Chart Data (Filtered by Supplier)\r\n supplier_labels = []\r\n repair_cost_values = []\r\n\r\n for row in supplier_data:\r\n supplier_labels.append(row[\"supplier\"])\r\n repair_cost_values.append(row[\"total_repair_cost\"])\r\n\r\n chart = {\r\n \"data\": {\r\n \"labels\": supplier_labels,\r\n \"datasets\": [\r\n {\r\n \"name\": \"Total Repair Cost\",\r\n \"values\": repair_cost_values,\r\n \"chartType\": \"bar\",\r\n \"color\": \"blue\"\r\n }\r\n ]\r\n },\r\n \"type\": \"bar\",\r\n \"barOptions\": {\r\n \"stacked\": False # Not stacked, just a simple bar chart\r\n }\r\n }\r\n\r\n return columns, work_orders, None, chart\r\n\r\ndata = execute(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Suppliers Repair Cost Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Suppliers Repair Cost Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing Manager" + }, + { + "parent": "Suppliers Repair Cost Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Suppliers Repair Cost Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Suppliers Repair Cost Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Suppliers Repair Cost Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + }, + { + "parent": "Suppliers Repair Cost Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + }, + { + "parent": "Suppliers Repair Cost Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "Suppliers Repair Cost Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Sales Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "mandatory": 0, + "options": "Department", + "parent": "Work_order department wise", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:12.219083", + "module": "Asset Lite", + "name": "Work_order department wise", + "prepared_report": 0, + "query": null, + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Work_order department wise", + "report_script": "def execute(filters):\r\n result = []\r\n\r\n # Fetch the department filter value\r\n department = filters.get(\"department\")\r\n\r\n # Query to fetch work orders, their departments, status, and work order type\r\n if department:\r\n query = \"\"\"\r\n SELECT\r\n name,\r\n department,\r\n repair_status,\r\n work_order_type\r\n FROM\r\n `tabWork_Order`\r\n WHERE\r\n department = %s\r\n ORDER BY\r\n name\r\n \"\"\"\r\n result = frappe.db.sql(query, department, as_dict=True)\r\n sql1 = \"\"\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Open' AND department = %s\"\"\"\r\n sql2 = \"\"\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Work In Progress' AND department = %s\"\"\"\r\n sql3 = \"\"\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Pending Review' AND department = %s\"\"\"\r\n sql4 = \"\"\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Completed' AND department = %s\"\"\"\r\n sql5 = \"\"\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Closed' AND department = %s\"\"\"\r\n sql6 = \"\"\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status IN ('Open', 'Work In Progress', 'Pending Review', 'Completed', 'Closed', 'Cancelled') AND department = %s\"\"\"\r\n report_summary = [\r\n {\"value\": row.count, \"label\": \"Open\"} for row in frappe.db.sql(sql1, (department,), as_dict=True)\r\n ] + [\r\n {\"value\": row.count, \"label\": \"Work In Progress\"} for row in frappe.db.sql(sql2, (department,), as_dict=True)\r\n ] + [\r\n {\"value\": row.count, \"label\": \"Pending Review\"} for row in frappe.db.sql(sql3, (department,), as_dict=True)\r\n ] + [\r\n {\"value\": row.count, \"label\": \"Completed\"} for row in frappe.db.sql(sql4, (department,), as_dict=True)\r\n ] + [\r\n {\"value\": row.count, \"label\": \"Closed\"} for row in frappe.db.sql(sql5, (department,), as_dict=True)\r\n ] + [\r\n {\"value\": row.count, \"label\": \"Total Work Orders\"} for row in frappe.db.sql(sql6, (department,), as_dict=True)\r\n ]\r\n\r\n else:\r\n # If no department is selected, return all work orders\r\n query_no_department = \"\"\"\r\n SELECT\r\n name,\r\n department,\r\n repair_status,\r\n work_order_type\r\n FROM\r\n `tabWork_Order`\r\n ORDER BY\r\n name\r\n \"\"\"\r\n result = frappe.db.sql(query_no_department, as_dict=True)\r\n sql1 = \"\"\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Open'\"\"\"\r\n sql2 = \"\"\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Work In Progress'\"\"\"\r\n sql3 = \"\"\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Pending Review'\"\"\"\r\n sql4 = \"\"\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Completed'\"\"\"\r\n sql5 = \"\"\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Closed'\"\"\"\r\n sql6 = \"\"\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status IN ('Open', 'Work In Progress', 'Pending Review', 'Completed', 'Closed', 'Cancelled')\"\"\"\r\n report_summary = [\r\n {\"value\": row.count, \"label\": \"Open\"} for row in frappe.db.sql(sql1, as_dict=True)\r\n ] + [\r\n {\"value\": row.count, \"label\": \"Work In Progress\"} for row in frappe.db.sql(sql2, as_dict=True)\r\n ] + [\r\n {\"value\": row.count, \"label\": \"Pending Review\"} for row in frappe.db.sql(sql3, as_dict=True)\r\n ] + [\r\n {\"value\": row.count, \"label\": \"Completed\"} for row in frappe.db.sql(sql4, as_dict=True)\r\n ] + [\r\n {\"value\": row.count, \"label\": \"Closed\"} for row in frappe.db.sql(sql5, as_dict=True)\r\n ] + [\r\n {\"value\": row.count, \"label\": \"Total Work Orders\"} for row in frappe.db.sql(sql6, as_dict=True)\r\n ]\r\n\r\n # Create a dictionary to count work orders per work order type and status\r\n work_order_type_status_count = {}\r\n \r\n for row in result:\r\n work_order_type = row['work_order_type']\r\n status = row['repair_status']\r\n if work_order_type not in work_order_type_status_count:\r\n work_order_type_status_count[work_order_type] = {}\r\n if status in work_order_type_status_count[work_order_type]:\r\n work_order_type_status_count[work_order_type][status] = work_order_type_status_count[work_order_type][status] + 1 # Increment count for the status\r\n else:\r\n work_order_type_status_count[work_order_type][status] = 1 # Initialize count for the status\r\n\r\n # Prepare data for the chart\r\n chart_labels = list(work_order_type_status_count.keys()) # Work order type names\r\n chart_datasets = []\r\n\r\n # Collect unique statuses across all work order types\r\n unique_statuses = set()\r\n for statuses in work_order_type_status_count.values():\r\n unique_statuses.update(statuses.keys())\r\n unique_statuses = sorted(unique_statuses) # Sort statuses alphabetically\r\n\r\n # Prepare datasets for each status\r\n for status in unique_statuses:\r\n values = []\r\n for work_order_type in chart_labels:\r\n values.append(work_order_type_status_count[work_order_type].get(status, 0))\r\n \r\n chart_datasets.append({\r\n \"name\": status, # Status name\r\n \"values\": values # Counts per work order type\r\n })\r\n\r\n # Define the columns to be displayed in the report\r\n columns = [\r\n {\"fieldname\": \"name\", \"label\": \"Work Order ID\", \"fieldtype\": \"Link\", \"options\": \"Work Order\", \"width\": 200},\r\n {\"fieldname\": \"department\", \"label\": \"Department\", \"fieldtype\": \"Link\", \"options\": \"Department\", \"width\": 150},\r\n {\"fieldname\": \"repair_status\", \"label\": \"Status\", \"fieldtype\": \"Data\", \"width\": 100},\r\n {\"fieldname\": \"work_order_type\", \"label\": \"Work Order Type\", \"fieldtype\": \"Data\", \"width\": 150},\r\n ]\r\n \r\n # Configure the chart to show the count of work orders per work order type and status\r\n chart = {\r\n \"data\": {\r\n \"labels\": chart_labels, # Work order type names\r\n \"datasets\": chart_datasets # Each dataset corresponds to a status\r\n },\r\n \"type\": \"bar\", # You can use 'bar', 'line', 'pie', etc.\r\n \"colors\": [\"#CCCCB7\", \"#52B2BF\", \"#9EC1A4\", \"#058D7C\", \"#A3A5CF\"] # Customize the colors as needed\r\n }\r\n\r\n return columns, result, None, chart, report_summary\r\n\r\ndata = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Work_order department wise", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Work_order department wise", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing Manager" + }, + { + "parent": "Work_order department wise", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Work_order department wise", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Work_order department wise", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Work_order department wise", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + }, + { + "parent": "Work_order department wise", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "mandatory": 0, + "options": "Department", + "parent": "Asset Maintenance Assignees Status Count Department", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:12.180938", + "module": "Asset Lite", + "name": "Asset Maintenance Assignees Status Count Department", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset Maintenance Log", + "reference_report": null, + "report_name": "Asset Maintenance Assignees Status Count Department", + "report_script": "def execute(filters):\r\n result = []\r\n\r\n # Fetch the department filter value\r\n department = filters.get(\"department\")\r\n\r\n # Base query with a JOIN\r\n query = \"\"\"\r\n SELECT \r\n a.department,\r\n aml.item_name AS \"Item Name\", \r\n aml.maintenance_status AS \"Maintenance Status\", \r\n aml.assign_to_name AS \"Assigned To\", \r\n \r\n SUM(CASE \r\n WHEN aml.due_date = aml.completion_date AND aml.maintenance_status ='Completed' THEN 1 \r\n ELSE 0 \r\n END) AS \"Completed On Time\",\r\n SUM(CASE \r\n WHEN aml.completion_date < aml.due_date AND aml.maintenance_status = 'Completed' THEN 1 \r\n ELSE 0 \r\n END) AS \"Completed Within Time\",\r\n SUM(CASE \r\n WHEN aml.completion_date > aml.due_date THEN 1 \r\n ELSE 0 \r\n END) AS \"Delay In Completion\",\r\n SUM(CASE \r\n WHEN aml.maintenance_status = 'Planned' AND aml.completion_date IS NULL AND aml.due_date > CURRENT_DATE() THEN 1 \r\n ELSE 0 \r\n END) AS \"Pending\",\r\n SUM(CASE \r\n WHEN aml.maintenance_status = 'Planned' AND aml.completion_date IS NULL AND aml.due_date < CURRENT_DATE() THEN 1 \r\n ELSE 0 \r\n END) AS \"Overdue\", \r\n SUM(CASE \r\n WHEN aml.maintenance_status = 'Cancelled' THEN 1 \r\n ELSE 0 \r\n END) AS \"Cancelled\" \r\n FROM \r\n `tabAsset Maintenance Log` aml\r\n JOIN \r\n `tabAsset` a ON aml.asset_maintenance = a.name -- Adjust the join condition based on your actual field names\r\n \"\"\"\r\n\r\n # Conditional filter for department\r\n if department:\r\n query =query+ \" WHERE a.department = %s\" # Filter by department from the Asset table\r\n query_params = (department,)\r\n else:\r\n query_params = ()\r\n\r\n # Complete query\r\n query =query+ \" GROUP BY aml.assign_to_name\"\r\n\r\n # Execute the query\r\n result = frappe.db.sql(query, query_params, as_dict=True)\r\n\r\n # Define the columns\r\n columns = [\r\n {\"fieldname\": \"department\", \"label\": \"Department\", \"fieldtype\": \"Link\", \"options\": \"Department\", \"width\": 200},\r\n {\"fieldname\": \"Item Name\", \"label\": \"Item Name\", \"fieldtype\": \"Link\", \"options\": \"Employee\", \"width\": 200},\r\n {\"fieldname\": \"Maintenance Status\", \"label\": \"Maintenance Status\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"fieldname\": \"Assigned To\", \"label\": \"Assigned To\", \"fieldtype\": \"Int\", \"width\": 100},\r\n {\"fieldname\": \"Completed On Time\", \"label\": \"Completed On Time\", \"fieldtype\": \"Int\", \"width\": 150},\r\n {\"fieldname\": \"Completed Within Time\", \"label\": \"Completed Within Time\", \"fieldtype\": \"Int\", \"width\": 150},\r\n {\"fieldname\": \"Delay In Completion\", \"label\": \"Delay In Completion\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"fieldname\": \"Pending\", \"label\": \"Pending\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"fieldname\": \"Overdue\", \"label\": \"Overdue\", \"fieldtype\": \"Int\", \"width\": 100},\r\n {\"fieldname\": \"Cancelled\", \"label\": \"Cancelled\", \"fieldtype\": \"Int\", \"width\": 150},\r\n ] \r\n\r\n return columns, result # Return both columns and result\r\ndata = execute (filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Maintenance Assignees Status Count Department", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "Asset Maintenance Assignees Status Count Department", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Maintenance Assignees Status Count Department", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "mandatory": 0, + "options": "Department", + "parent": "Asset wise Count Department", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:12.142399", + "module": "Asset Lite", + "name": "Asset wise Count Department", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset Maintenance Log", + "reference_report": null, + "report_name": "Asset wise Count Department", + "report_script": "def execute(filters):\r\n result = []\r\n\r\n # Fetch the department filter value\r\n department = filters.get(\"department\")\r\n\r\n # Base query with a JOIN\r\n query = \"\"\"\r\n select \r\n a.department,\r\n aml.item_name AS \"Item Name\", \r\n aml.maintenance_status AS \"Maintenance Status\", \r\n sum(case when due_date = completion_date AND maintenance_status ='Completed' then 1 else 0 end) as \"Completed On Time\" ,\r\n sum(case when completion_date < due_date and maintenance_status = 'Completed' then 1 else 0 end) as \"Completed Within Time\",\r\n sum(case when completion_date >due_date then 1 else 0 end ) as \"Delay In Completion\",\r\n sum(case when maintenance_status = 'Planned' AND completion_date IS null AND due_date > current_date() then 1 else 0 end ) as \"Pending\",\r\n sum(case when maintenance_status = 'Planned' AND completion_date IS null AND due_date < current_date() then 1 else 0 end) as \"Overdue\" , \r\n sum(case when maintenance_status = 'Cancelled' then 1 else 0 end) as \"Cancelled\" \r\n FROM \r\n `tabAsset Maintenance Log` aml\r\n JOIN \r\n `tabAsset` a ON aml.asset_maintenance = a.name -- Adjust the join condition based on your actual field names\r\n \"\"\"\r\n \r\n \r\n # Conditional filter for department\r\n if department:\r\n query =query+ \" WHERE a.department = %s\" # Filter by department from the Asset table\r\n query_params = (department,)\r\n else:\r\n query_params = ()\r\n query =query+ \" GROUP BY aml.item_name\"\r\n \r\n\r\n # Execute the query\r\n result = frappe.db.sql(query, query_params, as_dict=True)\r\n\r\n # Define the columns\r\n columns = [\r\n {\"fieldname\": \"department\", \"label\": \"Department\", \"fieldtype\": \"Link\", \"options\": \"Department\", \"width\": 200},\r\n {\"fieldname\": \"Item Name\", \"label\": \"Item Name\", \"fieldtype\": \"Link\", \"options\": \"Employee\", \"width\": 200},\r\n {\"fieldname\": \"Maintenance Status\", \"label\": \"Maintenance Status\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"fieldname\": \"Completed On Time\", \"label\": \"Completed On Time\", \"fieldtype\": \"Int\", \"width\": 100},\r\n {\"fieldname\": \"Completed Within Time\", \"label\": \"Completed Within Time\", \"fieldtype\": \"Int\", \"width\": 150},\r\n {\"fieldname\": \"Delay In Completion\", \"label\": \"Delay In Completion\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"fieldname\": \"Pending\", \"label\": \"Pending\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"fieldname\": \"Overdue\", \"label\": \"Overdue\", \"fieldtype\": \"Int\", \"width\": 100},\r\n {\"fieldname\": \"Cancelled\", \"label\": \"Cancelled\", \"fieldtype\": \"Int\", \"width\": 150},\r\n ] \r\n\r\n return columns, result # Return both columns and result\r\ndata = execute (filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset wise Count Department", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "Asset wise Count Department", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset wise Count Department", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "support_plan", + "fieldtype": "Link", + "label": "Support Plan", + "mandatory": 0, + "options": "Support Plans", + "parent": "Support Plan", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:11.979434", + "module": "Asset Lite", + "name": "Support Plan", + "prepared_report": 0, + "query": null, + "ref_doctype": "Support Plans", + "reference_report": null, + "report_name": "Support Plan", + "report_script": "def execute(filters=None):\r\n columns = [\r\n {\"label\": \"Asset ID\", \"fieldname\": \"asset_id\", \"fieldtype\": \"Link\", \"options\": \"Asset\"},\r\n {\"label\": \"Asset Name\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Supplier\", \"fieldname\": \"supplier\", \"fieldtype\": \"Link\", \"options\": \"Supplier\"},\r\n {\"label\": \"Class\", \"fieldname\": \"class\", \"fieldtype\": \"Data\"}\r\n ]\r\n \r\n result = []\r\n\r\n # Define the class filter\r\n asset_class_filter = \"Class A\"\r\n\r\n # Fetch Assets where class is \"Class A\"\r\n assets = frappe.get_all('Asset', filters={'custom_class': asset_class_filter}, \r\n fields=['name as asset_id', 'asset_name', 'supplier', 'custom_class'])\r\n\r\n # Append each asset to the result\r\n for asset in assets:\r\n result.append({\r\n \"asset_id\": asset['asset_id'],\r\n \"asset_name\": asset['asset_name'],\r\n \"supplier\": asset['supplier'],\r\n \"class\": asset['custom_class']\r\n })\r\n\r\n return columns, result\r\ndata = execute(filters=None)\r\n\r\n\r\n\r\n\r\ndef execute(filters):\r\n columns = [\r\n {\"label\": \"Support Plan Name\", \"fieldname\": \"support_plan_name\", \"fieldtype\": \"Link\", \"options\": \"Support Plans\"},\r\n {\"label\": \"Frequency\", \"fieldname\": \"frequency\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Warranty\", \"fieldname\": \"warranty\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Service Contract\", \"fieldname\": \"service_contract\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Comprehensive\", \"fieldname\": \"spare_parts\", \"fieldtype\": \"Check\"},\r\n {\"label\": \"Spare Parts & Labour\", \"fieldname\": \"spare_parts_labour\", \"fieldtype\": \"Check\"},\r\n {\"label\": \"Labour Only\", \"fieldname\": \"labour\", \"fieldtype\": \"Check\"},\r\n {\"label\": \"PPM Only\", \"fieldname\": \"ppm_only\", \"fieldtype\": \"Check\"},\r\n {\"label\": \"Asset Name\", \"fieldname\": \"asset_id\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Asset ID\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Warranty Start Date\", \"fieldname\": \"custom_warranty_start_date\", \"fieldtype\": \"Date\"},\r\n {\"label\": \"Warranty End Date\", \"fieldname\": \"custom_warranty_end_date\", \"fieldtype\": \"Date\"},\r\n {\"label\": \"Service Start Date\", \"fieldname\": \"custom_service_contract_start\", \"fieldtype\": \"Date\"},\r\n {\"label\": \"Service End Date\", \"fieldname\": \"custom_service_contract_end\", \"fieldtype\": \"Date\"}\r\n ]\r\n \r\n result = []\r\n\r\n # Check if a filter is applied\r\n support_plan_filter = filters.get(\"support_plan\") if filters and \"support_plan\" in filters else None\r\n\r\n # Fetch Support Plans based on the filter if applied, else fetch all\r\n support_plan_filters = {}\r\n if support_plan_filter:\r\n support_plan_filters[\"name\"] = support_plan_filter\r\n \r\n support_plans = frappe.get_all('Support Plans', filters=support_plan_filters, fields=['name', 'frequency', 'warranty', 'service_contract', 'spare_parts', \"spare_parts_labour\", \"labour\", \"ppm_only\"])\r\n \r\n for plan in support_plans:\r\n \r\n # Convert 1/0 to Yes/No for boolean fields\r\n warranty_status = \"Yes\" if plan['warranty'] == 1 else \"No\"\r\n service_contract_status = \"Yes\" if plan['service_contract'] == 1 else \"No\"\r\n \r\n # Get Assets related to the Support Plan\r\n assets = frappe.get_all('Asset', filters={'custom_support_plan': plan['name']}, fields=['asset_name', 'name', 'custom_warranty_start_date', 'custom_warranty_end_date', 'custom_service_contract_start', 'custom_service_contract_end'])\r\n \r\n if assets:\r\n # Use the first asset in the same row as the support plan details\r\n first_asset = assets[0]\r\n result.append({\r\n \"support_plan_name\": plan['name'],\r\n \"frequency\": plan['frequency'],\r\n \"warranty\": plan['warranty'],\r\n \"service_contract\": plan['service_contract'],\r\n \"spare_parts\": plan['spare_parts'],\r\n \"spare_parts_labour\": plan['spare_parts_labour'],\r\n \"labour\": plan['labour'],\r\n \"ppm_only\": plan['ppm_only'],\r\n \"asset_id\": first_asset['asset_name'],\r\n \"asset_name\": first_asset['name'],\r\n \"custom_warranty_start_date\": first_asset['custom_warranty_start_date'],\r\n \"custom_warranty_end_date\": first_asset['custom_warranty_end_date'],\r\n \"custom_service_contract_start\": first_asset['custom_service_contract_start'],\r\n \"custom_service_contract_end\": first_asset['custom_service_contract_end']\r\n })\r\n \r\n # Add any remaining assets as separate rows\r\n for asset in assets[1:]:\r\n result.append({\r\n \"support_plan_name\": \"\", # Leave support plan name empty for subsequent asset rows\r\n \"frequency\": \"\",\r\n \"warranty\": \"\",\r\n \"service_contract\": \"\",\r\n \"spare_parts\": 0,\r\n \"spare_parts_labour\": 0,\r\n \"labour\": 0,\r\n \"ppm_only\": 0,\r\n \"asset_id\": asset['asset_name'],\r\n \"asset_name\": asset['name'],\r\n \"custom_warranty_start_date\": asset['custom_warranty_start_date'],\r\n \"custom_warranty_end_date\": asset['custom_warranty_end_date'],\r\n \"custom_service_contract_start\": asset['custom_service_contract_start'],\r\n \"custom_service_contract_end\": asset['custom_service_contract_end']\r\n })\r\n else:\r\n # If there are no assets, show the support plan details with empty asset fields\r\n result.append({\r\n \"support_plan_name\": plan['name'],\r\n \"frequency\": plan['frequency'],\r\n \"warranty\": plan['warranty'],\r\n \"service_contract\": plan['service_contract'],\r\n \"spare_parts\": plan['spare_parts'],\r\n \"spare_parts_labour\": plan['spare_parts_labour'],\r\n \"labour\": plan['labour'],\r\n \"ppm_only\": plan['ppm_only'],\r\n \"asset_id\": None,\r\n \"asset_name\": None,\r\n \"custom_warranty_start_date\": None,\r\n \"custom_warranty_end_date\": None,\r\n \"custom_service_contract_start\": None,\r\n \"custom_service_contract_end\": None\r\n })\r\n \r\n return columns, result\r\ndata = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Support Plan", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "Support Plan", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Support Plan", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Support Plan", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "Yes", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-02-18 09:20:47.386840", + "module": "Asset Lite", + "name": "Maintenance Percentage of Replacement Asset Value (MPRAV)", + "prepared_report": 0, + "query": null, + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Maintenance Percentage of Replacement Asset Value (MPRAV)", + "report_script": null, + "report_type": "Script Report", + "roles": [ + { + "parent": "Maintenance Percentage of Replacement Asset Value (MPRAV)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "Maintenance Percentage of Replacement Asset Value (MPRAV)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Stock User" + }, + { + "parent": "Maintenance Percentage of Replacement Asset Value (MPRAV)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-04-22 14:49:10.855537", + "module": "Asset Lite", + "name": "Technicians Avg", + "prepared_report": 0, + "query": null, + "ref_doctype": "Feedback", + "reference_report": null, + "report_name": "Technicians Avg", + "report_script": "\r\ndef execute(filters=None):\r\n columns= [\r\n {\"label\": \"Technician\", \"fieldname\": \"technician\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Total Feedbacks\", \"fieldname\": \"total_feedbacks\", \"fieldtype\": \"Int\", \"width\": 150},\r\n {\"label\": \"Raw Avg Rating\", \"fieldname\": \"avg_rating\", \"fieldtype\": \"Float\", \"width\": 150}, # Rating field type\r\n {\"label\": \"Avg Rating (%)\", \"fieldname\": \"rating_percentage\", \"fieldtype\": \"Percent\", \"width\": 150}\r\n ]\r\n\r\n\r\n technician_ratings = {}\r\n\r\n # Fetch feedback data\r\n feedbacks = frappe.get_all(\r\n \"Feedback\",\r\n filters={},\r\n fields=[\"work_order\", \"overall\"]\r\n )\r\n\r\n for feedback in feedbacks:\r\n # Fetch the assigned technician from the Work Order\r\n work_order = frappe.db.get_value(\"Work_Order\", feedback[\"work_order\"], \"assigned_technician\")\r\n\r\n if not work_order:\r\n continue # Skip if no technician assigned\r\n\r\n technician = work_order\r\n\r\n if technician not in technician_ratings:\r\n technician_ratings[technician] = {\"total_rating\": 0, \"count\": 0}\r\n\r\n # Fix for rating field normalization (0-1 scale) → Convert to 5-point scale\r\n rating_value = feedback[\"overall\"] * 5\r\n\r\n technician_ratings[technician][\"total_rating\"] =technician_ratings[technician][\"total_rating\"]+ rating_value\r\n technician_ratings[technician][\"count\"] = technician_ratings[technician][\"count\"]+1\r\n\r\n # Convert into report data format\r\n report_data = []\r\n for technician, rating_data in technician_ratings.items():\r\n total_feedbacks = rating_data[\"count\"]\r\n avg_rating = (rating_data[\"total_rating\"] / total_feedbacks) if total_feedbacks else 0\r\n rating_percentage = (avg_rating / 5) * 100 # Convert to percentage\r\n\r\n report_data.append({\r\n \"technician\": technician,\r\n \"total_feedbacks\": total_feedbacks,\r\n \"avg_rating\": avg_rating, # Raw avg rating\r\n \"rating_percentage\": rating_percentage # Percentage\r\n })\r\n\r\n return columns,report_data\r\n \r\ndata=execute(filters=None)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Technicians Avg", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "Technicians Avg", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Technicians Avg", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "asset", + "fieldtype": "Link", + "label": "Asset", + "mandatory": 0, + "options": "Asset", + "parent": "Total Hrs and Downtime Hrs by Asset", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-04-22 14:49:11.360974", + "module": "Asset Lite", + "name": "Total Hrs and Downtime Hrs by Asset", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Total Hrs and Downtime Hrs by Asset", + "report_script": "def execute(filters):\r\n columns = [\r\n {\"label\": \"\", \"fieldname\": \"name\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Total Hours of All Assets\", \"fieldname\": \"total_hours_sum\", \"fieldtype\": \"Float\"},\r\n {\"label\": \"Total Downtime Hours of All Assets\", \"fieldname\": \"downtime_hours_sum\", \"fieldtype\": \"Float\"},\r\n {\"label\": \"Total Uptime Hours of All Assets\", \"fieldname\": \"uptime_hours_sum\", \"fieldtype\": \"Float\"}\r\n ]\r\n \r\n result = []\r\n \r\n # Get the department and class filters from the input\r\n department = filters.get(\"department\") if filters else None\r\n asset_class = filters.get(\"class\") if filters else None\r\n asset_name = filters.get(\"asset\") if filters else None\r\n \r\n # Build the filter conditions based on the department and class filters\r\n asset_filters = {}\r\n if department:\r\n asset_filters['department'] = department\r\n if asset_class:\r\n asset_filters['custom_class'] = asset_class\r\n if asset_name:\r\n asset_filters['name'] = asset_name\r\n \r\n # Fetch all assets with their total hours and downtime hours based on filters\r\n assets = frappe.get_all('Asset', filters=asset_filters, fields=['custom_total_hours', 'custom_down_time'])\r\n \r\n # Initialize sums\r\n total_hours_sum = round(sum(asset['custom_total_hours'] or 0 for asset in assets))\r\n downtime_hours_sum = round(sum(asset['custom_down_time'] or 0 for asset in assets))\r\n uptime_hours_sum = round(total_hours_sum - downtime_hours_sum)\r\n \r\n if total_hours_sum == 0:\r\n total_hours_sum = downtime_hours_sum\r\n uptime_hours_sum = round(total_hours_sum - downtime_hours_sum)\r\n \r\n \r\n \r\n # Add the sums to the result\r\n result.append({\r\n \"name\":\"Hours\",\r\n \"total_hours_sum\": total_hours_sum,\r\n \"downtime_hours_sum\": downtime_hours_sum,\r\n \"uptime_hours_sum\": uptime_hours_sum\r\n })\r\n \r\n return columns, result\r\n\r\ndata = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Total Hrs and Downtime Hrs by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Total Hrs and Downtime Hrs by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Total Hrs and Downtime Hrs by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Total Hrs and Downtime Hrs by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Total Hrs and Downtime Hrs by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Total Hrs and Downtime Hrs by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + }, + { + "parent": "Total Hrs and Downtime Hrs by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "mandatory": 0, + "options": "Department", + "parent": "Asset Maintenance Frequency Department", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "name", + "fieldtype": "Link", + "label": "Asset", + "mandatory": 0, + "options": "Asset", + "parent": "Asset Maintenance Frequency Department", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:10.886475", + "module": "Asset Lite", + "name": "Asset Maintenance Frequency Department", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset Maintenance Log", + "reference_report": null, + "report_name": "Asset Maintenance Frequency Department", + "report_script": "def execute(filters):\r\n result = []\r\n\r\n # Fetch the department filter value\r\n department = filters.get(\"department\")\r\n\r\n # Define the query\r\n if department:\r\n query = \"\"\"\r\n SELECT\r\n a.department,\r\n aml.item_name AS item,\r\n SUM(CASE WHEN maintenance_status = 'Planned' THEN 1 ELSE 0 END) AS Planned,\r\n SUM(CASE WHEN maintenance_status = 'Completed' THEN 1 ELSE 0 END) AS Completed,\r\n SUM(CASE WHEN maintenance_status = 'Cancelled' THEN 1 ELSE 0 END) AS Cancelled,\r\n SUM(CASE WHEN maintenance_status = 'Overdue' THEN 1 ELSE 0 END) AS Overdue,\r\n COUNT(aml.item_name) AS `Total Count`\r\n FROM \r\n `tabAsset Maintenance Log` aml\r\n JOIN \r\n `tabAsset` a ON aml.asset_maintenance = a.name \r\n WHERE\r\n department = %s\r\n GROUP BY\r\n aml.item_name\r\n \"\"\"\r\n result = frappe.db.sql(query, (department,), as_dict=True)\r\n\r\n else:\r\n query = \"\"\"\r\n SELECT\r\n a.department,\r\n aml.item_name AS item,\r\n SUM(CASE WHEN maintenance_status = 'Planned' THEN 1 ELSE 0 END) AS Planned,\r\n SUM(CASE WHEN maintenance_status = 'Completed' THEN 1 ELSE 0 END) AS Completed,\r\n SUM(CASE WHEN maintenance_status = 'Cancelled' THEN 1 ELSE 0 END) AS Cancelled,\r\n SUM(CASE WHEN maintenance_status = 'Overdue' THEN 1 ELSE 0 END) AS Overdue,\r\n COUNT(aml.item_name) AS `Total Count`\r\n FROM \r\n `tabAsset Maintenance Log` aml\r\n JOIN \r\n `tabAsset` a ON aml.asset_maintenance = a.name \r\n GROUP BY\r\n aml.item_name\r\n \"\"\"\r\n result = frappe.db.sql(query, as_dict=True)\r\n\r\n # Define the columns for the report\r\n columns = [\r\n {\"fieldname\": \"department\", \"label\": \"Department\", \"fieldtype\": \"Link\",\"options\":\"Department\", \"width\": 150},\r\n {\"fieldname\": \"item\", \"label\": \"Item Name\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"fieldname\": \"Planned\", \"label\": \"Planned\", \"fieldtype\": \"Int\", \"width\": 100},\r\n {\"fieldname\": \"Completed\", \"label\": \"Completed\", \"fieldtype\": \"Int\", \"width\": 100},\r\n {\"fieldname\": \"Cancelled\", \"label\": \"Cancelled\", \"fieldtype\": \"Int\", \"width\": 100},\r\n {\"fieldname\": \"Overdue\", \"label\": \"Overdue\", \"fieldtype\": \"Int\", \"width\": 100},\r\n {\"fieldname\": \"Total Count\", \"label\": \"Total Count\", \"fieldtype\": \"Int\", \"width\": 100}\r\n ]\r\n\r\n return columns, result\r\ndata = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Maintenance Frequency Department", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "Asset Maintenance Frequency Department", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Maintenance Frequency Department", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset Maintenance Frequency Department", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "mandatory": 0, + "options": "Supplier", + "parent": "Supplier Score", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "Yes", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2024-12-19 13:04:47.947899", + "module": "Asset Lite", + "name": "Supplier Score", + "prepared_report": 0, + "query": null, + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Supplier Score", + "report_script": "", + "report_type": "Script Report", + "roles": [ + { + "parent": "Supplier Score", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "Yes", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2024-12-19 12:25:59.662186", + "module": "Asset Lite", + "name": "Supplier Down Time", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Supplier Down Time", + "report_script": null, + "report_type": "Script Report", + "roles": [ + { + "parent": "Supplier Down Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Supplier Down Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Supplier Down Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Supplier Down Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Supplier Down Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Supplier Down Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + }, + { + "parent": "Supplier Down Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "Yes", + "javascript": null, + "json": "{}", + "letter_head": "Test", + "modified": "2025-01-07 22:30:21.848955", + "module": "Asset Lite", + "name": "Preventive maintenance compliance (PMC)", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset Maintenance Log", + "reference_report": null, + "report_name": "Preventive maintenance compliance (PMC)", + "report_script": null, + "report_type": "Script Report", + "roles": [ + { + "parent": "Preventive maintenance compliance (PMC)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "Preventive maintenance compliance (PMC)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Preventive maintenance compliance (PMC)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + } + ], + "timeout": 0 + }, + { + "add_total_row": 1, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "asset", + "fieldtype": "Link", + "label": "Asset", + "mandatory": 0, + "options": "Asset", + "parent": "Asset Count (Supplier) by Asset", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-04-22 14:49:11.387931", + "module": "Asset Lite", + "name": "Asset Count (Supplier) by Asset", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Asset Count (Supplier) by Asset", + "report_script": "\r\ndef execute(filters=None):\r\n if not filters:\r\n filters = {}\r\n\r\n result = []\r\n asset = filters.get(\"asset\") # Fetch asset filter\r\n\r\n # SQL Query to fetch asset details\r\n if asset:\r\n query = \"\"\"\r\n SELECT\r\n name,\r\n asset_name,\r\n custom_serial_number\r\n FROM\r\n `tabAsset`\r\n WHERE\r\n name = %s\r\n ORDER BY\r\n asset_name\r\n \"\"\"\r\n result = frappe.db.sql(query, (asset,), as_dict=True)\r\n else:\r\n # If no asset filter is applied, return all assets\r\n query_all = \"\"\"\r\n SELECT\r\n name,\r\n asset_name,\r\n custom_serial_number\r\n FROM\r\n `tabAsset`\r\n ORDER BY\r\n asset_name\r\n \"\"\"\r\n result = frappe.db.sql(query_all, as_dict=True)\r\n\r\n # Create a dictionary to count occurrences of each asset\r\n asset_count = {}\r\n\r\n for row in result:\r\n asset_name = row['asset_name']\r\n asset_count[asset_name] = asset_count.get(asset_name, 0) + 1\r\n\r\n # Prepare data for the chart\r\n chart_labels = list(asset_count.keys()) # Asset names\r\n chart_values = list(asset_count.values()) # Count of assets\r\n\r\n # Define the columns for the report\r\n columns = [\r\n {\"fieldname\": \"name\", \"label\": \"Asset ID\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 200},\r\n {\"fieldname\": \"asset_name\", \"label\": \"Asset Name\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"fieldname\": \"custom_serial_number\", \"label\": \"serial\", \"fieldtype\": \"Data\", \"width\": 150},\r\n ]\r\n\r\n # Configure the chart (Pie chart showing asset distribution)\r\n chart = {\r\n \"data\": {\r\n \"labels\": chart_labels, # Asset Names\r\n \"datasets\": [\r\n {\r\n \"name\": \"Asset Count\",\r\n \"values\": chart_values # Count of each asset type\r\n }\r\n ]\r\n },\r\n \"type\": \"pie\", # Can be 'bar', 'line', or 'pie'\r\n \"colors\": [\"#ECAD4B\", \"#39E4A5\", \"#B4CD29\"] # Custom colors\r\n }\r\n\r\n return columns, result, None, chart\r\n\r\ndata = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Count (Supplier) by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Asset Count (Supplier) by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Asset Count (Supplier) by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Asset Count (Supplier) by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset Count (Supplier) by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Count (Supplier) by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + }, + { + "parent": "Asset Count (Supplier) by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "mandatory": 0, + "options": "Department", + "parent": "Asset Class A", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:12.098412", + "module": "Asset Lite", + "name": "Asset Class A", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Asset Class A", + "report_script": "def execute(filters=None):\r\n columns = [\r\n {\"label\": \"Asset ID\", \"fieldname\": \"asset_id\", \"fieldtype\": \"Link\", \"options\": \"Asset\"},\r\n {\"label\": \"Asset Name\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Supplier\", \"fieldname\": \"supplier\", \"fieldtype\": \"Link\", \"options\": \"Supplier\"},\r\n {\"label\": \"Class\", \"fieldname\": \"class\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Department\", \"fieldname\": \"department\", \"fieldtype\": \"Link\", \"options\": \"Department\"}\r\n ]\r\n \r\n result = []\r\n department_count = {} # To store the count of assets by department\r\n\r\n # Define the class filter\r\n asset_class_filter = \"Class A\"\r\n\r\n # Add an optional department filter (from filters if provided)\r\n department_filter = filters.get(\"department\") if filters else None\r\n \r\n # Create the filters dictionary\r\n asset_filters = {'custom_class': asset_class_filter}\r\n \r\n # Add department filter if provided\r\n if department_filter:\r\n asset_filters['department'] = department_filter\r\n\r\n # Fetch Assets where class is \"Class A\" and optionally filter by department\r\n assets = frappe.get_all('Asset', filters=asset_filters, \r\n fields=['name as asset_id', 'asset_name', 'supplier', 'custom_class', 'department'])\r\n\r\n # Append each asset to the result and count by department\r\n for asset in assets:\r\n result.append({\r\n \"asset_id\": asset['asset_id'],\r\n \"asset_name\": asset['asset_name'],\r\n \"supplier\": asset['supplier'],\r\n \"class\": asset['custom_class'],\r\n \"department\": asset['department']\r\n })\r\n\r\n # Count assets by department\r\n department = asset['department']\r\n if department:\r\n department_count[department] = department_count.get(department, 0) + 1\r\n\r\n # Prepare chart data (labels and values for each department)\r\n chart_labels = list(department_count.keys()) # Department names\r\n chart_values = list(department_count.values()) # Count of assets in each department\r\n\r\n # Create the chart configuration\r\n chart = {\r\n \"data\": {\r\n \"labels\": chart_labels, # Department names\r\n \"datasets\": [\r\n {\r\n \"name\": \"Number of Assets\",\r\n \"values\": chart_values # Asset counts for each department\r\n }\r\n ]\r\n },\r\n \"type\": \"bar\", # You can use 'bar', 'line', 'pie', etc.\r\n \"colors\": [\"#FF6384\"] # Customize colors for the chart\r\n }\r\n\r\n return columns, result, None, chart\r\n\r\n# Example call with filters\r\ndata = execute(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Class A", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Asset Class A", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Asset Class A", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Asset Class A", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset Class A", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Class A", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + }, + { + "parent": "Asset Class A", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "mandatory": 0, + "options": "Department", + "parent": "Asset Class B", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:12.055822", + "module": "Asset Lite", + "name": "Asset Class B", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Asset Class B", + "report_script": "def execute(filters):\r\n columns = [\r\n {\"label\": \"Asset ID\", \"fieldname\": \"asset_id\", \"fieldtype\": \"Link\", \"options\": \"Asset\"},\r\n {\"label\": \"Asset Name\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Supplier\", \"fieldname\": \"supplier\", \"fieldtype\": \"Link\", \"options\": \"Supplier\"},\r\n {\"label\": \"Class\", \"fieldname\": \"class\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Department\", \"fieldname\": \"department\", \"fieldtype\": \"Link\", \"options\": \"Department\"}\r\n ]\r\n \r\n result = []\r\n department_count = {} # To store the count of assets by department\r\n\r\n # Define the class filter\r\n asset_class_filter = \"Class B\"\r\n\r\n # Add an optional department filter (from filters if provided)\r\n department_filter = filters.get(\"department\") if filters else None\r\n \r\n # Create the filters dictionary\r\n asset_filters = {'custom_class': asset_class_filter}\r\n \r\n # Add department filter if provided\r\n if department_filter:\r\n asset_filters['department'] = department_filter\r\n\r\n # Fetch Assets where class is \"Class A\" and optionally filter by department\r\n assets = frappe.get_all('Asset', filters=asset_filters, \r\n fields=['name as asset_id', 'asset_name', 'supplier', 'custom_class', 'department'])\r\n\r\n # Append each asset to the result and count by department\r\n for asset in assets:\r\n result.append({\r\n \"asset_id\": asset['asset_id'],\r\n \"asset_name\": asset['asset_name'],\r\n \"supplier\": asset['supplier'],\r\n \"class\": asset['custom_class'],\r\n \"department\": asset['department']\r\n })\r\n\r\n # Count assets by department\r\n department = asset['department']\r\n if department:\r\n department_count[department] = department_count.get(department, 0) + 1\r\n\r\n # Prepare chart data (labels and values for each department)\r\n chart_labels = list(department_count.keys()) # Department names\r\n chart_values = list(department_count.values()) # Count of assets in each department\r\n\r\n # Create the chart configuration\r\n chart = {\r\n \"data\": {\r\n \"labels\": chart_labels, # Department names\r\n \"datasets\": [\r\n {\r\n \"name\": \"Number of Assets\",\r\n \"values\": chart_values # Asset counts for each department\r\n }\r\n ]\r\n },\r\n \"type\": \"bar\", # You can use 'bar', 'line', 'pie', etc.\r\n \"colors\": [\"#FF6384\"] # Customize colors for the chart\r\n }\r\n\r\n return columns, result, None, chart\r\n\r\n# Example call with filters\r\ndata = execute(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Class B", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Asset Class B", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Asset Class B", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Asset Class B", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset Class B", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Class B", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + }, + { + "parent": "Asset Class B", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "mandatory": 0, + "options": "Department", + "parent": "Asset Class C", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:12.016518", + "module": "Asset Lite", + "name": "Asset Class C", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Asset Class C", + "report_script": "def execute(filters):\r\n columns = [\r\n {\"label\": \"Asset ID\", \"fieldname\": \"asset_id\", \"fieldtype\": \"Link\", \"options\": \"Asset\"},\r\n {\"label\": \"Asset Name\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Supplier\", \"fieldname\": \"supplier\", \"fieldtype\": \"Link\", \"options\": \"Supplier\"},\r\n {\"label\": \"Class\", \"fieldname\": \"class\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Department\", \"fieldname\": \"department\", \"fieldtype\": \"Link\", \"options\": \"Department\"}\r\n ]\r\n \r\n result = []\r\n department_count = {} # To store the count of assets by department\r\n\r\n # Define the class filter\r\n asset_class_filter = \"Class C\"\r\n\r\n # Add an optional department filter (from filters if provided)\r\n department_filter = filters.get(\"department\") if filters else None\r\n \r\n # Create the filters dictionary\r\n asset_filters = {'custom_class': asset_class_filter}\r\n \r\n # Add department filter if provided\r\n if department_filter:\r\n asset_filters['department'] = department_filter\r\n\r\n # Fetch Assets where class is \"Class A\" and optionally filter by department\r\n assets = frappe.get_all('Asset', filters=asset_filters, \r\n fields=['name as asset_id', 'asset_name', 'supplier', 'custom_class', 'department'])\r\n\r\n # Append each asset to the result and count by department\r\n for asset in assets:\r\n result.append({\r\n \"asset_id\": asset['asset_id'],\r\n \"asset_name\": asset['asset_name'],\r\n \"supplier\": asset['supplier'],\r\n \"class\": asset['custom_class'],\r\n \"department\": asset['department']\r\n })\r\n\r\n # Count assets by department\r\n department = asset['department']\r\n if department:\r\n department_count[department] = department_count.get(department, 0) + 1\r\n\r\n # Prepare chart data (labels and values for each department)\r\n chart_labels = list(department_count.keys()) # Department names\r\n chart_values = list(department_count.values()) # Count of assets in each department\r\n\r\n # Create the chart configuration\r\n chart = {\r\n \"data\": {\r\n \"labels\": chart_labels, # Department names\r\n \"datasets\": [\r\n {\r\n \"name\": \"Number of Assets\",\r\n \"values\": chart_values # Asset counts for each department\r\n }\r\n ]\r\n },\r\n \"type\": \"bar\", # You can use 'bar', 'line', 'pie', etc.\r\n \"colors\": [\"#FF6384\"] # Customize colors for the chart\r\n }\r\n\r\n return columns, result, None, chart\r\n\r\n# Example call with filters\r\ndata = execute(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Class C", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Asset Class C", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Asset Class C", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Asset Class C", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset Class C", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Class C", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + }, + { + "parent": "Asset Class C", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "company", + "fieldtype": "Link", + "label": "Hospital Name", + "mandatory": 0, + "options": "Company", + "parent": "Work Order Status by Asset", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": "", + "json": null, + "letter_head": "", + "modified": "2025-04-22 14:49:11.088059", + "module": "Asset Lite", + "name": "Work Order Status by Asset", + "prepared_report": 0, + "query": "", + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Work Order Status by Asset", + "report_script": "def get_result(filters=None):\r\n if filters is None:\r\n filters = {}\r\n\r\n columns = [\r\n {\"label\": \"Hospital Name\", \"fieldname\": \"company\", \"fieldtype\": \"Link\", \"options\": \"Company\", \"width\": 200},\r\n {\"label\": \"Work Order Type\", \"fieldname\": \"work_order_type\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Open\", \"fieldname\": \"open_count\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"label\": \"Work In Progress\", \"fieldname\": \"work_in_progress_count\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"label\": \"Pending Review\", \"fieldname\": \"pending_review_count\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"label\": \"Completed\", \"fieldname\": \"completed_count\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"label\": \"Closed\", \"fieldname\": \"closed_count\", \"fieldtype\": \"Int\", \"width\": 200}\r\n ]\r\n\r\n # Build Additional Filters\r\n additional_filters = \"\"\r\n filter_fields = [\"work_order_type\", \"repair_status\", \"company\"]\r\n\r\n for field in filter_fields:\r\n if filters.get(field):\r\n additional_filters =additional_filters + f\" AND wt.{field} = '{filters.get(field)}'\"\r\n\r\n # Main Query with Company Filter\r\n query = f\"\"\"\r\n SELECT\r\n company,\r\n work_order_type,\r\n SUM(CASE WHEN repair_status = 'Open' THEN 1 ELSE 0 END) AS open_count,\r\n SUM(CASE WHEN repair_status = 'Work In Progress' THEN 1 ELSE 0 END) AS work_in_progress_count,\r\n SUM(CASE WHEN repair_status = 'Pending Review' THEN 1 ELSE 0 END) AS pending_review_count,\r\n SUM(CASE WHEN repair_status = 'Completed' THEN 1 ELSE 0 END) AS completed_count,\r\n SUM(CASE WHEN repair_status = 'Closed' THEN 1 ELSE 0 END) AS closed_count\r\n FROM\r\n `tabWork_Order` as wt\r\n WHERE\r\n repair_status IN ('Open', 'Work In Progress', 'Pending Review', 'Completed', 'Closed', 'Cancelled') {additional_filters}\r\n GROUP BY\r\n company, work_order_type\r\n \"\"\"\r\n \r\n result = frappe.db.sql(query, as_dict=True)\r\n\r\n # Summary Queries with Company Filter\r\n sql_queries = {\r\n \"Open\": \"SELECT COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Open'\",\r\n \"Work In Progress\": \"SELECT COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Work In Progress'\",\r\n \"Pending Review\": \"SELECT COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Pending Review'\",\r\n \"Completed\": \"SELECT COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Completed'\",\r\n \"Closed\": \"SELECT COUNT(*) as count FROM `tabWork_Order` WHERE repair_status = 'Closed'\",\r\n \"Total Work Orders\": \"SELECT COUNT(*) as count FROM `tabWork_Order` WHERE repair_status IN ('Open', 'Work In Progress', 'Pending Review', 'Completed', 'Closed', 'Cancelled')\"\r\n }\r\n\r\n # Apply Company Filter to Summary Queries (Corrected Indentation)\r\n if filters.get(\"company\"): # ✅ Proper indentation\r\n company_filter = f\" AND company = '{filters.get('company')}'\"\r\n for key in sql_queries:\r\n sql_queries[key] = sql_queries[key] + company_filter # ✅ Correct fix\r\n\r\n # Execute Summary Queries\r\n report_summary = [{\"value\": row.count, \"label\": label} for label, query in sql_queries.items() for row in frappe.db.sql(query, as_dict=True)]\r\n\r\n # Chart Data\r\n chart = {\r\n \"data\": {\r\n \"labels\": [row[\"work_order_type\"] for row in result],\r\n \"datasets\": [\r\n {\"name\": \"Open\", \"values\": [row[\"open_count\"] for row in result]},\r\n {\"name\": \"Work In Progress\", \"values\": [row[\"work_in_progress_count\"] for row in result]},\r\n {\"name\": \"Pending Review\", \"values\": [row[\"pending_review_count\"] for row in result]},\r\n {\"name\": \"Completed\", \"values\": [row[\"completed_count\"] for row in result]},\r\n {\"name\": \"Closed\", \"values\": [row[\"closed_count\"] for row in result]},\r\n ]\r\n },\r\n \"type\": \"bar\",\r\n \"barOptions\": {\r\n \"stacked\": 1,\r\n \"spaceRatio\": 0.6\r\n },\r\n \"colors\": [\"#CCCCB7\", \"#52B2BF\", \"#9EC1A4\", \"#058D7C\", \"#A3A5CF\"],\r\n }\r\n\r\n return columns, result, None, chart, report_summary\r\n\r\n# Calling the function\r\ndata = get_result(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Work Order Status by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "Work Order Status by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Work Order Status by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Work Order Status by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "mandatory": 0, + "options": "Department", + "parent": "Asset List Department wise", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": "frappe.query_reports[\"Asset List Department wise\"] = {\r\n onload: function(report) {\r\n // Fetch the logged-in user's department\r\n frappe.call({\r\n method: \"frappe.client.get_value\",\r\n args: {\r\n doctype: \"Employee\",\r\n filters: {\"user_id\": frappe.session.user},\r\n fieldname: \"department\"\r\n },\r\n callback: function(r) {\r\n if (r.message && r.message.department) {\r\n // Set the department filter value\r\n let department = r.message.department;\r\n report.set_filter_value(\"department\", department);\r\n\r\n // Now, set this value in the chart filter as well\r\n const chart = report.page.charts.find(chart => chart.chart_name === \"Asset Department Wisr\");\r\n if (chart) {\r\n chart.update_filter_value(\"department\", department);\r\n chart.refresh(); // Refresh to apply the filter\r\n }\r\n }\r\n }\r\n });\r\n }\r\n};\r\n", + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:12.255598", + "module": "Asset Lite", + "name": "Asset List Department wise", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Asset List Department wise", + "report_script": "def execute(filters):\r\n result = []\r\n\r\n # Fetch the department filter value\r\n department = filters.get(\"department\")\r\n\r\n # Query to fetch asset names, departments, status, and location\r\n if department:\r\n query = \"\"\"\r\n SELECT\r\n name,\r\n asset_name,\r\n department,\r\n status,\r\n location\r\n FROM\r\n `tabAsset`\r\n WHERE\r\n department = %s\r\n ORDER BY\r\n asset_name\r\n \"\"\"\r\n result = frappe.db.sql(query, department, as_dict=True)\r\n else:\r\n # If no department is selected, return all assets\r\n query_no_department = \"\"\"\r\n SELECT\r\n name,\r\n asset_name,\r\n department,\r\n status,\r\n location\r\n FROM\r\n `tabAsset`\r\n ORDER BY\r\n asset_name\r\n \"\"\"\r\n result = frappe.db.sql(query_no_department, as_dict=True)\r\n\r\n # Create a dictionary to count assets per department\r\n department_count = {}\r\n \r\n for row in result:\r\n dept = row['department']\r\n if dept in department_count:\r\n department_count[dept] = department_count[dept] + 1 # Increment count\r\n else:\r\n department_count[dept] = 1 # Initialize count\r\n\r\n # Prepare data for the chart\r\n chart_labels = list(department_count.keys()) # Departments\r\n chart_values = list(department_count.values()) # Count of assets\r\n\r\n # Define the columns to be displayed in the report\r\n columns = [\r\n {\"fieldname\": \"name\", \"label\": \"Asset ID\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 200},\r\n {\"fieldname\": \"asset_name\", \"label\": \"Asset Name\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 200},\r\n {\"fieldname\": \"status\", \"label\": \"Status\", \"fieldtype\": \"Data\", \"width\": 100},\r\n {\"fieldname\": \"location\", \"label\": \"Location\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"fieldname\": \"department\", \"label\": \"Department\", \"fieldtype\": \"Link\", \"options\": \"Department\", \"width\": 150}\r\n ]\r\n\r\n # Configure the chart to show the number of assets per department\r\n chart = {\r\n \"data\": {\r\n \"labels\": chart_labels, # Department names\r\n \"datasets\": [\r\n {\r\n \"name\": \"Number of Assets\",\r\n \"values\": chart_values # Count of assets for each department\r\n }\r\n ]\r\n },\r\n \"type\": \"pie\", # You can use 'bar', 'line', 'pie', etc.\r\n \"colors\": [\"#3498db\"] # Customize the color as needed\r\n }\r\n\r\n return columns, result, None, chart\r\ndata = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset List Department wise", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Asset List Department wise", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Asset List Department wise", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Asset List Department wise", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset List Department wise", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset List Department wise", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "Yes", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-03-05 21:09:42.461336", + "module": "Asset Lite", + "name": "Supplier Total Score", + "prepared_report": 0, + "query": null, + "ref_doctype": "Supplier Scorecard", + "reference_report": null, + "report_name": "Supplier Total Score", + "report_script": null, + "report_type": "Script Report", + "roles": [ + { + "parent": "Supplier Total Score", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "company", + "fieldtype": "Link", + "label": "Hospital Name", + "mandatory": 0, + "options": "Company", + "parent": "Asset Maintenance Frequency Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "year", + "fieldtype": "Data", + "label": "Year", + "mandatory": 0, + "options": "\n2020\n2021\n2022\n2023\n2024\n2025\n2026\n2027\n2028\n2029\n2030", + "parent": "Asset Maintenance Frequency Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "month", + "fieldtype": "Select", + "label": "Month", + "mandatory": 0, + "options": "\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember", + "parent": "Asset Maintenance Frequency Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "", + "modified": "2025-04-22 14:49:11.266824", + "module": "Asset Lite", + "name": "Asset Maintenance Frequency Report", + "prepared_report": 0, + "query": "select\r\n custom_asset_names as Item,\r\n SUM(CASE WHEN maintenance_status = 'Planned' THEN 1 ELSE 0 END) AS Planned,\r\n SUM(CASE WHEN maintenance_status = 'Completed' THEN 1 ELSE 0 END) as \"Completed\",\r\n SUM(CASE WHEN maintenance_status = 'Cancelled' THEN 1 ELSE 0 END) as \"Cancelled\",\r\n SUM(CASE WHEN maintenance_status = 'Overdue' THEN 1 ELSE 0 END) as \"Overdue\",\r\n COUNT(custom_asset_names) as \"Total Count\"\r\nfrom \r\n `tabAsset Maintenance Log`\r\ngroup by\r\n custom_asset_names", + "ref_doctype": "Asset Maintenance Log", + "reference_report": "", + "report_name": "Asset Maintenance Frequency Report", + "report_script": "\r\n\r\ndef execute(filters=None):\r\n if not filters:\r\n filters = {}\r\n\r\n # Build dynamic SQL filters\r\n conditions = \"1=1\" # Always true (used for dynamic filtering)\r\n query_params = {}\r\n\r\n # Apply Company Filter\r\n if filters.get(\"company\"):\r\n conditions =conditions + \" AND a.company = %(company)s\"\r\n query_params[\"company\"] = filters.get(\"company\")\r\n\r\n # Apply Year & Month Filters\r\n if filters.get(\"year\"):\r\n conditions =conditions + \" AND YEAR(aml.creation) = %(year)s\"\r\n query_params[\"year\"] = int(filters.get(\"year\"))\r\n\r\n if filters.get(\"month\"):\r\n month_map = {\r\n \"January\": 1, \"February\": 2, \"March\": 3, \"April\": 4,\r\n \"May\": 5, \"June\": 6, \"July\": 7, \"August\": 8,\r\n \"September\": 9, \"October\": 10, \"November\": 11, \"December\": 12\r\n }\r\n month_num = month_map.get(filters.get(\"month\"))\r\n if month_num:\r\n conditions =conditions + \" AND MONTH(aml.creation) = %(month)s\"\r\n query_params[\"month\"] = month_num\r\n\r\n # SQL Query\r\n query = f\"\"\"\r\n SELECT\r\n aml.custom_asset_names AS item,\r\n a.company AS company,\r\n SUM(CASE WHEN aml.maintenance_status = 'Planned' THEN 1 ELSE 0 END) AS planned,\r\n SUM(CASE WHEN aml.maintenance_status = 'Completed' THEN 1 ELSE 0 END) AS completed,\r\n SUM(CASE WHEN aml.maintenance_status = 'Cancelled' THEN 1 ELSE 0 END) AS cancelled,\r\n SUM(CASE WHEN aml.maintenance_status = 'Overdue' THEN 1 ELSE 0 END) AS overdue,\r\n COUNT(aml.custom_asset_names) AS total_count\r\n FROM \r\n `tabAsset Maintenance Log` aml\r\n LEFT JOIN\r\n `tabAsset` a ON aml.asset_name = a.name\r\n WHERE\r\n {conditions}\r\n GROUP BY\r\n aml.custom_asset_names, a.company\r\n ORDER BY \r\n total_count DESC\r\n \"\"\"\r\n\r\n # Execute query\r\n result = frappe.db.sql(query, query_params, as_dict=True)\r\n\r\n # Define Columns\r\n columns = [\r\n {\"label\": \"Item\", \"fieldname\": \"item\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Hospital Name\", \"fieldname\": \"company\", \"fieldtype\": \"Link\", \"options\": \"Company\", \"width\": 200},\r\n {\"label\": \"Planned\", \"fieldname\": \"planned\", \"fieldtype\": \"Int\", \"width\": 100},\r\n {\"label\": \"Completed\", \"fieldname\": \"completed\", \"fieldtype\": \"Int\", \"width\": 100},\r\n {\"label\": \"Cancelled\", \"fieldname\": \"cancelled\", \"fieldtype\": \"Int\", \"width\": 100},\r\n {\"label\": \"Overdue\", \"fieldname\": \"overdue\", \"fieldtype\": \"Int\", \"width\": 100},\r\n {\"label\": \"Total Count\", \"fieldname\": \"total_count\", \"fieldtype\": \"Int\", \"width\": 100},\r\n ]\r\n\r\n return columns, result\r\n\r\ndata = execute(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Maintenance Frequency Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Maintenance Frequency Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + } + ], + "timeout": 0 + }, + { + "add_total_row": 1, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "hospital", + "fieldtype": "Link", + "label": "Hospital Name", + "mandatory": 0, + "options": "Company", + "parent": "PM Status Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "month", + "fieldtype": "Select", + "label": "Month", + "mandatory": 0, + "options": "\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember", + "parent": "PM Status Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "year", + "fieldtype": "Select", + "label": "Year", + "mandatory": 0, + "options": "\n2020\n2021\n2022\n2023\n2024\n2025\n2026\n2027\n2028\n2029\n2030", + "parent": "PM Status Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-04-22 14:49:11.037963", + "module": "Asset Lite", + "name": "PM Status Report", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset Maintenance Log", + "reference_report": null, + "report_name": "PM Status Report", + "report_script": "def execute(filters=None):\r\n if not filters:\r\n filters = {}\r\n\r\n conditions = [\"aml.custom_hospital_name IS NOT NULL AND aml.custom_hospital_name != ''\"]\r\n query_params = {}\r\n\r\n # Apply Year Filter\r\n if filters.get(\"year\"):\r\n conditions.append(\"YEAR(aml.due_date) = %(year)s\")\r\n query_params[\"year\"] = filters.get(\"year\")\r\n\r\n # Apply Month Filter\r\n if filters.get(\"month\"):\r\n month_mapping = {\r\n \"January\": 1, \"February\": 2, \"March\": 3, \"April\": 4,\r\n \"May\": 5, \"June\": 6, \"July\": 7, \"August\": 8,\r\n \"September\": 9, \"October\": 10, \"November\": 11, \"December\": 12\r\n }\r\n month_num = month_mapping.get(filters.get(\"month\"))\r\n if month_num:\r\n conditions.append(\"MONTH(aml.due_date) = %(month)s\")\r\n query_params[\"month\"] = month_num\r\n\r\n # Apply Hospital Filter\r\n if filters.get(\"hospital\"):\r\n conditions.append(\"aml.custom_hospital_name = %(hospital)s\")\r\n query_params[\"hospital\"] = filters.get(\"hospital\")\r\n\r\n # Construct WHERE Clause\r\n where_clause = \"WHERE \" + \" AND \".join(conditions) if conditions else \"\"\r\n\r\n # Fetch Summary Counts (Grouped by Hospital)\r\n summary_query = f\"\"\"\r\n SELECT\r\n SUM(CASE WHEN aml.maintenance_status = 'Planned' THEN 1 ELSE 0 END) AS planned,\r\n SUM(CASE WHEN aml.maintenance_status = 'Completed' THEN 1 ELSE 0 END) AS completed,\r\n SUM(CASE WHEN aml.maintenance_status = 'Overdue' THEN 1 ELSE 0 END) AS overdue,\r\n COUNT(aml.name) AS total_pm\r\n FROM `tabAsset Maintenance Log` aml\r\n {where_clause}\r\n \"\"\"\r\n summary_data = frappe.db.sql(summary_query, query_params, as_dict=True)\r\n\r\n # Fetch Hospital-wise Summary for Chart\r\n hospital_query = f\"\"\"\r\n SELECT\r\n aml.custom_hospital_name AS company,\r\n SUM(CASE WHEN aml.maintenance_status = 'Planned' THEN 1 ELSE 0 END) AS planned,\r\n SUM(CASE WHEN aml.maintenance_status = 'Completed' THEN 1 ELSE 0 END) AS completed,\r\n SUM(CASE WHEN aml.maintenance_status = 'Overdue' THEN 1 ELSE 0 END) AS overdue\r\n FROM `tabAsset Maintenance Log` aml\r\n {where_clause}\r\n GROUP BY aml.custom_hospital_name\r\n \"\"\"\r\n hospital_summary = frappe.db.sql(hospital_query, query_params, as_dict=True)\r\n\r\n # Fetch Detailed Table Data\r\n details_query = f\"\"\"\r\n SELECT\r\n aml.name AS log_id,\r\n aml.asset_name AS asset_id,\r\n aml.custom_asset_names AS asset_name,\r\n aml.maintenance_status AS status,\r\n aml.due_date AS due_date,\r\n aml.custom_hospital_name AS company,\r\n aml.custom_pm_overdue_reason,\r\n aml.custom_early_completion_reason,\r\n aml.custom_accepted_by_moh,\r\n am.custom_site_contractor,\r\n am.custom_subcontractor,\r\n am.custom_service_coverage,\r\n am.custom_service_agreement,\r\n am.custom_price_per_pm\r\n FROM `tabAsset Maintenance Log` aml\r\n LEFT JOIN `tabAsset Maintenance` am ON aml.asset_maintenance = am.name\r\n {where_clause}\r\n ORDER BY aml.due_date DESC\r\n \"\"\"\r\n table_data = frappe.db.sql(details_query, query_params, as_dict=True)\r\n\r\n # Define Report Columns\r\n columns = [\r\n {\"label\": \"Log ID\", \"fieldname\": \"log_id\", \"fieldtype\": \"Link\", \"options\": \"Asset Maintenance Log\", \"width\": 150},\r\n {\"label\": \"Hospital Name\", \"fieldname\": \"company\", \"fieldtype\": \"Link\", \"options\": \"Company\", \"width\": 200},\r\n {\"label\": \"Asset ID\", \"fieldname\": \"asset_id\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 200},\r\n {\"label\": \"Asset Name\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Data\", \"width\": 250},\r\n {\"label\": \"Maintenance Status\", \"fieldname\": \"status\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Due Date\", \"fieldname\": \"due_date\", \"fieldtype\": \"Date\", \"width\": 150},\r\n {\"label\": \"Price Per PM\", \"fieldname\": \"custom_price_per_pm\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Site Contractor\", \"fieldname\": \"custom_site_contractor\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Subcontractor\", \"fieldname\": \"custom_subcontractor\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Service Coverage\", \"fieldname\": \"custom_service_coverage\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Service Agreement\", \"fieldname\": \"custom_service_agreement\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Overdue Reason\", \"fieldname\": \"custom_pm_overdue_reason\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Early Completion Reason\", \"fieldname\": \"custom_early_completion_reason\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Accepted By MOH\", \"fieldname\": \"custom_accepted_by_moh\", \"fieldtype\": \"Check\", \"width\": 100},\r\n ]\r\n\r\n # ✅ Prepare Chart Data (Stacked Bar Chart)\r\n labels = []\r\n planned_values = []\r\n completed_values = []\r\n overdue_values = []\r\n total_values = [] # New Total Column\r\n \r\n for row in hospital_summary:\r\n labels.append(row[\"company\"] or \"Unknown\")\r\n planned = row[\"planned\"] or 0\r\n completed = row[\"completed\"] or 0\r\n overdue = row[\"overdue\"] or 0\r\n \r\n planned_values.append(planned)\r\n completed_values.append(completed)\r\n overdue_values.append(overdue)\r\n total_values.append(planned + completed + overdue) # Sum of all statuses\r\n \r\n # ✅ Define Updated Chart with \"Total\"\r\n chart = {\r\n \"data\": {\r\n \"labels\": labels,\r\n \"datasets\": [\r\n {\"name\": \"Overdue\", \"values\": overdue_values, \"chartType\": \"bar\", \"color\": \"red\"},\r\n {\"name\": \"Planned\", \"values\": planned_values, \"chartType\": \"bar\", \"color\": \"orange\"},\r\n {\"name\": \"Completed\", \"values\": completed_values, \"chartType\": \"bar\", \"color\": \"green\"},\r\n {\"name\": \"Total\", \"values\": total_values, \"chartType\": \"bar\", \"color\": \"blue\"}, # ✅ New Total Bar\r\n ]\r\n },\r\n \"type\": \"bar\",\r\n \"barOptions\": {\r\n \"stacked\": True # ✅ Stack all bars including Total\r\n }\r\n }\r\n\r\n # Prepare Report Summary Data\r\n report_summary = [\r\n {\"label\": \"Planned\", \"value\": summary_data[0][\"planned\"], \"indicator\": \"orange\"},\r\n {\"label\": \"Completed\", \"value\": summary_data[0][\"completed\"], \"indicator\": \"green\"},\r\n {\"label\": \"Overdue\", \"value\": summary_data[0][\"overdue\"], \"indicator\": \"red\"},\r\n {\"label\": \"Total PM\", \"value\": summary_data[0][\"total_pm\"], \"indicator\": \"blue\"}\r\n ]\r\n\r\n return columns, table_data, None, chart, report_summary\r\n\r\ndata = execute(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "PM Status Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "PM Status Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "PM Status Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "PM Status Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 1, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "company", + "fieldtype": "Link", + "label": "Hospital Name", + "mandatory": 0, + "options": "Company", + "parent": "Asset Cost History", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "mandatory": 0, + "options": "Supplier", + "parent": "Asset Cost History", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "", + "modified": "2025-07-30 21:47:53.262796", + "module": "Asset Lite", + "name": "Asset Cost History", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Asset Cost History", + "report_script": "def execute(filters=None):\r\n if not filters:\r\n filters = {}\r\n\r\n # ✅ Define Allowed Companies (Hospitals)\r\n #allowed_companies = [\r\n # \"King AbdulAziz Specialist Hospital\",\r\n # \"Domat Al Jandal Hospital\",\r\n # \"Al Jouf Hospital\",\r\n # \"Tabarjal Hospital\"\r\n #]\r\n \r\n # ✅ Get all company names from the Company DocType\r\n allowed_companies = [row.name for row in frappe.get_all(\"Company\", fields=[\"name\"])]\r\n\r\n # ✅ Apply Filters & Conditions\r\n conditions = [\"a.company IN %(allowed_companies)s\"] # Restrict to specific hospitals\r\n query_params = {\"allowed_companies\": allowed_companies}\r\n\r\n # ✅ Apply Additional Company Filter (if selected)\r\n if filters.get(\"company\") and filters[\"company\"] in allowed_companies:\r\n conditions.append(\"a.company = %(company)s\")\r\n query_params[\"company\"] = filters[\"company\"]\r\n\r\n # ✅ Apply Supplier Filter (if selected)\r\n if filters.get(\"supplier\"):\r\n conditions.append(\"a.supplier = %(supplier)s\")\r\n query_params[\"supplier\"] = filters[\"supplier\"]\r\n\r\n # ✅ Construct WHERE Clause\r\n where_clause = \"WHERE \" + \" AND \".join(conditions) if conditions else \"\"\r\n\r\n # ✅ Fetch Asset Data\r\n query = f\"\"\"\r\n SELECT\r\n a.name AS asset_id,\r\n a.asset_name AS asset_name,\r\n a.company AS company,\r\n a.gross_purchase_amount AS gross_amount,\r\n a.custom_total_spare_parts_amount AS spare_parts_amount,\r\n a.supplier AS supplier,\r\n a.custom_serial_number AS serial_number,\r\n COALESCE((\r\n SELECT SUM(sp.qty) FROM `tabSpare Parts` sp \r\n WHERE sp.parent = a.name\r\n ), 0) AS spare_parts_qty\r\n \r\n FROM `tabAsset` a\r\n {where_clause}\r\n ORDER BY a.asset_name\r\n \"\"\"\r\n data = frappe.db.sql(query, query_params, as_dict=True)\r\n\r\n # ✅ Define Report Columns\r\n columns = [\r\n {\"label\": \"Asset ID\", \"fieldname\": \"asset_id\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 150},\r\n {\"label\": \"Asset Name\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Hospital Name\", \"fieldname\": \"company\", \"fieldtype\": \"Link\", \"options\": \"Company\", \"width\": 150},\r\n {\"label\": \"Gross Purchase Amount\", \"fieldname\": \"gross_amount\", \"fieldtype\": \"Currency\", \"width\": 200},\r\n {\"label\": \"Repair Cost\", \"fieldname\": \"spare_parts_amount\", \"fieldtype\": \"Currency\", \"width\": 200},\r\n {\"label\": \"Spare Parts Quantity\", \"fieldname\": \"spare_parts_qty\", \"fieldtype\": \"Int\", \"width\": 180},\r\n {\"label\": \"Supplier\", \"fieldname\": \"supplier\", \"fieldtype\": \"Link\", \"options\": \"Supplier\", \"width\": 200},\r\n {\"label\": \"Serial Number\", \"fieldname\": \"serial_number\", \"fieldtype\": \"Data\", \"width\": 150},\r\n ]\r\n\r\n # 🔧 FIXED: Apply same filters to chart query as main table query\r\n chart_conditions = [\"company IN %(allowed_companies)s\"]\r\n chart_params = {\"allowed_companies\": allowed_companies}\r\n\r\n # ✅ Apply Company Filter to Chart (CRITICAL FIX)\r\n if filters.get(\"company\") and filters[\"company\"] in allowed_companies:\r\n chart_conditions.append(\"company = %(company)s\")\r\n chart_params[\"company\"] = filters[\"company\"]\r\n\r\n # ✅ Apply Supplier Filter to Chart\r\n if filters.get(\"supplier\"):\r\n chart_conditions.append(\"supplier = %(supplier)s\")\r\n chart_params[\"supplier\"] = filters[\"supplier\"]\r\n\r\n # ✅ Construct Chart WHERE Clause\r\n chart_where_clause = \"WHERE \" + \" AND \".join(chart_conditions) if chart_conditions else \"\"\r\n\r\n # 🔧 FIXED: Updated chart query with proper filtering\r\n supplier_query = f\"\"\"\r\n SELECT supplier, COUNT(name) AS asset_count\r\n FROM `tabAsset`\r\n {chart_where_clause}\r\n AND supplier IS NOT NULL AND supplier != ''\r\n GROUP BY supplier \r\n ORDER BY asset_count DESC\r\n \"\"\"\r\n\r\n supplier_data = frappe.db.sql(supplier_query, chart_params, as_dict=True)\r\n\r\n # ✅ Prepare Supplier vs Asset Count Chart\r\n supplier_labels = []\r\n asset_count_values = []\r\n\r\n for row in supplier_data:\r\n supplier_labels.append(row[\"supplier\"])\r\n asset_count_values.append(row[\"asset_count\"])\r\n\r\n # 🎯 Enhanced chart with better title based on filters\r\n chart_title = \"Asset Count by Supplier\"\r\n if filters.get(\"company\"):\r\n chart_title = f\"Asset Count by Supplier - {filters['company']}\"\r\n \r\n chart = {\r\n \"data\": {\r\n \"labels\": supplier_labels,\r\n \"datasets\": [\r\n {\r\n \"name\": \"Asset Count\",\r\n \"values\": asset_count_values,\r\n \"chartType\": \"bar\",\r\n \"color\": \"blue\"\r\n }\r\n ]\r\n },\r\n \"type\": \"bar\",\r\n \"title\": chart_title,\r\n \"barOptions\": {\r\n \"stacked\": False # Not stacked, just a simple bar chart\r\n }\r\n }\r\n\r\n return columns, data, None, chart\r\n\r\ndata = execute(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Cost History", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Asset Cost History", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Asset Cost History", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Asset Cost History", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset Cost History", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Cost History", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + }, + { + "parent": "Asset Cost History", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "Yes", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-01-08 11:51:04.404667", + "module": "Asset Lite", + "name": "Maintenance Response Time", + "prepared_report": 0, + "query": null, + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Maintenance Response Time", + "report_script": null, + "report_type": "Script Report", + "roles": [ + { + "parent": "Maintenance Response Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Maintenance Response Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing Manager" + }, + { + "parent": "Maintenance Response Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Maintenance Response Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Maintenance Response Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Maintenance Response Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + }, + { + "parent": "Maintenance Response Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + }, + { + "parent": "Maintenance Response Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "Maintenance Response Time", + "parentfield": "roles", + "parenttype": "Report", + "role": "Sales Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "mandatory": 0, + "options": null, + "parent": "Engineers working Hours", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "mandatory": 0, + "options": null, + "parent": "Engineers working Hours", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "company", + "fieldtype": "Link", + "label": "Hospital Name", + "mandatory": 0, + "options": "Company", + "parent": "Engineers working Hours", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:11.313582", + "module": "Asset Lite", + "name": "Engineers working Hours", + "prepared_report": 0, + "query": null, + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Engineers working Hours", + "report_script": "\r\ndef execute(filters):\r\n # Define the columns for the report\r\n columns = [\r\n {\"label\": \"Engineer\", \"fieldname\": \"engineer\", \"fieldtype\": \"Link\", \"options\": \"User\", \"width\": 200},\r\n {\"label\": \"Engineer Name\", \"fieldname\": \"engineer_name\", \"fieldtype\": \"Data\", \"width\": 200}, # New Column\r\n {\"label\": \"Total Hours Spent\", \"fieldname\": \"total_hours\", \"fieldtype\": \"Float\", \"width\": 150},\r\n {\"label\": \"Company\", \"fieldname\": \"company\", \"fieldtype\": \"Link\", \"options\": \"Company\", \"width\": 200}, # Added Company Column\r\n ]\r\n\r\n # Prepare conditions and parameters\r\n conditions = []\r\n params = {}\r\n\r\n # Add date range filter if provided\r\n if filters.get(\"from_date\") and filters.get(\"to_date\"):\r\n conditions.append(\"wo.creation BETWEEN %(start_date)s AND %(end_date)s\")\r\n params[\"start_date\"] = f\"{filters['from_date']} 00:00:00\"\r\n params[\"end_date\"] = f\"{filters['to_date']} 23:59:59\"\r\n\r\n # Add company filter if provided\r\n if filters.get(\"company\"):\r\n conditions.append(\"wo.company = %(company)s\")\r\n params[\"company\"] = filters[\"company\"]\r\n\r\n # Build the WHERE clause\r\n where_clause = \" AND \" + \" AND \".join(conditions) if conditions else \"\"\r\n\r\n # SQL Query with Company Filter\r\n query = f\"\"\"\r\n SELECT\r\n t.allocated_to AS engineer,\r\n u.full_name AS engineer_name, \r\n wo.company AS company,\r\n SUM(wo.total_hours_spent) AS total_hours\r\n FROM\r\n `tabToDo` t\r\n INNER JOIN\r\n `tabWork_Order` wo ON wo.name = t.reference_name\r\n LEFT JOIN\r\n `tabUser` u ON u.name = t.allocated_to -- Joining tabUser to get full_name\r\n WHERE\r\n t.reference_type = 'Work_Order' AND wo.docstatus = 1\r\n {where_clause}\r\n GROUP BY\r\n t.allocated_to, u.full_name, wo.company\r\n \"\"\"\r\n\r\n # Execute the query with parameters\r\n result = frappe.db.sql(query, params, as_dict=True)\r\n \r\n # Ensure total_hours is rounded to 2 decimal places\r\n for row in result:\r\n row[\"total_hours\"] = round(row[\"total_hours\"], 2) if row[\"total_hours\"] is not None else 0.00\r\n\r\n return columns, result\r\n\r\n\r\ndata = execute(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Engineers working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Engineers working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing Manager" + }, + { + "parent": "Engineers working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Engineers working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Engineers working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Engineers working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + }, + { + "parent": "Engineers working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + }, + { + "parent": "Engineers working Hours", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "Yes", + "javascript": null, + "json": null, + "letter_head": "Test", + "modified": "2025-01-07 20:06:07.388338", + "module": "Asset Lite", + "name": "Planned maintenance percentage (PMP)", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset Maintenance Log", + "reference_report": null, + "report_name": "Planned maintenance percentage (PMP)", + "report_script": null, + "report_type": "Script Report", + "roles": [ + { + "parent": "Planned maintenance percentage (PMP)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "Planned maintenance percentage (PMP)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Planned maintenance percentage (PMP)", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "company", + "fieldtype": "Link", + "label": "Hospital Name", + "mandatory": 0, + "options": "Company", + "parent": "Asset Maintenance Assignments", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "", + "modified": "2025-04-22 14:49:11.010300", + "module": "Asset Lite", + "name": "Asset Maintenance Assignments", + "prepared_report": 0, + "query": "\r\ndef execute(filters=None):\r\n if not filters:\r\n filters = {}\r\n\r\n # Define Company Filter (Optional)\r\n company_filter = \"\"\r\n if filters.get(\"company\"):\r\n company_filter = \"AND a.company = %(company)s\"\r\n\r\n # SQL Query with Company Filter\r\n query = f\"\"\"\r\n SELECT \r\n aml.item_name AS item_name,\r\n aml.maintenance_status AS maintenance_status,\r\n aml.assign_to_name AS assigned_to,\r\n SUM(CASE \r\n WHEN aml.due_date = aml.completion_date \r\n AND aml.maintenance_status = 'Completed' THEN 1 \r\n ELSE 0 \r\n END) AS completed_on_time,\r\n SUM(CASE \r\n WHEN aml.completion_date < aml.due_date \r\n AND aml.maintenance_status = 'Completed' THEN 1 \r\n ELSE 0 \r\n END) AS completed_within_time,\r\n SUM(CASE \r\n WHEN aml.completion_date > aml.due_date THEN 1 \r\n ELSE 0 \r\n END) AS delay_in_completion,\r\n SUM(CASE \r\n WHEN aml.maintenance_status = 'Planned' \r\n AND aml.completion_date IS NULL \r\n AND aml.due_date > CURDATE() THEN 1 \r\n ELSE 0 \r\n END) AS pending,\r\n SUM(CASE \r\n WHEN aml.maintenance_status = 'Planned' \r\n AND aml.completion_date IS NULL \r\n AND aml.due_date < CURDATE() THEN 1 \r\n ELSE 0 \r\n END) AS overdue,\r\n SUM(CASE \r\n WHEN aml.maintenance_status = 'Cancelled' THEN 1 \r\n ELSE 0 \r\n END) AS cancelled\r\n FROM \r\n `tabAsset Maintenance Log` aml\r\n LEFT JOIN \r\n `tabAsset` a ON aml.asset_name = a.name\r\n WHERE \r\n 1=1 {company_filter} -- Apply Company Filter\r\n GROUP BY \r\n aml.assign_to_name\r\n ORDER BY \r\n completed_within_time DESC;\r\n \"\"\"\r\n\r\n # Fetch Data from Database\r\n result = frappe.db.sql(query, filters, as_dict=True)\r\n\r\n # Define Report Columns\r\n columns = [\r\n {\"label\": \"Item Name\", \"fieldname\": \"item_name\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Maintenance Status\", \"fieldname\": \"maintenance_status\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Assigned To\", \"fieldname\": \"assigned_to\", \"fieldtype\": \"Data\", \"width\": 180},\r\n {\"label\": \"Completed On Time\", \"fieldname\": \"completed_on_time\", \"fieldtype\": \"Int\", \"width\": 130},\r\n {\"label\": \"Completed Within Time\", \"fieldname\": \"completed_within_time\", \"fieldtype\": \"Int\", \"width\": 150},\r\n {\"label\": \"Delay In Completion\", \"fieldname\": \"delay_in_completion\", \"fieldtype\": \"Int\", \"width\": 150},\r\n {\"label\": \"Pending\", \"fieldname\": \"pending\", \"fieldtype\": \"Int\", \"width\": 120},\r\n {\"label\": \"Overdue\", \"fieldname\": \"overdue\", \"fieldtype\": \"Int\", \"width\": 120},\r\n {\"label\": \"Cancelled\", \"fieldname\": \"cancelled\", \"fieldtype\": \"Int\", \"width\": 120},\r\n ]\r\n\r\n return columns, result\r\n", + "ref_doctype": "Asset Maintenance Log", + "reference_report": null, + "report_name": "Asset Maintenance Assignments", + "report_script": "def execute(filters=None):\r\n if not filters:\r\n filters = {}\r\n \r\n # Define Company Filter (Optional)\r\n company_filter = \"\"\r\n if filters.get(\"company\"):\r\n company_filter = \"AND a.company = %(company)s\"\r\n\r\n # SQL Query with Company Filter\r\n query = f\"\"\"\r\n SELECT \r\n aml.item_name AS item_name,\r\n a.company AS company,\r\n aml.maintenance_status AS maintenance_status,\r\n aml.assign_to_name AS assigned_to,\r\n SUM(CASE \r\n WHEN aml.due_date = aml.completion_date \r\n AND aml.maintenance_status = 'Completed' THEN 1 \r\n ELSE 0 \r\n END) AS completed_on_time,\r\n SUM(CASE \r\n WHEN aml.completion_date < aml.due_date \r\n AND aml.maintenance_status = 'Completed' THEN 1 \r\n ELSE 0 \r\n END) AS completed_within_time,\r\n SUM(CASE \r\n WHEN aml.completion_date > aml.due_date THEN 1 \r\n ELSE 0 \r\n END) AS delay_in_completion,\r\n SUM(CASE \r\n WHEN aml.maintenance_status = 'Planned' \r\n AND aml.completion_date IS NULL \r\n AND aml.due_date > CURDATE() THEN 1 \r\n ELSE 0 \r\n END) AS pending,\r\n SUM(CASE \r\n WHEN aml.maintenance_status = 'Planned' \r\n AND aml.completion_date IS NULL \r\n AND aml.due_date < CURDATE() THEN 1 \r\n ELSE 0 \r\n END) AS overdue,\r\n SUM(CASE \r\n WHEN aml.maintenance_status = 'Cancelled' THEN 1 \r\n ELSE 0 \r\n END) AS cancelled\r\n FROM \r\n `tabAsset Maintenance Log` aml\r\n LEFT JOIN \r\n `tabAsset` a ON aml.asset_name = a.name\r\n WHERE \r\n 1=1 {company_filter}\r\n GROUP BY \r\n aml.assign_to_name\r\n ORDER BY \r\n completed_within_time DESC;\r\n \"\"\"\r\n\r\n # Fetch Data from Database\r\n result = frappe.db.sql(query, filters, as_dict=True)\r\n\r\n # Define Report Columns\r\n columns = [\r\n {\"label\": \"Item Name\", \"fieldname\": \"item_name\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Hospital Name\", \"fieldname\": \"company\", \"fieldtype\": \"Link\",\"options\":\"Company\",\"width\": 200},\r\n {\"label\": \"Maintenance Status\", \"fieldname\": \"maintenance_status\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Assigned To\", \"fieldname\": \"assigned_to\", \"fieldtype\": \"Data\", \"width\": 180},\r\n {\"label\": \"Completed On Time\", \"fieldname\": \"completed_on_time\", \"fieldtype\": \"Int\", \"width\": 130},\r\n {\"label\": \"Completed Within Time\", \"fieldname\": \"completed_within_time\", \"fieldtype\": \"Int\", \"width\": 150},\r\n {\"label\": \"Delay In Completion\", \"fieldname\": \"delay_in_completion\", \"fieldtype\": \"Int\", \"width\": 150},\r\n {\"label\": \"Pending\", \"fieldname\": \"pending\", \"fieldtype\": \"Int\", \"width\": 120},\r\n {\"label\": \"Overdue\", \"fieldname\": \"overdue\", \"fieldtype\": \"Int\", \"width\": 120},\r\n {\"label\": \"Cancelled\", \"fieldname\": \"cancelled\", \"fieldtype\": \"Int\", \"width\": 120},\r\n ]\r\n\r\n return columns, result\r\n\r\ndata = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Maintenance Assignments", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "Asset Maintenance Assignments", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Maintenance Assignments", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset Maintenance Assignments", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "mandatory": 0, + "options": "Department", + "parent": "Total Hrs and Downtime Hrs", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "class", + "fieldtype": "Select", + "label": "Class", + "mandatory": 0, + "options": "\nClass A\nClass B\nClass C", + "parent": "Total Hrs and Downtime Hrs", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "asset", + "fieldtype": "Link", + "label": "Asset", + "mandatory": 0, + "options": "Asset", + "parent": "Total Hrs and Downtime Hrs", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:11.498639", + "module": "Asset Lite", + "name": "Total Hrs and Downtime Hrs", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Total Hrs and Downtime Hrs", + "report_script": "def execute(filters):\r\n columns = [\r\n {\"label\": \"\", \"fieldname\": \"name\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Total Hours of All Assets\", \"fieldname\": \"total_hours_sum\", \"fieldtype\": \"Float\"},\r\n {\"label\": \"Total Downtime Hours of All Assets\", \"fieldname\": \"downtime_hours_sum\", \"fieldtype\": \"Float\"},\r\n {\"label\": \"Total Uptime Hours of All Assets\", \"fieldname\": \"uptime_hours_sum\", \"fieldtype\": \"Float\"}\r\n ]\r\n \r\n result = []\r\n \r\n # Get the department and class filters from the input\r\n department = filters.get(\"department\") if filters else None\r\n asset_class = filters.get(\"class\") if filters else None\r\n asset_name = filters.get(\"asset\") if filters else None\r\n \r\n # Build the filter conditions based on the department and class filters\r\n asset_filters = {}\r\n if department:\r\n asset_filters['department'] = department\r\n if asset_class:\r\n asset_filters['custom_class'] = asset_class\r\n if asset_name:\r\n asset_filters['name'] = asset_name\r\n \r\n # Fetch all assets with their total hours and downtime hours based on filters\r\n assets = frappe.get_all('Asset', filters=asset_filters, fields=['custom_total_hours', 'custom_down_time'])\r\n \r\n # Initialize sums\r\n total_hours_sum = round(sum(asset['custom_total_hours'] or 0 for asset in assets))\r\n downtime_hours_sum = round(sum(asset['custom_down_time'] or 0 for asset in assets))\r\n uptime_hours_sum = round(total_hours_sum - downtime_hours_sum)\r\n \r\n if total_hours_sum == 0:\r\n total_hours_sum = downtime_hours_sum\r\n uptime_hours_sum = round(total_hours_sum - downtime_hours_sum)\r\n \r\n \r\n \r\n # Add the sums to the result\r\n result.append({\r\n \"name\":\"Hours\",\r\n \"total_hours_sum\": total_hours_sum,\r\n \"downtime_hours_sum\": downtime_hours_sum,\r\n \"uptime_hours_sum\": uptime_hours_sum\r\n })\r\n \r\n return columns, result\r\n\r\ndata = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Total Hrs and Downtime Hrs", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Total Hrs and Downtime Hrs", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Total Hrs and Downtime Hrs", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Total Hrs and Downtime Hrs", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Total Hrs and Downtime Hrs", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Total Hrs and Downtime Hrs", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + }, + { + "parent": "Total Hrs and Downtime Hrs", + "parentfield": "roles", + "parenttype": "Report", + "role": "Employee" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "mandatory": 0, + "options": "Department", + "parent": "Asset Up and Down", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "custom_class", + "fieldtype": "Select", + "label": "Class", + "mandatory": 0, + "options": "\nClass A\nClass B\nClass C", + "parent": "Asset Up and Down", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "name", + "fieldtype": "Link", + "label": "Asset", + "mandatory": 0, + "options": "Asset", + "parent": "Asset Up and Down", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": "frappe.query_reports[\"Asset Up and Down\"] = {\n filters: [\n {\n fieldname: \"asset_name\",\n label: __(\"Asset\"),\n fieldtype: \"Link\",\n options: \"Asset\"\n },\n {\n fieldname: \"department\",\n label: __(\"Department\"),\n fieldtype: \"Link\",\n options: \"Department\"\n },\n {\n fieldname: \"custom_class\",\n label: __(\"Class\"),\n fieldtype: \"Data\"\n }\n ]\n};\n", + "json": null, + "letter_head": null, + "modified": "2025-04-22 14:49:11.436123", + "module": "Asset Lite", + "name": "Asset Up and Down", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Asset Up and Down", + "report_script": "def execute(filters=None):\n if filters is None:\n filters = {}\n\n result = []\n status_count = {}\n\n # Fetch the department, name, and custom_class filter values (if any)\n department = filters.get(\"department\")\n name = filters.get(\"name\")\n custom_class = filters.get(\"custom_class\")\n\n # SQL query to fetch asset ID, asset name, status, department, and class\n if department and custom_class and name:\n query = \"\"\"\n SELECT\n name AS name,\n asset_name,\n custom_device_status AS status,\n department,\n name,\n custom_class\n FROM\n `tabAsset`\n WHERE\n department = %s AND custom_class = %s AND name = %s\n ORDER BY\n asset_name\n \"\"\"\n result = frappe.db.sql(query, (department, custom_class, name), as_dict=True)\n \n elif department and custom_class:\n query = \"\"\"\n SELECT\n name AS name,\n asset_name,\n custom_device_status AS status,\n department,\n name,\n custom_class\n FROM\n `tabAsset`\n WHERE\n department = %s AND custom_class = %s\n ORDER BY\n asset_name\n \"\"\"\n result = frappe.db.sql(query, (department, custom_class), as_dict=True)\n \n elif department and name:\n query = \"\"\"\n SELECT\n name AS name,\n asset_name,\n custom_device_status AS status,\n department,\n name,\n custom_class\n FROM\n `tabAsset`\n WHERE\n department = %s AND name = %s\n ORDER BY\n asset_name\n \"\"\"\n result = frappe.db.sql(query, (department, name), as_dict=True)\n \n elif custom_class and name:\n query = \"\"\"\n SELECT\n name AS name,\n asset_name,\n custom_device_status AS status,\n department,\n name,\n custom_class\n FROM\n `tabAsset`\n WHERE\n custom_class = %s AND name = %s\n ORDER BY\n asset_name\n \"\"\"\n result = frappe.db.sql(query, (custom_class, name), as_dict=True)\n elif department:\n query = \"\"\"\n SELECT\n name AS name,\n asset_name,\n custom_device_status AS status,\n department,\n name,\n custom_class\n FROM\n `tabAsset`\n WHERE\n department = %s\n ORDER BY\n asset_name\n \"\"\"\n result = frappe.db.sql(query, (department,), as_dict=True)\n elif custom_class:\n query = \"\"\"\n SELECT\n name AS name,\n asset_name,\n custom_device_status AS status,\n department,\n name,\n custom_class\n FROM\n `tabAsset`\n WHERE\n custom_class = %s\n ORDER BY\n asset_name\n \"\"\"\n result = frappe.db.sql(query, (custom_class,), as_dict=True)\n elif name:\n query = \"\"\"\n SELECT\n name AS name,\n asset_name,\n custom_device_status AS status,\n department,\n name,\n custom_class\n FROM\n `tabAsset`\n WHERE\n name = %s\n ORDER BY\n asset_name\n \"\"\"\n result = frappe.db.sql(query, (name,), as_dict=True)\n else:\n query_no_filters = \"\"\"\n SELECT\n name AS name,\n asset_name,\n custom_device_status AS status,\n department,\n name,\n custom_class\n FROM\n `tabAsset`\n ORDER BY\n asset_name\n \"\"\"\n result = frappe.db.sql(query_no_filters, as_dict=True)\n\n # Prepare data for the chart\n for row in result:\n status = row['status']\n if status:\n normalized_status = status.strip().lower()\n status_count[normalized_status] = status_count.get(normalized_status, 0) + 1\n\n chart_labels = list(status_count.keys())\n chart_values = list(status_count.values())\n\n columns = [\n {\"fieldname\": \"name\", \"label\": \"Asset ID\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 200},\n {\"fieldname\": \"asset_name\", \"label\": \"Asset Name\", \"fieldtype\": \"Data\", \"width\": 200},\n {\"fieldname\": \"status\", \"label\": \"Status\", \"fieldtype\": \"Data\", \"width\": 100},\n {\"fieldname\": \"department\", \"label\": \"Department\", \"fieldtype\": \"Link\", \"options\": \"Department\", \"width\": 150},\n {\"fieldname\": \"custom_class\", \"label\": \"Class\", \"fieldtype\": \"Data\", \"width\": 200},\n ]\n\n status_colors = [\"#2ba63d\" if status == 'up' else \"#FF0000\" if status == 'down' else \"#0000FF\" for status in chart_labels]\n\n chart = {\n \"data\": {\n \"labels\": chart_labels,\n \"datasets\": [{\"name\": \"Number of Assets\", \"values\": chart_values}]\n },\n \"type\": \"pie\",\n \"colors\": status_colors\n }\n\n return columns, result, None, chart\n\ndata = execute(filters)\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Up and Down", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Asset Up and Down", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Asset Up and Down", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Asset Up and Down", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset Up and Down", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Up and Down", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "repair_status", + "fieldtype": "Select", + "label": "Status", + "mandatory": 0, + "options": "\nOpen\nWork In Progress\nPending Review\nCompleted\nClosed", + "parent": "Work Order Status", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "work_order_type", + "fieldtype": "Link", + "label": "Work Order Type", + "mandatory": 0, + "options": "Issue Type", + "parent": "Work Order Status", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "wo_name", + "fieldtype": "Link", + "label": "Work Order", + "mandatory": 0, + "options": "Work_Order", + "parent": "Work Order Status", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "asset_type", + "fieldtype": "Link", + "label": "Asset Type", + "mandatory": 0, + "options": "Asset Type", + "parent": "Work Order Status", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": "", + "json": null, + "letter_head": "", + "modified": "2025-06-20 13:40:07.036876", + "module": "Asset Lite", + "name": "Work Order Status", + "prepared_report": 0, + "query": "", + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Work Order Status", + "report_script": "def get_result(filters):\r\n\r\n columns = [\r\n {\"label\": _(\"Work Order Type\"), \"fieldname\": \"work_order_type\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": _(\"Open\"), \"fieldname\": \"open_count\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"label\": _(\"Work In Progress\"), \"fieldname\": \"work_in_progress_count\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"label\": _(\"Pending Review\"), \"fieldname\": \"pending_review_count\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"label\": _(\"Completed\"), \"fieldname\": \"completed_count\", \"fieldtype\": \"Int\", \"width\": 200},\r\n {\"label\": _(\"Closed\"), \"fieldname\": \"closed_count\", \"fieldtype\": \"Int\", \"width\": 200}\r\n ]\r\n\r\n additional_filters = \"\"\r\n filter_fields = ['work_order_type','repair_status','asset_type']\r\n\r\n for field in filter_fields:\r\n if filters.get(field):\r\n additional_filters = additional_filters + f\" AND wt.{field} = '{filters.get(field)}'\"\r\n\r\n \r\n query = f\"\"\"\r\n SELECT\r\n name as wo_name,\r\n asset_type,\r\n work_order_type,\r\n SUM(CASE WHEN repair_status = 'Open' THEN 1 ELSE 0 END) AS open_count,\r\n SUM(CASE WHEN repair_status = 'Work In Progress' THEN 1 ELSE 0 END) AS work_in_progress_count,\r\n SUM(CASE WHEN repair_status = 'Pending Review' THEN 1 ELSE 0 END) AS pending_review_count,\r\n SUM(CASE WHEN repair_status = 'Completed' THEN 1 ELSE 0 END) AS completed_count,\r\n SUM(CASE WHEN repair_status = 'Closed' THEN 1 ELSE 0 END) AS closed_count\r\n FROM\r\n `tabWork_Order` as wt\r\n WHERE\r\n repair_status IN ('Open', 'Work In Progress', 'Pending Review', 'Completed', 'Closed','Cancelled') {additional_filters}\r\n GROUP BY\r\n work_order_type\r\n \"\"\"\r\n result = frappe.db.sql(query, as_dict=1)\r\n\r\n \r\n #data1 = []\r\n\r\n # Append the columns as the first element\r\n #data1.append(columns)\r\n\r\n # Append the result rows to the data list\r\n #data1.extend(result)\r\n\r\n sql1=\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status IN ('Open')\"\r\n sql2=\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status IN ('Work In Progress')\"\r\n sql3=\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status IN ('Pending Review')\"\r\n sql4=\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status IN ('Completed')\"\r\n sql5=\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status IN ('Closed')\"\r\n sql6=\"SELECT repair_status, COUNT(*) as count FROM `tabWork_Order` WHERE repair_status IN ('Open', 'Work In Progress', 'Pending Review', 'Completed', 'Closed', 'Cancelled')\"\r\n report_summary = [\r\n {\"value\": row.count, \"label\": \"Open\"} for row in frappe.db.sql(sql1, as_dict=True)]+[\r\n {\"value\": row.count, \"label\": \"Work In Progress\"} for row in frappe.db.sql(sql2, as_dict=True)]+[\r\n {\"value\": row.count, \"label\": \"Pending Review\"} for row in frappe.db.sql(sql3, as_dict=True)]+[\r\n {\"value\": row.count, \"label\": \"Completed\"} for row in frappe.db.sql(sql4, as_dict=True)]+[\r\n {\"value\": row.count, \"label\": \"Closed\"} for row in frappe.db.sql(sql5, as_dict=True)]+[\r\n {\"value\": row.count, \"label\": \"Total Work Orders\"} for row in frappe.db.sql(sql6, as_dict=True)\r\n ]\r\n\r\n #chart = {\r\n # \"data\": {\r\n # \"labels\": [row['label'] for row in report_summary],\r\n # \"datasets\": [\r\n # {\r\n # \"name\": \"Count\",\r\n # \"values\": [row['value'] for row in report_summary],\r\n # }\r\n # ]\r\n # },\r\n # \"type\": \"bar\"\r\n #}\r\n chart = {\r\n \"data\": {\r\n \"labels\": [row['work_order_type'] for row in result],\r\n \"datasets\": [\r\n {\r\n \"name\": \"Open\",\r\n \"values\": [row['open_count'] for row in result],\r\n },\r\n {\r\n \"name\": \"Work In Progress\",\r\n \"values\": [row['work_in_progress_count'] for row in result],\r\n },\r\n {\r\n \"name\": \"Pending Review\",\r\n \"values\": [row['pending_review_count'] for row in result],\r\n },\r\n {\r\n \"name\": \"Completed\",\r\n \"values\": [row['completed_count'] for row in result],\r\n },\r\n {\r\n \"name\": \"Closed\",\r\n \"values\": [row['closed_count'] for row in result],\r\n },\r\n ]\r\n },\r\n \"type\": \"bar\",\r\n \"barOptions\": {\r\n \"stacked\": 1,\r\n \"spaceRatio\": 0.6\r\n },\r\n \"colors\": [\"#CCCCB7\", \"#52B2BF\", \"#9EC1A4\", \"#058D7C\", \"#A3A5CF\"],\r\n }\r\n return columns,result,None,chart,report_summary\r\n\r\n\r\ndata = get_result(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Work Order Status", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "Work Order Status", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 1, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "company", + "fieldtype": "Link", + "label": "Hospital Name", + "mandatory": 0, + "options": "Company", + "parent": "Overall Work Order Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "month", + "fieldtype": "Select", + "label": "Month", + "mandatory": 0, + "options": "\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember", + "parent": "Overall Work Order Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "year", + "fieldtype": "Select", + "label": "Year", + "mandatory": 0, + "options": "\n2020\n2021\n2022\n2023\n2024\n2025\n2026\n2027\n2028\n2029\n2030", + "parent": "Overall Work Order Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "mandatory": 0, + "options": "Supplier", + "parent": "Overall Work Order Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "asset", + "fieldtype": "Link", + "label": "Asset ID", + "mandatory": 0, + "options": "Asset", + "parent": "Overall Work Order Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "asset_type", + "fieldtype": "Link", + "label": "Asset Type", + "mandatory": 0, + "options": "Asset Type", + "parent": "Overall Work Order Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "", + "modified": "2025-06-20 11:43:00.066139", + "module": "Asset Lite", + "name": "Overall Work Order Report", + "prepared_report": 0, + "query": null, + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Overall Work Order Report", + "report_script": "\r\ndef execute(filters=None):\r\n if not filters:\r\n filters = {}\r\n\r\n # Define Filters\r\n filters_dict = {}\r\n\r\n # Apply Company Filter\r\n if filters.get(\"company\"):\r\n filters_dict[\"company\"] = filters.get(\"company\")\r\n \r\n if filters.get(\"asset\"):\r\n filters_dict[\"asset\"] = filters.get(\"asset\")\r\n \r\n if filters.get(\"asset_type\"):\r\n filters_dict[\"asset_type\"] = filters.get(\"asset_type\")\r\n \r\n if filters.get(\"supplier\"):\r\n filters_dict[\"supplier\"] = filters.get(\"supplier\")\r\n\r\n # Apply Month & Year Filters\r\n if filters.get(\"year\"):\r\n year_num = int(filters[\"year\"]) # Ensure it's an integer\r\n\r\n if filters.get(\"month\"):\r\n # Map month names to numbers\r\n month_map = {\r\n \"January\": 1, \"February\": 2, \"March\": 3, \"April\": 4,\r\n \"May\": 5, \"June\": 6, \"July\": 7, \"August\": 8,\r\n \"September\": 9, \"October\": 10, \"November\": 11, \"December\": 12\r\n }\r\n month_num = month_map.get(filters[\"month\"])\r\n\r\n if month_num:\r\n first_day = f\"{year_num}-{month_num:02d}-01\"\r\n last_day = frappe.utils.get_last_day(first_day) # Get last day of the month dynamically\r\n filters_dict[\"creation\"] = [\"between\", [first_day, last_day]]\r\n else:\r\n # If only year is provided, filter for the entire year\r\n first_day = f\"{year_num}-01-01\"\r\n last_day = f\"{year_num}-12-31\"\r\n filters_dict[\"creation\"] = [\"between\", [first_day, last_day]]\r\n\r\n # Fetch Work Orders with Filters\r\n work_orders = frappe.get_all(\r\n \"Work_Order\",\r\n filters=filters_dict,\r\n fields=[\r\n \"name\", \"company\", \"work_order_type\", \"repair_status\", \"total_repair_cost\",\r\n \"completion_date\", \"first_responded_on\", \"custom_deadline_date\", \"failure_date\",\r\n \"supplier\", \"serial_number\", \"asset_name\", \"repair_cost\", \"asset\", \"asset_type\",\r\n \"assigned_technician\", \"custom_maintenance_manager\", \"custom_priority_\", \"creation\"\r\n ],\r\n order_by=\"creation desc\"\r\n )\r\n\r\n # Define Report Columns\r\n columns = [\r\n {\"label\": \"Work Order No\", \"fieldname\": \"name\", \"fieldtype\": \"Link\", \"options\": \"Work_Order\", \"width\": 150},\r\n {\"label\": \"Hospital Name\", \"fieldname\": \"company\", \"fieldtype\": \"Data\", \"width\": 120},\r\n {\"label\": \"Asset ID\", \"fieldname\": \"asset\", \"fieldtype\": \"Link\", \"options\": \"Asset\", \"width\": 120},\r\n {\"label\": \"Asset Type\", \"fieldname\": \"asset_type\", \"fieldtype\": \"Link\", \"options\": \"Asset Type\", \"width\": 120},\r\n {\"label\": \"Asset Name\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Data\", \"width\": 180},\r\n {\"label\": \"Supplier\", \"fieldname\": \"supplier\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Priority\", \"fieldname\": \"custom_priority_\", \"fieldtype\": \"Data\", \"width\": 100},\r\n {\"label\": \"Work Order Type\", \"fieldname\": \"work_order_type\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Repair Status\", \"fieldname\": \"repair_status\", \"fieldtype\": \"Data\", \"width\": 120},\r\n {\"label\": \"Spare Used - Qty\", \"fieldname\": \"spares_used\", \"fieldtype\": \"Data\", \"width\": 250},\r\n {\"label\": \"Spare Cost (SAR)\", \"fieldname\": \"spare_cost\", \"fieldtype\": \"Currency\", \"width\": 120},\r\n {\"label\": \"Purchase Items - Qty\", \"fieldname\": \"invoice_items\", \"fieldtype\": \"Data\", \"width\": 170},\r\n {\"label\": \"Purchase Cost (SAR)\", \"fieldname\": \"repair_cost\", \"fieldtype\": \"Currency\", \"width\": 120},\r\n {\"label\": \"Total Repair Cost (SAR)\", \"fieldname\": \"total_repair_cost\", \"fieldtype\": \"Currency\", \"width\": 120},\r\n {\"label\": \"Failure Date\", \"fieldname\": \"failure_date\", \"fieldtype\": \"Date\", \"width\": 120},\r\n {\"label\": \"First Responded\", \"fieldname\": \"first_responded_on\", \"fieldtype\": \"Date\", \"width\": 120},\r\n {\"label\": \"Completion Date\", \"fieldname\": \"completion_date\", \"fieldtype\": \"Date\", \"width\": 120},\r\n {\"label\": \"Deadline Date\", \"fieldname\": \"custom_deadline_date\", \"fieldtype\": \"Date\", \"width\": 120},\r\n \r\n {\"label\": \"Serial Number\", \"fieldname\": \"serial_number\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Assigned To\", \"fieldname\": \"custom_maintenance_manager\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Created On\", \"fieldname\": \"creation\", \"fieldtype\": \"Datetime\", \"width\": 150}\r\n ]\r\n\r\n # Fetch Spare Parts for Each Work Order\r\n for wo in work_orders:\r\n spare_parts = frappe.get_all(\r\n \"Asset Repair Consumed Item\",\r\n filters={\"parent\": wo[\"name\"]},\r\n fields=[\"item_code\", \"consumed_quantity\"]\r\n )\r\n\r\n if spare_parts:\r\n wo[\"spares_used\"] = \"\\n\".join([f\"{sp['item_code']} - {sp['consumed_quantity']}\" for sp in spare_parts])\r\n else:\r\n wo[\"spares_used\"] = \"\"\r\n\r\n # Fetch Purchase Invoice linked to Work Order from invoice_table\r\n invoice_links = frappe.get_all(\r\n \"PI Table\",\r\n filters={\"parent\": wo[\"name\"]},\r\n fields=[\"purchase_invoice\"]\r\n )\r\n\r\n invoice_items_list = []\r\n for invoice in invoice_links:\r\n if invoice[\"purchase_invoice\"]:\r\n items = frappe.get_all(\r\n \"Purchase Invoice Item\",\r\n filters={\"parent\": invoice[\"purchase_invoice\"]},\r\n fields=[\"item_code\", \"qty\"]\r\n )\r\n invoice_items_list.extend([f\"{item['item_code']} - {item['qty']}\" for item in items])\r\n\r\n wo[\"invoice_items\"] = \"\\n\".join(invoice_items_list) if invoice_items_list else \" \"\r\n\r\n # **Calculate Spare Cost** = Total Repair Cost - Purchase Cost\r\n wo[\"spare_cost\"] = (wo.get(\"total_repair_cost\") or 0) - (wo.get(\"repair_cost\") or 0)\r\n\r\n return columns, work_orders\r\n\r\ndata = execute(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Overall Work Order Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Overall Work Order Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing Manager" + }, + { + "parent": "Overall Work Order Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Overall Work Order Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Overall Work Order Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Overall Work Order Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + }, + { + "parent": "Overall Work Order Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + }, + { + "parent": "Overall Work Order Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "Overall Work Order Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Sales Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "company", + "fieldtype": "Link", + "label": "Hospital Name", + "mandatory": 0, + "options": "Company", + "parent": "Asset Up and Down by Asset", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "custom_asset_type", + "fieldtype": "Link", + "label": "Asset Type", + "mandatory": 0, + "options": "Asset Type", + "parent": "Asset Up and Down by Asset", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": "", + "json": null, + "letter_head": "", + "modified": "2025-07-30 21:08:29.851080", + "module": "Asset Lite", + "name": "Asset Up and Down by Asset", + "prepared_report": 1, + "query": null, + "ref_doctype": "Asset", + "reference_report": null, + "report_name": "Asset Up and Down by Asset", + "report_script": "def execute(filters=None):\r\n if filters is None:\r\n filters = {}\r\n\r\n result = []\r\n status_count = {}\r\n\r\n # Fetch the department, company, and custom_class filter values (if any)\r\n department = filters.get(\"department\")\r\n company = filters.get(\"company\")\r\n custom_asset_type = filters.get(\"custom_asset_type\")\r\n custom_class = filters.get(\"custom_class\")\r\n\r\n \r\n if company and custom_asset_type:\r\n query = \"\"\"\r\n SELECT\r\n company AS company,\r\n asset_name,\r\n custom_asset_type AS custom_asset_type,\r\n custom_device_status AS status,\r\n department,\r\n company,\r\n custom_class\r\n FROM\r\n `tabAsset`\r\n WHERE\r\n company = %s AND custom_asset_type = %s\r\n ORDER BY\r\n asset_name\r\n \"\"\"\r\n result = frappe.db.sql(query, (company, custom_asset_type), as_dict=True)\r\n\r\n elif company:\r\n query = \"\"\"\r\n SELECT\r\n company AS company,\r\n asset_name,\r\n custom_asset_type AS custom_asset_type,\r\n custom_device_status AS status,\r\n department,\r\n company,\r\n custom_class\r\n FROM\r\n `tabAsset`\r\n WHERE\r\n company = %s\r\n ORDER BY\r\n asset_name\r\n \"\"\"\r\n result = frappe.db.sql(query, (company,), as_dict=True)\r\n\r\n elif custom_asset_type:\r\n query = \"\"\"\r\n SELECT\r\n company AS company,\r\n asset_name,\r\n custom_asset_type AS custom_asset_type,\r\n custom_device_status AS status,\r\n department,\r\n company,\r\n custom_class\r\n FROM\r\n `tabAsset`\r\n WHERE\r\n custom_asset_type = %s\r\n ORDER BY\r\n asset_name\r\n \"\"\"\r\n result = frappe.db.sql(query, (custom_asset_type,), as_dict=True)\r\n\r\n else:\r\n query_no_filters = \"\"\"\r\n SELECT\r\n company AS company,\r\n asset_name,\r\n custom_asset_type AS custom_asset_type,\r\n custom_device_status AS status,\r\n department,\r\n company,\r\n custom_class\r\n FROM\r\n `tabAsset`\r\n ORDER BY\r\n asset_name\r\n \"\"\"\r\n result = frappe.db.sql(query_no_filters, as_dict=True)\r\n\r\n # Prepare data for the chart\r\n for row in result:\r\n status = row['status']\r\n if status:\r\n normalized_status = status.strip().lower()\r\n status_count[normalized_status] = status_count.get(normalized_status, 0) + 1\r\n\r\n chart_labels = list(status_count.keys())\r\n chart_values = list(status_count.values())\r\n\r\n columns = [\r\n {\"fieldname\": \"company\", \"label\": \"Hospital Name\", \"fieldtype\": \"Link\", \"options\": \"Company\", \"width\": 200},\r\n {\"fieldname\": \"asset_name\", \"label\": \"Asset Name\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"fieldname\": \"custom_asset_type\", \"label\": \"Asset Type\", \"fieldtype\": \"Link\", \"options\": \"Asset Type\",\"width\": 200},\r\n {\"fieldname\": \"status\", \"label\": \"Status\", \"fieldtype\": \"Data\", \"width\": 100},\r\n {\"fieldname\": \"department\", \"label\": \"Department\", \"fieldtype\": \"Link\", \"options\": \"Department\", \"width\": 150},\r\n {\"fieldname\": \"custom_class\", \"label\": \"Class\", \"fieldtype\": \"Data\", \"width\": 200},\r\n ]\r\n\r\n status_colors = [\"#2ba63d\" if status == 'up' else \"#FF0000\" if status == 'down' else \"#0000FF\" for status in chart_labels]\r\n\r\n chart = {\r\n \"data\": {\r\n \"labels\": chart_labels,\r\n \"datasets\": [{\"company\": \"Number of Assets\", \"values\": chart_values}]\r\n },\r\n \"type\": \"donut\",\r\n \"colors\": status_colors\r\n }\r\n\r\n return columns, result, None, chart\r\n\r\ndata = execute(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Asset Up and Down by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "Asset Up and Down by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Asset Up and Down by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Asset Up and Down by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Asset Up and Down by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Asset Up and Down by Asset", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance User" + } + ], + "timeout": 0 + }, + { + "add_total_row": 1, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "company", + "fieldtype": "Link", + "label": "Hospital Name", + "mandatory": 0, + "options": "Company", + "parent": "Work Order Priority Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + }, + { + "default": null, + "fieldname": "asset_type", + "fieldtype": "Link", + "label": "Asset Type", + "mandatory": 0, + "options": "Asset Type", + "parent": "Work Order Priority Report", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "", + "modified": "2025-06-20 10:46:08.247007", + "module": "Asset Lite", + "name": "Work Order Priority Report", + "prepared_report": 0, + "query": null, + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Work Order Priority Report", + "report_script": "\r\ndef execute(filters=None):\r\n if not filters:\r\n filters = {}\r\n\r\n conditions = [\"wo.custom_priority_ IS NOT NULL\"] # Exclude NULL priorities\r\n query_params = {}\r\n\r\n # Apply Company Filter\r\n if filters.get(\"company\"):\r\n conditions.append(\"wo.company = %(company)s\")\r\n query_params[\"company\"] = filters.get(\"company\")\r\n \r\n if filters.get(\"asset_type\"):\r\n conditions.append(\"wo.asset_type = %(asset_type)s\")\r\n query_params[\"asset_type\"] = filters.get(\"asset_type\")\r\n\r\n # Construct WHERE clause safely\r\n where_clause = \"WHERE \" + \" AND \".join(conditions) if conditions else \"\"\r\n\r\n # SQL Query\r\n query = f\"\"\"\r\n SELECT\r\n wo.company AS company,\r\n wo.asset_type AS asset_type,\r\n wo.custom_priority_ AS priority,\r\n COUNT(wo.name) AS work_order_count\r\n FROM \r\n `tabWork_Order` wo\r\n {where_clause}\r\n GROUP BY\r\n wo.company, wo.custom_priority_\r\n ORDER BY \r\n wo.company, work_order_count DESC\r\n \"\"\"\r\n\r\n # Execute query with safe parameters\r\n result = frappe.db.sql(query, query_params, as_dict=True)\r\n\r\n # Define Report Columns\r\n columns = [\r\n {\"label\": \"Company\", \"fieldname\": \"company\", \"fieldtype\": \"Link\", \"options\": \"Company\", \"width\": 200},\r\n {\"label\": \"Priority\", \"fieldname\": \"priority\", \"fieldtype\": \"Data\", \"width\": 150},\r\n {\"label\": \"Work Order Count\", \"fieldname\": \"work_order_count\", \"fieldtype\": \"Int\", \"width\": 150}\r\n ]\r\n\r\n # Prepare data for Stacked Chart\r\n company_wise_data = {} # Dictionary to store company-wise work order counts by priority\r\n priority_list = set() # To keep track of all unique priorities\r\n\r\n for row in result:\r\n company = row[\"company\"]\r\n priority = row[\"priority\"]\r\n count = row[\"work_order_count\"]\r\n\r\n priority_list.add(priority) # Store unique priorities\r\n\r\n if company not in company_wise_data:\r\n company_wise_data[company] = {}\r\n\r\n company_wise_data[company][priority] = count\r\n\r\n # Convert dictionary to chart format\r\n labels = list(company_wise_data.keys()) # X-Axis: Companies\r\n datasets = []\r\n\r\n priority_list = sorted(priority_list) # Ensure consistent order for priority labels\r\n\r\n # Priority Color Mapping\r\n color_map = {\r\n \"Urgent\": \"red\",\r\n \"Normal\": \"blue\"\r\n }\r\n\r\n # Prepare datasets for each priority\r\n for priority in priority_list:\r\n dataset = {\r\n \"name\": priority,\r\n \"values\": [company_wise_data[company].get(priority, 0) for company in labels],\r\n \"chartType\": \"bar\"\r\n }\r\n datasets.append(dataset)\r\n\r\n # Define Stacked Chart\r\n chart = {\r\n \"data\": {\r\n \"labels\": labels,\r\n \"datasets\": datasets\r\n },\r\n \"type\": \"bar\", # Bar Chart\r\n \"colors\": [color_map.get(priority, \"gray\") for priority in priority_list], # Apply colors\r\n \"barOptions\": {\"stacked\": True} # Enable stacking\r\n }\r\n\r\n return columns, result, None, chart # Returning stacked chart\r\n\r\ndata = execute(filters)\r\n", + "report_type": "Script Report", + "roles": [ + { + "parent": "Work Order Priority Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Work Order Priority Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing Manager" + }, + { + "parent": "Work Order Priority Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "End user" + }, + { + "parent": "Work Order Priority Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Work Order Priority Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Work Order Priority Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + }, + { + "parent": "Work Order Priority Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + }, + { + "parent": "Work Order Priority Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "Work Order Priority Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Sales Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "hospital", + "fieldtype": "Link", + "label": "Hospital", + "mandatory": 0, + "options": "Company", + "parent": "Planned PM", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": null, + "json": null, + "letter_head": "", + "modified": "2025-09-18 13:28:25.902877", + "module": "Asset Lite", + "name": "Planned PM", + "prepared_report": 0, + "query": null, + "ref_doctype": "Asset Maintenance Log", + "reference_report": null, + "report_name": "Planned PM", + "report_script": "#import frappe\r\n#from frappe.utils import nowdate, get_last_day\r\n\r\ndef execute(filters=None):\r\n today = frappe.utils.nowdate()\r\n end_of_month = frappe.utils.get_last_day(today)\r\n\r\n # Initialize filters dictionary\r\n planned_filters = {\r\n \"maintenance_status\": \"Planned\",\r\n \"due_date\": [\"between\", [today, end_of_month]]\r\n }\r\n\r\n overdue_filters = {\r\n \"maintenance_status\": [\"in\", [\"Overdue\"]],\r\n \"due_date\": [\"<\", today]\r\n }\r\n\r\n # Apply hospital filter if provided\r\n if filters and filters.get(\"hospital\"):\r\n planned_filters[\"custom_hospital_name\"] = filters.get(\"hospital\")\r\n overdue_filters[\"custom_hospital_name\"] = filters.get(\"hospital\")\r\n\r\n # Fetch Planned PMs due from today till end of month\r\n planned_data = frappe.db.get_all(\r\n \"Asset Maintenance Log\",\r\n filters=planned_filters,\r\n fields=[\r\n \"name\", \"asset_name\", \"custom_asset_names\", \"due_date\", \"maintenance_status\", \"assign_to_name\", \"custom_hospital_name\"\r\n ],\r\n order_by=\"due_date\"\r\n )\r\n\r\n # Fetch Overdue PMs\r\n overdue_data = frappe.db.get_all(\r\n \"Asset Maintenance Log\",\r\n filters=overdue_filters,\r\n fields=[\r\n \"name\", \"asset_name\", \"custom_asset_names\", \"due_date\", \"maintenance_status\", \"assign_to_name\", \"custom_hospital_name\"\r\n ],\r\n order_by=\"due_date\"\r\n )\r\n\r\n # Combine both datasets\r\n data = planned_data + overdue_data\r\n\r\n # Define report columns\r\n columns = [\r\n {\"label\": \"Log ID\", \"fieldname\": \"name\", \"fieldtype\": \"Link\", \"options\": \"Asset Maintenance Log\"},\r\n {\"label\": \"Asset ID\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Link\", \"options\": \"Asset\"},\r\n {\"label\": \"Asset Name\", \"fieldname\": \"custom_asset_names\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Hospital\", \"fieldname\": \"custom_hospital_name\", \"fieldtype\": \"Link\", \"options\": \"Company\"},\r\n {\"label\": \"Due Date\", \"fieldname\": \"due_date\", \"fieldtype\": \"Date\"},\r\n {\"label\": \"Status\", \"fieldname\": \"maintenance_status\", \"fieldtype\": \"Data\"},\r\n {\"label\": \"Assigned To\", \"fieldname\": \"assign_to_name\", \"fieldtype\": \"Data\"},\r\n ]\r\n\r\n return columns, data\r\n\r\n# Example call\r\ndata = execute(filters)\r\n\r\n\r\n\r\n# def execute(filters=None):\r\n# today = frappe.utils.nowdate()\r\n# next_month_start = frappe.utils.get_first_day(frappe.utils.add_months(today, 1))\r\n# next_month_end = frappe.utils.get_last_day(frappe.utils.add_months(today, 1))\r\n\r\n# data = frappe.db.get_all(\r\n# \"Asset Maintenance Log\",\r\n# filters={\r\n# \"maintenance_status\": \"Planned\",\r\n# \"due_date\": [\"between\", [next_month_start, next_month_end]],\r\n# },\r\n# fields=[\r\n# \"name\", \"asset_name\", \"maintenance_status\",\"custom_asset_names\", \"due_date\", \"assign_to_name\"\r\n# ],\r\n# order_by=\"due_date\"\r\n# )\r\n\r\n# columns = [\r\n# {\"label\": \"Log ID\", \"fieldname\": \"name\", \"fieldtype\": \"Link\", \"options\": \"Asset Maintenance Log\"},\r\n# {\"label\": \"Asset ID\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Link\", \"options\": \"Asset\"},\r\n# {\"label\": \"Asset Name\", \"fieldname\": \"custom_asset_names\", \"fieldtype\": \"Data\"},\r\n# {\"label\": \"Due Date\", \"fieldname\": \"due_date\", \"fieldtype\": \"Date\"},\r\n# {\"label\": \"Status\", \"fieldname\": \"maintenance_status\", \"fieldtype\": \"Data\"},\r\n# {\"label\": \"Assigned To\", \"fieldname\": \"assign_to_name\", \"fieldtype\": \"Data\"},\r\n# ]\r\n\r\n# return columns, data\r\n\r\n# data = execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Planned PM", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing User" + }, + { + "parent": "Planned PM", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Planned PM", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Planned PM", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + }, + { + "parent": "Planned PM", + "parentfield": "roles", + "parenttype": "Report", + "role": "Cluster Manager" + }, + { + "parent": "Planned PM", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 1, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [ + { + "default": null, + "fieldname": "Asset", + "fieldtype": "Link", + "label": "Asset", + "mandatory": 0, + "options": null, + "parent": "Repair Cost", + "parentfield": "filters", + "parenttype": "Report", + "wildcard_filter": 0 + } + ], + "is_standard": "No", + "javascript": "frappe.query_reports[\"Repair Cost\"] = {\n filters: [\n {\n fieldname: \"year\",\n label: __(\"Year\"),\n fieldtype: \"Int\",\n default: new Date().getFullYear(), // Current Year\n reqd: 1 // Make it mandatory\n },\n {\n fieldname: \"month\",\n label: __(\"Month\"),\n fieldtype: \"Select\",\n options: [\n \n \" \", \"January\", \"February\", \"March\", \"April\", \"May\", \"June\",\n \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"\n ],\n \n },\n \n {\n fieldname: \"class\",\n label: __(\"Class\"),\n fieldtype: \"Select\",\n options: [\n \"\",\"Class A\",\"Class B\",\"Class C\"\n ],\n \n },\n {\n fieldname: \"vendor\",\n label: __(\"Vendor\"),\n fieldtype: \"Link\",\n options: \"Supplier\"\n },\n {\n fieldname: \"asset_name\",\n label: __(\"Asset\"),\n fieldtype: \"Link\",\n options: \"Asset\"\n \n },\n {\n fieldname: \"work_order\",\n label: __(\"Work Order\"),\n fieldtype: \"Link\",\n options: \"Work_Order\"\n \n },\n {\n fieldname: \"department\",\n label: __(\"Department\"),\n fieldtype: \"Link\",\n options: \"Department\"\n \n }\n \n \n ]\n};", + "json": null, + "letter_head": null, + "modified": "2025-05-22 15:11:38.867845", + "module": "Asset Lite", + "name": "Repair Cost", + "prepared_report": 0, + "query": null, + "ref_doctype": "Work_Order", + "reference_report": null, + "report_name": "Repair Cost", + "report_script": "def execute(filters=None):\r\n if not filters:\r\n filters = {}\r\n\r\n # Ensure Year is provided\r\n if not filters.get('year'):\r\n frappe.throw(_(\"Please select a Year to proceed.\"))\r\n\r\n def get_days_in_month(year: int, month_name: str) -> int:\r\n month_map = {\r\n \"January\": 1, \"February\": 2, \"March\": 3, \"April\": 4,\r\n \"May\": 5, \"June\": 6, \"July\": 7, \"August\": 8,\r\n \"September\": 9, \"October\": 10, \"November\": 11, \"December\": 12,\r\n }\r\n month = month_map.get(month_name)\r\n month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\r\n\r\n # Handle leap year for February\r\n if month == 2:\r\n if (int(year) % 4 == 0 and int(year) % 100 != 0) or (int(year) % 400 == 0):\r\n return 29\r\n else:\r\n return 28\r\n else:\r\n return month_days[month - 1]\r\n\r\n def get_dates_of_month(year: int, month_name: str) -> list:\r\n total_days = get_days_in_month(year, month_name)\r\n month_map = {\r\n \"January\": 1, \"February\": 2, \"March\": 3, \"April\": 4,\r\n \"May\": 5, \"June\": 6, \"July\": 7, \"August\": 8,\r\n \"September\": 9, \"October\": 10, \"November\": 11, \"December\": 12,\r\n }\r\n month = month_map.get(month_name)\r\n return [f\"{year}-{month:02d}-{day:02d}\" for day in range(1, total_days + 1)]\r\n\r\n columns = [\r\n {\"label\": \"Asset\", \"fieldname\": \"asset_name\", \"fieldtype\": \"Link\",\"options\": \"Asset\", \"width\": 200},\r\n {\"label\": \"Work Order\", \"fieldname\": \"work_order\", \"fieldtype\": \"Link\",\"options\": \"Work_Order\", \"width\": 200},\r\n {\"label\": \"Item\", \"fieldname\": \"item_code\", \"fieldtype\": \"Data\", \"width\": 200},\r\n {\"label\": \"Quantity\", \"fieldname\": \"quantity\", \"fieldtype\": \"Float\", \"width\": 120},\r\n {\"label\": \"Amount\", \"fieldname\": \"amount\", \"fieldtype\": \"Currency\", \"width\": 120},\r\n ]\r\n\r\n result1 = []\r\n year = filters.get('year')\r\n month = filters.get('month')\r\n department = filters.get('department') # Optional department filter\r\n asset_class = filters.get('class') # Optional class filter\r\n vendor = filters.get('vendor') # Optional vendor filter\r\n asset_name = filters.get('asset_name')\r\n work_order = filters.get('work_order')\r\n\r\n # If month is not provided, show data for the whole year\r\n if month:\r\n dates_in_month = get_dates_of_month(year, month)\r\n date_filter = \"wo.failure_date BETWEEN %(start_date)s AND %(end_date)s\"\r\n else:\r\n date_filter = \"YEAR(wo.failure_date) = %(year)s\" # Filter by year only\r\n\r\n # Dynamically construct where clause for optional filters\r\n where_conditions = [date_filter]\r\n if department:\r\n where_conditions.append(\"wo.department = %(department)s\")\r\n if asset_class:\r\n where_conditions.append(\"asset.custom_class = %(class)s\")\r\n if vendor:\r\n where_conditions.append(\"wo.vendor = %(vendor)s\") # Vendor filter directly from Work Order\r\n if asset_name:\r\n where_conditions.append(\"asset.name = %(asset_name)s\") \r\n if work_order:\r\n where_conditions.append(\"wo.name = %(work_order)s\")\r\n\r\n # Construct final WHERE clause\r\n where_clause = \" AND \".join(where_conditions)\r\n\r\n # Fetch data for Work Orders, Material Requests, and Purchase Orders\r\n results = frappe.db.sql(\r\n f\"\"\"\r\n SELECT \r\n wo.name AS work_order,\r\n asset.name as asset_name,\r\n mri.item_code AS item_code,\r\n SUM(mri.qty) AS quantity,\r\n SUM(po_item.rate * mri.qty) AS amount\r\n FROM `tabWork_Order` wo\r\n LEFT JOIN `tabMaterial Request` mr ON mr.custom_work_order = wo.name\r\n LEFT JOIN `tabMaterial Request Item` mri ON mri.parent = mr.name\r\n LEFT JOIN `tabPurchase Order Item` po_item ON po_item.material_request = mr.name AND po_item.item_code = mri.item_code\r\n LEFT JOIN `tabAsset` asset ON wo.asset = asset.name\r\n WHERE {where_clause}\r\n GROUP BY wo.name, asset.name, mri.item_code\r\n ORDER BY amount ASC\r\n \"\"\",\r\n {\r\n \"start_date\": dates_in_month[0] if month else None,\r\n \"end_date\": dates_in_month[-1] if month else None,\r\n \"year\": year,\r\n \"department\": department,\r\n \"class\": asset_class,\r\n \"vendor\": vendor, # Pass the vendor filter value\r\n \"work_order\": work_order,\r\n \"asset_name\": asset_name,\r\n },\r\n as_dict=True\r\n )\r\n\r\n ## Prepare final report data\r\n # for row in results:\r\n # result1.append({\r\n # \"asset_name\": row.get(\"asset_name\"),\r\n # \"work_order\": row.get(\"work_order\"),\r\n # \"item_code\": row.get(\"item_code\"),\r\n # \"quantity\": float(row.get(\"quantity\") or 0),\r\n # \"amount\": float(row.get(\"amount\") or 0),\r\n # })\r\n \r\n for row in results:\r\n if row.get(\"item_code\"): # **Ensures only rows with an item_code are included**\r\n result1.append({\r\n \"asset_name\": row.get(\"asset_name\"),\r\n \"work_order\": row.get(\"work_order\"),\r\n \"item_code\": row.get(\"item_code\"),\r\n \"quantity\": float(row.get(\"quantity\") or 0),\r\n \"amount\": float(row.get(\"amount\") or 0),\r\n })\r\n\r\n return columns, result1\r\n\r\ndata=execute(filters)", + "report_type": "Script Report", + "roles": [ + { + "parent": "Repair Cost", + "parentfield": "roles", + "parenttype": "Report", + "role": "Quality Manager" + }, + { + "parent": "Repair Cost", + "parentfield": "roles", + "parenttype": "Report", + "role": "Manufacturing Manager" + }, + { + "parent": "Repair Cost", + "parentfield": "roles", + "parenttype": "Report", + "role": "Technician" + }, + { + "parent": "Repair Cost", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance Manager" + }, + { + "parent": "Repair Cost", + "parentfield": "roles", + "parenttype": "Report", + "role": "Maintenance User" + }, + { + "parent": "Repair Cost", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + }, + { + "parent": "Repair Cost", + "parentfield": "roles", + "parenttype": "Report", + "role": "Finance Manager" + } + ], + "timeout": 0 + } +] \ No newline at end of file diff --git a/asset_lite/fixtures/role.json b/asset_lite/fixtures/role.json new file mode 100644 index 0000000..9cfdb38 --- /dev/null +++ b/asset_lite/fixtures/role.json @@ -0,0 +1,743 @@ +[ + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:19.094250", + "name": "All", + "restrict_to_domain": null, + "role_name": "All", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:19.117689", + "name": "Desk User", + "restrict_to_domain": null, + "role_name": "Desk User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:19.135948", + "name": "Administrator", + "restrict_to_domain": null, + "role_name": "Administrator", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:19.155222", + "name": "System Manager", + "restrict_to_domain": null, + "role_name": "System Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:19.173127", + "name": "Guest", + "restrict_to_domain": null, + "role_name": "Guest", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:20.444323", + "name": "Website Manager", + "restrict_to_domain": null, + "role_name": "Website Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:21.265113", + "name": "Dashboard Manager", + "restrict_to_domain": null, + "role_name": "Dashboard Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:24.039903", + "name": "Workspace Manager", + "restrict_to_domain": null, + "role_name": "Workspace Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:24.690340", + "name": "Report Manager", + "restrict_to_domain": null, + "role_name": "Report Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:26.377446", + "name": "Script Manager", + "restrict_to_domain": null, + "role_name": "Script Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:31.947922", + "name": "Inbox User", + "restrict_to_domain": null, + "role_name": "Inbox User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:38.777401", + "name": "Prepared Report User", + "restrict_to_domain": null, + "role_name": "Prepared Report User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:45.317483", + "name": "Blogger", + "restrict_to_domain": null, + "role_name": "Blogger", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:50.870457", + "name": "Knowledge Base Contributor", + "restrict_to_domain": null, + "role_name": "Knowledge Base Contributor", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:50.879219", + "name": "Knowledge Base Editor", + "restrict_to_domain": null, + "role_name": "Knowledge Base Editor", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:35:51.226238", + "name": "Newsletter Manager", + "restrict_to_domain": null, + "role_name": "Newsletter Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:36:03.376844", + "name": "Purchase User", + "restrict_to_domain": null, + "role_name": "Purchase User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:36:03.382037", + "name": "Accounts Manager", + "restrict_to_domain": null, + "role_name": "Accounts Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:36:03.393822", + "name": "Accounts User", + "restrict_to_domain": null, + "role_name": "Accounts User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:36:03.407907", + "name": "Sales User", + "restrict_to_domain": null, + "role_name": "Sales User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:36:20.721069", + "name": "Maintenance User", + "restrict_to_domain": null, + "role_name": "Maintenance User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:36:21.139134", + "name": "Sales Master Manager", + "restrict_to_domain": null, + "role_name": "Sales Master Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-12-02 12:03:08.192040", + "name": "Maintenance Manager", + "restrict_to_domain": null, + "role_name": "Maintenance Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:36:21.214703", + "name": "Sales Manager", + "restrict_to_domain": null, + "role_name": "Sales Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:36:21.265634", + "name": "Purchase Manager", + "restrict_to_domain": null, + "role_name": "Purchase Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:36:21.300925", + "name": "Purchase Master Manager", + "restrict_to_domain": null, + "role_name": "Purchase Master Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:36:30.941434", + "name": "Translator", + "restrict_to_domain": null, + "role_name": "Translator", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:38:27.560209", + "name": "Auditor", + "restrict_to_domain": null, + "role_name": "Auditor", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:38:33.807572", + "name": "Employee", + "restrict_to_domain": null, + "role_name": "Employee", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:38:55.043319", + "name": "Stock User", + "restrict_to_domain": null, + "role_name": "Stock User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:38:55.085051", + "name": "Stock Manager", + "restrict_to_domain": null, + "role_name": "Stock Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:39:09.591960", + "name": "HR Manager", + "restrict_to_domain": null, + "role_name": "HR Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:39:15.793051", + "name": "Manufacturing Manager", + "restrict_to_domain": null, + "role_name": "Manufacturing Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:39:22.567962", + "name": "Projects User", + "restrict_to_domain": null, + "role_name": "Projects User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:39:23.129226", + "name": "Projects Manager", + "restrict_to_domain": null, + "role_name": "Projects Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:39:24.665470", + "name": "Manufacturing User", + "restrict_to_domain": null, + "role_name": "Manufacturing User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:39:24.703795", + "name": "HR User", + "restrict_to_domain": null, + "role_name": "HR User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:39:33.545346", + "name": "Item Manager", + "restrict_to_domain": null, + "role_name": "Item Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:39:39.041152", + "name": "Delivery Manager", + "restrict_to_domain": null, + "role_name": "Delivery Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:39:39.120493", + "name": "Delivery User", + "restrict_to_domain": null, + "role_name": "Delivery User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:39:39.164591", + "name": "Fleet Manager", + "restrict_to_domain": null, + "role_name": "Fleet Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:40:58.334106", + "name": "Customer", + "restrict_to_domain": null, + "role_name": "Customer", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:39:42.853853", + "name": "Academics User", + "restrict_to_domain": null, + "role_name": "Academics User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:39:57.597870", + "name": "Fulfillment User", + "restrict_to_domain": null, + "role_name": "Fulfillment User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:39:58.626027", + "name": "Quality Manager", + "restrict_to_domain": null, + "role_name": "Quality Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:40:20.875114", + "name": "Support Team", + "restrict_to_domain": null, + "role_name": "Support Team", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:40:26.597194", + "name": "Agriculture User", + "restrict_to_domain": null, + "role_name": "Agriculture User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:40:26.686632", + "name": "Agriculture Manager", + "restrict_to_domain": null, + "role_name": "Agriculture Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:40:58.335720", + "name": "Supplier", + "restrict_to_domain": null, + "role_name": "Supplier", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-19 19:40:54.989931", + "name": "Analytics", + "restrict_to_domain": null, + "role_name": "Analytics", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-13 09:46:01.365842", + "name": "End user", + "restrict_to_domain": null, + "role_name": "End user", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-13 09:55:47.630048", + "name": "Technician", + "restrict_to_domain": null, + "role_name": "Technician", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-17 17:29:43.223879", + "name": "Finance User", + "restrict_to_domain": null, + "role_name": "Finance User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-09-17 17:36:27.388207", + "name": "Finance Manager", + "restrict_to_domain": null, + "role_name": "Finance Manager", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2022-12-07 21:36:44.284615", + "name": "Insights Admin", + "restrict_to_domain": null, + "role_name": "Insights Admin", + "two_factor_auth": 0 + }, + { + "desk_access": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2022-12-07 21:36:44.121244", + "name": "Insights User", + "restrict_to_domain": null, + "role_name": "Insights User", + "two_factor_auth": 0 + }, + { + "desk_access": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Role", + "home_page": null, + "is_custom": 0, + "modified": "2024-12-26 17:36:24.120200", + "name": "Asset Manager", + "restrict_to_domain": null, + "role_name": "Asset Manager", + "two_factor_auth": 0 + } +] \ No newline at end of file diff --git a/asset_lite/fixtures/server_script.json b/asset_lite/fixtures/server_script.json new file mode 100644 index 0000000..ed094d5 --- /dev/null +++ b/asset_lite/fixtures/server_script.json @@ -0,0 +1,705 @@ +[ + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Insert", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-09-02 18:08:19.628822", + "module": "Asset Lite", + "name": "Issue", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Issue", + "script": "recipients = [\r\n 'support@seeraarabia.com','hussein.albeshri@seeraarabia.com','mohamed.elhawary@seeraarabia.com'\r\n \r\n]\r\n\r\nfrappe.sendmail(\r\n recipients=recipients,\r\n subject=doc.subject,\r\n message=\"Dear Sir/Madam

\"+doc.subject+\"

Regards,
Support Team\",\r\n)", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Insert", + "enable_rate_limit": 0, + "event_frequency": "Daily", + "modified": "2024-10-17 15:40:12.770634", + "module": "Asset Lite", + "name": "Support Plan status set", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": null, + "script": "# Get today's date as a datetime.date object\r\ntoday = frappe.utils.today()\r\ntoday_date = frappe.utils.getdate(today)\r\n\r\n# Fetch all documents from the 'Support Plans' doctype\r\nsupport_plans = frappe.get_all('Asset', fields=['name','custom_warranty_start_date', 'custom_warranty_end_date', 'custom_warranty_status','custom_service_contract_start'\r\n,'custom_service_contract_end','custom_service_contract_status'])\r\n# warrantys = frappe.get_all('Warranty', fields=['name','warranty_start_date', 'warranty_end_date', 'warranty_status'])\r\n\r\nfor plan in support_plans:\r\n doc = frappe.get_doc('Asset', plan.name)\r\n\r\n # Convert service contract date objects if they are not None\r\n start_date = frappe.utils.getdate(doc.custom_service_contract_start) if doc.custom_service_contract_start else None\r\n end_date = frappe.utils.getdate(doc.custom_service_contract_end) if doc.custom_service_contract_end else None\r\n \r\n\r\n # Check and update service contract status\r\n if start_date and end_date:\r\n if start_date <= today_date <= end_date:\r\n doc.custom_service_contract_status = 'Active'\r\n elif today_date > end_date:\r\n doc.custom_service_contract_status = 'Expired'\r\n else:\r\n doc.custom_service_contract_status = ''\r\n \r\n \r\n # Get the regular warranty start and end dates\r\n war_start_date = frappe.utils.getdate(doc.custom_warranty_start_date) if doc.custom_warranty_start_date else None\r\n war_end_date = frappe.utils.getdate(doc.custom_warranty_end_date) if doc.custom_warranty_end_date else None\r\n \r\n # Get the extended warranty start and end dates\r\n extended_war_start_date = frappe.utils.getdate(doc.custom_extended_start_date) if doc.custom_extended_start_date else None\r\n extended_war_end_date = frappe.utils.getdate(doc.custom_extended_end_date) if doc.custom_extended_end_date else None\r\n \r\n # Set the status based on the warranty period and extended warranty\r\n if war_start_date and war_end_date:\r\n if war_start_date <= today_date <= war_end_date:\r\n doc.custom_warranty_status = 'Active'\r\n elif extended_war_start_date and extended_war_end_date and extended_war_start_date <= today_date <= extended_war_end_date:\r\n doc.custom_warranty_status = 'Active'\r\n elif today_date > war_end_date:\r\n doc.custom_warranty_status = 'Expired'\r\n else:\r\n doc.custom_warranty_status = ''\r\n\r\n # Save the updated document\r\n doc.save()\r\n \r\n# for warranty in warrantys:\r\n# doc1 = frappe.get_doc('Warranty', warranty.name)\r\n \r\n# # Convert warranty_start and warranty_end to date objects if they are not None\r\n# warranty_start = frappe.utils.getdate(doc1.warranty_start_date) if doc1.warranty_start_date else None\r\n# warranty_end = frappe.utils.getdate(doc1.warranty_end_date) if doc1.warranty_end_date else None\r\n \r\n# # Check and update warranty status\r\n# if warranty_start and warranty_end:\r\n# if warranty_start <= today_date <= warranty_end:\r\n# doc1.warranty_status = 'Active'\r\n# elif today_date > warranty_end:\r\n# doc1.warranty_status = 'Expired'\r\n# else:\r\n# doc1.warranty_status = ''\r\n \r\n# # Save the updated document\r\n# doc1.save()\r\n", + "script_type": "Scheduler Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-08-26 13:50:56.568464", + "module": "Asset Lite", + "name": "Auto Set User Names", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Work_Order", + "script": "amount = 0\nif doc and doc.invoice_table:\n for row in doc.invoice_table:\n amount = amount + row.cost\n \n doc.repair_cost = amount\n \nstock_amt = 0 \nif doc and doc.stock_items:\n for row in doc.stock_items:\n if row.total_value:\n stock_amt = stock_amt + row.total_value\n \n doc.total_repair_cost = stock_amt\n \ndoc.total_repair_cost = amount + stock_amt\n \n \n\n\n# user = frappe.get_doc('User', frappe.session.user)\n# for role in user.roles:\n# if((role.role ==\"End user\" or role.role ==\"PHCC End User\")and role.role !=\"Technician\" and role.role !=\"Maintenance Manager\" \n# and role.role !=\"PHCC Site Manager\"):\n# doc.end_user = user.full_name\n\n \nuser = frappe.get_doc(\"User\", frappe.session.user)\n\n# Collect roles into a set\nroles = {r.role for r in user.roles}\n\n# Allowed vs excluded\nallowed = {\"End user\", \"PHCC End User\"}\nexcluded = {\"Technician\", \"Maintenance Manager\", \"PHCC Site Manager\",\"System Manager\",\"General WOA\",\"General Contractor\"}\n\n# Logic\nif roles.intersection(allowed) and not roles.intersection(excluded):\n if not doc.end_user:\n doc.end_user = user.full_name\n \n\n\nif doc.custom_assign_to_contractor and not doc.serviced_by:\n tech_user = frappe.get_doc(\"User\", doc.custom_assign_to_contractor)\n doc.serviced_by = tech_user.full_name\n \nif doc.custom_assigned_supervisor and not doc.bio_med_dept:\n sup_user = frappe.get_doc(\"User\", doc.custom_assigned_supervisor)\n doc.bio_med_dept = sup_user.full_name\n\n \n# if(role.role ==\"Technician\"):\n# doc.serviced_by = user.full_name\n \n# if(role.role ==\"Maintenance Manager\"):\n# doc.bio_med_dept = user.full_name\n \n\n\n# Stock availability check \nif doc.stock_items:\n \n for item in doc.stock_items: # Loop through all stock items in Work Order\n item_code = item.item_code\n required_qty = float(item.consumed_quantity or 0)\n # required_qty = float(item.consumed_quantity) or 0\n warehouse = item.warehouse # Make sure you have a warehouse field in your stock table\n \n if not item_code or not warehouse:\n continue # Skip if no item or warehouse is set\n\n # ✅ Fetch Available Stock from Stock Ledger\n available_qty = frappe.db.get_value(\"Bin\", \n {\"item_code\": item_code, \"warehouse\": warehouse}, \"actual_qty\") or 0\n \n available_qty = float(available_qty)\n\n # ✅ompare Available vs Required Quantity\n if required_qty > available_qty:\n frappe.throw(\n f\"❌ Insufficient stock for {item_code} in warehouse {warehouse}. \"\n f\"Available: {available_qty}, Required: {required_qty}.\"\n )\n \n \n \n# # Script to auto assign technician to PHCC site Work orders\n\n# if doc.site_name:\n# site = doc.site_name\n\n# # Check if ToDo already exists for this Work Order\n# existing_todo = frappe.db.exists(\"ToDo\", {\n# \"reference_type\": \"Work_Order\",\n# \"reference_name\": doc.name\n# })\n# print(existing_todo)\n# if not existing_todo:\n \n# # Step 1: Get users with permission to this Mobile Team Site\n# permitted_users = frappe.get_all(\n# \"User Permission\",\n# filters={\n# \"allow\": \"Mobile Team Site\",\n# \"for_value\": site\n# },\n# fields=[\"user\"]\n# )\n# print(permitted_users)\n# if permitted_users:\n \n# # Step 2: Filter users with 'Technician' role\n# technician_users = []\n# for entry in permitted_users:\n# user_roles = frappe.get_all(\"Has Role\", filters={\"parent\": entry.user}, fields=[\"role\"])\n# if any(r[\"role\"] == \"Technician\" for r in user_roles):\n# technician_users.append(entry.user)\n# print(technician_users)\n# if technician_users:\n# # Step 3: Assign to first technician user found (or apply logic to choose)\n# assigned_user = technician_users[0]\n# print(assigned_user)\n \n# # Create the ToDo\n# frappe.get_doc({\n# \"doctype\": \"ToDo\",\n# \"allocated_to\": assigned_user,\n# \"description\": f\"Work Order Assigned: {doc.name}\",\n# \"reference_type\": \"Work_Order\",\n# \"reference_name\": doc.name,\n# \"status\": \"Open\"\n# }).insert(ignore_permissions=True)\n \n# frappe.msgprint(f\"Assigned Work Order {doc.name} to Technician: {assigned_user}\")\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 1, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Insert", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-04-22 12:40:36.864716", + "module": "Asset Lite", + "name": "Auto creation of asset from Item", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Item", + "script": "if doc.is_fixed_asset == 1:\r\n new_asset = frappe.get_doc({\r\n \"doctype\": \"Asset\",\r\n \"item_code\": doc.item_code,\r\n \"location\": doc.custom_location,\r\n \"asset_name\": doc.item_code,\r\n \"asset_category\":doc.asset_category,\r\n \"gross_purchase_amount\":doc.custom_gross_purchase_amount,\r\n \"available_for_use_date\":doc.custom_available_for_use_date,\r\n \"is_existing_asset\":1,\r\n \"custom_asset_type\":doc.custom_asset_type,\r\n \"company\":\"King Fahad Specialist Hospital-Dammam\"\r\n })\r\n new_asset.insert()\r\n frappe.msgprint(\"Fixed Asset has been created\")", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2024-10-17 15:44:33.022436", + "module": "Asset Lite", + "name": "Auto set warranty status", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Support Plans", + "script": "# Get today's date as a datetime.date object\ntoday = frappe.utils.today()\ntoday_date = frappe.utils.getdate(today)\n\nstart_date = frappe.utils.getdate(doc.custom_service_contract_start) if doc.custom_service_contract_start else None\nend_date = frappe.utils.getdate(doc.custom_service_contract_end) if doc.custom_service_contract_end else None\n\n\n# Check and update service contract status\nif start_date and end_date:\n if start_date <= today_date <= end_date:\n doc.custom_service_contract_status = 'Active'\n elif today_date > end_date:\n doc.custom_service_contract_status = 'Expired'\n else:\n doc.custom_service_contract_status = ''\n \n\n# Get the regular warranty start and end dates\nwar_start_date = frappe.utils.getdate(doc.custom_warranty_start_date) if doc.custom_warranty_start_date else None\nwar_end_date = frappe.utils.getdate(doc.custom_warranty_end_date) if doc.custom_warranty_end_date else None\n\n# Get the extended warranty start and end dates\nextended_war_start_date = frappe.utils.getdate(doc.custom_extended_start_date) if doc.custom_extended_start_date else None\nextended_war_end_date = frappe.utils.getdate(doc.custom_extended_end_date) if doc.custom_extended_end_date else None\n\n# Set the status based on the warranty period and extended warranty\nif war_start_date and war_end_date:\n if war_start_date <= today_date <= war_end_date:\n doc.custom_warranty_status = 'Active'\n elif extended_war_start_date and extended_war_end_date and extended_war_start_date <= today_date <= extended_war_end_date:\n doc.custom_warranty_status = 'Active'\n elif today_date > war_end_date:\n doc.custom_warranty_status = 'Expired'\n else:\n doc.custom_warranty_status = ''\n \n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2024-09-17 13:26:06.877577", + "module": "Asset Lite", + "name": "Warranty status Set", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Warranty", + "script": "# Get today's date as a datetime.date object\ntoday = frappe.utils.today()\ntoday_date = frappe.utils.getdate(today)\n\n # Convert warranty_start and warranty_end to date objects if they are not None\nwarranty_start = frappe.utils.getdate(doc.warranty_start_date) if doc.warranty_start_date else None\nwarranty_end = frappe.utils.getdate(doc.warranty_end_date) if doc.warranty_end_date else None\n\n# Check and update warranty status\nif warranty_start and warranty_end:\n if warranty_start <= today_date <= warranty_end:\n doc.warranty_status = 'Active'\n elif today_date > warranty_end:\n doc.warranty_status = 'Expired'\n else:\n doc.warranty_status = ''", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Insert", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-12-31 21:23:58.326478", + "module": "Asset Lite", + "name": "PPM autocreation", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Asset Maintenance Log", + "script": "# Check if maintenance status is \"Planned\" and custom asset type is provided\r\nif doc.maintenance_status == \"Planned\" and doc.custom_asset_type:\r\n \r\n # Fetch the PPM Template based on the asset type in the Asset Maintenance Log\r\n ppm_template = frappe.db.get_value('PPM Templates', {'asset_type': doc.custom_asset_type}, 'name')\r\n\r\n if ppm_template:\r\n # Create a new PPM document using the fetched template and asset maintenance log details\r\n ppm_doc = frappe.get_doc({\r\n \"doctype\": \"PPM\",\r\n \"asset_maintenance_log\": doc.name, \r\n \"data\": ppm_template, \r\n })\r\n\r\n # Insert the PPM document into the database\r\n ppm_doc.insert() \r\n\r\n frappe.msgprint(f\"Planned Preventive Maintenance created for Asset {doc.asset_name}\")\r\n else:\r\n ppm_doc1 = frappe.get_doc({\r\n \"doctype\": \"PPM\",\r\n \"asset_maintenance_log\": doc.name, \r\n \"data\": \"CT Scan\", \r\n })\r\n\r\n # Insert the PPM document into the database\r\n ppm_doc1.insert()\r\n \r\n frappe.msgprint(f\"Planned Preventive Maintenance created for Asset {doc.asset_name}\")\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-11-09 21:40:28.934021", + "module": "Asset Lite", + "name": "Asset status Update", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Work_Order", + "script": "\r\n# Fetch all work orders with status 'Open' or 'Work In Progress'\r\nwork_orders = frappe.get_all(\"Work_Order\", filters={\"repair_status\": [\"in\", [\"Open\", \"Work In Progress\",\"Pending Review\"]]}, fields=[\"name\", \"asset\"])\r\n\r\n# Create a set of assets that are linked to work orders with status 'Open' or 'Work In Progress'\r\ndown_assets = set()\r\nfor wo in work_orders:\r\n if wo.asset:\r\n down_assets.add(wo.asset)\r\n\r\n# Fetch all assets\r\nassets = frappe.get_all(\"Asset\", fields=[\"name\", \"custom_device_status\", \"docstatus\"])\r\n\r\nfor asset in assets:\r\n # Skip if the asset is cancelled\r\n if asset[\"docstatus\"] == 2: # 2 means the document is cancelled\r\n continue\r\n \r\n # Determine the new status based on the work orders\r\n new_status = \"Down\" if asset[\"name\"] in down_assets else \"Up\"\r\n if asset[\"custom_device_status\"] != new_status:\r\n # Update the asset status if it has changed\r\n # asset_doc = frappe.get_doc(\"Asset\", asset[\"name\"])\r\n # asset_doc.custom_device_status = new_status\r\n # asset_doc.save(ignore_permissions=True)\r\n frappe.db.set_value(\"Asset\", asset.name, \"custom_device_status\", new_status)\r\n\r\n\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Insert", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-04-22 12:40:37.019545", + "module": "Asset Lite", + "name": "PPM Update", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Asset Maintenance Log", + "script": "# Check if maintenance status is \"Planned\" and custom asset type is provided\r\nif doc.maintenance_status == \"Planned\" and doc.custom_asset_type:\r\n\r\n # Fetch the PPM Template based on the asset type in the Asset Maintenance Log\r\n ppm_template = frappe.db.get_value('PPM Templates', {'asset_type': doc.custom_asset_type}, 'name')\r\n if ppm_template:\r\n \r\n if not doc.custom_table:\r\n doc.custom_template = ppm_template\r\n template_data = frappe.get_doc(\"PPM Templates\", ppm_template)\r\n\r\n for item in template_data.ppm_template_table:\r\n doc.append(\"custom_table\", {\r\n \"maintenance_name\": item.maintenance_name,\r\n })\r\n \r\n else:\r\n if not doc.custom_table:\r\n template_data = frappe.get_doc(\"PPM Templates\", \"CT Scan\")\r\n\r\n for item in template_data.ppm_template_table:\r\n doc.append(\"custom_table\", {\r\n \"maintenance_name\": item.maintenance_name,\r\n })\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 1, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-04-22 12:40:36.994832", + "module": "Asset Lite", + "name": "Plus Button", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Work_Order", + "script": "\nif doc.workflow_state == \"Repair InProgress\":\n # Inject link in Connections when the state is \"Repair InProgress\"\n doc.add_comment(\"Comment\", \"Connections enabled for Repair InProgress state.\")\n \nelse:\n # Return false or remove links as needed for other states\n pass", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Save (Submitted Document)", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-04-22 12:40:36.903945", + "module": "Asset Lite", + "name": "Asset warranty end date addition", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Work_Order", + "script": "penalty_value = int(float(doc.penalty))\r\n\r\nif penalty_value> 0:\r\n # Ensure an asset is linked to the work order\r\n if not doc.asset:\r\n frappe.throw(_(\"No Asset is linked to this Work Order.\"))\r\n \r\n # Fetch the asset document\r\n asset = frappe.get_doc(\"Asset\", doc.asset)\r\n \r\n # Get the current warranty end date (default to today if not set)\r\n current_warranty_end_date = asset.custom_warranty_end_date or frappe.utils.nowdate()\r\n \r\n # Calculate the new warranty end date by adding penalty days\r\n new_warranty_end_date = frappe.utils.add_days(current_warranty_end_date, penalty_value)\r\n \r\n # Update the asset with the new warranty end date\r\n asset.custom_warranty_end_date = new_warranty_end_date\r\n asset.save(ignore_permissions=True)\r\n \r\n # Notify the user about the update\r\n frappe.msgprint(\r\n \"The warranty end date for Asset \" \r\n + str(asset.name) \r\n + \" has been updated \" \r\n \r\n )\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-08-26 13:49:25.456532", + "module": "Asset Lite", + "name": "Location", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Asset", + "script": "# Server Script for Asset Doctype\r\nif not doc.location:\r\n doc.location = \"Ohud\"\r\n \r\n# if doc and doc.custom_serial_number:\r\n# if frappe.db.exists(\"Asset\", {\"custom_serial_number\": doc.custom_serial_number,\"name\": [\"!=\", doc.name]}):\r\n# frappe.throw(f\"Serial Number: {doc.custom_serial_number} is already exists. Please enter a unique Serial Number.\")\r\n\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 1, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Submit", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-04-22 12:40:36.959715", + "module": "Asset Lite", + "name": "Depreciation post date", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Asset", + "script": "assets = frappe.get_all('Asset', filters={'calculate_depreciation': 1}, fields=['name'])\r\n\r\nfor asset in assets:\r\n # Get the full asset document\r\n asset_doc = frappe.get_doc(\"Asset\", asset.name)\r\n# Ensure the child table 'finance_books' exists in the asset document\r\n if asset_doc.finance_books:\r\n for row in asset_doc.finance_books:\r\n # Get the depreciation start date from the child table row\r\n depreciation_start_date = frappe.utils.getdate(row.depreciation_start_date)\r\n today = frappe.utils.getdate(frappe.utils.today())\r\n \r\n # Check if the depreciation start date matches today's date\r\n if depreciation_start_date == today:\r\n \r\n # Email subject and message\r\n subject = f\"Reminder: Depreciation is Posted\"\r\n message = f\"\"\"\r\n This is a reminder that the depreciation for Asset starts today.\r\n \r\n Depreciation Start Date: {row.depreciation_start_date}\r\n \r\n \"\"\"\r\n \r\n email_recipients = []\r\n users = frappe.get_all(\"User\")\r\n for user in users:\r\n userdoc = frappe.get_doc(\"User\", user.name)\r\n # print(doc.roles)\r\n for row in userdoc.roles:\r\n # print(row.role)\r\n if row.role == \"Maintenance Manager\":\r\n print(userdoc.email)\r\n email_recipients.append(userdoc.email)\r\n # email_recipients = [\"maintenancemanager@gmail.com\"] # Replace with actual manager emails\r\n \r\n # Send the email notification\r\n frappe.sendmail(\r\n recipients=email_recipients,\r\n subject=subject,\r\n message=message\r\n )\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-04-22 12:40:36.937380", + "module": "Asset Lite", + "name": "Work_order email", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Work_Order", + "script": "if doc.workflow_state == \"Completed\":\n # Fetch the user's roles\n user_roles = frappe.db.get_all(\n 'Has Role',\n filters={'parent': doc.owner},\n fields=['role'],\n pluck='role'\n )\n\n # Check if the user does NOT have the 'Asset Manager' role\n if \"Asset Manager\" not in user_roles:\n # Create the notification message with a link\n subject = f\"Feedback Required for Work Order {doc.name}\"\n quality_feedback_link = f\"/app/quality-feedback/new-quality-feedback-1\"\n message = f\"\"\"\n Hello {doc.owner},\n
\n Please provide your feedback for Work Order {doc.name}. \n Click here to provide your feedback.\n
\n Thank you!\n \"\"\"\n\n # Send notification\n frappe.sendmail(\n recipients=[doc.owner],\n subject=subject,\n message=message,\n )", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Submit", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-04-22 12:40:36.924935", + "module": "Asset Lite", + "name": "Work order", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Work Job Order", + "script": "if doc:\n doc.completion_date = frappe.utils.now_datetime()\n doc.job_completed = \"Yes\"", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 1, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Submit", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-04-22 12:40:36.880055", + "module": "Asset Lite", + "name": "Auto Create Feedback", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Feedback", + "script": "new_feedback = frappe.get_doc({\r\n \"doctype\": \"Feedback\",\r\n \"work_order\": doc.name,\r\n\r\n})\r\nnew_feedback.insert()\r\nfrappe.msgprint('Feedback Created')", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Insert", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2026-01-06 17:11:37.557942", + "module": "Asset Lite", + "name": "Create Item on Asset", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Asset", + "script": "# Check if an item already exists\r\nitem_exists = frappe.db.exists(\"Item\", doc.asset_name)\r\n\r\nif not item_exists:\r\n # Create new item since it does not exist\r\n new_item = frappe.get_doc({\r\n \"doctype\": \"Item\",\r\n \"item_code\": doc.asset_name,\r\n \"item_name\": doc.asset_name,\r\n \"asset_category\": \"Bio Medical\",\r\n \"custom_gross_purchase_amount\": doc.gross_purchase_amount,\r\n \"custom_available_for_use_date\": doc.available_for_use_date,\r\n \"is_fixed_asset\": 1,\r\n \"is_stock_item\":0,\r\n \"custom_asset_type\": doc.custom_asset_type,\r\n \"item_group\": \"Biomedical\",\r\n \"stock_uom\": \"Nos\",\r\n \"custom_hospital_name\":doc.company\r\n })\r\n new_item.insert(ignore_permissions=True) # Ignore permission issues\r\n # frappe.msgprint(f\"Item {doc.asset_name} has been created\")\r\n\r\n # Assign newly created item to asset's item_code field\r\n doc.item_code = doc.asset_name\r\n\r\nelse:\r\n # If item exists, fetch the document and assign the name\r\n item_doc = frappe.get_doc(\"Item\", item_exists)\r\n doc.item_code = item_doc.name\r\n\r\n\r\n# if doc.custom_local_id:\r\n# if frappe.db.exists(\"Asset\", {\"custom_local_id\": doc.custom_local_id}):\r\n# frappe.throw(f\"Local ID: {doc.custom_local_id} is already exists. Please enter a unique LOCAL ID.\")\r\n# if doc.custom_serial_number:\r\n# if frappe.db.exists(\"Asset\", {\"custom_serial_number\": doc.custom_serial_number}):\r\n# frappe.throw(f\"Serial Number: {doc.custom_serial_number} is already exists. Please enter a unique Serial Number.\")\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-04-24 14:17:13.388211", + "module": "Asset Lite", + "name": "Purchase Invoice", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Purchase Invoice", + "script": "if doc.custom_purchase_order and not doc.custom_work_order:\r\n po = frappe.get_doc(\"Purchase Order\", doc.custom_purchase_order)\r\n \r\n if po.name and po.items:\r\n first_item = po.items[0] # Get the first row\r\n material_request = first_item.material_request # Get the Material Request from first row\r\n\r\n if material_request:\r\n mr_doc = frappe.get_doc(\"Material Request\", material_request)\r\n if mr_doc.custom_work_order: # Ensure the Work Order field exists\r\n doc.custom_work_order = mr_doc.custom_work_order\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Save (Submitted Document)", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-04-22 12:40:36.801438", + "module": "Asset Lite", + "name": "Asset Down Time Updation from WO", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Work_Order", + "script": "if doc.asset:\r\n ast = frappe.get_doc(\"Asset\", doc.asset)\r\n \r\n if doc.failure_date and doc.completion_date:\r\n failure_datetime = frappe.utils.get_datetime(doc.failure_date)\r\n completed_datetime = frappe.utils.get_datetime(doc.completion_date)\r\n\r\n # Calculate the difference in hours\r\n downtime = (completed_datetime - failure_datetime).total_seconds() / 3600\r\n\r\n # If there's already a downtime recorded, add to it\r\n existing_downtime = ast.custom_down_time or 0\r\n total_downtime = round(existing_downtime + downtime, 2)\r\n\r\n # Update the Asset's downtime\r\n frappe.db.set_value(\"Asset\", ast.name, \"custom_down_time\", total_downtime)\r\n \r\n if doc.first_responded_on and doc.completion_date:\r\n first_datetime = frappe.utils.get_datetime(doc.first_responded_on)\r\n completed_datetime = frappe.utils.get_datetime(doc.completion_date)\r\n \r\n # Calculate the difference in hours\r\n hrs_spent = (completed_datetime - first_datetime).total_seconds() / 3600\r\n \r\n doc.total_hours_spent = hrs_spent\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Submit", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2026-01-02 15:13:18.919755", + "module": "Asset Lite", + "name": "Update Spares from WO to Asset", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Work_Order", + "script": "if doc.stock_items:\r\n \r\n \r\n\r\n stock_entry = frappe.new_doc(\"Stock Entry\")\r\n stock_entry.stock_entry_type = \"Material Issue\" # Reduces stock\r\n stock_entry.purpose = \"Material Issue\"\r\n # stock_entry.company = doc.company\r\n stock_entry.custom_job_order = doc.name # Link Work Order\r\n \r\n company = doc.company\r\n \r\n for item in doc.stock_items:\r\n item_rate = frappe.db.get_value(\"Item\", item.item_code, \"valuation_rate\") or 0\r\n company = frappe.db.get_value(\"Item Default\", {\"parent\": item.item_code}, \"company\")\r\n \r\n stock_entry.append(\"items\", {\r\n \"s_warehouse\": item.warehouse, # Warehouse to deduct from\r\n \"item_code\": item.item_code,\r\n \"qty\": item.consumed_quantity,\r\n \"uom\": frappe.db.get_value(\"Item\", item.item_code, \"stock_uom\"),\r\n \"basic_rate\": item.valuation_rate or item_rate,\r\n })\r\n stock_entry.company = company\r\n # Save and submit Stock Entry\r\n stock_entry.flags.ignore_permissions = True\r\n stock_entry.insert()\r\n stock_entry.submit()\r\n frappe.msgprint(f\"Stock deducted successfully via Stock Entry {stock_entry.name}\")\r\n\r\n \r\n \r\n\r\n\r\n\r\n\r\n\r\n# if not doc.asset:\r\n# frappe.throw(\"Asset ID is required to update spare parts.\")\r\n \r\nif doc.asset:\r\n # Fetch the Asset document linked to this Work Order\r\n asset_doc = frappe.get_doc(\"Asset\", doc.asset)\r\n total_amount = 0\r\n for item in doc.stock_items:\r\n total_amount = total_amount +item.total_value\r\n asset_doc.append(\"custom_spare_parts\", {\r\n \"work_order\":doc.name,\r\n \"item_code\": item.item_code,\r\n \"item_name\": item.item_code,\r\n \"qty\": item.consumed_quantity,\r\n \"rate\": item.valuation_rate,\r\n \"amount\": item.total_value\r\n })\r\n \r\n # if doc.repair_cost:\r\n # total_amount = total_amount +doc.repair_cost\r\n \r\n for row in doc.invoice_table:\r\n pi = frappe.get_doc(\"Purchase Invoice\", row.purchase_invoice)\r\n for pi_row in pi.items:\r\n asset_doc.append(\"custom_spare_parts\", {\r\n \"work_order\":doc.name,\r\n \"item_code\": pi_row.item_code,\r\n \"item_name\": pi_row.item_name,\r\n \"qty\": pi_row.qty,\r\n \"rate\": pi_row.rate,\r\n \"amount\": pi_row.amount\r\n })\r\n \r\n asset_doc.custom_total_spare_parts_amount = asset_doc.custom_total_spare_parts_amount + doc.total_repair_cost\r\n \r\n # Save the Asset document\r\n asset_doc.save()\r\n # frappe.msgprint(f\"Spare parts updated for Asset {doc.asset}\")\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 1, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-04-22 12:40:36.699028", + "module": "Asset Lite", + "name": "To calculate No of PMs", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Asset Maintenance", + "script": "# if doc.custom_start_date and doc.custom_end_date:\r\n# start_date = frappe.utils.getdate(doc.custom_start_date)\r\n# end_date = frappe.utils.getdate(doc.custom_end_date)\r\n# total_amount = doc.custom_total_amount or 0 # Ensure it's not None\r\n# total_pm_count = 0 # Initialize total PM count\r\n\r\n# # Mapping Periodicity to Date Increment Functions\r\n# periodicity_mapping = {\r\n# \"Daily\": lambda date: frappe.utils.add_days(date, 1),\r\n# \"Weekly\": lambda date: frappe.utils.add_days(date, 7),\r\n# \"Monthly\": lambda date: frappe.utils.add_months(date, 1),\r\n# \"Quarterly\": lambda date: frappe.utils.add_months(date, 3),\r\n# \"Half-yearly\": lambda date: frappe.utils.add_months(date, 6),\r\n# \"Yearly\": lambda date: frappe.utils.add_years(date, 1),\r\n# \"2 Yearly\": lambda date: frappe.utils.add_years(date, 2),\r\n# \"3 Yearly\": lambda date: frappe.utils.add_years(date, 3),\r\n# }\r\n\r\n# # Loop through the child table to process each periodicity\r\n# for task in doc.get(\"asset_maintenance_tasks\"):\r\n# periodicity = task.periodicity # Assuming the field is named \"periodicity\"\r\n\r\n# if not periodicity or periodicity not in periodicity_mapping:\r\n# continue # Skip invalid periodicities\r\n\r\n# increment_function = periodicity_mapping[periodicity]\r\n\r\n# # First maintenance happens after the first periodic interval\r\n# temp_date = increment_function(start_date)\r\n# pm_count = 0 # PM count for this periodicity\r\n\r\n# # Count PM occurrences between start and end date\r\n# while temp_date <= end_date:\r\n# pm_count =pm_count + 1\r\n# temp_date = increment_function(temp_date)\r\n\r\n# total_pm_count =total_pm_count + pm_count # Add to total count\r\n\r\n# # Update the document fields\r\n# doc.custom_no_of_pms = total_pm_count\r\n\r\n# # Calculate Price Per PM\r\n# doc.custom_price_per_pm = (total_amount / total_pm_count) if total_pm_count > 0 else 0\r\n\r\n\r\nif (doc.custom_start_date and doc.custom_end_date):\r\n # frappe.throw(\"Start Date and End Date are required.\")\r\n\r\n start_date = frappe.utils.getdate(doc.custom_start_date)\r\n end_date = frappe.utils.getdate(doc.custom_end_date)\r\n total_amount = doc.custom_total_amount\r\n total_pm_count = 0 # Initialize total PM count\r\n \r\n # Mapping Periodicity to Date Increment Functions\r\n periodicity_mapping = {\r\n \"Daily\": lambda date: frappe.utils.add_days(date, 1),\r\n \"Weekly\": lambda date: frappe.utils.add_days(date, 7),\r\n \"Monthly\": lambda date: frappe.utils.add_months(date, 1),\r\n \"Quarterly\": lambda date: frappe.utils.add_months(date, 3),\r\n \"Half-yearly\": lambda date: frappe.utils.add_months(date, 6),\r\n \"Yearly\": lambda date: frappe.utils.add_years(date, 1),\r\n \"2 Yearly\": lambda date: frappe.utils.add_years(date, 2),\r\n \"3 Yearly\": lambda date: frappe.utils.add_years(date, 3),\r\n }\r\n \r\n # Loop through the child table to process each periodicity\r\n for task in doc.get(\"asset_maintenance_tasks\"):\r\n periodicity = task.periodicity # Assuming the field is named \"periodicity\"\r\n \r\n if not periodicity or periodicity not in periodicity_mapping:\r\n continue # Skip invalid periodicities\r\n \r\n increment_function = periodicity_mapping[periodicity]\r\n temp_date = start_date\r\n pm_count = 0 # PM count for this periodicity\r\n \r\n # Count PM occurrences between start and end date\r\n while temp_date <= end_date:\r\n pm_count =pm_count + 1\r\n temp_date = increment_function(temp_date)\r\n \r\n total_pm_count =total_pm_count + pm_count # Add to total count\r\n \r\n # Update the document fields\r\n doc.custom_no_of_pms = total_pm_count\r\n \r\n # Calculate Price Per PM\r\n if total_amount and total_pm_count > 0:\r\n doc.custom_price_per_pm = total_amount / total_pm_count\r\n else:\r\n doc.custom_price_per_pm = 0", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-04-22 12:40:36.725618", + "module": "Asset Lite", + "name": "To Set Rating in WO", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Feedback", + "script": "if doc.work_order and doc.overall:\r\n frappe.db.set_value(\"Work_Order\", doc.work_order, \"feedback_rating\", doc.overall)\r\n # frappe.db.commit()", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-05-22 18:30:29.780919", + "module": "Asset Lite", + "name": "To set Proce Per PMS and Total No of PMs", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Asset Maintenance", + "script": "if doc.custom_service_coverage_table:\r\n total_pms = 0\r\n total_pms_exclude_warranty = 0\r\n\r\n for row in doc.custom_service_coverage_table:\r\n # Ensure no_of_pms is at least 0\r\n no_of_pms = int(row.no_of_pms or 0)\r\n total_pms =total_pms + int(no_of_pms or 0)\r\n\r\n # Ensure service_agreement is not None before checking\r\n if row.service_agreement and row.service_agreement != \"Warranty\":\r\n total_pms_exclude_warranty =total_pms_exclude_warranty + no_of_pms\r\n\r\n # Assign calculated values\r\n doc.custom_no_of_pms = total_pms\r\n # doc.total_pms_exclude_warranty = total_pms_exclude_warranty # Ensure it's updated\r\n\r\n # Avoid division by zero\r\n if total_pms_exclude_warranty > 0:\r\n doc.custom_price_per_pm = doc.custom_total_amount / total_pms_exclude_warranty\r\n else:\r\n doc.custom_price_per_pm = 0 # Set a fallback value\r\n \r\n \r\n \r\nif doc.custom_service_coverage_table:\r\n today_date = frappe.utils.getdate(frappe.utils.today())\r\n\r\n for row in doc.custom_service_coverage_table:\r\n if row.start_date and row.end_date:\r\n start = frappe.utils.getdate(row.start_date)\r\n end = frappe.utils.getdate(row.end_date)\r\n\r\n # Check if today's date is within the start and end date range\r\n if start <= today_date <= end:\r\n if row.active != 'Yes':\r\n row.active = 'Yes'\r\n else:\r\n if row.active != 'No':\r\n row.active = 'No'\r\n\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Insert", + "enable_rate_limit": 0, + "event_frequency": "Hourly", + "modified": "2024-10-17 14:13:14.465565", + "module": "Asset Lite", + "name": "Asset Update in Support Plan", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": null, + "script": "try:\r\n # Get all support plans\r\n support_plans = frappe.get_all(\"Support Plans\", fields=[\"name\"])\r\n\r\n for plan in support_plans:\r\n # Fetch all assets linked to this support plan\r\n assets = frappe.get_all(\"Asset\", filters={\"custom_support_plan\": plan.name}, fields=[\"name\", \"asset_name\", \"status\"])\r\n\r\n # Fetch the support plan document\r\n support_plan_doc = frappe.get_doc(\"Support Plans\", plan.name)\r\n\r\n # Clear the existing asset list table\r\n support_plan_doc.set(\"asset_list\", [])\r\n\r\n # Populate the asset list table with assets linked to this support plan\r\n for asset in assets:\r\n support_plan_doc.append(\"asset_list\", {\r\n \"asset_name\": asset.asset_name,\r\n \"asset_id\": asset.name,\r\n \r\n })\r\n\r\n # Save the updated support plan document\r\n support_plan_doc.save(ignore_permissions=True)\r\n\r\nexcept Exception as e:\r\n frappe.log_error(f\"Error updating support plans: {str(e)}\", \"Support Plan Update Error\")\r\n\r\n\r\n", + "script_type": "Scheduler Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 1, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Insert", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-04-22 12:40:37.040174", + "module": "Asset Lite", + "name": "Asset Total Hours", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Asset", + "script": "# Fetch all assets where 'current_datetime' is set\r\nassets = frappe.get_all(\"Asset\", filters={\"custom_installation_date\": [\"is\", \"set\"]}, fields=[\"name\", \"custom_installation_date\"])\r\n \r\n# Iterate over each asset to calculate and update the total hours\r\nfor asset in assets:\r\n # Parse the 'current_datetime' value as a datetime object\r\n installation_datetime = frappe.utils.get_datetime(asset.custom_installation_date)\r\n current_datetime = frappe.utils.now_datetime()\r\n\r\n # Calculate the time difference in hours\r\n time_diff = (current_datetime - installation_datetime).total_seconds() / 3600\r\n \r\n # Update the 'total_hours' field for the asset\r\n frappe.db.set_value(\"Asset\", asset.name, \"custom_total_hours\", round(time_diff, 2))\r\n frappe.db.commit()\r\n\r\n\r\n", + "script_type": "Scheduler Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": "* */5 * * * ", + "disabled": 1, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Insert", + "enable_rate_limit": 0, + "event_frequency": "Cron", + "modified": "2025-04-22 12:40:36.818236", + "module": "Asset Lite", + "name": "Asset Down Time", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Asset", + "script": "# Fetch all assets\r\nassets = frappe.get_all(\"Asset\", fields=[\"name\",\"available_for_use_date\"])\r\n\r\n# Iterate over each asset\r\nfor asset in assets:\r\n total_up_time = 0.0\r\n total_hrs = 0.0\r\n \r\n total_downtime = 0.0\r\n \r\n\r\n # Fetch all work orders related to this asset with both failure and completion datetimes set\r\n work_orders = frappe.get_all(\r\n \"Work_Order\",\r\n filters={\r\n \"asset\": asset.name,\r\n \"failure_date\": [\"is\", \"set\"],\r\n \"completion_date\": [\"is\", \"set\"]\r\n },\r\n fields=[\"name\", \"failure_date\", \"completion_date\"]\r\n )\r\n\r\n # Calculate total downtime for all work orders of the asset\r\n for wo in work_orders:\r\n failure_datetime = frappe.utils.get_datetime(wo.failure_date)\r\n completed_datetime = frappe.utils.get_datetime(wo.completion_date)\r\n\r\n # Calculate the difference in hours\r\n downtime = (completed_datetime - failure_datetime).total_seconds() / 3600\r\n\r\n # Debug: Log the asset, work order, and calculated downtime\r\n log_message = (\r\n f\"Asset: {asset.name}, Work Order: {wo.name}, \"\r\n f\"Failure datetime: {failure_datetime}, Completion datetime: {completed_datetime}, \"\r\n f\"Calculated downtime: {downtime} hours\"\r\n )\r\n if len(log_message) > 140:\r\n log_message = log_message[:137] + '...'\r\n frappe.log_error(log_message, \"Work Order Downtime Debug\")\r\n\r\n total_downtime =total_downtime+ downtime\r\n\r\n # Update the 'custom_down_time' field in the Asset doctype\r\n if total_downtime > 0: # Only update if downtime is greater than zero\r\n frappe.db.set_value(\"Asset\", asset.name, \"custom_down_time\", round(total_downtime, 2))\r\n \r\n asset_doc = frappe.get_doc(\"Asset\", asset.name)\r\n if asset_doc.available_for_use_date:\r\n # Convert available_for_use_date to datetime object\r\n available_date = frappe.utils.get_datetime(asset_doc.available_for_use_date)\r\n \r\n # Get the current date and time\r\n current_date = frappe.utils.now_datetime()\r\n \r\n # Calculate the time difference in days\r\n day_diff = (current_date - available_date).days\r\n \r\n # Convert days to hours\r\n total_hours = day_diff * 24\r\n \r\n frappe.db.set_value(\"Asset\", asset_doc.name, \"custom_total_hours\", round(total_hours, 2))\r\n \r\n total_up_time = total_hours - total_downtime\r\n frappe.db.set_value(\"Asset\", asset_doc.name, \"custom_up_time\", round(total_up_time, 2))\r\n \r\n \r\n\r\n# Commit the transaction after processing all assets\r\nfrappe.db.commit()\r\n", + "script_type": "Scheduler Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 1, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Insert", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-06-06 13:28:02.658953", + "module": "Asset Lite", + "name": "Auto create the permission", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "User", + "script": "if doc.custom_site_name and doc.name:\r\n if not frappe.db.exists(\"User Permission\", {\"user\": doc.name, \"allow\": \"Hospital\", \"for_value\": doc.custom_site_name}):\r\n user_per = frappe.get_doc({\r\n \"doctype\": \"User Permission\",\r\n \"user\": doc.name,\r\n \"allow\": \"Hospital\",\r\n \"for_value\": doc.custom_site_name,\r\n \"apply_to_all_doctypes\": 1\r\n })\r\n user_per.flags.ignore_validate_links = True\r\n user_per.flags.ignore_validate = True\r\n user_per.insert(ignore_permissions=True)", + "script_type": "DocType Event" + }, + { + "allow_guest": 1, + "api_method": "get_assets", + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Insert", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-09-18 13:11:11.122120", + "module": "Asset Lite", + "name": "Asset Fetching", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": null, + "script": "# @frappe.whitelist()\r\ndef get_assets(company=None, custom_modality=None, custom_manufacturer=None, custom_device_status=None, custom_model=None):\r\n try:\r\n filters = {}\r\n \r\n filters[\"docstatus\"] = 1\r\n # Build filters dictionary (only add non-empty values)\r\n if company:\r\n filters[\"company\"] = company\r\n \r\n # if asset_name:\r\n # filters[\"asset_name\"] = asset_name\r\n if custom_modality:\r\n filters[\"custom_modality\"] = custom_modality\r\n if custom_manufacturer:\r\n filters[\"custom_manufacturer\"] = custom_manufacturer\r\n if custom_device_status:\r\n filters[\"custom_device_status\"] = custom_device_status\r\n if custom_model:\r\n filters[\"custom_model\"] = custom_model\r\n \r\n # Fetch assets\r\n assets = frappe.get_all(\r\n \"Asset\", \r\n filters=filters, \r\n fields=[\"name\", \"asset_name\",\"custom_modality\", \"company\", \"custom_manufacturer\", \"custom_device_status\",\"custom_model\"]\r\n )\r\n \r\n \r\n # Get assets that already have Asset Maintenance entries\r\n existing_maintenance_assets = frappe.get_all(\r\n \"Asset Maintenance\",\r\n filters={\r\n \"docstatus\": [\"<\", 2] # Not cancelled (0=Draft, 1=Submitted)\r\n },\r\n fields=[\"name\"],\r\n pluck=\"name\" # Returns only the asset values as a list\r\n )\r\n \r\n \r\n \r\n # Filter out assets that already have maintenance\r\n filtered_assets = []\r\n excluded_count = 0\r\n \r\n for asset in assets:\r\n if asset.name not in existing_maintenance_assets:\r\n filtered_assets.append(asset)\r\n else:\r\n excluded_count =excluded_count + 1\r\n frappe.errprint(f\"Excluded asset {asset.name} - already has maintenance\")\r\n \r\n \r\n \r\n # Return the filtered results\r\n frappe.response['message'] = filtered_assets\r\n \r\n except Exception as e:\r\n frappe.errprint(f\"Error in get_assets: {str(e)}\")\r\n frappe.response['message'] = []\r\n frappe.throw(f\"Error fetching assets: {str(e)}\")\r\n\r\n# Get parameters from form_dict\r\ncompany = frappe.form_dict.get(\"company\")\r\n# asset_name = frappe.form_dict.get(\"asset_name\") \r\ncustom_modality = frappe.form_dict.get(\"custom_modality\")\r\ncustom_manufacturer = frappe.form_dict.get(\"custom_manufacturer\")\r\ncustom_device_status = frappe.form_dict.get(\"custom_device_status\")\r\ncustom_model = frappe.form_dict.get(\"custom_model\")\r\n\r\n\r\n# Call the function\r\nget_assets(\r\n company=company,\r\n # asset_name=asset_name,\r\n custom_modality = custom_modality,\r\n custom_manufacturer=custom_manufacturer,\r\n custom_device_status=custom_device_status,\r\n custom_model = custom_model\r\n \r\n)", + "script_type": "API" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Submit", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-12-31 15:14:19.689324", + "module": "Asset Lite", + "name": "Auto Creation Of Asset maintenance", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "PM Schedule Generator", + "script": "for row in doc.maintenance_entries:\r\n # Get values from Asset Doc\r\n asset_doc = frappe.get_doc(\"Asset\", row.asset)\r\n\r\n # ---------- create Asset Maintenance ----------\r\n am = frappe.get_doc({\r\n \"doctype\": \"Asset Maintenance\",\r\n\r\n # --- top-level fields ---\r\n \"custom_pm_schedule\":doc.name,\r\n \"asset_name\": row.asset,\r\n \"company\": doc.hospital,\r\n \"maintenance_team\": doc.maintenance_team,\r\n \"maintenance_manager_name\": doc.maintenance_manager,\r\n\r\n \"custom_site_contractor\": asset_doc.custom_site_contractor,\r\n \"custom_subcontractor\": asset_doc.custom_subcontractor,\r\n \"custom_service_coverage\": asset_doc.custom_service_coverage,\r\n \"custom_total_amount\": asset_doc.custom_total_amount,\r\n\r\n # --- service-coverage child table ---\r\n \"custom_service_coverage_table\": [{\r\n \"service_agreement\": \"Contract\",\r\n \"start_date\": doc.start_date,\r\n \"end_date\": doc.end_date\r\n }],\r\n\r\n # --- maintenance-tasks child table ---\r\n \"asset_maintenance_tasks\": [{\r\n \"maintenance_task\": \"Maintenance Task\",\r\n \"maintenance_status\": \"Planned\",\r\n \"periodicity\": doc.periodicity,\r\n \"assign_to\": doc.assign_to,\r\n \"start_date\": doc.start_date,\r\n \"next_due_date\": doc.due_date\r\n }]\r\n })\r\n\r\n am.insert(ignore_permissions=True)\r\n # am.submit() # Uncomment if you want to auto-submit\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-12-31 15:02:49.747284", + "module": "Asset Lite", + "name": "Update Due Date", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "PM Schedule Generator", + "script": "\r\nmapping_days = {\r\n \"Daily\": 1,\r\n \"Weekly\": 7,\r\n}\r\n\r\nmapping_months = {\r\n \"Monthly\": 1,\r\n \"Quarterly\": 3,\r\n \"Half-yearly\": 6,\r\n \"Yearly\": 12,\r\n \"2 Yearly\": 24,\r\n \"3 Yearly\": 36,\r\n}\r\n\r\n\r\nif doc.start_date and doc.periodicity:\r\n # Day‑based intervals\r\n if doc.periodicity in mapping_days:\r\n doc.due_date = frappe.utils.add_days(doc.start_date, mapping_days[doc.periodicity])\r\n\r\n # Month‑based intervals\r\n elif doc.periodicity in mapping_months:\r\n doc.due_date = frappe.utils.add_months(doc.start_date, mapping_months[doc.periodicity])\r\n\r\n\r\n\r\nif doc.maintenance_entries:\r\n for entry in doc.maintenance_entries:\r\n if entry.asset:\r\n asset = frappe.get_doc(\"Asset\", entry.asset)\r\n\r\n if asset.asset_name:\r\n # Set or update asset_name if missing or changed\r\n if not entry.asset_name or entry.asset_name != asset.asset_name:\r\n entry.asset_name = asset.asset_name", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Insert", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-07-10 11:54:34.343111", + "module": "Asset Lite", + "name": "Asset Maintenance PM nos", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Asset Maintenance", + "script": "# Calculates no_of_pms for each row in custom_service_coverage_table\r\n# based on the first maintenance task's periodicity.\r\n\r\n# import math\r\n# from frappe.utils import getdate\r\n\r\nif doc.asset_maintenance_tasks:\r\n\r\n periodicity = doc.asset_maintenance_tasks[0].periodicity # first task only\r\n \r\n periodicity_mapping = {\r\n \"Daily\": 1,\r\n \"Weekly\": 7,\r\n \"Monthly\": 30,\r\n \"Quarterly\": 90,\r\n \"Half-yearly\": 180,\r\n \"Yearly\": 365,\r\n \"2 Yearly\": 730,\r\n \"3 Yearly\": 1095,\r\n }\r\n\r\n days_per_period = periodicity_mapping.get(periodicity)\r\n if days_per_period:\r\n for row in doc.custom_service_coverage_table:\r\n if row.start_date and row.end_date:\r\n start = frappe.utils.getdate(row.start_date)\r\n end = frappe.utils.getdate(row.end_date)\r\n diff_days = (end - start).days\r\n \r\n # floor division gives whole periods\r\n row.no_of_pms = diff_days // days_per_period\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": "0 0 * * 0", + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Insert", + "enable_rate_limit": 0, + "event_frequency": "Cron", + "modified": "2025-09-03 16:18:34.270759", + "module": "Asset Lite", + "name": "Planned PM Report Script", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": null, + "script": "\r\ndef send_planned_pm_mail():\r\n # Get start and end of current month\r\n today = frappe.utils.nowdate()\r\n # start_date = frappe.utils.get_first_day(frappe.utils.add_months(today, 1))\r\n # end_date = frappe.utils.get_last_day(frappe.utils.add_months(today, 1))\r\n \r\n start_date = frappe.utils.nowdate()\r\n end_date = frappe.utils.get_last_day(today)\r\n\r\n # Get users with role \"Maintenance Manager\"\r\n role_users = frappe.get_all(\r\n \"Has Role\",\r\n filters={\"role\": \"Maintenance Manager\"},\r\n fields=[\"parent\"],\r\n distinct=True\r\n )\r\n \r\n \r\n for user in role_users:\r\n user_id = user[\"parent\"]\r\n\r\n # Skip Administrator & disabled users\r\n if user_id.lower() == \"administrator\":\r\n continue\r\n if frappe.db.get_value(\"User\", user_id, \"enabled\") != 1:\r\n continue\r\n\r\n # Get user details\r\n user_doc = frappe.get_doc(\"User\", user_id)\r\n user_email = user_doc.email\r\n if user_doc.custom_site_name:\r\n user_hospital = user_doc.get(\"custom_site_name\") # 👈 your custom hospital field\r\n print(user_hospital)\r\n \r\n if not user_email or not user_hospital:\r\n continue\r\n \r\n # Encode hospital filter in report URL\r\n # Example: https://site/app/report/Planned%20PM?hospital=Hospital%20Name\r\n report_base = \"https://asm-aljouf.seeraarabia.com/app/query-report/Planned%20PM\"\r\n report_link = f\"{report_base}?hospital={user_hospital}\"\r\n \r\n # Prepare email\r\n subject = f\"Planned PM Report for {user_hospital}: {start_date} to {end_date}\"\r\n message = f\"\"\"\r\n

Dear {user_doc.full_name},

\r\n

This is the Planned PM Report for your hospital: {user_hospital}

\r\n

Period: {start_date} to {end_date}

\r\n

You can view the report here: {report_link}

\r\n

Regards,
Maintenance Team

\r\n \"\"\"\r\n print(message)\r\n \r\n # Send email\r\n frappe.sendmail(\r\n recipients=[user_email],\r\n subject=subject,\r\n message=message\r\n )\r\n \r\n\r\n pm_recipient = \"projectmanager@gmail.com\"\r\n if pm_recipient:\r\n \r\n # Prepare email content\r\n subject = f\"Planned PM Report: {start_date} to {end_date}\"\r\n report_link1 = \"https://asm-aljouf.seeraarabia.com/app/report/Planned%20PM\"\r\n message = f\"\"\"\r\n

Dear Team,

\r\n

This is the Planned PM Report for the period: {start_date} to {end_date}.

\r\n

You can view the report here: {report_link1}

\r\n

Regards,
Maintenance Team

\r\n \"\"\"\r\n \r\n # Send the email\r\n frappe.sendmail(\r\n # recipients=recipients,\r\n recipients=pm_recipient,\r\n subject=subject,\r\n message=message\r\n )\r\n\r\n# Call the function\r\nsend_planned_pm_mail()\r\n", + "script_type": "Scheduler Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 1, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-08-26 13:50:01.482363", + "module": "Asset Lite", + "name": "Auto Assign PHCC Work_Order", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Work_Order", + "script": "# Script to auto assign technician to PHCC site Work orders\n\nif doc.name and doc.site_name and doc.asset_type and doc.asset_type == \"Non Biomedical\":\n site = doc.site_name\n \n # name = doc.name\n\n # Check if ToDo already exists for this Work Order\n existing_todo = frappe.db.exists(\"ToDo\", {\n \"reference_type\": \"Work_Order\",\n \"reference_name\": doc.name\n })\n print(existing_todo)\n if not existing_todo:\n \n # Step 1: Get users with permission to this Mobile Team Site\n permitted_users = frappe.get_all(\n \"User Permission\",\n filters={\n \"allow\": \"Mobile Team Site\",\n \"for_value\": site\n },\n fields=[\"user\"]\n )\n print(permitted_users)\n if permitted_users:\n \n # Step 2: Filter users with 'Technician' role\n technician_users = []\n for entry in permitted_users:\n user_roles = frappe.get_all(\"Has Role\", filters={\"parent\": entry.user}, fields=[\"role\"])\n if any(r[\"role\"] == \"Technician\" for r in user_roles):\n technician_users.append(entry.user)\n print(technician_users)\n if technician_users:\n # Step 3: Assign to first technician user found (or apply logic to choose)\n assigned_user = technician_users[0]\n print(assigned_user)\n \n # Create the ToDo\n frappe.get_doc({\n \"doctype\": \"ToDo\",\n \"allocated_to\": assigned_user,\n \"description\": f\"Work Order Assigned: {doc.name}\",\n \"reference_type\": \"Work_Order\",\n \"reference_name\": doc.name,\n \"status\": \"Open\"\n }).insert(ignore_permissions=True)\n \n frappe.msgprint(f\"Assigned Work Order {doc.name} to Technician: {assigned_user}\")\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 1, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "After Save", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2025-08-26 13:49:53.175838", + "module": "Asset Lite", + "name": "Notifications", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Work_Order", + "script": "\r\ndef send_notification_on_workflow_change(doc, method):\r\n # Only run when workflow_state changes\r\n if not doc.has_value_changed(\"workflow_state\"):\r\n return\r\n\r\n hospital = getattr(doc, \"hospital\", None)\r\n if not hospital:\r\n frappe.log_error(f\"No hospital linked in {doc.name}\", \"Workflow Notification Error\")\r\n return\r\n\r\n recipients = set()\r\n\r\n # Step 1: Get all users with that hospital\r\n hospital_users = frappe.get_all(\"User\", filters={\"custom_site_name\": hospital}, pluck=\"name\")\r\n\r\n if not hospital_users:\r\n frappe.log_error(f\"No users found for hospital: {hospital}\", \"Workflow Notification Error\")\r\n return\r\n\r\n # Step 2: Get users with Maintenance Manager role\r\n mm_users = frappe.get_all(\r\n \"Has Role\",\r\n filters={\r\n \"role\": \"Maintenance Manager\",\r\n \"parent\": [\"in\", hospital_users]\r\n },\r\n pluck=\"parent\"\r\n )\r\n\r\n # Step 3: Get users with Technician role\r\n tech_users = frappe.get_all(\r\n \"Has Role\",\r\n filters={\r\n \"role\": \"Technician\",\r\n \"parent\": [\"in\", hospital_users]\r\n },\r\n pluck=\"parent\"\r\n )\r\n\r\n # Combine recipients\r\n recipients.update(mm_users)\r\n recipients.update(tech_users)\r\n\r\n # Step 4: Send notifications\r\n for user in recipients:\r\n frappe.new_doc(\"Notification Log\").update({\r\n \"type\": \"Alert\",\r\n \"document_type\": doc.doctype,\r\n \"document_name\": doc.name,\r\n \"subject\": f\"Workflow changed to {doc.workflow_state}\",\r\n \"from_user\": frappe.session.user,\r\n \"for_user\": user\r\n }).insert(ignore_permissions=True)\r\n", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": "assign_supervisor_or_technician", + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Insert", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2026-01-02 12:53:20.623970", + "module": "Asset Lite", + "name": "Assign Supervisor", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": null, + "script": "def assign_supervisor_or_technician(work_order, action, asset_type):\r\n \"\"\"Assign Maintenance Manager or Technician depending on workflow action\"\"\"\r\n doc = frappe.get_doc(\"Work_Order\", work_order)\r\n \r\n if action == \"Apply\":\r\n mm_user = None\r\n \r\n # Non Biomedical: Assign General WOA\r\n if asset_type == \"Non Biomedical\":\r\n mm_user = frappe.db.sql(\"\"\"\r\n SELECT parent\r\n FROM `tabHas Role`\r\n WHERE role = 'General WOA'\r\n AND parent IN (\r\n SELECT name FROM `tabUser`\r\n WHERE enabled = 1 AND custom_site_name = %s\r\n )\r\n AND parent NOT IN (\r\n SELECT parent FROM `tabHas Role` WHERE role = 'System Manager'\r\n )\r\n LIMIT 1\r\n \"\"\", (doc.company,), as_dict=True)\r\n \r\n # All other asset types (Biomedical, Bio Medical, or any other): Assign Maintenance Manager\r\n else:\r\n mm_user = frappe.db.sql(\"\"\"\r\n SELECT parent\r\n FROM `tabHas Role`\r\n WHERE role = 'Maintenance Manager'\r\n AND parent IN (SELECT name FROM `tabUser` WHERE enabled = 1 AND custom_site_name = %s)\r\n LIMIT 1\r\n \"\"\", (doc.company,), as_dict=True)\r\n \r\n if mm_user and not doc.custom_assigned_supervisor:\r\n assigned_user = mm_user[0].parent\r\n user_doc = frappe.get_doc(\"User\", assigned_user)\r\n \r\n # Use frappe.db.set_value to avoid version conflicts\r\n frappe.db.set_value(\"Work_Order\", work_order, {\r\n \"custom_assigned_supervisor\": assigned_user,\r\n \"bio_med_dept\": user_doc.full_name\r\n }, update_modified=False)\r\n \r\n return {\"assigned_to\": assigned_user}\r\n \r\n # Send For Repair: Only for NON \"Non Biomedical\" asset types\r\n elif action == \"Send For Repair\" and asset_type != \"Non Biomedical\":\r\n tech_user = frappe.db.sql(\"\"\"\r\n SELECT parent\r\n FROM `tabHas Role`\r\n WHERE role = 'Technician'\r\n AND parent IN (\r\n SELECT name FROM `tabUser`\r\n WHERE enabled = 1 AND custom_site_name = %s\r\n )\r\n AND parent NOT IN (\r\n SELECT parent FROM `tabHas Role` WHERE role = 'Maintenance Manager'\r\n )\r\n LIMIT 1\r\n \"\"\", (doc.company,), as_dict=True)\r\n \r\n if tech_user and not doc.custom_assign_to_contractor:\r\n assigned_user = tech_user[0].parent\r\n user_doc = frappe.get_doc(\"User\", assigned_user)\r\n \r\n # Use frappe.db.set_value to avoid version conflicts\r\n frappe.db.set_value(\"Work_Order\", work_order, {\r\n \"custom_assign_to_contractor\": assigned_user,\r\n \"serviced_by\": user_doc.full_name\r\n }, update_modified=False)\r\n \r\n return {\"assigned_to\": assigned_user}\r\n \r\n return {\"assigned_to\": None}\r\n\r\n# Get parameters from request\r\naction = frappe.form_dict.get(\"action\")\r\nwork_order = frappe.form_dict.get(\"work_order\")\r\nasset_type = frappe.form_dict.get(\"asset_type\")\r\n\r\n# Call the function\r\nassign_supervisor_or_technician(\r\n work_order=work_order,\r\n action=action,\r\n asset_type=asset_type\r\n)", + "script_type": "API" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Insert", + "enable_rate_limit": 0, + "event_frequency": "All", + "modified": "2026-01-09 13:21:21.936865", + "module": "Asset Lite", + "name": "Update Item Defaults", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": "Item", + "script": "row = doc.append(\"item_defaults\", {})\r\nrow.company = \"Iman General Hospital\"\r\nrow.default_warehouse = \"Iman Central Warehouse - IGH\"", + "script_type": "DocType Event" + }, + { + "allow_guest": 0, + "api_method": null, + "cron_format": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Server Script", + "doctype_event": "Before Insert", + "enable_rate_limit": 0, + "event_frequency": "Daily", + "modified": "2025-12-31 19:37:57.211400", + "module": "Asset Lite", + "name": "Asset Maintenance Log Notifications", + "rate_limit_count": 5, + "rate_limit_seconds": 86400, + "reference_doctype": null, + "script": "def send_asset_maintenance_due_notifications():\r\n today = frappe.utils.nowdate()\r\n\r\n # 1️⃣ Get Planned Asset Maintenance Logs due today\r\n logs = frappe.get_all(\r\n \"Asset Maintenance Log\",\r\n filters={\r\n \"maintenance_status\": \"Planned\",\r\n \"due_date\": today\r\n },\r\n fields=[\"name\", \"asset_maintenance\"]\r\n )\r\n\r\n if not logs:\r\n return\r\n\r\n notified_users = set()\r\n\r\n for log in logs:\r\n if not log.asset_maintenance:\r\n continue\r\n\r\n # 2️⃣ Get related ToDos\r\n todos = frappe.get_all(\r\n \"ToDo\",\r\n filters={\r\n \"reference_type\": \"Asset Maintenance\",\r\n \"reference_name\": log.asset_maintenance,\r\n \"status\": [\"!=\", \"Closed\"]\r\n },\r\n fields=[\"allocated_to\"]\r\n )\r\n\r\n for todo in todos:\r\n user = todo.allocated_to\r\n if not user or user in notified_users:\r\n continue\r\n\r\n notified_users.add(user)\r\n\r\n # 3️⃣ Create Notification\r\n notification = frappe.get_doc({\r\n \"doctype\": \"Notification Log\",\r\n \"subject\": \"Asset Maintenance Due Today\",\r\n \"email_content\": (\r\n f\"Asset Maintenance Log {log.name} \"\r\n f\"is due today ({today}).
\"\r\n f\"Please take necessary action.\"\r\n ),\r\n \"for_user\": user,\r\n \"type\": \"Alert\",\r\n \"document_type\": \"Asset Maintenance Log\",\r\n \"document_name\": log.name\r\n })\r\n notification.insert(ignore_permissions=True)\r\n\r\n # 4️⃣ Send Email\r\n frappe.sendmail(\r\n recipients=[user],\r\n subject=\"Asset Maintenance Due Today\",\r\n message=(\r\n f\"Dear User,

\"\r\n f\"Asset Maintenance Log{log.name} \"\r\n f\"is due today ({today}).

\"\r\n f\"Please take necessary action.

\"\r\n f\"Regards,
\"\r\n f\"ERP System\"\r\n )\r\n )\r\n\r\n frappe.db.commit()\r\n\r\n\r\n# Run function\r\nsend_asset_maintenance_due_notifications()\r\n", + "script_type": "Scheduler Event" + } +] \ No newline at end of file diff --git a/asset_lite/fixtures/translation.json b/asset_lite/fixtures/translation.json new file mode 100644 index 0000000..f84b0e8 --- /dev/null +++ b/asset_lite/fixtures/translation.json @@ -0,0 +1,1263 @@ +[ + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-05-30 18:41:03.956342", + "name": "1k0fk43veb", + "source_text": "6598", + "translated_text": "معرف النظام" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:24:11.670631", + "name": "a484lgcrc7", + "source_text": "Work Order Status", + "translated_text": "حالة أمر العمل" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:24:40.586418", + "name": "a4h5m4o10u", + "source_text": "Work Order Type", + "translated_text": "نوع أمر العمل" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:25:51.634091", + "name": "a57c46a42c", + "source_text": "Priority", + "translated_text": "درجة الاهمية" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:26:31.089735", + "name": "a5jml8so86", + "source_text": "Work Performed", + "translated_text": "العمل الذي تم تنفيذه" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:28:26.309640", + "name": "a6nnubk4c8", + "source_text": "Nature of Complaint", + "translated_text": "طبيعة الشكوى" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:29:07.097315", + "name": "a74fo87pkt", + "source_text": "Customer Comments", + "translated_text": "تعليقات العملاء" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:29:54.320574", + "name": "a7j7hbq40s", + "source_text": "Bio-Med Dept", + "translated_text": "قسم الطب الحيوي" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:31:37.928662", + "name": "a8jjcg2u1l", + "source_text": "End user", + "translated_text": "المستخدم النهائي" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:32:09.135276", + "name": "a8tbjugtvq", + "source_text": "Serviced By", + "translated_text": "تم تقديم الخدمة بواسطة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:32:39.708687", + "name": "a96tffka8c", + "source_text": "Total Main Hour At Site", + "translated_text": "إجمالي الساعات الرئيسية في الموقع" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:33:07.770459", + "name": "a9fll0s5a9", + "source_text": "Total Travel Hour", + "translated_text": "إجمالي ساعة السفر" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:33:42.789242", + "name": "a9qji9hobb", + "source_text": "Total Hours", + "translated_text": "مجموع الساعات" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:34:50.222475", + "name": "aafmb0sclo", + "source_text": "Description", + "translated_text": "وصف" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:35:33.862934", + "name": "aatahb5fkf", + "source_text": "Defective Spare Parts", + "translated_text": "قطع الغيار المعيبة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:36:32.357853", + "name": "abfjim91nt", + "source_text": "Feedback", + "translated_text": "تعليق" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:37:02.331548", + "name": "abov4cnc9s", + "source_text": "Penalty", + "translated_text": "جزاء" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:37:40.059997", + "name": "ac4o5un1pp", + "source_text": "Total Hours Spent", + "translated_text": "إجمالي الساعات المستغرقة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:38:48.629004", + "name": "acq67sa90c", + "source_text": "Diffrence", + "translated_text": "اختلاف" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:39:30.518191", + "name": "ad791alfsb", + "source_text": "Job Completed", + "translated_text": "تم الانتهاء من المهمة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:41:17.989271", + "name": "ae8r4ji27l", + "source_text": "Response Details", + "translated_text": "تفاصيل الرد" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:41:47.527118", + "name": "aei33o9ft3", + "source_text": "Service Contract Details", + "translated_text": "تفاصيل عقد الخدمة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-19 21:50:33.568440", + "name": "aer68n8oqn", + "source_text": "Warranty Details", + "translated_text": "الضمان" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:43:00.188757", + "name": "af6iovcmd9", + "source_text": "Warranty And Service Contract Details", + "translated_text": "تفاصيل عقد الضمان والخدمة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-19 21:56:17.501127", + "name": "afiuko8sm3", + "source_text": "Manufacturer", + "translated_text": "الشركة الصانعة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:44:08.136630", + "name": "afu18ev94p", + "source_text": "Vendor", + "translated_text": "بائع" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:44:46.072635", + "name": "ag9spuej2j", + "source_text": "Model", + "translated_text": "نموذج" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:45:17.870651", + "name": "agjqlveur8", + "source_text": "Make", + "translated_text": "يصنع" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:47:18.728810", + "name": "ahpj4vrel6", + "source_text": "Department", + "translated_text": "قسم" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:53:14.192254", + "name": "al8mt2lp8r", + "source_text": "Assigned Technician", + "translated_text": "الفني المكلف" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:53:34.655044", + "name": "alf24n48gt", + "source_text": "Assigned Manager", + "translated_text": "المدير المكلف" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:54:19.347361", + "name": "alt1o3170m", + "source_text": "First Responded On", + "translated_text": "وقت الاستجابة الأولى" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:55:16.546070", + "name": "ametn877v8", + "source_text": "Failure Date", + "translated_text": "تاريخ العطل" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:56:24.848528", + "name": "an48ibjvpe", + "source_text": "Comprehensive", + "translated_text": "شامل" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:57:04.788250", + "name": "angnprqi4a", + "source_text": "Spare Parts & Labour", + "translated_text": "قطع الغيار والعمالة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:57:48.085490", + "name": "anu8tcor78", + "source_text": "Service Contract Details", + "translated_text": "تفاصيل عقد الخدمة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 22:59:59.867886", + "name": "ap7en3tglp", + "source_text": "PPM Only", + "translated_text": "PPM فقط" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-05 23:02:09.310021", + "name": "aqft7mvhtv", + "source_text": "Work Order Number", + "translated_text": "رقم أمر العمل" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-20 18:36:44.500408", + "name": "hf7dcd7egb", + "source_text": "Hospital Name", + "translated_text": "اسم المستشفى" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-20 18:39:54.157904", + "name": "hh2ld8e3rq", + "source_text": "Manufacturing Year", + "translated_text": "سنة التصنيع" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-20 18:44:46.099413", + "name": "hjtt0o78gf", + "source_text": "Assigned To", + "translated_text": "مُخصص لـ" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-20 18:53:10.568764", + "name": "horhjebpj3", + "source_text": "Site Contractor", + "translated_text": "المقاول الرئيسي" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 00:03:37.886389", + "name": "neoinfogbq", + "source_text": "Work_Order", + "translated_text": "أمر العمل" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 00:16:15.575457", + "name": "nkp03b3rp6", + "source_text": "Add Asset", + "translated_text": "إضافة جهاز" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 00:27:04.431659", + "name": "nnnlhlq58q", + "source_text": "Asset Type", + "translated_text": "نوع الجهاز" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 00:28:43.275503", + "name": "ntf0epnjme", + "source_text": "Serial Number", + "translated_text": "الرقم التسلسلي" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 00:36:46.366109", + "name": "o1i53hdbbj", + "source_text": "Class", + "translated_text": "الفئة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 00:38:41.930645", + "name": "o2p0d8rn0k", + "source_text": "System ID", + "translated_text": "رقم الجهاز" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 16:01:29.077853", + "name": "o4gau5athv", + "source_text": "Asset", + "translated_text": "الأجهزة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 00:49:03.318426", + "name": "o9c9up86vo", + "source_text": "Asset Name", + "translated_text": "إسم الجهاز" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:00:05.781426", + "name": "ofr903baqr", + "source_text": "Contract Number", + "translated_text": "رقم العقد" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:03:52.724292", + "name": "oi27nkuqaf", + "source_text": "Coverage", + "translated_text": "العقد" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:05:12.918978", + "name": "oir9pdab9t", + "source_text": "Subcontractor", + "translated_text": "مقاول الباطن" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:06:35.086613", + "name": "ojku80k0ha", + "source_text": "Service Agreement", + "translated_text": "نوع العقد" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:07:47.670515", + "name": "okbkldg81a", + "source_text": "Service Coverage", + "translated_text": "نوع التغطية" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:09:16.994589", + "name": "ol7hdjm7s1", + "source_text": "Total Amount", + "translated_text": "القيمة الإجمالية" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:12:52.326152", + "name": "onars2q520", + "source_text": "End of Life", + "translated_text": "تاريخ نهاية الخدمة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:18:34.676218", + "name": "oqlq0o0pdu", + "source_text": "Room Number", + "translated_text": "رقم الغرفة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:20:14.689499", + "name": "orl2ni51lg", + "source_text": "Owner", + "translated_text": "المالك" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:37:59.981620", + "name": "p61v0vsn0k", + "source_text": "ID", + "translated_text": "الرمز" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:39:27.457484", + "name": "p6tak1us2i", + "source_text": "Device Status", + "translated_text": "حالة الجهاز" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:46:06.878570", + "name": "paq4kdlbdq", + "source_text": "Filter", + "translated_text": "فلتر" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:49:16.861573", + "name": "pclgfm1uur", + "source_text": "Movement", + "translated_text": "الحركة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:51:16.742102", + "name": "pdqvnu6qtv", + "source_text": "Repair", + "translated_text": "الإصلاح" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:53:40.224839", + "name": "pf7qupsl4f", + "source_text": "Asset History", + "translated_text": "تاريخ الجهاز" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:54:55.477681", + "name": "pfva269k7q", + "source_text": "Total Spare Parts Amount", + "translated_text": "تكلفة قطع الغيار" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 01:59:56.162369", + "name": "pit9a2kc6p", + "source_text": "Item Name", + "translated_text": "إسم قطعة الغيار" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 02:01:18.763402", + "name": "pjn337ef90", + "source_text": "Item Code", + "translated_text": "رمز القطعة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 02:04:16.399762", + "name": "pktmndl9n2", + "source_text": "Part Number", + "translated_text": "رقم القطعة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 02:04:56.991072", + "name": "plr9a28o8d", + "source_text": "Item Cost Per Unit", + "translated_text": "سعر الوحدة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 02:05:38.709416", + "name": "pm8bml40ca", + "source_text": "Warranty In Months", + "translated_text": "مدة الضمان بالأشهر" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 02:12:15.024735", + "name": "pq46e6pedb", + "source_text": "Type of Maintenance", + "translated_text": "نوع الصيانة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 02:17:06.463897", + "name": "psv8th9bg7", + "source_text": "Title", + "translated_text": "الوصف" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 02:19:23.038208", + "name": "pu9u3fnq5h", + "source_text": "Purpose", + "translated_text": "الغرض" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "en", + "modified": "2025-03-21 02:23:33.880182", + "name": "pve58f1sku", + "source_text": "Company", + "translated_text": "Hospital" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 02:27:07.931745", + "name": "q2r779a3p8", + "source_text": "Work Orders", + "translated_text": "أمر العمل" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 15:46:54.263970", + "name": "8jfikcnm74", + "source_text": "Owner", + "translated_text": "المالك" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 15:48:34.688996", + "name": "8oea62e9or", + "source_text": "Supplier", + "translated_text": "الوكيل المورد" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 15:56:25.893532", + "name": "8t1i12kvjk", + "source_text": "Spare Parts", + "translated_text": "قطع الغيار" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 15:57:58.930473", + "name": "8tulvgcg1p", + "source_text": "Comments", + "translated_text": "ملاحظات" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 16:00:01.708570", + "name": "8v51agcjqd", + "source_text": "Stock", + "translated_text": "المخزون" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-21 16:07:56.607271", + "name": "93pe93dtmq", + "source_text": "Item", + "translated_text": "قطع الغيار" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-22 16:22:33.318261", + "name": "3o3dvmov5c", + "source_text": "Asset ID", + "translated_text": "رقم الجهاز" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-26 15:55:07.660151", + "name": "cn14e6024f", + "source_text": "Excel", + "translated_text": "ملف إكسل" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-26 18:44:31.748859", + "name": "fq9dc3m3cm", + "source_text": "Pending Reason", + "translated_text": "سبب تعليق أمر العمل" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-26 18:49:43.117237", + "name": "ftants0era", + "source_text": "Nature of Complaint", + "translated_text": "وصف العطل" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-03-26 18:53:56.832426", + "name": "fvq1c7lubn", + "source_text": "Nature of Complaint", + "translated_text": "وصف العطل" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-04-21 19:35:45.232789", + "name": "u82fjh8pac", + "source_text": "Warranty", + "translated_text": "ضمان" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-04-21 19:38:22.172661", + "name": "ubaujp6cpt", + "source_text": "Frame Work", + "translated_text": "إتفاقية إطارية" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-04-21 19:45:30.540831", + "name": "ue94lk3ie0", + "source_text": "Comprehensive", + "translated_text": "تغطية شاملة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-04-21 19:51:03.329936", + "name": "uiopqre6vg", + "source_text": "Labour", + "translated_text": "أجور عماله" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-04-21 20:05:07.118938", + "name": "ur0f1dt852", + "source_text": "Draft", + "translated_text": "نموذج أولي" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-05-23 17:00:16.861197", + "name": "duj9kbqh3q", + "source_text": "Work Orders In Progress", + "translated_text": "أوامر العمل قيد التقدم" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-05-23 17:00:41.323535", + "name": "9uqt9ritdr", + "source_text": "Total No. of Assets", + "translated_text": "إجمالي عدد الأصول" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "en", + "modified": "2025-05-24 14:50:27.669629", + "name": "3u94ghesip", + "source_text": "Inventory", + "translated_text": "المخزون" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-06-12 08:30:03.618979", + "name": "cu9c0ch6m5", + "source_text": "Active Map", + "translated_text": "خريطة نشطة" + }, + { + "context": null, + "contributed": 0, + "contribution_docname": null, + "contribution_status": "", + "docstatus": 0, + "doctype": "Translation", + "language": "ar", + "modified": "2025-06-30 15:01:25.339217", + "name": "anb3jphuja", + "source_text": "Submit", + "translated_text": "تنفيذ" + } +] \ No newline at end of file diff --git a/asset_lite/fixtures/user_permission.json b/asset_lite/fixtures/user_permission.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/asset_lite/fixtures/user_permission.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/asset_lite/fixtures/workflow.json b/asset_lite/fixtures/workflow.json new file mode 100644 index 0000000..47da068 --- /dev/null +++ b/asset_lite/fixtures/workflow.json @@ -0,0 +1,2283 @@ +[ + { + "docstatus": 0, + "doctype": "Workflow", + "document_type": "PPM OF ELECTRICAL PANELS", + "is_active": 1, + "modified": "2024-12-12 19:11:58.318892", + "name": "PPM OF ELECTRICAL PANELS", + "override_status": 0, + "send_email_alert": 0, + "states": [ + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF ELECTRICAL PANELS", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF ELECTRICAL PANELS", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Sent to technician", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF ELECTRICAL PANELS", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Approval", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF ELECTRICAL PANELS", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Rejected", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF ELECTRICAL PANELS", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Approved", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + } + ], + "transitions": [ + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Sent to technician", + "parent": "PPM OF ELECTRICAL PANELS", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Send For Approval", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": null, + "next_state": "Pending Approval", + "parent": "PPM OF ELECTRICAL PANELS", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Sent to technician", + "workflow_builder_id": null + }, + { + "action": "Approve", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Approved", + "parent": "PPM OF ELECTRICAL PANELS", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Reject", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Rejected", + "parent": "PPM OF ELECTRICAL PANELS", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + } + ], + "workflow_data": null, + "workflow_name": "PPM OF ELECTRICAL PANELS", + "workflow_state_field": "workflow_state" + }, + { + "docstatus": 0, + "doctype": "Workflow", + "document_type": "PPM OF FIRE ALARM DEVICES", + "is_active": 1, + "modified": "2024-12-12 19:11:58.501893", + "name": "PPM OF FIRE ALARM DEVICES", + "override_status": 0, + "send_email_alert": 0, + "states": [ + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF FIRE ALARM DEVICES", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF FIRE ALARM DEVICES", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Sent to technician", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF FIRE ALARM DEVICES", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Approval", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF FIRE ALARM DEVICES", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Rejected", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF FIRE ALARM DEVICES", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Approved", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + } + ], + "transitions": [ + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Sent to technician", + "parent": "PPM OF FIRE ALARM DEVICES", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Send For Approval", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": null, + "next_state": "Pending Approval", + "parent": "PPM OF FIRE ALARM DEVICES", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Sent to technician", + "workflow_builder_id": null + }, + { + "action": "Approve", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Approved", + "parent": "PPM OF FIRE ALARM DEVICES", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Reject", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Rejected", + "parent": "PPM OF FIRE ALARM DEVICES", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + } + ], + "workflow_data": null, + "workflow_name": "PPM OF FIRE ALARM DEVICES", + "workflow_state_field": "workflow_state" + }, + { + "docstatus": 0, + "doctype": "Workflow", + "document_type": "PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", + "is_active": 1, + "modified": "2024-12-12 19:11:58.649076", + "name": "PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", + "override_status": 0, + "send_email_alert": 0, + "states": [ + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Sent to technician", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Approval", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Rejected", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Approved", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + } + ], + "transitions": [ + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Sent to technician", + "parent": "PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Send For Approval", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": null, + "next_state": "Pending Approval", + "parent": "PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Sent to technician", + "workflow_builder_id": null + }, + { + "action": "Approve", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Approved", + "parent": "PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Reject", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Rejected", + "parent": "PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + } + ], + "workflow_data": null, + "workflow_name": "PPM OF ELECTRICAL FIXTURES INSIDE ROOMS", + "workflow_state_field": "workflow_state" + }, + { + "docstatus": 0, + "doctype": "Workflow", + "document_type": "PPM OF CT SCAN MACHINE", + "is_active": 1, + "modified": "2024-12-12 19:11:58.778342", + "name": "PPM OF CT SCAN MACHINE", + "override_status": 0, + "send_email_alert": 0, + "states": [ + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF CT SCAN MACHINE", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF CT SCAN MACHINE", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Sent to technician", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF CT SCAN MACHINE", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Approval", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF CT SCAN MACHINE", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Rejected", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF CT SCAN MACHINE", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Approved", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + } + ], + "transitions": [ + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Sent to technician", + "parent": "PPM OF CT SCAN MACHINE", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Send For Approval", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": null, + "next_state": "Pending Approval", + "parent": "PPM OF CT SCAN MACHINE", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Sent to technician", + "workflow_builder_id": null + }, + { + "action": "Approve", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Approved", + "parent": "PPM OF CT SCAN MACHINE", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Reject", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Rejected", + "parent": "PPM OF CT SCAN MACHINE", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + } + ], + "workflow_data": null, + "workflow_name": "PPM OF CT SCAN MACHINE", + "workflow_state_field": "workflow_state" + }, + { + "docstatus": 0, + "doctype": "Workflow", + "document_type": "PPM OF MRI SCAN MACHINE", + "is_active": 1, + "modified": "2024-12-12 19:11:58.903013", + "name": "PPM OF MRI SCAN MACHINE", + "override_status": 0, + "send_email_alert": 0, + "states": [ + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF MRI SCAN MACHINE", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF MRI SCAN MACHINE", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Sent to technician", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF MRI SCAN MACHINE", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Approval", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF MRI SCAN MACHINE", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Rejected", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM OF MRI SCAN MACHINE", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Approved", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + } + ], + "transitions": [ + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Sent to technician", + "parent": "PPM OF MRI SCAN MACHINE", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Send For Approval", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": null, + "next_state": "Pending Approval", + "parent": "PPM OF MRI SCAN MACHINE", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Sent to technician", + "workflow_builder_id": null + }, + { + "action": "Approve", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Approved", + "parent": "PPM OF MRI SCAN MACHINE", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Reject", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Rejected", + "parent": "PPM OF MRI SCAN MACHINE", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + } + ], + "workflow_data": null, + "workflow_name": "PPM OF MRI SCAN MACHINE", + "workflow_state_field": "workflow_state" + }, + { + "docstatus": 0, + "doctype": "Workflow", + "document_type": "Material Request", + "is_active": 1, + "modified": "2025-02-17 21:00:22.691674", + "name": "Purchase Request", + "override_status": 0, + "send_email_alert": 1, + "states": [ + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Purchase Request", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Purchase Request", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Applied", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Finance User", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Purchase Request", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Sent to Procurement User", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Finance User", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Purchase Request", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Rejected", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Finance User", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Purchase Request", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Approved", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + } + ], + "transitions": [ + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": null, + "next_state": "Applied", + "parent": "Purchase Request", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Send For Approval", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Sent to Procurement User", + "parent": "Purchase Request", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Applied", + "workflow_builder_id": null + }, + { + "action": "Reject", + "allow_self_approval": 1, + "allowed": "Finance User", + "condition": null, + "next_state": "Rejected", + "parent": "Purchase Request", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Sent to Procurement User", + "workflow_builder_id": null + }, + { + "action": "Approve", + "allow_self_approval": 1, + "allowed": "Finance User", + "condition": null, + "next_state": "Approved", + "parent": "Purchase Request", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Sent to Procurement User", + "workflow_builder_id": null + } + ], + "workflow_data": null, + "workflow_name": "Purchase Request", + "workflow_state_field": "workflow_state" + }, + { + "docstatus": 0, + "doctype": "Workflow", + "document_type": "Purchase Order", + "is_active": 1, + "modified": "2024-12-12 19:11:59.023214", + "name": "Purchase Order", + "override_status": 0, + "send_email_alert": 1, + "states": [ + { + "allow_edit": "Finance User", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Purchase Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Finance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Purchase Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Approval", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Finance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Purchase Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Rejected", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Finance Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Purchase Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Approved", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Finance Manager", + "avoid_status_override": 0, + "doc_status": "2", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Purchase Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Cancelled", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + } + ], + "transitions": [ + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "Finance User", + "condition": null, + "next_state": "Pending Approval", + "parent": "Purchase Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Approve", + "allow_self_approval": 1, + "allowed": "Finance Manager", + "condition": "", + "next_state": "Approved", + "parent": "Purchase Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Reject", + "allow_self_approval": 1, + "allowed": "Finance Manager", + "condition": "", + "next_state": "Rejected", + "parent": "Purchase Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Cancel", + "allow_self_approval": 1, + "allowed": "Finance Manager", + "condition": null, + "next_state": "Cancelled", + "parent": "Purchase Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Approved", + "workflow_builder_id": null + } + ], + "workflow_data": null, + "workflow_name": "Purchase Order", + "workflow_state_field": "workflow_state" + }, + { + "docstatus": 0, + "doctype": "Workflow", + "document_type": "PPM", + "is_active": 1, + "modified": "2024-12-12 19:11:59.320482", + "name": "PPM", + "override_status": 0, + "send_email_alert": 0, + "states": [ + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Sent to technician", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Approval", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Rejected", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "PPM", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Approved", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + } + ], + "transitions": [ + { + "action": "Send For Approval", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": null, + "next_state": "Pending Approval", + "parent": "PPM", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Sent to technician", + "workflow_builder_id": null + }, + { + "action": "Approve", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Approved", + "parent": "PPM", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Reject", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Rejected", + "parent": "PPM", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + } + ], + "workflow_data": null, + "workflow_name": "PPM", + "workflow_state_field": "workflow_state" + }, + { + "docstatus": 0, + "doctype": "Workflow", + "document_type": "Asset Maintenance Log", + "is_active": 1, + "modified": "2026-01-29 15:12:46.870927", + "name": "Asset Maintenance Log", + "override_status": 0, + "send_email_alert": 0, + "states": [ + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Asset Maintenance Log", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Asset Maintenance Log", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Asset Maintenance Log", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Applied", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Asset Maintenance Log", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Approved", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Asset Maintenance Log", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Rejected", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Asset Maintenance Log", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Applied", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + } + ], + "transitions": [ + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": null, + "next_state": "Applied", + "parent": "Asset Maintenance Log", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Approve", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Approved", + "parent": "Asset Maintenance Log", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Applied", + "workflow_builder_id": null + }, + { + "action": "Reject", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Rejected", + "parent": "Asset Maintenance Log", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Applied", + "workflow_builder_id": null + }, + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": null, + "next_state": "Applied", + "parent": "Asset Maintenance Log", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Rejected", + "workflow_builder_id": null + }, + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Applied", + "parent": "Asset Maintenance Log", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + } + ], + "workflow_data": null, + "workflow_name": "Asset Maintenance Log", + "workflow_state_field": "workflow_state" + }, + { + "docstatus": 0, + "doctype": "Workflow", + "document_type": "Work_Order", + "is_active": 0, + "modified": "2026-01-09 09:12:45.581476", + "name": "Work Job Order", + "override_status": 0, + "send_email_alert": 0, + "states": [ + { + "allow_edit": "End user", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": "repair_status", + "update_value": "Open", + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Sent To Maintenance manger", + "update_field": "repair_status", + "update_value": "Open", + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Repair InProgress", + "update_field": "repair_status", + "update_value": "Work In Progress", + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Repair InProgress", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Finance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Purchase", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Approval", + "update_field": "repair_status", + "update_value": "Pending Review", + "workflow_builder_id": null + }, + { + "allow_edit": "Cluster Manager", + "avoid_status_override": 0, + "doc_status": "2", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Cancelled", + "update_field": "repair_status", + "update_value": "Cancelled", + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Rejected", + "update_field": "", + "update_value": "", + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Completed", + "update_field": "repair_status", + "update_value": "Completed", + "workflow_builder_id": null + }, + { + "allow_edit": "PHCC Site Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Completed", + "update_field": "repair_status", + "update_value": "Completed", + "workflow_builder_id": null + }, + { + "allow_edit": "PHCC End User", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": "repair_status", + "update_value": "Open", + "workflow_builder_id": null + }, + { + "allow_edit": "PHCC Site Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Sent To Site Manager", + "update_field": "repair_status", + "update_value": "Open", + "workflow_builder_id": null + }, + { + "allow_edit": "PHCC Site Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Approval", + "update_field": "repair_status", + "update_value": "Pending Review", + "workflow_builder_id": null + }, + { + "allow_edit": "PHCC Site Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Rejected", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": "repair_status", + "update_value": "Open", + "workflow_builder_id": null + }, + { + "allow_edit": "Cluster Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Closed", + "update_field": "repair_status", + "update_value": "Closed", + "workflow_builder_id": null + }, + { + "allow_edit": "General WOA", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Sent to General WOA", + "update_field": "repair_status", + "update_value": "Open", + "workflow_builder_id": null + }, + { + "allow_edit": "General Contractor", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Repair InProgress", + "update_field": "repair_status", + "update_value": "Work In Progress", + "workflow_builder_id": null + }, + { + "allow_edit": "General Contractor", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Approval", + "update_field": "repair_status", + "update_value": "Pending Review", + "workflow_builder_id": null + }, + { + "allow_edit": "General WOA", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Job Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Approval", + "update_field": "repair_status", + "update_value": "Pending Review", + "workflow_builder_id": null + } + ], + "transitions": [ + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "End user", + "condition": "doc.asset_type != \"Non Biomedical\" and not doc.site_name", + "next_state": "Sent To Maintenance manger", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": "doc.asset_type == \"Biomedical\"", + "next_state": "Sent To Maintenance manger", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Send For Repair", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Repair InProgress", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Sent To Maintenance manger", + "workflow_builder_id": null + }, + { + "action": "Send For Approval", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": "", + "next_state": "Pending Approval", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Repair InProgress", + "workflow_builder_id": null + }, + { + "action": "Material Request", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": "doc.need_procurement == 1", + "next_state": "Pending Purchase", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Repair InProgress", + "workflow_builder_id": null + }, + { + "action": "Send For Repair", + "allow_self_approval": 1, + "allowed": "Finance Manager", + "condition": null, + "next_state": "Repair InProgress", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Purchase", + "workflow_builder_id": null + }, + { + "action": "Accept", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": "", + "next_state": "Completed", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Reject", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Rejected", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "PHCC End User", + "condition": "doc.site_name and doc.asset_type == \"Biomedical\"", + "next_state": "Sent To Maintenance manger", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Close", + "allow_self_approval": 1, + "allowed": "Cluster Manager", + "condition": null, + "next_state": "Closed", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Sent To Maintenance manger", + "workflow_builder_id": null + }, + { + "action": "Close", + "allow_self_approval": 1, + "allowed": "Cluster Manager", + "condition": null, + "next_state": "Closed", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Repair InProgress", + "workflow_builder_id": null + }, + { + "action": "Close", + "allow_self_approval": 1, + "allowed": "Cluster Manager", + "condition": null, + "next_state": "Closed", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Purchase", + "workflow_builder_id": null + }, + { + "action": "Close", + "allow_self_approval": 1, + "allowed": "Cluster Manager", + "condition": null, + "next_state": "Closed", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Close", + "allow_self_approval": 1, + "allowed": "Cluster Manager", + "condition": null, + "next_state": "Closed", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Sent to General WOA", + "workflow_builder_id": null + }, + { + "action": "Re-Open", + "allow_self_approval": 1, + "allowed": "Cluster Manager", + "condition": "doc.asset_type != \"Non Biomedical\"", + "next_state": "Sent To Maintenance manger", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Closed", + "workflow_builder_id": null + }, + { + "action": "Re-Open", + "allow_self_approval": 1, + "allowed": "Cluster Manager", + "condition": "doc.site_name and doc.asset_type == \"Non Biomedical\"", + "next_state": "Sent to General WOA", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Closed", + "workflow_builder_id": null + }, + { + "action": "Cancel", + "allow_self_approval": 1, + "allowed": "Cluster Manager", + "condition": null, + "next_state": "Cancelled", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Completed", + "workflow_builder_id": null + }, + { + "action": "Cancel", + "allow_self_approval": 1, + "allowed": "Cluster Manager", + "condition": "doc.docstatus == 1", + "next_state": "Cancelled", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Rejected", + "workflow_builder_id": null + }, + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "PHCC End User", + "condition": "doc.site_name and doc.asset_type == \"Non Biomedical\"", + "next_state": "Sent to General WOA", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Send For Repair", + "allow_self_approval": 1, + "allowed": "General WOA", + "condition": "doc.custom_assign_to_contractor", + "next_state": "Repair InProgress", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Sent to General WOA", + "workflow_builder_id": null + }, + { + "action": "Send For Approval", + "allow_self_approval": 1, + "allowed": "General Contractor", + "condition": null, + "next_state": "Pending Approval", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Repair InProgress", + "workflow_builder_id": null + }, + { + "action": "Accept", + "allow_self_approval": 1, + "allowed": "General WOA", + "condition": null, + "next_state": "Completed", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Reject", + "allow_self_approval": 1, + "allowed": "General WOA", + "condition": null, + "next_state": "Rejected", + "parent": "Work Job Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + } + ], + "workflow_data": null, + "workflow_name": "Work Job Order", + "workflow_state_field": "workflow_state" + }, + { + "docstatus": 0, + "doctype": "Workflow", + "document_type": "Work_Order", + "is_active": 1, + "modified": "2026-01-29 14:26:57.557634", + "name": "Work Order", + "override_status": 0, + "send_email_alert": 0, + "states": [ + { + "allow_edit": "End user", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": "repair_status", + "update_value": "Open", + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Sent to Team Leader", + "update_field": "repair_status", + "update_value": "Open", + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Repair InProgress", + "update_field": "repair_status", + "update_value": "Work In Progress", + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Repair InProgress", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Finance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Purchase", + "update_field": null, + "update_value": null, + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Approval", + "update_field": "repair_status", + "update_value": "Pending Review", + "workflow_builder_id": null + }, + { + "allow_edit": "Cluster Manager", + "avoid_status_override": 0, + "doc_status": "2", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Cancelled", + "update_field": "repair_status", + "update_value": "Cancelled", + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Rejected", + "update_field": "", + "update_value": "", + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Completed", + "update_field": "repair_status", + "update_value": "Completed", + "workflow_builder_id": null + }, + { + "allow_edit": "PHCC Site Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Completed", + "update_field": "repair_status", + "update_value": "Completed", + "workflow_builder_id": null + }, + { + "allow_edit": "PHCC End User", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": "repair_status", + "update_value": "Open", + "workflow_builder_id": null + }, + { + "allow_edit": "PHCC Site Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Sent To Site Manager", + "update_field": "repair_status", + "update_value": "Open", + "workflow_builder_id": null + }, + { + "allow_edit": "PHCC Site Manager", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Pending Approval", + "update_field": "repair_status", + "update_value": "Pending Review", + "workflow_builder_id": null + }, + { + "allow_edit": "Technician", + "avoid_status_override": 0, + "doc_status": "0", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Draft", + "update_field": "repair_status", + "update_value": "Open", + "workflow_builder_id": null + }, + { + "allow_edit": "Cluster Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Closed", + "update_field": "repair_status", + "update_value": "Closed", + "workflow_builder_id": null + }, + { + "allow_edit": "Maintenance Manager", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Closed", + "update_field": "repair_status", + "update_value": "Closed", + "workflow_builder_id": null + }, + { + "allow_edit": "End user", + "avoid_status_override": 0, + "doc_status": "1", + "is_optional_state": 0, + "message": null, + "next_action_email_template": null, + "parent": "Work Order", + "parentfield": "states", + "parenttype": "Workflow", + "state": "Closed", + "update_field": "repair_status", + "update_value": "Closed", + "workflow_builder_id": null + } + ], + "transitions": [ + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "End user", + "condition": "", + "next_state": "Sent to Team Leader", + "parent": "Work Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": "doc.asset_type == \"Biomedical\"", + "next_state": "Sent to Team Leader", + "parent": "Work Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Send For Repair", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": "doc.custom_assign_to_contractor", + "next_state": "Repair InProgress", + "parent": "Work Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Sent to Team Leader", + "workflow_builder_id": null + }, + { + "action": "Send For Approval", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": "", + "next_state": "Pending Approval", + "parent": "Work Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Repair InProgress", + "workflow_builder_id": null + }, + { + "action": "Material Request", + "allow_self_approval": 1, + "allowed": "Technician", + "condition": "doc.need_procurement == 1", + "next_state": "Pending Purchase", + "parent": "Work Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Repair InProgress", + "workflow_builder_id": null + }, + { + "action": "Send For Repair", + "allow_self_approval": 1, + "allowed": "Finance Manager", + "condition": null, + "next_state": "Repair InProgress", + "parent": "Work Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Purchase", + "workflow_builder_id": null + }, + { + "action": "Accept", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": "", + "next_state": "Completed", + "parent": "Work Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Reject", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Rejected", + "parent": "Work Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Pending Approval", + "workflow_builder_id": null + }, + { + "action": "Apply", + "allow_self_approval": 1, + "allowed": "PHCC End User", + "condition": "doc.site_name and doc.asset_type == \"Biomedical\"", + "next_state": "Sent to Team Leader", + "parent": "Work Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Draft", + "workflow_builder_id": null + }, + { + "action": "Close", + "allow_self_approval": 1, + "allowed": "End user", + "condition": null, + "next_state": "Closed", + "parent": "Work Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Completed", + "workflow_builder_id": null + }, + { + "action": "Cancel", + "allow_self_approval": 1, + "allowed": "Cluster Manager", + "condition": null, + "next_state": "Cancelled", + "parent": "Work Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Closed", + "workflow_builder_id": null + }, + { + "action": "Cancel", + "allow_self_approval": 1, + "allowed": "Cluster Manager", + "condition": "doc.docstatus == 1", + "next_state": "Cancelled", + "parent": "Work Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Rejected", + "workflow_builder_id": null + }, + { + "action": "Re-Open", + "allow_self_approval": 1, + "allowed": "Maintenance Manager", + "condition": null, + "next_state": "Repair InProgress", + "parent": "Work Order", + "parentfield": "transitions", + "parenttype": "Workflow", + "state": "Completed", + "workflow_builder_id": null + } + ], + "workflow_data": null, + "workflow_name": "Work Order", + "workflow_state_field": "workflow_state" + } +] \ No newline at end of file diff --git a/asset_lite/fixtures/workflow_action_master.json b/asset_lite/fixtures/workflow_action_master.json new file mode 100644 index 0000000..de12920 --- /dev/null +++ b/asset_lite/fixtures/workflow_action_master.json @@ -0,0 +1,93 @@ +[ + { + "docstatus": 0, + "doctype": "Workflow Action Master", + "modified": "2024-09-10 18:16:06.155342", + "name": "Review", + "workflow_action_name": "Review" + }, + { + "docstatus": 0, + "doctype": "Workflow Action Master", + "modified": "2024-09-26 15:20:48.094725", + "name": "Re-Apply", + "workflow_action_name": "Re-Apply" + }, + { + "docstatus": 0, + "doctype": "Workflow Action Master", + "modified": "2024-09-17 12:40:56.916724", + "name": "Purchase Request", + "workflow_action_name": "Purchase Request" + }, + { + "docstatus": 0, + "doctype": "Workflow Action Master", + "modified": "2024-09-10 18:16:06.151314", + "name": "Approve", + "workflow_action_name": "Approve" + }, + { + "docstatus": 0, + "doctype": "Workflow Action Master", + "modified": "2025-02-17 20:01:17.214898", + "name": "Material Request", + "workflow_action_name": "Material Request" + }, + { + "docstatus": 0, + "doctype": "Workflow Action Master", + "modified": "2025-08-26 14:03:30.525232", + "name": "Re-Open", + "workflow_action_name": "Re-Open" + }, + { + "docstatus": 0, + "doctype": "Workflow Action Master", + "modified": "2024-09-13 11:10:00.762984", + "name": "Send For Approval", + "workflow_action_name": "Send For Approval" + }, + { + "docstatus": 0, + "doctype": "Workflow Action Master", + "modified": "2024-09-13 11:12:08.448832", + "name": "Accept", + "workflow_action_name": "Accept" + }, + { + "docstatus": 0, + "doctype": "Workflow Action Master", + "modified": "2024-09-10 18:16:06.153201", + "name": "Reject", + "workflow_action_name": "Reject" + }, + { + "docstatus": 0, + "doctype": "Workflow Action Master", + "modified": "2024-09-17 17:36:53.058367", + "name": "Cancel", + "workflow_action_name": "Cancel" + }, + { + "docstatus": 0, + "doctype": "Workflow Action Master", + "modified": "2024-09-13 11:09:03.772501", + "name": "Send For Repair", + "workflow_action_name": "Send For Repair" + }, + { + "docstatus": 0, + "doctype": "Workflow Action Master", + "modified": "2025-08-11 12:02:06.266995", + "name": "Close", + "workflow_action_name": "Close" + }, + { + "docstatus": 0, + "doctype": "Workflow Action Master", + "modified": "2024-09-13 11:05:10.460074", + "name": "Apply", + "workflow_action_name": "Apply" + } +] \ No newline at end of file diff --git a/asset_lite/fixtures/workflow_state.json b/asset_lite/fixtures/workflow_state.json new file mode 100644 index 0000000..7394f0f --- /dev/null +++ b/asset_lite/fixtures/workflow_state.json @@ -0,0 +1,164 @@ +[ + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2024-09-13 16:38:33.051049", + "name": "Sent to technician", + "style": "", + "workflow_state_name": "Sent to technician" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2025-05-22 18:15:15.263145", + "name": "Pending", + "style": "", + "workflow_state_name": "Pending" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2025-05-22 18:15:15.188448", + "name": "Sent to maintenance Manager", + "style": "", + "workflow_state_name": "Sent to maintenance Manager" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2024-09-17 17:29:51.495347", + "name": "Sent to Procurement User", + "style": "", + "workflow_state_name": "Sent to Procurement User" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2025-06-17 15:42:44.566036", + "name": "Sent To Site Manager", + "style": "", + "workflow_state_name": "Sent To Site Manager" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2025-08-26 14:02:06.495142", + "name": "Sent to General WOA", + "style": "", + "workflow_state_name": "Sent to General WOA" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2024-09-17 17:37:02.717022", + "name": "Cancelled", + "style": "", + "workflow_state_name": "Cancelled" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2025-05-22 18:15:15.104122", + "name": "Pending Purchase", + "style": "", + "workflow_state_name": "Pending Purchase" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "ok-sign", + "modified": "2024-09-10 18:16:06.109888", + "name": "Approved", + "style": "Success", + "workflow_state_name": "Approved" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "remove", + "modified": "2024-09-10 18:16:06.113226", + "name": "Rejected", + "style": "Danger", + "workflow_state_name": "Rejected" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2024-09-17 17:31:29.957725", + "name": "Applied", + "style": "", + "workflow_state_name": "Applied" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2026-01-12 15:31:11.081747", + "name": "Sent to Team Leader", + "style": "", + "workflow_state_name": "Sent to Team Leader" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2025-08-11 12:01:35.474110", + "name": "Closed", + "style": "", + "workflow_state_name": "Closed" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2025-05-22 18:15:15.034033", + "name": "Completed", + "style": "Success", + "workflow_state_name": "Completed" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2025-05-22 18:15:14.987359", + "name": "Sent To Maintenance manger", + "style": "Info", + "workflow_state_name": "Sent To Maintenance manger" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2024-09-13 13:50:24.315353", + "name": "Pending Approval", + "style": "Primary", + "workflow_state_name": "Pending Approval" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2025-05-22 18:15:14.732015", + "name": "Repair InProgress", + "style": "Primary", + "workflow_state_name": "Repair InProgress" + }, + { + "docstatus": 0, + "doctype": "Workflow State", + "icon": "", + "modified": "2024-09-13 13:51:23.086380", + "name": "Draft", + "style": "Warning", + "workflow_state_name": "Draft" + } +] \ No newline at end of file diff --git a/asset_lite/fixtures/workspace.json b/asset_lite/fixtures/workspace.json new file mode 100644 index 0000000..7f31c3e --- /dev/null +++ b/asset_lite/fixtures/workspace.json @@ -0,0 +1,155 @@ +[ + { + "charts": [ + { + "chart_name": "Asset Maintenance Assignees Status Count", + "label": "Asset Maintenance Assignees Status Count", + "parent": "Asset Management", + "parentfield": "charts", + "parenttype": "Workspace" + }, + { + "chart_name": "Up & Down Time Chart", + "label": "Up & Down Time Chart", + "parent": "Asset Management", + "parentfield": "charts", + "parenttype": "Workspace" + }, + { + "chart_name": "Repair Cost", + "label": "Repair Cost", + "parent": "Asset Management", + "parentfield": "charts", + "parenttype": "Workspace" + }, + { + "chart_name": "Asset Maintenance Frequency Chart", + "label": "Asset Maintenance Frequency Chart", + "parent": "Asset Management", + "parentfield": "charts", + "parenttype": "Workspace" + }, + { + "chart_name": "Work Order Status Chart", + "label": "Work Order Status Chart", + "parent": "Asset Management", + "parentfield": "charts", + "parenttype": "Workspace" + }, + { + "chart_name": "PPM Status", + "label": "PPM Status", + "parent": "Asset Management", + "parentfield": "charts", + "parenttype": "Workspace" + }, + { + "chart_name": "Maintenance - Asset wise Count", + "label": "Maintenance - Asset wise Count", + "parent": "Asset Management", + "parentfield": "charts", + "parenttype": "Workspace" + }, + { + "chart_name": "PPM Template Counts", + "label": "PPM Template Counts", + "parent": "Asset Management", + "parentfield": "charts", + "parenttype": "Workspace" + } + ], + "content": "[{\"id\":\"yYK_-DexUD\",\"type\":\"header\",\"data\":{\"text\":\"Asset Management\",\"col\":12}},{\"id\":\"vSRTdGFowh\",\"type\":\"custom_block\",\"data\":{\"custom_block_name\":\"Shortcuts\",\"col\":12}},{\"id\":\"zrPZ7O6g46\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"ME84l9QyCI\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total No. of Assets\",\"col\":4}},{\"id\":\"mztJCGcLAH\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Open Work Orders\",\"col\":4}},{\"id\":\"YJWVESaNY3\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Work Orders In Progress\",\"col\":4}},{\"id\":\"Lg0i-UI4zn\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Completed Work Orders\",\"col\":4}},{\"id\":\"f7T4hhF8Kb\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"FcWcAIBtBn\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Repair Cost\",\"col\":3}},{\"id\":\"x09_bP_Mgy\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Planned PMs\",\"col\":3}},{\"id\":\"EzjHiZUJ-9\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"N0fMmDFu9y\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Up & Down Time Chart\",\"col\":12}},{\"id\":\"LWcpCDqX7i\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Work Order Status Chart\",\"col\":12}},{\"id\":\"tJ1-94niQL\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Maintenance - Asset wise Count\",\"col\":12}},{\"id\":\"xdAje_Ro-7\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Asset Maintenance Assignees Status Count\",\"col\":12}},{\"id\":\"tvDISsBzdy\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Asset Maintenance Frequency Chart\",\"col\":12}},{\"id\":\"o60N46aqap\",\"type\":\"chart\",\"data\":{\"chart_name\":\"PPM Status\",\"col\":12}},{\"id\":\"EMMRueDlE7\",\"type\":\"chart\",\"data\":{\"chart_name\":\"PPM Template Counts\",\"col\":12}},{\"id\":\"-fgpm6K12i\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Repair Cost\",\"col\":12}}]", + "custom_blocks": [ + { + "custom_block_name": "Shortcuts", + "label": "Shortcuts", + "parent": "Asset Management", + "parentfield": "custom_blocks", + "parenttype": "Workspace" + } + ], + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "LedgerIcon-new", + "indicator_color": "", + "is_hidden": 0, + "label": "Asset Management", + "links": [], + "modified": "2025-09-18 13:29:15.514753", + "module": "Asset Lite", + "name": "Asset Management", + "number_cards": [ + { + "label": "Total No. of Assets", + "number_card_name": "Total No of Assets", + "parent": "Asset Management", + "parentfield": "number_cards", + "parenttype": "Workspace" + }, + { + "label": "Open Work Orders", + "number_card_name": "Open Work Orders", + "parent": "Asset Management", + "parentfield": "number_cards", + "parenttype": "Workspace" + }, + { + "label": "Work Orders In Progress", + "number_card_name": "Work Orders In Progress", + "parent": "Asset Management", + "parentfield": "number_cards", + "parenttype": "Workspace" + }, + { + "label": "Completed Work Orders", + "number_card_name": "Completed Work Orders", + "parent": "Asset Management", + "parentfield": "number_cards", + "parenttype": "Workspace" + } + ], + "parent_page": "", + "public": 1, + "quick_lists": [], + "restrict_to_domain": null, + "roles": [], + "sequence_id": 28.0, + "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "format": null, + "icon": null, + "kanban_board": null, + "label": "Repair Cost", + "link_to": "Repair Cost", + "parent": "Asset Management", + "parentfield": "shortcuts", + "parenttype": "Workspace", + "restrict_to_domain": null, + "stats_filter": null, + "type": "Report", + "url": null + }, + { + "color": "Grey", + "doc_view": "List", + "format": null, + "icon": null, + "kanban_board": null, + "label": "Planned PMs", + "link_to": "Planned PM", + "parent": "Asset Management", + "parentfield": "shortcuts", + "parenttype": "Workspace", + "restrict_to_domain": null, + "stats_filter": null, + "type": "Report", + "url": null + } + ], + "title": "Asset Management" + } +] \ No newline at end of file diff --git a/asset_lite/hooks.py b/asset_lite/hooks.py new file mode 100644 index 0000000..7114602 --- /dev/null +++ b/asset_lite/hooks.py @@ -0,0 +1,311 @@ +app_name = "asset_lite" +app_title = "Asset Lite" +app_publisher = "seyfert" +app_description = "Asset Management System" +app_email = "seyfert@example.com" +app_license = "mit" + +# Apps +# ------------------ +# required_apps = [] + +# Each item in the list will be shown as an app in the apps page +# add_to_apps_screen = [ +# { +# "name": "asset_lite", +# "logo": "/assets/asset_lite/logo.png", +# "title": "Asset Lite", +# "route": "/asset_lite", +# "has_permission": "asset_lite.api.permission.has_app_permission" +# } +# ] + +# Includes in +# ------------------ + +# include js, css files in header of desk.html +# app_include_css = "/assets/asset_lite/css/asset_lite.css" +app_include_css = "/assets/asset_lite/css/custom.css?v=1.0.2" +# app_include_js = "/assets/asset_lite/js/asset_lite.js" +app_include_js = "/assets/asset_lite/js/dashboard_embed.js?v=1.0.28" + +# include js, css files in header of web template +# web_include_css = "/assets/asset_lite/css/asset_lite.css" +# web_include_js = "/assets/asset_lite/js/asset_lite.js" + +# include custom scss in every website theme (without file extension ".scss") +# website_theme_scss = "asset_lite/public/scss/website" + +# include js, css files in header of web form +# webform_include_js = {"doctype": "public/js/doctype.js"} +# webform_include_css = {"doctype": "public/css/doctype.css"} + +# include js in page +# page_js = {"page" : "public/js/file.js"} + +# include js in doctype views +doctype_js = {"Asset Maintenance Log" : "public/js/custom_asset_maintenance_log.js"} +# doctype_list_js = {"doctype" : "public/js/doctype_list.js"} +# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} +# doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"} + +# Svg Icons +# ------------------ +# include app icons in desk +# app_include_icons = "asset_lite/public/icons.svg" + +# Home Pages +# ---------- + +# application home page (will override Website Settings) +# home_page = "login" +on_session_creation = "asset_lite.api.api.set_default_homepage" + + +# website user home page (by Role) +# role_home_page = { +# "Role": "home_page" +# } + +# Generators +# ---------- + +# automatically create page for each record of this doctype +# website_generators = ["Web Page"] + +# Jinja +# ---------- + +# add methods and filters to jinja environment +# jinja = { +# "methods": "asset_lite.utils.jinja_methods", +# "filters": "asset_lite.utils.jinja_filters" +# } + +# Installation +# ------------ + +# before_install = "asset_lite.install.before_install" +# after_install = "asset_lite.install.after_install" + +# Uninstallation +# ------------ + +# before_uninstall = "asset_lite.uninstall.before_uninstall" +# after_uninstall = "asset_lite.uninstall.after_uninstall" + +# Integration Setup +# ------------------ +# To set up dependencies/integrations with other apps +# Name of the app being installed is passed as an argument + +# before_app_install = "asset_lite.utils.before_app_install" +# after_app_install = "asset_lite.utils.after_app_install" + +# Integration Cleanup +# ------------------- +# To clean up dependencies/integrations with other apps +# Name of the app being uninstalled is passed as an argument + +# before_app_uninstall = "asset_lite.utils.before_app_uninstall" +# after_app_uninstall = "asset_lite.utils.after_app_uninstall" + +# Desk Notifications +# ------------------ +# See frappe.core.notifications.get_notification_config + +# notification_config = "asset_lite.notifications.get_notification_config" + +# Permissions +# ----------- +# Permissions evaluated in scripted ways + +# permission_query_conditions = { +# "Event": "frappe.desk.doctype.event.event.get_permission_query_conditions", +# } +# +# has_permission = { +# "Event": "frappe.desk.doctype.event.event.has_permission", +# } + +# DocType Class +# --------------- +# Override standard doctype classes + +override_doctype_class = { +# "Supplier Scorecard Criteria":"asset_lite.supplier_score_criteria_override.CustomSupplierScorecardCriteria" +# "ToDo": "custom_app.overrides.CustomToDo" +} + +# Document Events +# --------------- +# Hook on document methods and events + +doc_events = { + "Asset":{ + "before_save": "asset_lite.public.py.asset.generate_asset_qr" + } +} + +# Scheduled Tasks +# --------------- + +# scheduler_events = { +# "all": [ +# "asset_lite.tasks.all" +# ], +# "daily": [ +# "asset_lite.tasks.daily" +# ], +# "hourly": [ +# "asset_lite.tasks.hourly" +# ], +# "weekly": [ +# "asset_lite.tasks.weekly" +# ], +# "monthly": [ +# "asset_lite.tasks.monthly" +# ], +# } + +# Testing +# ------- + +# before_tests = "asset_lite.install.before_tests" + +# Overriding Methods +# ------------------------------ +# +# override_whitelisted_methods = { +# "frappe.desk.doctype.event.event.get_events": "asset_lite.event.get_events" +# } +# +# each overriding function accepts a `data` argument; +# generated from the base implementation of the doctype dashboard, +# along with any modifications made in other Frappe apps +# override_doctype_dashboards = { +# "Task": "asset_lite.task.get_dashboard_data" +# } + +# exempt linked doctypes from being automatically cancelled +# +# auto_cancel_exempted_doctypes = ["Auto Repeat"] + +# Ignore links to specified DocTypes when deleting documents +# ----------------------------------------------------------- + +# ignore_links_on_delete = ["Communication", "ToDo"] + +# Request Events +# ---------------- +# before_request = ["asset_lite.utils.before_request"] +# after_request = ["asset_lite.utils.after_request"] + +# Job Events +# ---------- +# before_job = ["asset_lite.utils.before_job"] +# after_job = ["asset_lite.utils.after_job"] + +# User Data Protection +# -------------------- + +# user_data_fields = [ +# { +# "doctype": "{doctype_1}", +# "filter_by": "{filter_by}", +# "redact_fields": ["{field_1}", "{field_2}"], +# "partial": 1, +# }, +# { +# "doctype": "{doctype_2}", +# "filter_by": "{filter_by}", +# "partial": 1, +# }, +# { +# "doctype": "{doctype_3}", +# "strict": False, +# }, +# { +# "doctype": "{doctype_4}" +# } +# ] + +# Authentication and authorization +# -------------------------------- + +# auth_hooks = [ +# "asset_lite.auth.validate" +# ] + +# Automatically update python controller files with type annotations for this app. +# export_python_type_annotations = True + +# default_log_clearing_doctypes = { +# "Logging DocType Name": 30 # days to retain logs +# } + +fixtures = [ + "Workflow", + "Workflow State", + "Workflow Action Master", + "Custom DocPerm", + "Translation", + + {"doctype": "Custom Field", "filters": [ + [ + "module", "=", "Asset Lite" + ] + ]}, + {"doctype": "Property Setter", "filters": [ + [ + "module", "=", "Asset Lite" + ] + ]}, + # { + # "doctype": "Role", + # "filters": [ + # ["creation", ">", "2024-09-12"] + # ] + # }, + {"doctype": "Workspace", "filters": [ + [ + "module", "=", "Asset Lite" + ] + ]}, + {"dt": "Print Format", "filters": {"custom_format": 1}}, + {"doctype": "Client Script", "filters": [["module","=","Asset Lite"]]}, + {"doctype": "Server Script", "filters": [["module","=","Asset Lite"]]}, + {"doctype": "Notification", "filters": [["is_standard","=",0]]}, + {"doctype": "Report", "filters": [["module","=","Asset Lite"]]}, + { + "doctype": "Dashboard Chart", + "filters": [ + ["is_standard", "=", 0] + ] + }, + { + "doctype": "Number Card", + "filters": [ + ["is_standard", "=", 0] + ] + }, + { + "doctype": "Dashboard", + "filters": [ + ["is_standard", "=", 0] + ] + }, + + { + "doctype": "Company", + "filters": [ + ["domain", "=", "Healthcare"] + ] + }, + { + "doctype": "User Permission", + "filters": [ + ["allow", "=", "Hospital"] + ] + }, +] diff --git a/asset_lite/map.py b/asset_lite/map.py new file mode 100644 index 0000000..d97a27e --- /dev/null +++ b/asset_lite/map.py @@ -0,0 +1,76 @@ +import frappe +@frappe.whitelist() +def get_custom_html_data(filters=None): + return { + "labels": ["Custom"], + "datasets": [{"name": "Custom HTML", "values": [1]}], + "type": "custom", + "custom_html": True + } + + + + +# asset_lite/api.py +import frappe +from frappe import _ + +@frappe.whitelist() +def get_active_map_data(hospital=None): + filters = {"latitude": ["!=", ""], "longitude": ["!=", ""]} + if hospital: + filters["name"] = hospital + + hospitals = frappe.get_all("Location", fields=["name", "latitude", "longitude"], filters=filters) + results = [] + + for h in hospitals: + name = h.name + + def count(doctype, filters): + try: + return frappe.db.count(doctype, filters) + except: + return 0 + + data = { + "name": name, + "latitude": h.latitude, + "longitude": h.longitude, + "assets": count("Asset", {"company": name}), + "normal_work_orders": count("Work_Order", { + "company": name, "custom_priority_": "Normal", + "repair_status": ["in", ["Open", "Work In Progress"]] + }), + "urgent_work_orders": count("Work_Order", { + "company": name, "custom_priority_": "Urgent", + "repair_status": ["in", ["Open", "Work In Progress"]] + }), + + # Work Orders by status (for table) + "wo_open": count("Work_Order", {"company": name, "repair_status": "Open"}), + "wo_progress": count("Work_Order", {"company": name, "repair_status": "Work In Progress"}), + "wo_review": count("Work_Order", {"company": name, "repair_status": "Pending Review"}), + "wo_completed": count("Work_Order", {"company": name, "repair_status": "Completed"}), + + + "planned_maintenance": count("Asset Maintenance Log", { + "custom_hospital_name": name, + "maintenance_status": "Planned" + }), + "completed_maintenance": count("Asset Maintenance Log", { + "custom_hospital_name": name, + "maintenance_status": "Completed" + }), + "overdue_maintenance": count("Asset Maintenance Log", { + "custom_hospital_name": name, + "maintenance_status": "Overdue" + }) + } + + results.append(data) + + return results + + + diff --git a/asset_lite/modules.txt b/asset_lite/modules.txt new file mode 100644 index 0000000..73e842e --- /dev/null +++ b/asset_lite/modules.txt @@ -0,0 +1 @@ +Asset Lite \ No newline at end of file diff --git a/asset_lite/patches.txt b/asset_lite/patches.txt new file mode 100644 index 0000000..f15c3a9 --- /dev/null +++ b/asset_lite/patches.txt @@ -0,0 +1,6 @@ +[pre_model_sync] +# Patches added in this section will be executed before doctypes are migrated +# Read docs to understand patches: https://frappeframework.com/docs/v14/user/en/database-migrations + +[post_model_sync] +# Patches added in this section will be executed after doctypes are migrated \ No newline at end of file diff --git a/asset_lite/public/.gitkeep b/asset_lite/public/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/public/css/custom.css b/asset_lite/public/css/custom.css new file mode 100644 index 0000000..63cfd3d --- /dev/null +++ b/asset_lite/public/css/custom.css @@ -0,0 +1,9 @@ +.widget.number-widget-box { + cursor: pointer; + min-height: 84px; + padding: var(--number-card-padding); + border: 1px solid #1900ff !important; /* Already good */ + border-radius: 10px; /* Optional: rounded corners */ + box-shadow: 0 1px 3px #e03636; /* Optional: subtle shadow */ + background-color: hwb(72 95% 4%) !important; /* Optional: ensure background contrast */ +} \ No newline at end of file diff --git a/asset_lite/public/js/custom_asset_maintenance_log.js b/asset_lite/public/js/custom_asset_maintenance_log.js new file mode 100644 index 0000000..b53b300 --- /dev/null +++ b/asset_lite/public/js/custom_asset_maintenance_log.js @@ -0,0 +1,29 @@ + +frappe.ui.form.on('Asset Maintenance Log', { + refresh: function(frm) { + // Hide the default print icon + frm.page.hide_icon_group('print'); + + // Add custom button for PPM Sticker with a print icon + frm.add_custom_button( + ` ${__('PPM Sticker')}`, + + function() { + + // Set PPM Sticker as the default print format and open print preview + const customLink = `/printview?doctype=Asset Maintenance Log&name=${frm.doc.name}&trigger_print=0&format=PPM%20Asset&no_letterhead=0`; + window.open(customLink); + } + ); + + // Add custom button for PPM Service Report with a print icon + frm.add_custom_button( + ` ${__('Service Report')}`, + function() { + // Set Service Report as the default print format and open print preview + const customLink = `/printview?doctype=Asset Maintenance Log&name=${frm.doc.name}&trigger_print=0&format=PPM%20Asset%20Service&no_letterhead=0`; + window.open(customLink); + } + ); + } +}); \ No newline at end of file diff --git a/asset_lite/public/js/dashboard_embed.js b/asset_lite/public/js/dashboard_embed.js new file mode 100644 index 0000000..62c8abb --- /dev/null +++ b/asset_lite/public/js/dashboard_embed.js @@ -0,0 +1,117 @@ + +/*$(document).ready(function () { + const widgetNames = ["eh0tdvlmin","goctvitvje","fjbi1qp64g","6v10o9c31q","7sgvfh9372"]; + const iframeHTML = ` +
+ +
+ `; + + // Wait for dashboard to load + setTimeout(function () { + widgetNames.forEach(widgetName => { + $(`div[data-widget-name="${widgetName}"]`).each(function () { + const $chartContainer = $(this).find('.chart-container, .frappe-chart'); + if ($chartContainer.length) { + $chartContainer.html(iframeHTML); + } + }); + }); + }, 2000); +}); +*/ + +function runMapInjectionScript() { + const targetTitle = "Active Map Chart"; + const iframeHTML = ` +
+ +
+ `; + + function injectMap(retryCount = 0) { + let injected = false; + + $('div.widget.dashboard-widget-box').each(function () { + const $widget = $(this); + const title = $widget.find('.widget-title span.ellipsis').text().trim(); + + console.log("Found widget title:", title); + + if (title === targetTitle) { + const $chartContainer = $widget.find('.chart-container, .frappe-chart'); + + console.log("→ Checking chart container for:", title); + console.log("→ Chart container found:", $chartContainer.length); + console.log("→ Already injected:", $chartContainer.hasClass("map-injected")); + + if ($chartContainer.length && !$chartContainer.hasClass("map-injected")) { + $chartContainer.addClass("map-injected").html(iframeHTML); + console.log("✅ Map injected into:", title); + injected = true; + } + } + }); + + if (!injected && retryCount < 10) { + setTimeout(() => injectMap(retryCount + 1), 500); + } + } + + injectMap(); + + // Optionally reinject on dashboard refresh + $(document).on('dashboard-refresh', injectMap); +} + +// Run on first load +$(document).ready(runMapInjectionScript); + +// Also run when navigating via search or sidebar +frappe.router.on('change', function () { + // Small delay to let new page render + setTimeout(() => { + runMapInjectionScript(); + }, 300); +}); + + + +/*$(document).ready(function () { + const targetTitle = "Active Map Chart"; + const iframeHTML = ` +
+ +
+ `; + + function injectMap(retryCount = 0) { + let injected = false; + + $('div.widget.dashboard-widget-box').each(function () { + const $widget = $(this); + const title = $widget.find('.widget-title span.ellipsis').text().trim(); + console.log("Found widget title:", title); + + if (title === targetTitle) { + const $chartContainer = $widget.find('.chart-container, .frappe-chart'); + console.log("→ Checking chart container for:", title); + console.log("→ Chart container found:", $chartContainer.length); + console.log("→ Already injected:", $chartContainer.hasClass("map-injected")); + + if ($chartContainer.length && !$chartContainer.hasClass("map-injected")) { + $chartContainer.addClass("map-injected").html(iframeHTML); + injected = true; + } + } + }); + + if (!injected && retryCount < 10) { + setTimeout(() => injectMap(retryCount + 1), 500); + } + } + + injectMap(); + +}); +*/ diff --git a/asset_lite/public/py/asset.py b/asset_lite/public/py/asset.py new file mode 100644 index 0000000..7b54a30 --- /dev/null +++ b/asset_lite/public/py/asset.py @@ -0,0 +1,237 @@ +# import frappe +# import qrcode +# from io import BytesIO +# import base64 +# from PIL import Image +# import traceback + +# def generate_asset_qr(doc, method): +# try: +# # Debugging: Log that function was called +# frappe.logger().debug(f"QR Generation started for asset {doc.name}") + +# # Check if QR code already exists +# if doc.custom_asset_image: +# frappe.logger().debug(f"Asset {doc.name} already has a QR code: {doc.custom_asset_image}") +# return + +# # Generate QR code for the asset ID +# qr = qrcode.QRCode( +# version=1, +# error_correction=qrcode.constants.ERROR_CORRECT_L, +# box_size=10, +# border=4, +# ) +# qr.add_data(doc.name) # Use asset ID as QR code data +# qr.make(fit=True) + +# # Create an image from the QR Code +# img = qr.make_image(fill_color="black", back_color="white") + +# # Save the image to a buffer +# buffer = BytesIO() +# img.save(buffer, format="PNG") +# buffer.seek(0) + +# # Save QR code as a file attachment +# file_name = f"asset_qr_{doc.name}.png" + +# # Create a file in ERPNext using the file_data method +# file_doc = frappe.new_doc("File") +# file_doc.file_name = file_name +# file_doc.attached_to_doctype = "Asset" +# file_doc.attached_to_name = doc.name +# file_doc.attached_to_field = "custom_asset_image" # Specify the field +# file_doc.is_private = 0 + +# # Save the file content +# file_doc.save_file(buffer.getvalue(), file_name, is_private=0) + +# # Log the file URL +# frappe.logger().debug(f"File created with URL: {file_doc.file_url}") + +# # Update the asset with the QR code image - using direct SQL for reliability +# frappe.db.sql(""" +# UPDATE `tabAsset` +# SET custom_asset_image = %s +# WHERE name = %s +# """, (file_doc.file_url, doc.name)) + +# # Force a commit to ensure data is saved +# frappe.db.commit() + +# frappe.logger().debug(f"QR Code generation complete for asset {doc.name}") + +# except Exception as e: +# frappe.db.rollback() +# err_msg = f"Error generating QR code for asset {doc.name}: {str(e)}\n{traceback.format_exc()}" +# frappe.logger().error(err_msg) +# frappe.log_error(err_msg, "Asset QR Code Generation Error") + + +# import frappe +# import requests +# from frappe.utils.file_manager import save_file + +# def generate_asset_qr(doc, method): +# docname = doc.name +# if not docname: +# return + +# qr_url = f"https://quickchart.io/qr?text={frappe.utils.encode(docname)}" + +# # Fetch QR image +# response = requests.get(qr_url) +# if response.status_code != 200: +# frappe.throw("Failed to generate QR Code") + +# # Save file +# file_name = f"{docname}-qr.png" +# file_doc = save_file( +# file_name, +# content=response.content, +# dt="Asset", +# attached_to_field="custom_asset_image", +# dn=docname, +# decode=False, +# is_private=False +# ) + +# # Attach file path to the custom image field +# frappe.db.set_value("Asset", docname, "custom_asset_image", file_doc.file_url) +# # return True + +import frappe +import pyqrcode +import io +import base64 +import urllib.parse + +def generate_asset_qr(doc, method): + docname = doc.name + if not docname: + frappe.throw("Document name is required.") + + # Check if a file is already attached + existing_file = frappe.db.exists( + "File", + { + "attached_to_doctype": "Asset", + "attached_to_name": docname, + "attached_to_field": "custom_attach_image" + } + ) + + if existing_file: + return # QR already attached + + try: + # Get full ERPNext site URL + site_url = frappe.utils.get_url() + + # Build asset detail page URL + asset_url = f"{site_url}/asm_app/assets/{urllib.parse.quote(docname)}" + + # Log for debugging + frappe.logger().debug(f"Generating offline QR for: {asset_url}") + + # --- Generate QR Code Offline --- + qr_obj = pyqrcode.create(asset_url, error='H') # High error correction + + buffer = io.BytesIO() + qr_obj.png(buffer, scale=8) # Scale 8 gives 500×500-ish resolution + qr_png = buffer.getvalue() + + # Base64 encode + encoded_content = base64.b64encode(qr_png).decode("utf-8") + + # Create File document + file_doc = frappe.get_doc({ + "doctype": "File", + "file_name": f"{docname}-qr.png", + "attached_to_doctype": "Asset", + "attached_to_name": docname, + "attached_to_field": "custom_attach_image", + "content": encoded_content, + "decode": True, # decode base64 back into file + "is_private": 0 + }) + file_doc.insert(ignore_permissions=True) + + # Set link to Asset field + frappe.db.set_value("Asset", docname, "custom_attach_image", file_doc.file_url) + frappe.db.commit() + + frappe.logger().debug(f"QR code generated successfully for Asset: {docname}") + + except Exception as e: + frappe.log_error(f"Error generating QR for asset {docname}: {str(e)}", "QR Code Error") + raise + + +#import frappe +#import requests +#import base64 +#import urllib.parse + +#def generate_asset_qr(doc, method): +# docname = doc.name +# if not docname: +# frappe.throw("Document name is required.") + +# # Check if a file is already attached +# existing_file = frappe.db.exists( +# "File", +# { +# "attached_to_doctype": "Asset", +# "attached_to_name": docname, +# "attached_to_field": "custom_attach_image" +# } +# ) + +# if existing_file: +# return # Exit early if file already exists + +# try: +# # Get the site URL from the configuration +# site_url = frappe.utils.get_url() + +# # Create a direct link to the asset - ensure proper URL format +# asset_url = f"{site_url}/app/asset/{urllib.parse.quote(docname)}" + +# # Log the URL for debugging +# frappe.logger().debug(f"Asset URL for QR code: {asset_url}") + +# # Generate QR Code with the full URL - using higher resolution and error correction +# qr_url = f"https://quickchart.io/qr?text={urllib.parse.quote(asset_url)}&size=500&margin=10&ecLevel=H" +# response = requests.get(qr_url) +# frappe.logger().debug(response.url) + +# if response.status_code != 200: +# frappe.throw(f"Failed to generate QR code. Status code: {response.status_code}") + +# # Base64-encode the image content +# encoded_content = base64.b64encode(response.content).decode("utf-8") + +# # Create new File document +# file_doc = frappe.get_doc({ +# "doctype": "File", +# "file_name": f"{docname}-qr.png", +# "attached_to_doctype": "Asset", +# "attached_to_name": docname, +# "attached_to_field": "custom_attach_image", +# "content": encoded_content, +# "decode": True, +# "is_private": 0 +# }) +# file_doc.insert(ignore_permissions=True) + + # # Set file URL to the image field +# frappe.db.set_value("Asset", docname, "custom_attach_image", file_doc.file_url) +# frappe.db.commit() + +# # Log success +# frappe.logger().debug(f"QR code generated successfully for asset {docname}") + +# except Exception as e: +# frappe.log_error(f"Error generating QR code for asset {docname}: {str(e)}", "QR Code Error") diff --git a/asset_lite/supplier_score_criteria_override.py b/asset_lite/supplier_score_criteria_override.py new file mode 100644 index 0000000..0f1b414 --- /dev/null +++ b/asset_lite/supplier_score_criteria_override.py @@ -0,0 +1,33 @@ +import re + +import frappe +from frappe import _ +from frappe.model.document import Document + +from erpnext.buying.doctype.supplier_scorecard_criteria.supplier_scorecard_criteria import SupplierScorecardCriteria + +class CustomSupplierScorecardCriteria(SupplierScorecardCriteria): + def validate_formula(self): + # Evaluate the formula with 0's to ensure it is valid + test_formula = self.formula.replace("\r", "").replace("\n", "") + + # Find and replace all placeholders with 0 + regex = r"\{(.*?)\}" + mylist = re.finditer(regex, test_formula, re.MULTILINE | re.DOTALL) + for match in mylist: + test_formula = test_formula.replace("{" + match.group(1) + "}", "0") + + try: + # Use safe_eval with a custom safe division function + frappe.safe_eval( + test_formula, + None, + { + "max": max, + "min": min, + "safe_div": lambda x, y: x / y if y != 0 else 0, # Safe division logic + }, + ) + except Exception as e: + # Throw an error if formula evaluation fails + frappe.throw(_("Error evaluating the criteria formula: {0}").format(str(e))) \ No newline at end of file diff --git a/asset_lite/templates/__init__.py b/asset_lite/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/templates/pages/__init__.py b/asset_lite/templates/pages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asset_lite/www/entry.html b/asset_lite/www/entry.html new file mode 100644 index 0000000..59480e3 --- /dev/null +++ b/asset_lite/www/entry.html @@ -0,0 +1,52 @@ + + + + + + Seera Apps + + + +
+
+

Checking your session...

+
+ + + + \ No newline at end of file diff --git a/asset_lite/www/standalone-active-map.html b/asset_lite/www/standalone-active-map.html new file mode 100644 index 0000000..9deb54d --- /dev/null +++ b/asset_lite/www/standalone-active-map.html @@ -0,0 +1,287 @@ + + + + Active Map Public + + + + + +
+ + +
+
+ + + + + diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..8aa2645 --- /dev/null +++ b/license.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [year] [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..deb723c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,20 @@ +[project] +name = "asset_lite" +authors = [ + { name = "seyfert", email = "seyfert@example.com"} +] +description = "Asset Management System" +requires-python = ">=3.10" +readme = "README.md" +dynamic = ["version"] +dependencies = [ + # "frappe~=15.0.0" # Installed and managed by bench. +] + +[build-system] +requires = ["flit_core >=3.4,<4"] +build-backend = "flit_core.buildapi" + +# These dependencies are only installed when developer mode is enabled +[tool.bench.dev-dependencies] +# package_name = "~=1.1.0"