From c38e830597d716fe949e28089a34b3276be591db Mon Sep 17 00:00:00 2001 From: Ravi Gulhane Date: Fri, 16 Oct 2026 09:23:48 +0530 Subject: [PATCH] Survey Forms Changes --- app/Exports/AdherencePobDriveExport.php | 756 ++++++++++++++++++ .../AdherencepobdrivereportController.php | 179 +++++ app/Http/Controllers/FormController.php | 143 +++- .../SynchrobreatheReportcardController.php | 161 +--- .../Controllers/ZenovaReportController.php | 10 +- .../adherencepobdrivereport/report.blade.php | 200 +++++ ...d159-7312-476d-8c92-0c7683015351.blade.php | 2 +- ...58a6-d509-4ac0-9b18-6d366a74965e.blade.php | 14 +- ...8862-93e7-43b5-bf4e-7a29b30e3830.blade.php | 39 +- .../partials/doctor-chemist-chemist.blade.php | 2 +- .../partials/doctor-chemist-doctor.blade.php | 6 +- ...2f9a-fe45-4b16-843a-224f07ed0d7b.blade.php | 47 +- .../layouts/shared/left-sidebar.blade.php | 8 + routes/web.php | 2 + 14 files changed, 1410 insertions(+), 159 deletions(-) create mode 100644 app/Exports/AdherencePobDriveExport.php create mode 100644 app/Http/Controllers/AdherencepobdrivereportController.php create mode 100644 resources/views/adherencepobdrivereport/report.blade.php diff --git a/app/Exports/AdherencePobDriveExport.php b/app/Exports/AdherencePobDriveExport.php new file mode 100644 index 00000000..4d89314c --- /dev/null +++ b/app/Exports/AdherencePobDriveExport.php @@ -0,0 +1,756 @@ + */ + private $abmRows; + + /** @var array */ + private $rbmRows; + + /** @var array */ + private $tmRows; + + /** @var array> */ + private $dayTemplate; + + private $month; + + public function __construct($data, string $month) + { + $this->abmRows = $this->listRows($data, 'abmReport'); + $this->rbmRows = $this->listRows($data, 'rbmReport'); + $this->tmRows = $this->listRows($data, 'tmReport'); + $this->dayTemplate = $this->dayTemplate(); + $this->month = $month; + } + + public function download(): StreamedResponse + { + $spreadsheet = $this->spreadsheet(); + $filename = $this->filename(); + + return response()->streamDownload(function () use ($spreadsheet) { + $writer = new Xlsx($spreadsheet); + $writer->save('php://output'); + $spreadsheet->disconnectWorksheets(); + }, $filename, [ + 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + ]); + } + + /** + * @return array> + */ + public function sections(): array + { + return [ + $this->abmSection(), + $this->regionSection(), + $this->territorySection(), + ]; + } + + public function filename(): string + { + $slug = trim((string) preg_replace('/[^A-Za-z0-9]+/', '-', $this->month), '-'); + + return 'Adherence-Pack-POB-Drive-' . ($slug !== '' ? $slug : 'report') . '.xlsx'; + } + + public function spreadsheet(): Spreadsheet + { + $spreadsheet = new Spreadsheet(); + $abm = $spreadsheet->getActiveSheet(); + $abm->setTitle('Details ABM wise'); + $this->writeAbmSheet($abm); + + $region = $spreadsheet->createSheet(); + $region->setTitle('Region Daily Winners'); + $this->writeRegionSheet($region); + + $territory = $spreadsheet->createSheet(); + $territory->setTitle('Territory Daily Winners'); + $this->writeTerritorySheet($territory); + + $spreadsheet->setActiveSheetIndex(0); + + return $spreadsheet; + } + + private function writeAbmSheet($sheet): void + { + $identity = [ + 'ZBM', + 'RBM', + 'ABM HQ', + 'HQ Div', + 'ABM Name', + 'Emp ID', + 'Reporting Manager (RBM)', + 'Email', + 'Approved Count per ABM', + 'Remarks', + ]; + $summary = [ + 'Working Days', + 'Daily Target (POBs = Approved Count)', + 'Month Target (POBs)', + 'Total TMs Qualified (≥₹15k POB)', + 'Achievement %', + 'Total Chemists Qualified (≥₹15k POB)', + 'Days Daily Target Met', + 'Target Status', + ]; + $summaryHelp = [ + 'No. of Total W', + 'Count of Active TMs', + 'No. of Total W x Count of Active TMs', + 'Sum of count of all Days', + 'Total TMs Qualified / Month Target (POBs)', + 'No. of Unique Chemists with ≥₹15k POB Value', + 'No. of Days on which Daily Target is achieved', + 'If Month Target achieved = Achieved, If Month Target not achieved = In Progress', + ]; + + $dayStart = count($identity) + 1; + $summaryStart = $dayStart + count($this->dayTemplate); + $lastCol = $summaryStart + count($summary) - 1; + + $sheet->setCellValue('A1', 'Adherence Pack POB Drive – ABM – ' . $this->month); + $sheet->mergeCells('A1:' . $this->col($lastCol) . '1'); + $sheet->setCellValue('A3', 'Legend: pink = vacant HQ; green day = working day; grey day = holiday.'); + $sheet->mergeCells('A3:' . $this->col($lastCol) . '3'); + $sheet->setCellValue('A5', 'Data of no. of chemists with Adherence Pack POB ≥ ₹15,000'); + $sheet->setCellValue('J5', 'Day type (W / H) →'); + foreach ($this->dayTemplate as $index => $day) { + $sheet->setCellValue($this->col($dayStart + $index) . '5', $day['type'] === 'H' ? 'H' : 'W'); + } + + $help = [ + 'Zone', + 'Region', + 'Area', + 'as it is', + 'ABM Emp Name', + 'ABM Emp Code', + 'RBM Name', + 'ABM Email ID', + 'Count of Active TMs', + 'Blank Remarks Column', + ]; + $this->writeValues($sheet, 6, $help); + if (count($this->dayTemplate) > 0) { + $sheet->setCellValue($this->col($dayStart) . '6', 'No. of TMs with at least 1 Chemist with ≥₹15k POB Value'); + if (count($this->dayTemplate) > 1) { + $sheet->mergeCells($this->col($dayStart) . '6:' . $this->col($summaryStart - 1) . '6'); + } + } + $this->writeValues($sheet, 6, $summaryHelp, $summaryStart); + + $headers = $identity; + foreach ($this->dayTemplate as $day) { + $headers[] = $this->dayHeader($day['label']); + } + $headers = array_merge($headers, $summary); + $this->writeValues($sheet, 7, $headers); + $this->styleHeaderRow($sheet, 7, $lastCol); + $this->paintDayTypes($sheet, 5, $dayStart, false); + $this->paintDayHeaders($sheet, 7, $dayStart, false); + + $rowNumber = 8; + foreach ($this->abmRows as $row) { + $values = []; + foreach ($identity as $key) { + $values[] = $this->field($row, $key); + } + $dayMap = $this->dayMap($this->field($row, 'days')); + foreach ($this->dayTemplate as $day) { + $values[] = $this->dayValue($dayMap[$day['date']] ?? $day, 'value'); + } + foreach ($summary as $key) { + $values[] = $this->field($row, $key); + } + $this->writeValues($sheet, $rowNumber, $values); + $this->paintDataDays($sheet, $rowNumber, $dayStart, false); + if ($this->isVacantName($this->field($row, 'ABM HQ')) || $this->isVacantName($this->field($row, 'ABM Name'))) { + $this->paintRow($sheet, $rowNumber, $lastCol, self::PINK); + } + $rowNumber++; + } + + $this->finishSheet($sheet, 7, max(7, $rowNumber - 1), $lastCol, $dayStart); + $sheet->freezePane('A8'); + } + + private function writeRegionSheet($sheet): void + { + $columns = $this->regionColumns(); + $lastCol = 3 + count($columns); + $sheet->setCellValue('A1', 'Region-wise Daily Winner – Highest no. of chemists with Adherence Pack POB ≥ ₹15,000 – ' . $this->month); + $sheet->mergeCells('A1:' . $this->col($lastCol) . '1'); + + $typeRow = ['', '', '']; + $helpRow = ['', '', '']; + $headerRow = ['Region (RBM)', 'RBM Name', 'RBM Employee Code']; + $pairHelp = 0; + foreach ($columns as $column) { + if ($column['kind'] === 'total-tm' || $column['kind'] === 'total-chemist') { + $typeRow[] = 'Sum of count of all Days'; + $helpRow[] = $column['kind'] === 'total-tm' + ? 'No. of TMs with at least 1 Chemist with ≥₹15k POB Value' + : 'No. of Unique Chemists with ≥₹15k POB Value'; + $headerRow[] = $column['header']; + continue; + } + $typeRow[] = $column['type'] === 'H' ? 'H' : 'W'; + if ($pairHelp === 0 && $column['kind'] === 'tm') { + $helpRow[] = 'No. of TMs with at least 1 Chemist with ≥₹15k POB Value'; + $pairHelp = 1; + } elseif ($pairHelp === 1 && $column['kind'] === 'chemist') { + $helpRow[] = 'No. of Unique Chemists with ≥₹15k POB Value'; + $pairHelp = 2; + } else { + $helpRow[] = ''; + } + $headerRow[] = $column['header']; + } + + $this->writeValues($sheet, 2, $typeRow); + $this->writeValues($sheet, 3, $helpRow); + $this->writeValues($sheet, 4, $headerRow); + if (count($columns) >= 2 && $columns[count($columns) - 1]['kind'] === 'total-chemist') { + $sheet->mergeCells($this->col($lastCol - 1) . '2:' . $this->col($lastCol) . '2'); + } + $this->styleHeaderRow($sheet, 4, $lastCol); + $this->paintRegionDayMarks($sheet, $columns); + + $rowNumber = 5; + foreach ($this->rbmRows as $row) { + $dayMap = $this->dayMap($this->field($row, 'days')); + $values = [ + $this->field($row, 'Region (RBM)'), + $this->field($row, 'RBM Name'), + $this->field($row, 'RBM Employee Code'), + ]; + foreach ($columns as $column) { + if ($column['kind'] === 'total-tm') { + $values[] = $this->field($row, 'Total TMs Qualified (≥₹15k POB)'); + continue; + } + if ($column['kind'] === 'total-chemist') { + $values[] = $this->field($row, 'Total Chemists Qualified (≥₹15k POB)'); + continue; + } + $day = $dayMap[$column['date']] ?? $column['day']; + $values[] = $column['kind'] === 'holiday' + ? 'Holiday' + : $this->dayValue($day, $column['kind'] === 'chemist' ? 'chemistsQualified' : 'value'); + } + $this->writeValues($sheet, $rowNumber, $values); + $this->paintRegionDataDays($sheet, $rowNumber, $columns); + if ($this->isVacantName($values[0]) || $this->isVacantName($values[1])) { + $this->paintRow($sheet, $rowNumber, $lastCol, self::PINK); + } + $rowNumber++; + } + + $this->finishSheet($sheet, 4, max(4, $rowNumber - 1), $lastCol, 4); + $sheet->freezePane('A5'); + } + + private function writeTerritorySheet($sheet): void + { + $dayStart = 4; + $totalCol = $dayStart + count($this->dayTemplate); + $sheet->setCellValue('A1', 'Territory-wise Daily Winner – Highest no. of chemists with Adherence Pack POB ≥ ₹15,000 – ' . $this->month); + $sheet->mergeCells('A1:' . $this->col($totalCol) . '1'); + + $typeRow = ['', '', '']; + foreach ($this->dayTemplate as $day) { + $typeRow[] = $day['type'] === 'H' ? 'H' : 'W'; + } + $this->writeValues($sheet, 3, $typeRow); + if (count($this->dayTemplate) > 0) { + $sheet->setCellValue('D4', 'No. of Unique Chemists with ≥₹15k POB Value'); + } + $sheet->setCellValue($this->col($totalCol) . '4', 'Sum of all Days of No. of Unique Chemists with ≥₹15k POB Value'); + + $headers = ['Territory', 'TM Name', 'TM Employee Code']; + foreach ($this->dayTemplate as $day) { + $headers[] = $this->dayHeader($day['label']); + } + $headers[] = 'Total Chemists Qualified (≥₹15k POB)'; + $this->writeValues($sheet, 5, $headers); + $this->styleHeaderRow($sheet, 5, $totalCol); + $this->paintDayTypes($sheet, 3, $dayStart, true); + $this->paintDayHeaders($sheet, 5, $dayStart, true); + + $rowNumber = 6; + foreach ($this->tmRows as $row) { + $dayMap = $this->dayMap($this->field($row, 'days')); + $values = [ + $this->field($row, 'Territory'), + $this->field($row, 'TM Name'), + $this->field($row, 'TM Employee Code'), + ]; + foreach ($this->dayTemplate as $day) { + $values[] = $this->dayValue($dayMap[$day['date']] ?? $day, 'value'); + } + $values[] = $this->field($row, 'Total Chemists Qualified (≥₹15k POB)'); + $this->writeValues($sheet, $rowNumber, $values); + $this->paintDataDays($sheet, $rowNumber, $dayStart, true); + if ($this->isVacantName($values[0]) || $this->isVacantName($values[1])) { + $this->paintRow($sheet, $rowNumber, $totalCol, self::PINK); + } + $rowNumber++; + } + + $this->finishSheet($sheet, 5, max(5, $rowNumber - 1), $totalCol, $dayStart); + $sheet->freezePane('A6'); + } + + /** + * @return array + */ + private function abmSection(): array + { + $identity = [ + 'ZBM', + 'RBM', + 'ABM HQ', + 'HQ Div', + 'ABM Name', + 'Emp ID', + 'Reporting Manager (RBM)', + 'Email', + 'Approved Count per ABM', + 'Remarks', + ]; + $summary = [ + 'Working Days', + 'Daily Target (POBs = Approved Count)', + 'Month Target (POBs)', + 'Total TMs Qualified (≥₹15k POB)', + 'Achievement %', + 'Total Chemists Qualified (≥₹15k POB)', + 'Days Daily Target Met', + 'Target Status', + ]; + $dayTypes = array_fill(0, count($identity), ''); + $headers = $identity; + foreach ($this->dayTemplate as $day) { + $dayTypes[] = $day['type'] === 'H' ? 'H' : 'W'; + $headers[] = $this->dayHeader($day['label']); + } + $dayTypes = array_merge($dayTypes, array_fill(0, count($summary), '')); + $headers = array_merge($headers, $summary); + + $rows = []; + foreach ($this->abmRows as $row) { + $cells = []; + foreach ($identity as $key) { + $cells[] = $this->field($row, $key); + } + $dayMap = $this->dayMap($this->field($row, 'days')); + foreach ($this->dayTemplate as $day) { + $cells[] = $this->dayValue($dayMap[$day['date']] ?? $day, 'value'); + } + foreach ($summary as $key) { + $cells[] = $this->field($row, $key); + } + $rows[] = [ + 'cells' => $cells, + 'vacant' => $this->isVacantName($this->field($row, 'ABM HQ')) || $this->isVacantName($this->field($row, 'ABM Name')), + ]; + } + + return [ + 'id' => 'abm', + 'title' => 'Details ABM wise – ' . $this->month, + 'dayTypes' => $dayTypes, + 'headers' => $headers, + 'rows' => $rows, + ]; + } + + /** + * @return array + */ + private function regionSection(): array + { + $columns = $this->regionColumns(); + $dayTypes = ['', '', '']; + $headers = ['Region (RBM)', 'RBM Name', 'RBM Employee Code']; + foreach ($columns as $column) { + $dayTypes[] = $column['type'] === 'H' ? 'H' : ($column['type'] === 'W' ? 'W' : ''); + $headers[] = $column['header']; + } + + $rows = []; + foreach ($this->rbmRows as $row) { + $dayMap = $this->dayMap($this->field($row, 'days')); + $cells = [ + $this->field($row, 'Region (RBM)'), + $this->field($row, 'RBM Name'), + $this->field($row, 'RBM Employee Code'), + ]; + foreach ($columns as $column) { + if ($column['kind'] === 'total-tm') { + $cells[] = $this->field($row, 'Total TMs Qualified (≥₹15k POB)'); + continue; + } + if ($column['kind'] === 'total-chemist') { + $cells[] = $this->field($row, 'Total Chemists Qualified (≥₹15k POB)'); + continue; + } + $day = $dayMap[$column['date']] ?? $column['day']; + $cells[] = $column['kind'] === 'holiday' + ? 'Holiday' + : $this->dayValue($day, $column['kind'] === 'chemist' ? 'chemistsQualified' : 'value'); + } + $rows[] = [ + 'cells' => $cells, + 'vacant' => $this->isVacantName($cells[0]) || $this->isVacantName($cells[1]), + ]; + } + + return [ + 'id' => 'region', + 'title' => 'Region Daily Winners – ' . $this->month, + 'dayTypes' => $dayTypes, + 'headers' => $headers, + 'rows' => $rows, + ]; + } + + /** + * @return array + */ + private function territorySection(): array + { + $dayTypes = ['', '', '']; + $headers = ['Territory', 'TM Name', 'TM Employee Code']; + foreach ($this->dayTemplate as $day) { + $dayTypes[] = $day['type'] === 'H' ? 'H' : 'W'; + $headers[] = $this->dayHeader($day['label']); + } + $dayTypes[] = ''; + $headers[] = 'Total Chemists Qualified (≥₹15k POB)'; + + $rows = []; + foreach ($this->tmRows as $row) { + $dayMap = $this->dayMap($this->field($row, 'days')); + $cells = [ + $this->field($row, 'Territory'), + $this->field($row, 'TM Name'), + $this->field($row, 'TM Employee Code'), + ]; + foreach ($this->dayTemplate as $day) { + $cells[] = $this->dayValue($dayMap[$day['date']] ?? $day, 'value'); + } + $cells[] = $this->field($row, 'Total Chemists Qualified (≥₹15k POB)'); + $rows[] = [ + 'cells' => $cells, + 'vacant' => $this->isVacantName($cells[0]) || $this->isVacantName($cells[1]), + ]; + } + + return [ + 'id' => 'territory', + 'title' => 'Territory Daily Winners – ' . $this->month, + 'dayTypes' => $dayTypes, + 'headers' => $headers, + 'rows' => $rows, + ]; + } + + /** + * @return array> + */ + private function regionColumns(): array + { + $columns = []; + foreach ($this->dayTemplate as $day) { + $header = $this->dayHeader($day['label']); + if ($day['type'] === 'H') { + $columns[] = [ + 'kind' => 'holiday', + 'type' => 'H', + 'date' => $day['date'], + 'header' => $header, + 'day' => $day, + ]; + continue; + } + $columns[] = [ + 'kind' => 'tm', + 'type' => 'W', + 'date' => $day['date'], + 'header' => $header, + 'day' => $day, + ]; + $columns[] = [ + 'kind' => 'chemist', + 'type' => 'W', + 'date' => $day['date'], + 'header' => $header, + 'day' => $day, + ]; + } + $columns[] = ['kind' => 'total-tm', 'type' => '', 'header' => 'Total TMs Qualified (≥₹15k POB)']; + $columns[] = ['kind' => 'total-chemist', 'type' => '', 'header' => 'Total Chemists Qualified (≥₹15k POB)']; + + return $columns; + } + + /** + * @return array> + */ + private function dayTemplate(): array + { + foreach ([$this->abmRows, $this->rbmRows, $this->tmRows] as $rows) { + if ($rows === []) { + continue; + } + $days = $this->orderedDays($this->field($rows[0], 'days')); + if ($days !== []) { + return $days; + } + } + + return []; + } + + /** + * @param mixed $days + * @return array> + */ + private function orderedDays($days): array + { + if (is_object($days)) { + $days = (array) $days; + } + if (!is_array($days)) { + return []; + } + + $list = []; + foreach ($days as $label => $day) { + if (is_object($day)) { + $day = (array) $day; + } + if (!is_array($day)) { + continue; + } + $list[] = [ + 'label' => (string) $label, + 'date' => (string) ($day['date'] ?? ''), + 'type' => (string) ($day['type'] ?? 'W'), + 'value' => $day['value'] ?? 0, + 'chemistsQualified' => $day['chemistsQualified'] ?? 0, + ]; + } + usort($list, function ($a, $b) { + return strcmp($a['date'], $b['date']); + }); + + return $list; + } + + /** + * @param mixed $days + * @return array> + */ + private function dayMap($days): array + { + $map = []; + foreach ($this->orderedDays($days) as $day) { + if ($day['date'] !== '') { + $map[$day['date']] = $day; + } + } + + return $map; + } + + /** + * @param array $day + * @return mixed + */ + private function dayValue(array $day, string $field) + { + if (($day['type'] ?? '') === 'H' || ($day[$field] ?? null) === 'Holiday') { + return 'Holiday'; + } + $value = $day[$field] ?? 0; + + return is_numeric($value) ? 0 + $value : $value; + } + + private function dayHeader(string $label): string + { + $parts = preg_split('/\s+/', trim($label), 2); + if (is_array($parts) && count($parts) === 2) { + return $parts[0] . "\n" . $parts[1]; + } + + return $label; + } + + /** + * @param mixed $data + * @return array + */ + private function listRows($data, string $key): array + { + $rows = $this->field($data, $key); + if (is_object($rows)) { + $rows = (array) $rows; + } + + return is_array($rows) ? array_values($rows) : []; + } + + /** + * @param mixed $row + * @return mixed + */ + private function field($row, string $key) + { + if (is_array($row)) { + return $row[$key] ?? ''; + } + if (is_object($row) && isset($row->{$key})) { + return $row->{$key}; + } + + return ''; + } + + private function isVacantName($name): bool + { + return stripos(trim((string) $name), 'vacant') !== false; + } + + /** + * @param array $values + */ + private function writeValues($sheet, int $row, array $values, int $startColumn = 1): void + { + foreach ($values as $offset => $value) { + $sheet->setCellValue($this->col($startColumn + $offset) . $row, $value); + } + } + + private function paintDayTypes($sheet, int $row, int $startColumn, bool $singleColumn): void + { + foreach ($this->dayTemplate as $index => $day) { + $this->paintCell($sheet, $this->col($startColumn + $index) . $row, $day['type'] === 'H' ? self::GREY : self::GREEN); + } + if (!$singleColumn) { + return; + } + } + + private function paintDayHeaders($sheet, int $row, int $startColumn, bool $singleColumn): void + { + foreach ($this->dayTemplate as $index => $day) { + $this->paintCell($sheet, $this->col($startColumn + $index) . $row, $day['type'] === 'H' ? self::GREY : self::GREEN); + } + if ($singleColumn) { + return; + } + } + + private function paintDataDays($sheet, int $row, int $startColumn, bool $singleColumn): void + { + foreach ($this->dayTemplate as $index => $day) { + if ($day['type'] === 'H') { + $this->paintCell($sheet, $this->col($startColumn + $index) . $row, self::GREY); + } + } + if ($singleColumn) { + return; + } + } + + /** + * @param array> $columns + */ + private function paintRegionDayMarks($sheet, array $columns): void + { + foreach ($columns as $index => $column) { + if ($column['kind'] === 'total-tm' || $column['kind'] === 'total-chemist') { + continue; + } + $cell = $this->col(4 + $index); + $color = $column['type'] === 'H' ? self::GREY : self::GREEN; + $this->paintCell($sheet, $cell . '2', $color); + $this->paintCell($sheet, $cell . '4', $color); + } + } + + /** + * @param array> $columns + */ + private function paintRegionDataDays($sheet, int $row, array $columns): void + { + foreach ($columns as $index => $column) { + if (($column['type'] ?? '') === 'H') { + $this->paintCell($sheet, $this->col(4 + $index) . $row, self::GREY); + } + } + } + + private function paintRow($sheet, int $row, int $lastColumn, string $color): void + { + $sheet->getStyle('A' . $row . ':' . $this->col($lastColumn) . $row)->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setARGB($color); + } + + private function paintCell($sheet, string $cell, string $color): void + { + $sheet->getStyle($cell)->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setARGB($color); + } + + private function styleHeaderRow($sheet, int $row, int $lastColumn): void + { + $range = 'A' . $row . ':' . $this->col($lastColumn) . $row; + $sheet->getStyle($range)->getFont()->setBold(true); + $sheet->getStyle($range)->getAlignment()->setWrapText(true)->setVertical(Alignment::VERTICAL_CENTER)->setHorizontal(Alignment::HORIZONTAL_CENTER); + $sheet->getRowDimension($row)->setRowHeight(32); + } + + private function finishSheet($sheet, int $headerRow, int $lastRow, int $lastColumn, int $dayStart): void + { + $range = 'A1:' . $this->col($lastColumn) . $lastRow; + $sheet->getStyle($range)->getAlignment()->setVertical(Alignment::VERTICAL_CENTER); + $sheet->getStyle('A' . $headerRow . ':' . $this->col($lastColumn) . $lastRow)->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN); + $sheet->getStyle('A1')->getFont()->setBold(true)->setSize(14); + for ($column = 1; $column <= $lastColumn; $column++) { + $width = $column < $dayStart ? 22 : 14; + $sheet->getColumnDimension($this->col($column))->setWidth($width); + } + } + + private function col(int $index): string + { + return Coordinate::stringFromColumnIndex($index); + } +} diff --git a/app/Http/Controllers/AdherencepobdrivereportController.php b/app/Http/Controllers/AdherencepobdrivereportController.php new file mode 100644 index 00000000..8328fbc4 --- /dev/null +++ b/app/Http/Controllers/AdherencepobdrivereportController.php @@ -0,0 +1,179 @@ +_service = 'reports'; + $this->_module = 'team2_reports'; + $this->_viewname = 'adherencepobdrivereport'; + $this->_title = 'Adherence Pack POB Drive'; + $this->_base_url = '/adherencepobdrivereport'; + } + + public static function canDownload($user): bool + { + if (!$user || ($user->domain ?? '') !== 'team2') { + return false; + } + + $permissions = json_decode($user->permissions ?? '[]'); + if (is_array($permissions) && in_array('all_data_access', $permissions, true)) { + return true; + } + + $designation = strtoupper(trim(self::tokenDesignation($user->access_token ?? ''))); + + return $designation === 'HO' || $designation === 'ADMIN'; + } + + public function list(Request $request) + { + return redirect($this->_base_url . '/report'); + } + + public function report(Request $request) + { + $this->assertCanDownload(); + $months = $this->monthChoices(); + $selected = trim((string) $request->query('month', '')); + if (!in_array($selected, $months, true)) { + $selected = $this->currentMonth(); + } + + $reportError = session('reportError'); + $sections = []; + try { + $data = $this->fetchReport($selected); + $export = new AdherencePobDriveExport($data, $selected); + if (!method_exists($export, 'sections')) { + throw new \RuntimeException('The report view is out of date. Deploy app/Exports/AdherencePobDriveExport.php and reload PHP.'); + } + $sections = $export->sections(); + } catch (\Throwable $e) { + $reportError = $e->getMessage(); + } + + return $this->view('report')->with([ + 'title' => $this->_title, + 'base_url' => $this->_base_url, + 'months' => $months, + 'selectedMonth' => $selected, + 'reportError' => $reportError, + 'sections' => $sections, + ]); + } + + public function export(Request $request) + { + $this->assertCanDownload(); + $months = $this->monthChoices(); + $month = trim((string) $request->query('month', '')); + if (!in_array($month, $months, true)) { + $month = $this->currentMonth(); + } + + try { + $data = $this->fetchReport($month); + } catch (\RuntimeException $e) { + return $this->backToReport($month, $e->getMessage()); + } + + return (new AdherencePobDriveExport($data, $month))->download(); + } + + /** + * @return object + */ + private function fetchReport(string $month) + { + $token = Auth::user()->access_token ?? ''; + try { + $response = LambdaRequest::getrequest( + $this->_service, + $this->_module . '/adherence_report', + ['month' => $month], + $token, + 120 + ); + } catch (\Throwable $e) { + throw new \RuntimeException('Failed to load the adherence report.'); + } + + if (!is_object($response) || strtolower((string) ($response->status ?? '')) !== 'success' || !is_object($response->data ?? null)) { + $message = is_object($response) ? trim((string) ($response->message ?? '')) : ''; + throw new \RuntimeException($message !== '' ? $message : 'Failed to load the adherence report.'); + } + + return $response->data; + } + + /** + * @return array + */ + private function monthChoices(): array + { + $cursor = new \DateTimeImmutable(self::REPORT_START); + $end = new \DateTimeImmutable('first day of this month'); + $months = []; + while ($cursor <= $end) { + $months[] = $cursor->format('F Y'); + $cursor = $cursor->modify('+1 month'); + } + if ($months === []) { + $months[] = $this->currentMonth(); + } + + return $months; + } + + private function currentMonth(): string + { + return date('F Y'); + } + + private function assertCanDownload(): void + { + if (!self::canDownload(Auth::user())) { + abort(403, 'You do not have access to this report.'); + } + } + + private function backToReport(string $month, string $message) + { + return redirect($this->_base_url . '/report?month=' . urlencode($month))->with('reportError', $message); + } + + private static function tokenDesignation(string $token): string + { + $token = trim($token); + if (stripos($token, 'Bearer ') === 0) { + $token = trim(substr($token, 7)); + } + $parts = explode('.', $token); + if (count($parts) < 2) { + return ''; + } + $segment = strtr($parts[1], '-_', '+/'); + $pad = strlen($segment) % 4; + if ($pad > 0) { + $segment .= str_repeat('=', 4 - $pad); + } + $payload = json_decode(base64_decode($segment)); + $user = is_object($payload) ? ($payload->user ?? null) : null; + if (!is_object($user)) { + return ''; + } + + return (string) ($user->designation ?? ''); + } +} diff --git a/app/Http/Controllers/FormController.php b/app/Http/Controllers/FormController.php index 68c6e802..e28553b3 100644 --- a/app/Http/Controllers/FormController.php +++ b/app/Http/Controllers/FormController.php @@ -123,16 +123,9 @@ class FormController extends Controller } $supplyYes = ($post['supply_status'] ?? '') === 'Yes'; if (!$supplyYes) { - if (!$isExisting) { - $post['submission_status'] = 'Order Taken'; - } else { - unset($post['submission_status']); - } + $post['submission_status'] = 'Order Taken'; return $post; } - if (!$hasInvoice) { - return ['__error' => 'Upload an invoice as a JPG, JPEG, PNG, or PDF file.']; - } $actualUnits = 0; $actualValue = 0; foreach ($prices as $code => $price) { @@ -148,7 +141,7 @@ class FormController extends Controller $post['total_actual_pob_units'] = (string) $actualUnits; $post['total_actual_pob_value'] = (string) $actualValue; $post['supply_status'] = 'Yes'; - $post['submission_status'] = 'Invoice Uploaded'; + $post['submission_status'] = $hasInvoice ? 'Invoice Uploaded' : 'Submitted'; return $post; } @@ -5879,6 +5872,18 @@ class FormController extends Controller } try { $post = $this->processformdata($formdata->form_id, $post, $formdata->user_id, $formdata->partition); + if (!empty($post['__error'])) { + $message = $post['__error']; + unset($post['__error']); + if ($inceptAllocation && $inceptUsageId) { + $inceptAllocation->release($inceptUsageId); + } + return response()->json(['status' => 'failure', 'message' => $message]); + } + $replaceSubmissionId = $post['__replace_submission_id'] ?? ''; + $deleteReason = $post['__delete_reason'] ?? ''; + $reasonCode = $post['__reason_code'] ?? ''; + unset($post['__replace_submission_id'], $post['__delete_reason'], $post['__reason_code'], $post['reason_field_code']); Log::debug(print_r($post, true)); $finfo = LambdaRequest::postrequest('form', 'formentity/submitform', ['formdata' => $post, 'entity_id' => $formdata->form_id, 'user_id' => $formdata->user_id, 'partition' => $formdata->partition ]); } catch (\Throwable $e) { @@ -5892,6 +5897,19 @@ class FormController extends Controller Log::debug(print_r($finfo, true)); if($finfo->status == 'success') { + if ($replaceSubmissionId !== '') { + $marked = $this->markMotherForumSubmissionDeleted( + $formdata->partition, + $formdata->user_id, + $formdata->form_id, + $replaceSubmissionId, + $reasonCode, + $deleteReason + ); + if (!$marked) { + return response()->json(['status' => 'failure', 'message' => 'The new entry was saved, but the previous entry could not be marked Deleted.']); + } + } if ($inceptAllocation && $inceptUsageId) { $submissionId = $finfo->data->submission->id ?? ''; $inceptAllocation->attachSubmission($inceptUsageId, $submissionId); @@ -8009,8 +8027,115 @@ class FormController extends Controller return response()->json($allapprovers->data->approvals); } + protected function markMotherForumSubmissionDeleted($partition, $userId, $formId, $submissionId, $reasonCode, $reason): bool + { + $loaded = LambdaRequest::postrequest('form', 'formentity/getformdefination', [ + 'form_id' => $formId, + 'user_id' => $userId, + 'partition' => $partition, + 'id' => $submissionId, + ]); + $previous = json_decode(json_encode($loaded->data->formdata ?? new \stdClass()), true); + if (!is_array($previous) || $previous === []) { + Log::error('Mother Forum previous submission was not loaded: '.$submissionId); + return false; + } + unset($previous['title']); + $previous['id'] = $submissionId; + $previous['form_id'] = $formId; + $previous['record_status'] = 'Deleted'; + if ($reasonCode !== '') { + $previous[$reasonCode] = $reason; + } + $saved = LambdaRequest::postrequest('form', 'formentity/submitform', [ + 'formdata' => $previous, + 'entity_id' => $formId, + 'user_id' => $userId, + 'partition' => $partition, + ]); + + return ($saved->status ?? '') === 'success'; + } + protected function processformdata($form, $post, $user_id = 0, $partition = '' ) { switch($form) { + case '1791539482-b06c2f9a-fe45-4b16-843a-224f07ed0d7b': + $reasonCode = trim((string) ($post['reason_field_code'] ?? '')); + unset($post['reason_field_code']); + $post['record_status'] = 'Active'; + if (empty($post['id'])) { + $post['base_submission_id'] = ''; + break; + } + $previousId = $post['id']; + $loaded = LambdaRequest::postrequest('form', 'formentity/getformdefination', [ + 'form_id' => $form, + 'user_id' => $user_id, + 'partition' => $partition, + 'id' => $previousId, + ]); + $previous = json_decode(json_encode($loaded->data->formdata ?? new \stdClass()), true); + if (!is_array($previous) || $previous === []) { + $post['__error'] = 'Unable to load the entry being edited.'; + break; + } + $previousStatus = trim((string) ($previous['record_status'] ?? 'Active')); + if (strcasecmp($previousStatus, 'Deleted') === 0) { + $post['__error'] = 'This entry has been replaced and can no longer be edited.'; + break; + } + $reason = trim((string) ($reasonCode !== '' ? ($post[$reasonCode] ?? '') : '')); + if ($reason === '') { + $post['__error'] = 'Enter the reason for deletion.'; + break; + } + $baseId = trim((string) ($previous['base_submission_id'] ?? '')); + if ($baseId === '') { + $baseId = $previousId; + } + unset($post['id']); + $post['record_status'] = 'Active'; + $post['base_submission_id'] = $baseId; + if ($reasonCode !== '') { + $post[$reasonCode] = ''; + } + $post['__replace_submission_id'] = $previousId; + $post['__delete_reason'] = $reason; + $post['__reason_code'] = $reasonCode; + break; + case '1756965702-68add159-7312-476d-8c92-0c7683015351': + if (($post['chemist_list'] ?? '') !== 'Others') { + break; + } + $chemistName = trim((string) ($post['cheemist_name'] ?? '')); + if ($chemistName === '') { + $post['__error'] = 'Enter the chemist name.'; + break; + } + list($application, $domain) = explode("-", $partition); + $token = DB::table('users')->select("access_token")->where("domain", $domain)->where("permissions", 'like', '%contact_create%')->orderBy('updated_at', 'desc')->skip(0)->take(1)->get(); + if ($token->isEmpty() || empty($token[0]->access_token)) { + $post['__error'] = 'Unable to create the chemist. Please try again.'; + break; + } + $contactdata = [ + 'domain' => $domain, + 'application' => $application, + 'user_id' => $user_id, + 'name' => $chemistName, + 'type' => 'chemist', + 'msl_code' => strtotime("now").'-'.rand(1000,9999), + 'report_smartian_pob_other_chemists' => 'Yes', + ]; + $contact = LambdaRequest::postrequest('contact', 'contact/create', $contactdata, $token[0]->access_token); + $chemistId = $contact->data->item->id ?? null; + if (empty($chemistId)) { + Log::error('Other chemist create failed: '.print_r($contact, true)); + $post['__error'] = 'Unable to create the chemist. Please try again.'; + break; + } + $post['chemist_list'] = $chemistId; + break; case '1711609582-e93dba24-9c98-43e7-b97a-b22d64d9b9fc': case '1711609582-e93dba24-9c98-43e7-neb2-b22d64d9b9fc': list($application, $domain) = explode("-", $partition); diff --git a/app/Http/Controllers/SynchrobreatheReportcardController.php b/app/Http/Controllers/SynchrobreatheReportcardController.php index b1b0e4df..1de4e361 100644 --- a/app/Http/Controllers/SynchrobreatheReportcardController.php +++ b/app/Http/Controllers/SynchrobreatheReportcardController.php @@ -170,13 +170,15 @@ class SynchrobreatheReportcardController extends Controller protected function loadTerritories(string $token, ?string &$error): array { $error = null; - $params = []; - if ($this->hasAllDataAccess($this->tokenUser)) { - $params['positions'] = 1; - } try { - $response = LambdaRequest::getrequest('ums', 'hierarchy/get_all', $params, $token, 60); + $response = LambdaRequest::getrequest( + $this->_service, + $this->_module . '/synchrobreatheReport_card_territories', + [], + $token, + 60 + ); } catch (\Throwable $e) { $error = 'Failed to load territories.'; @@ -191,7 +193,29 @@ class SynchrobreatheReportcardController extends Controller return []; } - return $this->territoryOptions($this->hierarchyNodes($response)); + $data = is_object($response) ? ($response->data ?? null) : null; + $items = is_object($data) ? ($data->territories ?? []) : []; + if (!is_array($items)) { + return []; + } + + $options = []; + foreach ($items as $item) { + if (!is_object($item)) { + continue; + } + $id = trim((string) ($item->id ?? '')); + if ($id === '') { + continue; + } + $label = trim((string) ($item->label ?? '')); + $options[] = [ + 'id' => $id, + 'label' => $label !== '' ? $label : $id, + ]; + } + + return $options; } protected function fetchReport( @@ -237,71 +261,6 @@ class SynchrobreatheReportcardController extends Controller return $this->presentReport($data, $comment, $rbm, $zbm, $marketing); } - /** - * @param array $nodes - * @return array - */ - protected function territoryOptions(array $nodes): array - { - $parentIds = []; - foreach ($nodes as $node) { - $parentId = trim((string) ($node->parent_id ?? '')); - if ($parentId !== '' && strtolower($parentId) !== 'root') { - $parentIds[$parentId] = true; - } - } - - $options = []; - foreach ($nodes as $node) { - $id = trim((string) ($node->id ?? '')); - if ($id === '' || isset($parentIds[$id]) || $this->isVacant($node)) { - continue; - } - - $owner = $this->ownerName($node); - $territory = $this->formatHierarchyName($node->name ?? ''); - $label = $owner !== '' ? $owner . ' - ' . $territory : $territory; - $options[] = [ - 'id' => $id, - 'label' => $label, - ]; - } - - usort($options, function ($a, $b) { - return strcasecmp($a['label'], $b['label']); - }); - - return $options; - } - - /** - * @return array - */ - protected function hierarchyNodes($response): array - { - $data = is_object($response) ? ($response->data ?? null) : null; - if (!is_object($data)) { - return []; - } - - $nodes = []; - $items = $data->items ?? []; - if (is_array($items) || $items instanceof \Traversable) { - foreach ($items as $item) { - if (is_object($item) && !empty($item->id)) { - $nodes[(string) $item->id] = $item; - } - } - } - - $me = $data->me ?? null; - if (is_object($me) && !empty($me->id)) { - $nodes[(string) $me->id] = $me; - } - - return array_values($nodes); - } - protected function presentReport(object $data, string $comment, string $rbm, string $zbm, string $marketing): array { $info = is_object($data->tm_info ?? null) ? $data->tm_info : (object) []; @@ -431,66 +390,6 @@ class SynchrobreatheReportcardController extends Controller return number_format($number, 2); } - protected function formatHierarchyName(?string $name): string - { - $name = trim((string) $name); - $stripped = preg_replace('/_[^_]+$/', '', $name); - if (is_string($stripped) && $stripped !== '') { - return $stripped; - } - - return $name !== '' ? $name : 'Vacant'; - } - - protected function ownerName(object $node): string - { - $info = $node->owner_info ?? null; - if (is_object($info)) { - $name = trim((string) ($info->name ?? '')); - if ($name === '') { - $name = trim(((string) ($info->firstname ?? '')) . ' ' . ((string) ($info->lastname ?? ''))); - } - if ($name !== '') { - return $name; - } - } - - return ''; - } - - protected function isVacant(object $node): bool - { - $id = strtolower(trim((string) ($node->id ?? ''))); - if ($id === '' || $id === 'vacant') { - return true; - } - - $owner = strtolower(trim((string) ($node->owner ?? ''))); - if ($owner === '' || $owner === 'vacant') { - return true; - } - - $flag = $node->is_vacant ?? false; - if ($flag === true || $flag === 1 || $flag === '1' || $flag === 'true') { - return true; - } - - $status = strtolower(trim((string) ($node->status ?? 'active'))); - - return $status !== '' && $status !== 'active'; - } - - protected function hasAllDataAccess(?object $user): bool - { - if (!$user) { - return false; - } - - $flag = $user->all_data_access ?? false; - - return $flag === true || $flag === 1 || $flag === '1' || $flag === 'true'; - } - protected function unwrapResponse($response) { if (!is_object($response)) { diff --git a/app/Http/Controllers/ZenovaReportController.php b/app/Http/Controllers/ZenovaReportController.php index bcce1f2f..54ebbf3f 100644 --- a/app/Http/Controllers/ZenovaReportController.php +++ b/app/Http/Controllers/ZenovaReportController.php @@ -220,10 +220,16 @@ class ZenovaReportController extends Controller } $demo = (array) ($row->{'Demography & Details'} ?? []); + $ownName = $this->rowName($row); foreach ($demo as $key => $value) { - if (strcasecmp((string) $key, 'Name of Zone') === 0 && $value !== null && $value !== '') { - return (string) $value; + if (strcasecmp((string) $key, 'Name of Zone') !== 0 || $value === null || $value === '') { + continue; } + if (strcasecmp((string) $value, $ownName) === 0) { + continue; + } + + return (string) $value; } return ''; diff --git a/resources/views/adherencepobdrivereport/report.blade.php b/resources/views/adherencepobdrivereport/report.blade.php new file mode 100644 index 00000000..1ff55dfc --- /dev/null +++ b/resources/views/adherencepobdrivereport/report.blade.php @@ -0,0 +1,200 @@ +@extends('layouts.vertical', ['page_title' => $title ?? 'Adherence Pack POB Drive']) + +@section('css') + +@endsection + +@section('content') +
+
+
+
+
+

{{ $title }}

+

Adherence Pack POB of at least ₹15,000. Changing the month reloads the report.

+ + @if (!empty($reportError)) + + @endif + +
+
+ + +
+
+ +
+ @if (!empty($sections)) + + @endif +
+

Loading the report. This can take a minute.

+
+
+ + @if (!empty($sections)) + @php + $tabLabels = [ + 'abm' => 'Details ABM wise', + 'region' => 'Region Daily Winners', + 'territory' => 'Territory Daily Winners', + ]; + @endphp +
+
+ +
+ @foreach ($sections as $index => $section) + @php + $firstDay = null; + $lastDay = null; + foreach ($section['dayTypes'] as $dayIndex => $dayType) { + if ($dayType === 'W' || $dayType === 'H') { + if ($firstDay === null) { + $firstDay = $dayIndex; + } + $lastDay = $dayIndex; + } + } + @endphp +
+
+ + + + @foreach ($section['dayTypes'] as $dayType) + + @endforeach + + + @foreach ($section['headers'] as $headerIndex => $header) + @php + $dayType = $section['dayTypes'][$headerIndex] ?? ''; + $headerClass = $dayType === 'H' ? 'pob-day-h' : ($dayType === 'W' ? 'pob-day-w' : (($firstDay !== null && $headerIndex < $firstDay) ? 'pob-identity' : 'pob-summary')); + @endphp + + @endforeach + + + + @forelse ($section['rows'] as $row) + + @foreach ($row['cells'] as $cellIndex => $cell) + @php + $dayType = $section['dayTypes'][$cellIndex] ?? ''; + $cellClass = ''; + if ($dayType === 'H' || $cell === 'Holiday') { + $cellClass = 'pob-day-h'; + } elseif ($dayType === 'W') { + $cellClass = 'pob-day-w'; + } elseif (!empty($row['vacant']) && $firstDay !== null && $cellIndex < $firstDay) { + $cellClass = 'pob-vacant'; + } elseif ($cell === 'Achieved') { + $cellClass = 'pob-achieved'; + } + @endphp + + @endforeach + + @empty + + + + @endforelse + +
{{ $dayType }}
{!! nl2br(e($header)) !!}
{{ $cell }}
No rows for this month.
+
+
+ @endforeach +
+
+
+ @endif +
+
+
+@endsection + +@section('script') + +@endsection diff --git a/resources/views/form/rapport-team2/1756965702-68add159-7312-476d-8c92-0c7683015351.blade.php b/resources/views/form/rapport-team2/1756965702-68add159-7312-476d-8c92-0c7683015351.blade.php index 709f6d49..6917361a 100644 --- a/resources/views/form/rapport-team2/1756965702-68add159-7312-476d-8c92-0c7683015351.blade.php +++ b/resources/views/form/rapport-team2/1756965702-68add159-7312-476d-8c92-0c7683015351.blade.php @@ -782,7 +782,7 @@ if (!empty($submissions->data->submissions)) { Swal.fire({ icon: 'error', title: 'Data not submitted', - text: 'Something went wrong, please try again after some time!' + text: (data && data.message) ? data.message : 'Something went wrong, please try again after some time!' }) } }, diff --git a/resources/views/form/rapport-team3/1790230856-a5ff58a6-d509-4ac0-9b18-6d366a74965e.blade.php b/resources/views/form/rapport-team3/1790230856-a5ff58a6-d509-4ac0-9b18-6d366a74965e.blade.php index 28253085..685d216a 100644 --- a/resources/views/form/rapport-team3/1790230856-a5ff58a6-d509-4ac0-9b18-6d366a74965e.blade.php +++ b/resources/views/form/rapport-team3/1790230856-a5ff58a6-d509-4ac0-9b18-6d366a74965e.blade.php @@ -112,7 +112,7 @@ $(function () { var surveyContacts = []; var submittedIdentities = []; var contactsReady = false; - var unlockAllSections = false; + var unlockAllSections = Boolean($('#field-submission_id').val()); var savedAnswers = @json($savedAnswers); var photoFields = ['visiting_card', 'rx_pad', 'doctor_board', 'chemist_board']; @@ -558,6 +558,12 @@ $(function () { $(this).closest('.stockist-block').find('.stockist-name').val(this.value); }); $('#field-visit_type').on('change', function () { setVisit(this.value); }); + $('#doctor-fields').on('input', '#field-no_of_staff', function () { + var digits = String(this.value).replace(/\D/g, '').slice(0, 3); + if (this.value !== digits) { + this.value = digits; + } + }); $('#report-form').on('input change', '.survey-section :input', refreshSections); $('#chemist-fields').on('change', '.rcpa-doctor', function () { applyDoctor(this); refreshSections(); }); $('#chemist-fields').on('change', '.stockist-pick', function () { applyStockist(this); }); @@ -722,6 +728,11 @@ $(function () { fail('Enter the other specialty in letters only.'); return; } + var staffCount = $('#field-no_of_staff').val().trim(); + if (staffCount !== '' && !/^\d{1,3}$/.test(staffCount)) { + fail('No. of Staff can be up to 3 digits only.'); + return; + } var missingUnits = false; $('#doctor-fields .product-choice').each(function () { if ($(this).val() !== 'Yes') { @@ -854,6 +865,7 @@ $(function () { $('#report-form a').filter(function () { return $(this).text() === 'View current photo'; }).remove(); $('#report-form')[0].reset(); $('#field-submission_id').val('').attr('value', ''); + unlockAllSections = false; $('#field-visit_type').val('doctor'); setVisit('doctor'); loadContacts(); diff --git a/resources/views/form/rapport-team3/1791391025-cf688862-93e7-43b5-bf4e-7a29b30e3830.blade.php b/resources/views/form/rapport-team3/1791391025-cf688862-93e7-43b5-bf4e-7a29b30e3830.blade.php index 922be71f..1437cdab 100644 --- a/resources/views/form/rapport-team3/1791391025-cf688862-93e7-43b5-bf4e-7a29b30e3830.blade.php +++ b/resources/views/form/rapport-team3/1791391025-cf688862-93e7-43b5-bf4e-7a29b30e3830.blade.php @@ -186,7 +186,7 @@
Actual Values
- +
@@ -357,12 +357,34 @@ function setActualVisible(show) { $('#actual-section').toggleClass('d-none', !show); $('#actual-section').find('input').prop('disabled', !show); - $('#field-upload_invoice').prop('required', !!show); + $('#field-upload_invoice').prop('required', false); if (show) { recalc('.actual-units', '.actual-value', '#field-total_actual_pob_units', '#field-total_actual_pob_value'); } } + function unlockReportUnits() { + $('#report-form input[data-locked-unit]').remove(); + $('.pob-units').each(function () { + var input = $(this); + var name = input.attr('data-unit-name') || input.attr('name'); + if (name) input.attr('name', name); + input.prop('disabled', false).prop('readonly', false); + }); + } + + function lockReportUnits() { + unlockReportUnits(); + $('.pob-units').each(function () { + var input = $(this); + var name = input.attr('name'); + if (!name) return; + input.attr('data-unit-name', name); + $('').attr('data-locked-unit', '1').attr('name', name).val(input.val()).insertAfter(input); + input.prop('disabled', true).removeAttr('name'); + }); + } + function showReport(type, contactId) { $('#tabs-wrapper').addClass('d-none'); $('#report-panel').removeClass('d-none'); @@ -370,6 +392,7 @@ $('#report-title').text('POB Details (Without Visit)'); $('#contact-label').html((type === 'ebt_chemist' ? 'Select Chemist' : 'Select Doctor') + ' *'); $('#report-form')[0].reset(); + unlockReportUnits(); $('#field-submission-id').val(''); $('#field-record_date').prop('readonly', false); $('#field-contact_type').val(type); @@ -393,6 +416,7 @@ $('#report-title').text(mode === 'upload' ? 'Upload Invoice' : 'Edit Order'); $('#contact-label').html((type === 'ebt_chemist' ? 'Select Chemist' : 'Select Doctor') + ' *'); $('#report-form')[0].reset(); + unlockReportUnits(); $('#field-submission-id').val(submission.id || ''); $('#field-contact_type').val(type); initContactSearch(type); @@ -411,6 +435,7 @@ }); recalc('.pob-units', '.pob-value', '#field-total_pob_units', '#field-total_pob_value'); setActualVisible($('#field-supply_status').val() === 'Yes'); + lockReportUnits(); openContactId = data.contact_id || ''; } @@ -436,9 +461,10 @@ var info = submission.formdata || {}; var payload = esc(JSON.stringify(submission)); var button = ''; + var workflowStatus = String(submission.workflow_status || '').toLowerCase(); if (info.supply_status === 'No' && info.submission_status === 'Order Taken') { button = ''; - } else if (info.supply_status === 'Yes' && info.submission_status === 'Rejected') { + } else if (workflowStatus === 'rejected') { button = ''; } return '' + button + '' + esc(info.record_date || '') + '' + esc(info.submission_status || '') + '' + esc(info.total_pob_value || '') + '' + esc(info.total_actual_pob_value || '') + ''; @@ -518,7 +544,12 @@ var contactSelect = $('#field-contact_id'); contactSelect.prop('disabled', false); openContactId = contactSelect.val() || openContactId; - $('#report-form [name="submission_status"]').val($('#field-supply_status').val() === 'Yes' ? 'Invoice Uploaded' : 'Order Taken'); + var supplyYes = $('#field-supply_status').val() === 'Yes'; + var invoiceFile = document.getElementById('field-upload_invoice'); + var hasInvoice = invoiceFile && invoiceFile.files && invoiceFile.files.length > 0; + var submissionStatus = 'Order Taken'; + if (supplyYes) submissionStatus = hasInvoice ? 'Invoice Uploaded' : 'Submitted'; + $('#report-form [name="submission_status"]').val(submissionStatus); var payload = new FormData(form); contactSelect.prop('disabled', true); $('#pageloader').css('display', 'flex').hide().fadeIn(); diff --git a/resources/views/form/rapport-team3/partials/doctor-chemist-chemist.blade.php b/resources/views/form/rapport-team3/partials/doctor-chemist-chemist.blade.php index 873218c2..ae8e2cc5 100644 --- a/resources/views/form/rapport-team3/partials/doctor-chemist-chemist.blade.php +++ b/resources/views/form/rapport-team3/partials/doctor-chemist-chemist.blade.php @@ -1,5 +1,5 @@ @php -$chemistTypes = ['Attached','Floating','Semi','Semi/Sub-Stockists','Chain Pharmacy']; +$chemistTypes = ['Attached','Floating','Semi/Sub-Stockists','Chain Pharmacy']; $products = ['Asthalin Inhaler' => 'asthalin_inhaler','Asthalin Rotacap' => 'asthalin_rotacap','Aerocort Inhaler' => 'aerocort_inhaler','Aerocort Rotacap' => 'aerocort_rotacap','Aerocort Forte Rotacap' => 'aerocort_forte_rotacap']; $therapies = [ 'Nebulization No. of Respules per Month (all included)' => 'neb_respules', diff --git a/resources/views/form/rapport-team3/partials/doctor-chemist-doctor.blade.php b/resources/views/form/rapport-team3/partials/doctor-chemist-doctor.blade.php index 69853dc4..1e426158 100644 --- a/resources/views/form/rapport-team3/partials/doctor-chemist-doctor.blade.php +++ b/resources/views/form/rapport-team3/partials/doctor-chemist-doctor.blade.php @@ -1,6 +1,6 @@ @php -$degrees = ['B. Pharm','D. Pharm','M.B.B.S.','M.D.','B.A.M.S.','B.U.M.S.','B.H.M.S.','D.A.M.S.','D.H.M.S.','B.M.S.','D.M.S.','B.E.M.S.','R.M.P.','B.I.A.M.','D.I.A.M.','C.M.S.','C.M.S. & E.D.','C.C.M.P.','G.A.M.S.','B.P.T.','B.S.C.','Other']; -$specialties = ['G.P (General Physician)','R.M.P (Registered Medical Practitioner)','C.P (Consulting Physician)','Child Specialist','Pediatrician','Family Physician','Multi Speciality','Gynecologist','Urologist','Physiotherapy','D.C.H.','Other']; +$degrees = ['B. Pharm','D. Pharm','M.B.B.S.','M.D.','B.A.M.S.','B.U.M.S.','B.H.M.S.','D.A.M.S.','D.H.M.S.','B.M.S.','D.M.S.','B.E.M.S.','R.M.P.','B.I.A.M.','D.I.A.M.','C.M.S.','C.M.S. & E.D.','C.C.M.P.','G.A.M.S.','B.P.T.','B.S.C.','D.C.H.','Other']; +$specialties = ['G.P (General Physician)','R.M.P (Registered Medical Practitioner)','C.P (Consulting Physician)','Child Specialist','Pediatrician','Family Physician','Multi Speciality','Gynecologist','Urologist','Physiotherapy','Other']; $bands = ['0 to 10','10 to 20','20 to 30','30 to 40','40 to 50','More than 50']; $doctorTypes = ['Standalone Clinic','Nursing Home','Hospital','Dispensing Doctors (Doctor with Self Purchase)']; $products = ['Asthalin Inhaler' => 'asthalin_inhaler','Asthalin Rotacap' => 'asthalin_rotacap','Aerocort Inhaler' => 'aerocort_inhaler','Aerocort Rotacap' => 'aerocort_rotacap','Aerocort Forte Rotacap' => 'aerocort_forte_rotacap']; @@ -30,7 +30,7 @@ $products = ['Asthalin Inhaler' => 'asthalin_inhaler','Asthalin Rotacap' => 'ast
-
+
diff --git a/resources/views/form/rapport-team4/1791539482-b06c2f9a-fe45-4b16-843a-224f07ed0d7b.blade.php b/resources/views/form/rapport-team4/1791539482-b06c2f9a-fe45-4b16-843a-224f07ed0d7b.blade.php index 2ab5088d..a08c5266 100644 --- a/resources/views/form/rapport-team4/1791539482-b06c2f9a-fe45-4b16-843a-224f07ed0d7b.blade.php +++ b/resources/views/form/rapport-team4/1791539482-b06c2f9a-fe45-4b16-843a-224f07ed0d7b.blade.php @@ -61,11 +61,35 @@ $isEditMode = !empty($id); + $reasonQuestionCode = ''; + foreach ($questions as $question) { + $label = trim(preg_replace('/\s+/', ' ', strip_tags($question->question ?? ''))); + if (stripos($label, 'Reason for Deletion') !== false) { + $reasonQuestionCode = $question->code; + break; + } + } + + $savedFormdata = (isset($form->formdata) && is_object($form->formdata)) ? $form->formdata : null; + $recordStatus = ($savedFormdata && isset($savedFormdata->record_status)) ? (string) $savedFormdata->record_status : ''; + $isDeletedEntry = $isEditMode && strcasecmp($recordStatus, 'Deleted') === 0; + $baseSubmissionId = ($savedFormdata && !empty($savedFormdata->base_submission_id)) ? (string) $savedFormdata->base_submission_id : ''; + @endphp - + + @if($isDeletedEntry) +
+
This entry has been replaced and can no longer be edited.
+
+ @else
+ + + @if($reasonQuestionCode !== '') + + @endif @csrf @if($id) @@ -73,8 +97,16 @@ @endif @foreach($questions as $question) + @if(in_array($question->code, ['record_status', 'base_submission_id'], true)) + @continue + @endif + @php + $isReasonField = $reasonQuestionCode !== '' && $question->code === $reasonQuestionCode; + $fieldRequired = $isReasonField ? $isEditMode : ($question->required == 'yes'); + $fieldDisabled = $isReasonField && !$isEditMode; + @endphp
- + @switch($question->type) @case('hidden') @@ -83,13 +115,13 @@ @case('email') @case('tel') @case('date') - required == 'yes'?'required':'' }} value="{{isset($question->value)?$question->value:''}}"> + @break @case('number') - required == 'yes'?'required':'' }} value="{{isset($question->value)?$question->value:0}}"> + @break @case('textarea') - + @break @@ -124,7 +156,7 @@ @break --}} @case('dropdown') - @foreach ($question->options as $option) @php @@ -204,6 +236,7 @@
@endif
+ @endif
@@ -481,7 +514,7 @@ Swal.fire({ icon: 'error', title: 'Data not submitted', - text: 'Something went wrong, please try again after some time!' + text: (data && data.message) ? data.message : 'Something went wrong, please try again after some time!' }) } }, diff --git a/resources/views/layouts/shared/left-sidebar.blade.php b/resources/views/layouts/shared/left-sidebar.blade.php index 1d491faf..4a6d8ff4 100644 --- a/resources/views/layouts/shared/left-sidebar.blade.php +++ b/resources/views/layouts/shared/left-sidebar.blade.php @@ -175,6 +175,14 @@ {{-- @endif --}} + @if(\App\Http\Controllers\AdherencepobdrivereportController::canDownload(auth()->user())) +
  • + + + Adherence Pack POB Drive + +
  • + @endif @endif @if(auth()->user()->domain == 'gennovadc') diff --git a/routes/web.php b/routes/web.php index 5bc68adf..03db7d65 100755 --- a/routes/web.php +++ b/routes/web.php @@ -114,6 +114,7 @@ use App\Http\Controllers\ForacortchemistavailabilityController; use App\Http\Controllers\Team2unitsreportController; use App\Http\Controllers\SynchrobreatheReportcardController; use App\Http\Controllers\AdherencepackratioreportController; +use App\Http\Controllers\AdherencepobdrivereportController; use App\Http\Controllers\RevesmartlaunchdriveController; use App\Http\Controllers\PbsdoctoranalyticsReportController; use App\Http\Controllers\TitanshcpgatheringreportController; @@ -281,6 +282,7 @@ $modules = ['user' => UserController::class, 'team2unitsreport' => Team2unitsreportController::class, 'synchrobreatheReportcard' => SynchrobreatheReportcardController::class, 'adherencepackratioreport' => AdherencepackratioreportController::class, + 'adherencepobdrivereport' => AdherencepobdrivereportController::class, 'revesmartlaunchdrive' => RevesmartlaunchdriveController::class, 'pbsdoctoranalyticsreport' => PbsdoctoranalyticsReportController::class, 'titanshcpgatheringreport' => TitanshcpgatheringreportController::class,