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) }