From e5d6ff68b2c900b186418ecd778a8b1f949be696 Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Tue, 22 Sep 2026 12:06:13 +0100 Subject: [PATCH] M3 Integration fixes --- .../Controllers/ExecSummaryController.php | 23 +- .../M3/Http/Controllers/UserPickingStats4.php | 443 ++++++++---------- 2 files changed, 195 insertions(+), 271 deletions(-) diff --git a/Modules/M3/Http/Controllers/ExecSummaryController.php b/Modules/M3/Http/Controllers/ExecSummaryController.php index 66c0f906..5574606d 100755 --- a/Modules/M3/Http/Controllers/ExecSummaryController.php +++ b/Modules/M3/Http/Controllers/ExecSummaryController.php @@ -134,20 +134,21 @@ class ExecSummaryController extends Controller return null; } + $range = null; if ($period === 'custom') { - return [(clone $from)->modify('first day of this month')->format('Ymd'), (clone $from)->modify('last day of this month')->format('Ymd')]; + $range = [(clone $from)->modify('first day of this month')->format('Ymd'), (clone $from)->modify('last day of this month')->format('Ymd')]; + } else { + $to = self::monthDate($request->input('TOREPORTDATE')); + if ($to !== null) { + $range = match ($period) { + 'year', 'quarter' => [(clone $from)->modify('first day of January')->format('Ymd'), (clone $to)->modify('last day of December')->format('Ymd')], + 'monthly' => [(clone $from)->modify('first day of this month')->format('Ymd'), (clone $to)->modify('last day of this month')->format('Ymd')], + default => null, + }; + } } - $to = self::monthDate($request->input('TOREPORTDATE')); - if ($to === null) { - return null; - } - - return match ($period) { - 'year', 'quarter' => [(clone $from)->modify('first day of January')->format('Ymd'), (clone $to)->modify('last day of December')->format('Ymd')], - 'monthly' => [(clone $from)->modify('first day of this month')->format('Ymd'), (clone $to)->modify('last day of this month')->format('Ymd')], - default => null, - }; + return $range; } private static function monthDate(mixed $value): ?\DateTime diff --git a/Modules/M3/Http/Controllers/UserPickingStats4.php b/Modules/M3/Http/Controllers/UserPickingStats4.php index 15f037ce..dc4f3e04 100755 --- a/Modules/M3/Http/Controllers/UserPickingStats4.php +++ b/Modules/M3/Http/Controllers/UserPickingStats4.php @@ -11,287 +11,210 @@ class UserPickingStats4 extends Controller { public static function index(Request $request) { + $context = self::buildContext($request); + self::loadPickingData($context); + self::calculatePerformance($context); - // $start = microtime(true); - // dd($request); - // Filter by date - if (isset($request->dates)) { - $date = $request->dates; - $starting = $date; - } else { - $starting = date('Y-m-d', strtotime('-1 days')); - } + return $request->format === 'json' + ? self::jsonResponse($context) + : self::pageResponse($request, $context); + } - // #$min_work_hours_for_break = 4; - - // #$ignore_picks_that_last_longer_than_this_many_hours = 8; - $ignore_picks_that_last_longer_than_this_many_hours = implode(',', Config::get('arrays.ignore_picks_that_last_longer_than_this_many_hours')); - - // $user_array = Config::get('arrays.user_array'); - // 618 - // Get Logged User CO & WH - // Get Users from ICM428M - $co = ''; - $whs = ''; - $iCM428M = []; + private static function buildContext(Request $request): array + { + $starting = isset($request->dates) ? (string) $request->dates : date('Y-m-d', strtotime('-1 days')); $schema = Schema::connection(config('m3.connection', 'pgsql')); - if ($schema->hasTable('icm428m')) { - $profile = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m') - ->select('R428US', 'R428CO', 'R428WH') - ->where('R428US', '=', strtoupper(m3User())) - ->first(); - $co = $profile->R428CO ?? ''; - $whs = $profile->R428WH ?? ''; - - $iCM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m') - ->selectRaw("R428US as userprf, (trim(R428FIRST) || ' ' || trim(R428FAMILY)) as name, R428EMPCDE as empno") - ->where('R428STS', 'A')->where('R428EMPCDE', '!=', 0) - ->where('R428CO', $co)->where('R428WH', $whs)->get()->all(); + $users = self::users($schema); + $user = isset($request->user) ? (string) $request->user : ''; + $empno = 0; + foreach ($users as $candidate) { + if ($candidate['userprf'] === $user) { + $empno = $candidate['empno']; + break; + } } - $user_array = []; - // while($row = odbc_fetch_object($result)) { - $x = 0; - foreach ($iCM428M as $key => $row) { - $user_array[$x]['userprf'] = trim($row->USERPRF); - $user_array[$x]['name'] = trim($row->NAME); - $user_array[$x]['empno'] = $row->EMPNO; - $x++; - } - array_multisort(array_column($user_array, 'name'), SORT_ASC, $user_array); + return [ + 'starting' => $starting, + 'schema' => $schema, + 'users' => $users, + 'user' => $user, + 'empno' => $empno, + 'targets' => Config::get('arrays_custom.lph_per_code_array', []), + 'statistics' => [], + 'lbpfRows' => [], + 'lbpf' => 0, + 'monitor' => [], + 'totals' => ['value' => 0, 'weight' => 0, 'timediff' => 0, 'picked' => 0, 'entries' => 0], + 'performance' => [], + ]; + } - // fill array of Lines per Hour per Monitor - $lph_per_monitor_array = Config::get('arrays_custom.lph_per_code_array'); - if (isset($request->user)) { - $user = $request->user; - $empno = 0; - foreach ($user_array as $key => $val) { - if ($user_array[$key]['userprf'] == $user) { - $empno = $user_array[$key]['empno']; - } - } - } else { - $empno = 0; - $user = ''; + private static function users($schema): array + { + if (! $schema->hasTable('icm428m')) { + return []; } - // get data from LBPF for user and day - $payrollCodes = ''; - ksort($lph_per_monitor_array); - foreach ($lph_per_monitor_array as $key => $val) { - $payrollCodes .= $key . ','; + $connection = DB::connection(config('m3.connection', 'pgsql')); + $profile = $connection->table('icm428m')->select('R428CO', 'R428WH') + ->where('R428US', strtoupper(m3User()))->first(); + $rows = $connection->table('icm428m') + ->selectRaw("R428US as userprf, (trim(R428FIRST) || ' ' || trim(R428FAMILY)) as name, R428EMPCDE as empno") + ->where('R428STS', 'A')->where('R428EMPCDE', '!=', 0) + ->where('R428CO', $profile->R428CO ?? '')->where('R428WH', $profile->R428WH ?? '')->get(); + + $users = []; + foreach ($rows as $row) { + $users[] = ['userprf' => trim($row->USERPRF), 'name' => trim($row->NAME), 'empno' => $row->EMPNO]; } - $payrollCodes = substr($payrollCodes, 0, -1); + usort($users, static fn (array $a, array $b): int => $a['name'] <=> $b['name']); - $yr = substr($starting, 0, 4) - 2000; - $mth = substr($starting, 5, 2); - $day = substr($starting, 8, 2); - $sqlstmt = "select oppno, sum(cast (hrs as integer)+ (hrs-cast (hrs as integer))/0.6) as hrs from LBPF - where empno=$empno and lbyr=$yr and lbmo=$mth and lbdy=$day - and oppno in ($payrollCodes) - group by oppno"; + return $users; + } - $lbpf1 = $schema->hasTable('lbpf') - ? DB::connection(config('m3.connection', 'pgsql'))->select($sqlstmt) - : []; - - $lbpf = 0; - foreach ($lbpf1 as $key => $val) { - $lbpf += $val->HRS; + private static function loadPickingData(array &$context): void + { + $targets = $context['targets']; + ksort($targets); + $codes = implode(',', array_keys($targets)); + $date = $context['starting']; + $sql = sprintf( + 'select oppno, sum(cast (hrs as integer)+ (hrs-cast (hrs as integer))/0.6) as hrs from LBPF where empno=%d and lbyr=%d and lbmo=%d and lbdy=%d and oppno in (%s) group by oppno', + (int) $context['empno'], (int) substr($date, 0, 4) - 2000, (int) substr($date, 5, 2), (int) substr($date, 8, 2), $codes + ); + $context['lbpfRows'] = $context['schema']->hasTable('lbpf') + ? DB::connection(config('m3.connection', 'pgsql'))->select($sql) : []; + foreach ($context['lbpfRows'] as $row) { + $context['lbpf'] += $row->HRS; } - $sql = "select * from perfstat where dt='$starting' and a5pickcomu='$user' "; + if ((isset(request()->dates) || isset(request()->user)) && $context['schema']->hasTable('perfstat')) { + $context['statistics'] = DB::connection(config('m3.connection', 'pgsql'))->table('perfstat') + ->where('dt', $date)->where('a5pickcomu', $context['user'])->get()->all(); + } + } - if ((isset($request->dates) || isset($request->user)) && $schema->hasTable('perfstat')) { - $statistic4 = DB::connection(config('m3.connection', 'pgsql'))->select($sql); - } else { - $statistic4 = []; + private static function calculatePerformance(array &$context): void + { + self::initializeMonitors($context); + if ($context['statistics'] === []) { + return; } - // initialize arrays - $monitor_array = $totals_array = $perf_array = []; - $totals_array['value'] = 0; - $totals_array['weight'] = 0; - $totals_array['timediff'] = 0; - $totals_array['picked'] = 0; - $totals_array['entries'] = 0; - $diff = 0; - - foreach ($lbpf1 as $key => $val) { - $monitor_array[$val->OPPNO]['tot_hours'] = $val->HRS; - // Initialize these variables in case not existant in statistic4 - $monitor_array[$val->OPPNO]['header'] = 'Mon' . $val->OPPNO; - $monitor_array[$val->OPPNO]['timediff'] = 0; - $monitor_array[$val->OPPNO]['percent_per_monitor'] = 0; - $monitor_array[$val->OPPNO]['percent_per_activity'] = 0; - $monitor_array[$val->OPPNO]['lph_blended_per_activity'] = 0; - $monitor_array[$val->OPPNO]['picked'] = 0; - $monitor_array[$val->OPPNO]['weight'] = 0; - $monitor_array[$val->OPPNO]['lines'] = 0; - + self::accumulateStatistics($context); + $hours = $context['lbpf']; + $context['performance']['tot_hours'] = $hours; + $context['performance']['per_hour_actual'] = $hours != 0 ? $context['totals']['entries'] / $hours : 0; + self::calculateMonitorTargets($context); + $target = $context['monitor'][0]['lph_blended_per_activity'] ?? 0; + if ($target != 0) { + $context['performance']['performance_activity'] = $context['performance']['per_hour_actual'] / $target; } - if (! empty($statistic4)) { - // echo "in !empty
"; - // Initialize the Monitor array - foreach ($statistic4 as $key => $val) { - - // Initialize these variables in case not existant in LBPF - $monitor_array[$val->CODE]['header'] = 'Mon' . $statistic4[$key]->CODE; - $monitor_array[$val->CODE]['timediff'] = 0; - $monitor_array[$val->CODE]['percent_per_monitor'] = 0; - $monitor_array[$val->CODE]['percent_per_activity'] = 0; - $monitor_array[$val->CODE]['lph_blended_per_activity'] = 0; - $monitor_array[$val->CODE]['picked'] = 0; - $monitor_array[$val->CODE]['weight'] = 0; - $monitor_array[$val->CODE]['lines'] = 0; - } - - // add another element (0) in monitor_array to capture the total blended lph value expected - $monitor_array[0]['lph_blended_per_activity'] = 0; - $monitor_array[0]['header'] = '0'; - - // cycle thru stats again, and accumulate totals at global level (totals_array) and at monitor level (monitor_array) - foreach ($statistic4 as $key => $val) { - - $statistic4[$key]->WEIGHT = round($statistic4[$key]->WEIGHT); - $statistic4[$key]->A5VALUE = round($statistic4[$key]->A5VALUE); - $statistic4[$key]->A5PICKED = round($statistic4[$key]->A5PICKED); - - $statistic4[$key]->A5PICKCMTS = date('Y-m-d H:i', strtotime($statistic4[$key]->A5PICKCMTS)); - - // Calculate difference from previous time entry in seconds and save in TIMEDIFF key of STATISTIC4 array - // $time_now=strtotime($statistic4[$key]->A5PICKCMTSADJ); - $time_now = strtotime($statistic4[$key]->A5CMTSADJ); - if ($key > 0) { - $diff = round(abs($time_now - $time_prev)); - if ($diff > $ignore_picks_that_last_longer_than_this_many_hours * 60 * 60) { - $diff = -1; - } - } - - $statistic4[$key]->TIMEDIFF = $diff; - - if ($diff / 60 < 5) { - $statistic4[$key]->COLOUR = ''; - } - if ($diff / 60 >= 5 && $diff / 60 <= 10) { - $statistic4[$key]->COLOUR = 'yellow'; - } - if ($diff / 60 > 10 && $diff / 60 <= 15) { - $statistic4[$key]->COLOUR = '#88ace0'; - } - if ($diff / 60 > 15) { - $statistic4[$key]->COLOUR = 'red'; - } - - // save the time for previous entry - // $time_prev=strtotime($statistic4[$key]->A5PICKCMTSADJ); - $time_prev = strtotime($statistic4[$key]->A5CMTSADJ); - - // accumulate totals at monitor level - $monitor_array[$val->CODE]['timediff'] += $diff; - $monitor_array[$val->CODE]['picked'] += $statistic4[$key]->A5PICKED; - $monitor_array[$val->CODE]['weight'] += $statistic4[$key]->WEIGHT; - $monitor_array[$val->CODE]['lines']++; - - // calculate totals at global level - $totals_array['entries'] = count($statistic4); - $totals_array['weight'] += $statistic4[$key]->WEIGHT; - $totals_array['value'] += $statistic4[$key]->A5VALUE; - $totals_array['timediff'] += $diff; - $totals_array['picked'] += $statistic4[$key]->A5PICKED; - - } - // echo "after 2
"; print_r($statistic4);
-            // build array for Performance with Greg's time entry (at one point we had a [0] array as well)
-
-            if (! is_null($lbpf)) {
-
-                $perf_array['tot_hours'] = (isset($lbpf) ? $lbpf : 0);
-
-                if ($perf_array['tot_hours'] != 0) {
-                    $perf_array['per_hour_actual'] = $totals_array['entries'] / $perf_array['tot_hours'];
-                } else {
-                    $perf_array['per_hour_actual'] = 0;
-                }
-
-            }
-
-            // cycle through monitors array and compute percentage to allocate to each monitor (code)
-            ksort($monitor_array);
-            foreach ($monitor_array as $key => $val) {
-                if ($key != '0') {
-                    if (! isset($monitor_array[$key]['tot_hours'])) {
-                        $monitor_array[$key]['tot_hours'] = 0;
-                    }
-
-                    if ($totals_array['timediff'] != 0) {
-                        $monitor_array[$key]['percent_per_monitor'] = $monitor_array[$key]['timediff'] / $totals_array['timediff'];
-                    }
-
-                    if ($lbpf != 0) {
-                        $monitor_array[$key]['percent_per_activity'] = $monitor_array[$key]['tot_hours'] / $lbpf;
-                    }
-                    $monitor_array[$key]['lph_blended_per_activity'] = $monitor_array[$key]['percent_per_activity'] * (isset($lph_per_monitor_array[$key]) ? $lph_per_monitor_array[$key] : 0);
-                    $monitor_array[0]['lph_blended_per_activity'] += $monitor_array[$key]['lph_blended_per_activity'];
-
-                }
-            }
-
-            if ($monitor_array[0]['lph_blended_per_activity'] != 0) {
-                $perf_array['performance_activity'] = $perf_array['per_hour_actual'] / $monitor_array[0]['lph_blended_per_activity'];
-            }
+    }
 
+    private static function initializeMonitors(array &$context): void
+    {
+        foreach ($context['lbpfRows'] as $row) {
+            $context['monitor'][$row->OPPNO] = self::monitorDefaults('Mon' . $row->OPPNO, $row->HRS);
         }
-
-        // return encoded json if input field named format is json
-        if ($request->format == 'json') {
-
-            if (! array_key_exists('performance_activity', $perf_array)) {
-                $performance = 0;
-            } else {
-                $performance = number_format($perf_array['performance_activity'] * 100, 0);
-            }
-            if (! array_key_exists('0', $monitor_array)) {
-                $blendedTargetLPH = 0;
-            } else {
-                $blendedTargetLPH = number_format($monitor_array[0]['lph_blended_per_activity'], 1);
-            }
-            if (! array_key_exists('per_hour_actual', $perf_array)) {
-                $actualLPH = 0;
-            } else {
-                $actualLPH = number_format($perf_array['per_hour_actual'], 1);
-            }
-            if (! array_key_exists('tot_hours', $perf_array)) {
-                $grossHours = 0;
-            } else {
-                $grossHours = $perf_array['tot_hours'];
-            }
-
-            $json_data = [
-                'day' => $starting,
-                'performance' => $performance,
-                'blendedTargetLPH' => $blendedTargetLPH,
-                'actualLPH' => $actualLPH,
-                'grossHours' => $grossHours,
-            ];
-
-            return json_encode($json_data);
-
-        } else {
-            // display webpage of statistic version 4
-            $request->ports = Config::get('arrays.env_ports');
-            $no_data = 'N';
-            if (empty($statistic4) && $_SERVER['REQUEST_URI'] != '/UserPickingStats4') {
-                $no_data = 'Y';
-            }
-
-            return view(
-                'm3::liberty-theme.pages.UserPickingStats4',
-                compact('statistic4', 'starting', 'user', 'user_array', 'monitor_array', 'totals_array', 'perf_array', 'request', 'no_data'),
+        foreach ($context['statistics'] as $row) {
+            $context['monitor'][$row->CODE] = array_replace(
+                self::monitorDefaults('Mon' . $row->CODE, 0),
+                $context['monitor'][$row->CODE] ?? []
             );
-
         }
+        if ($context['statistics'] !== []) {
+            $context['monitor'][0] = self::monitorDefaults('0', 0);
+        }
+    }
 
+    private static function monitorDefaults(string $header, float|int $hours): array
+    {
+        return ['tot_hours' => $hours, 'header' => $header, 'timediff' => 0, 'percent_per_monitor' => 0,
+            'percent_per_activity' => 0, 'lph_blended_per_activity' => 0, 'picked' => 0, 'weight' => 0, 'lines' => 0];
+    }
+
+    private static function accumulateStatistics(array &$context): void
+    {
+        $previous = null;
+        $limit = (float) Config::get('arrays.ignore_picks_that_last_longer_than_this_many_hours', 8);
+        foreach ($context['statistics'] as $row) {
+            $row->WEIGHT = round($row->WEIGHT);
+            $row->A5VALUE = round($row->A5VALUE);
+            $row->A5PICKED = round($row->A5PICKED);
+            $row->A5PICKCMTS = date('Y-m-d H:i', strtotime($row->A5PICKCMTS));
+            $now = strtotime($row->A5CMTSADJ);
+            $diff = $previous === null ? 0 : round(abs($now - $previous));
+            $diff = $diff > $limit * 3600 ? -1 : $diff;
+            $row->TIMEDIFF = $diff;
+            $row->COLOUR = self::durationColour($diff);
+            $previous = $now;
+            self::addRowTotals($context, $row, $diff);
+        }
+    }
+
+    private static function durationColour(float|int $diff): string
+    {
+        $minutes = $diff / 60;
+
+        return match (true) {
+            $minutes < 5 => '',
+            $minutes <= 10 => 'yellow',
+            $minutes <= 15 => '#88ace0',
+            default => 'red',
+        };
+    }
+
+    private static function addRowTotals(array &$context, object $row, float|int $diff): void
+    {
+        $monitor = &$context['monitor'][$row->CODE];
+        $monitor['timediff'] += $diff;
+        $monitor['picked'] += $row->A5PICKED;
+        $monitor['weight'] += $row->WEIGHT;
+        $monitor['lines']++;
+        $context['totals']['entries']++;
+        $context['totals']['weight'] += $row->WEIGHT;
+        $context['totals']['value'] += $row->A5VALUE;
+        $context['totals']['timediff'] += $diff;
+        $context['totals']['picked'] += $row->A5PICKED;
+    }
+
+    private static function calculateMonitorTargets(array &$context): void
+    {
+        ksort($context['monitor']);
+        foreach ($context['monitor'] as $code => &$monitor) {
+            if ((string) $code === '0') {
+                continue;
+            }
+            $monitor['percent_per_monitor'] = $context['totals']['timediff'] != 0
+                ? $monitor['timediff'] / $context['totals']['timediff'] : 0;
+            $monitor['percent_per_activity'] = $context['lbpf'] != 0 ? $monitor['tot_hours'] / $context['lbpf'] : 0;
+            $monitor['lph_blended_per_activity'] = $monitor['percent_per_activity'] * ($context['targets'][$code] ?? 0);
+            $context['monitor'][0]['lph_blended_per_activity'] += $monitor['lph_blended_per_activity'];
+        }
+        unset($monitor);
+    }
+
+    private static function jsonResponse(array $context): string
+    {
+        return json_encode([
+            'day' => $context['starting'],
+            'performance' => number_format(($context['performance']['performance_activity'] ?? 0) * 100, 0),
+            'blendedTargetLPH' => number_format($context['monitor'][0]['lph_blended_per_activity'] ?? 0, 1),
+            'actualLPH' => number_format($context['performance']['per_hour_actual'] ?? 0, 1),
+            'grossHours' => $context['performance']['tot_hours'] ?? 0,
+        ]);
+    }
+
+    private static function pageResponse(Request $request, array $context)
+    {
+        $request->ports = Config::get('arrays.env_ports');
+        $no_data = $context['statistics'] === [] && ($_SERVER['REQUEST_URI'] ?? '') !== '/UserPickingStats4' ? 'Y' : 'N';
+        return view('m3::liberty-theme.pages.UserPickingStats4', [
+            'statistic4' => $context['statistics'], 'starting' => $context['starting'], 'user' => $context['user'],
+            'user_array' => $context['users'], 'monitor_array' => $context['monitor'], 'totals_array' => $context['totals'],
+            'perf_array' => $context['performance'], 'request' => $request, 'no_data' => $no_data,
+        ]);
     }
 }