Iman_AMS/asm_app/src/services/technicianWorkingHoursService.ts

105 lines
3.2 KiB
TypeScript

import { getYearToDateRange } from '../utils/dateUtils';
export const TECHNICIAN_WORKING_HOURS_REPORT = 'Technicians working Hours';
export type TechnicianWorkingHoursRow = {
engineer?: string;
technician_name?: string;
total_hours?: number | string;
is_total_row?: number | boolean;
};
export type TechnicianWorkingHoursFilters = {
work_order_type?: string;
from_date?: string;
to_date?: string;
company?: string;
site_name?: string;
};
export type TechnicianWorkingHoursChartData = {
labels: string[];
datasets: Array<{ name: string; values: number[]; colors?: string[] }>;
totalHours: number;
rows: TechnicianWorkingHoursRow[];
};
export const getDefaultTechnicianHoursDateRange = () => getYearToDateRange();
export const getTechnicianHoursLabel = (row: TechnicianWorkingHoursRow): string =>
(row.technician_name || row.engineer || 'Unknown').trim() || 'Unknown';
export const parseTechnicianHours = (value: unknown): number => {
if (value == null || value === '') return 0;
const parsed = Number(value);
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
};
export const buildTechnicianWorkingHoursChartData = (
rows: TechnicianWorkingHoursRow[]
): TechnicianWorkingHoursChartData | null => {
const dataRows = rows.filter(row => row && !row.is_total_row);
if (!dataRows.length) return null;
const labels = dataRows.map(getTechnicianHoursLabel);
const values = dataRows.map(row => parseTechnicianHours(row.total_hours));
const totalHours = values.reduce((sum, value) => sum + value, 0);
return {
labels,
datasets: [{ name: 'Total Hours', values, colors: ['#6366F1'] }],
totalHours,
rows: dataRows,
};
};
export const runTechnicianWorkingHoursReport = async (
filters: TechnicianWorkingHoursFilters
): Promise<TechnicianWorkingHoursRow[]> => {
const csrfToken =
typeof window !== 'undefined' && (window as any).csrf_token && (window as any).csrf_token !== 'None'
? (window as any).csrf_token
: null;
const headers: Record<string, string> = {
'Content-Type': 'application/json',
Accept: 'application/json',
};
if (csrfToken) {
headers['X-Frappe-CSRF-Token'] = csrfToken;
}
const cleanedFilters: Record<string, string> = {};
Object.entries(filters).forEach(([key, value]) => {
if (value) cleanedFilters[key] = value;
});
const response = await fetch('/api/method/frappe.desk.query_report.run', {
method: 'POST',
headers,
credentials: 'include',
body: JSON.stringify({
report_name: TECHNICIAN_WORKING_HOURS_REPORT,
filters: cleanedFilters,
ignore_prepared_report: 1,
}),
});
const payload = await response.json();
if (!response.ok) {
throw new Error(payload?.message || payload?.exc || 'Failed to load technician working hours');
}
const result = payload?.message?.result || payload?.message?.message?.result || [];
if (!Array.isArray(result)) return [];
return result.filter(row => row && !row.is_total_row);
};
export const fetchTechnicianWorkingHoursChart = async (
filters: TechnicianWorkingHoursFilters
): Promise<TechnicianWorkingHoursChartData | null> => {
const rows = await runTechnicianWorkingHoursReport(filters);
return buildTechnicianWorkingHoursChartData(rows);
};