Merge branch 'main' of http://192.168.1.200:3000/Stratawiz/Rapport-Laravel
Specific Folder FTP Upload / ftp-deploy (push) Successful in 1m45s
Details
Specific Folder FTP Upload / ftp-deploy (push) Successful in 1m45s
Details
This commit is contained in:
commit
0ed9417ace
|
|
@ -0,0 +1,756 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exports;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
class AdherencePobDriveExport
|
||||
{
|
||||
private const GREEN = 'C6EFCE';
|
||||
|
||||
private const GREY = 'D9D9D9';
|
||||
|
||||
private const PINK = 'F8CBAD';
|
||||
|
||||
/** @var array<int, mixed> */
|
||||
private $abmRows;
|
||||
|
||||
/** @var array<int, mixed> */
|
||||
private $rbmRows;
|
||||
|
||||
/** @var array<int, mixed> */
|
||||
private $tmRows;
|
||||
|
||||
/** @var array<int, array<string, mixed>> */
|
||||
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<int, array<string, mixed>>
|
||||
*/
|
||||
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<string, mixed>
|
||||
*/
|
||||
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<string, mixed>
|
||||
*/
|
||||
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<string, mixed>
|
||||
*/
|
||||
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<int, array<string, mixed>>
|
||||
*/
|
||||
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<int, array<string, mixed>>
|
||||
*/
|
||||
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<int, array<string, mixed>>
|
||||
*/
|
||||
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<string, array<string, mixed>>
|
||||
*/
|
||||
private function dayMap($days): array
|
||||
{
|
||||
$map = [];
|
||||
foreach ($this->orderedDays($days) as $day) {
|
||||
if ($day['date'] !== '') {
|
||||
$map[$day['date']] = $day;
|
||||
}
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $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<int, mixed>
|
||||
*/
|
||||
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<int, mixed> $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<int, array<string, mixed>> $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<int, array<string, mixed>> $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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exports\AdherencePobDriveExport;
|
||||
use App\Http\Requests\LambdaRequest;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class AdherencepobdrivereportController extends Controller
|
||||
{
|
||||
private const REPORT_START = '2026-04-01';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->_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<int, string>
|
||||
*/
|
||||
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 ?? '');
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<int, object> $nodes
|
||||
* @return array<int, array{id:string,label:string}>
|
||||
*/
|
||||
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<int, object>
|
||||
*/
|
||||
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)) {
|
||||
|
|
|
|||
|
|
@ -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 '';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,200 @@
|
|||
@extends('layouts.vertical', ['page_title' => $title ?? 'Adherence Pack POB Drive'])
|
||||
|
||||
@section('css')
|
||||
<style>
|
||||
.pob-table-wrap {
|
||||
overflow: auto;
|
||||
max-height: 70vh;
|
||||
}
|
||||
|
||||
.pob-table {
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pob-table th,
|
||||
.pob-table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.pob-table thead tr:nth-child(1) th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 3;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.pob-table thead tr:nth-child(2) th {
|
||||
position: sticky;
|
||||
top: 28px;
|
||||
z-index: 2;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.pob-table td.pob-day-w,
|
||||
.pob-table thead tr th.pob-day-w {
|
||||
background: #e2efda;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.pob-table td.pob-day-h,
|
||||
.pob-table thead tr th.pob-day-h {
|
||||
background: #d9d9d9;
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
.pob-table thead tr th.pob-identity {
|
||||
background: #c00000;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pob-table thead tr th.pob-summary {
|
||||
background: #8a1538;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pob-table td.pob-vacant {
|
||||
background: #fce4e4;
|
||||
color: #c00000;
|
||||
}
|
||||
|
||||
.pob-table td.pob-achieved {
|
||||
background: #c6efce;
|
||||
color: #006100;
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="page-title">{{ $title }}</h4>
|
||||
<p class="text-muted">Adherence Pack POB of at least ₹15,000. Changing the month reloads the report.</p>
|
||||
|
||||
@if (!empty($reportError))
|
||||
<div class="alert alert-danger" role="alert">{{ $reportError }}</div>
|
||||
@endif
|
||||
|
||||
<form method="get" action="{{ $base_url }}/report" id="pob-report-form" class="row g-2 align-items-end">
|
||||
<div class="col-md-4">
|
||||
<label for="field-month" class="form-label">Month</label>
|
||||
<select name="month" id="field-month" class="form-select">
|
||||
@foreach ($months as $month)
|
||||
<option value="{{ $month }}"@if (($selectedMonth ?? '') === $month) selected @endif>{{ $month }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-primary" id="pob-show">Show report</button>
|
||||
</div>
|
||||
@if (!empty($sections))
|
||||
<div class="col-auto">
|
||||
<a class="btn btn-outline-primary" href="{{ $base_url }}/export?month={{ urlencode($selectedMonth) }}">Download Excel</a>
|
||||
</div>
|
||||
@endif
|
||||
</form>
|
||||
<p class="text-muted mt-2 d-none" id="pob-loading">Loading the report. This can take a minute.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!empty($sections))
|
||||
@php
|
||||
$tabLabels = [
|
||||
'abm' => 'Details ABM wise',
|
||||
'region' => 'Region Daily Winners',
|
||||
'territory' => 'Territory Daily Winners',
|
||||
];
|
||||
@endphp
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
@foreach ($sections as $index => $section)
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link{{ $index === 0 ? ' active' : '' }}" id="pob-tab-{{ $section['id'] }}" data-bs-toggle="tab" data-bs-target="#pob-pane-{{ $section['id'] }}" type="button" role="tab" aria-controls="pob-pane-{{ $section['id'] }}" aria-selected="{{ $index === 0 ? 'true' : 'false' }}">{{ $tabLabels[$section['id']] ?? $section['title'] }}</button>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
<div class="tab-content pt-3">
|
||||
@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
|
||||
<div class="tab-pane fade{{ $index === 0 ? ' show active' : '' }}" id="pob-pane-{{ $section['id'] }}" role="tabpanel" aria-labelledby="pob-tab-{{ $section['id'] }}">
|
||||
<div class="pob-table-wrap">
|
||||
<table class="table table-bordered table-sm pob-table mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
@foreach ($section['dayTypes'] as $dayType)
|
||||
<th class="{{ $dayType === 'H' ? 'pob-day-h' : ($dayType === 'W' ? 'pob-day-w' : '') }}">{{ $dayType }}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
<tr>
|
||||
@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
|
||||
<th class="{{ $headerClass }}">{!! nl2br(e($header)) !!}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse ($section['rows'] as $row)
|
||||
<tr>
|
||||
@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
|
||||
<td class="{{ $cellClass }}">{{ $cell }}</td>
|
||||
@endforeach
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="{{ count($section['headers']) }}">No rows for this month.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('script')
|
||||
<script>
|
||||
document.getElementById('pob-report-form').addEventListener('submit', function () {
|
||||
document.getElementById('pob-loading').classList.remove('d-none');
|
||||
document.getElementById('pob-show').disabled = true;
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
|
|
@ -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!'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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); });
|
||||
|
|
@ -584,6 +590,10 @@ $(function () {
|
|||
return /^[A-Za-z0-9]+(?:[ ]+[A-Za-z0-9]+)*$/.test(value);
|
||||
}
|
||||
|
||||
function stockistAddressOk(value) {
|
||||
return /^(?=.*[A-Za-z0-9])[A-Za-z0-9 ,.\-\/#&()']+$/.test(value);
|
||||
}
|
||||
|
||||
function doctorAddressOk(value) {
|
||||
return /^[A-Za-z0-9]+(?:[ ,]+[A-Za-z0-9]+)*$/.test(value);
|
||||
}
|
||||
|
|
@ -718,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') {
|
||||
|
|
@ -773,7 +788,8 @@ $(function () {
|
|||
fail('A chemist with this mobile number already exists.');
|
||||
return;
|
||||
}
|
||||
var badStockist = false;
|
||||
var badStockistName = false;
|
||||
var badStockistAddress = false;
|
||||
$('#chemist-fields .stockist-pick').each(function () {
|
||||
if (this.value !== 'others') {
|
||||
return;
|
||||
|
|
@ -782,12 +798,19 @@ $(function () {
|
|||
var otherName = block.find('.stockist-other-name').val().trim();
|
||||
var otherAddress = block.find('.stockist-location').val().trim();
|
||||
block.find('.stockist-name').val(otherName);
|
||||
if (!addressOk(otherName) || !addressOk(otherAddress)) {
|
||||
badStockist = true;
|
||||
if (!addressOk(otherName)) {
|
||||
badStockistName = true;
|
||||
}
|
||||
if (!stockistAddressOk(otherAddress)) {
|
||||
badStockistAddress = true;
|
||||
}
|
||||
});
|
||||
if (badStockist) {
|
||||
fail('Other stockist name and address can contain letters, numbers and spaces only.');
|
||||
if (badStockistName) {
|
||||
fail('Other stockist name can contain letters, numbers and spaces only.');
|
||||
return;
|
||||
}
|
||||
if (badStockistAddress) {
|
||||
fail('Other stockist address can contain letters, numbers, spaces and symbols such as comma, period, hyphen, slash, hash, ampersand, parentheses and apostrophe.');
|
||||
return;
|
||||
}
|
||||
if (!therapiesFilled('#chemist-fields')) {
|
||||
|
|
@ -842,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();
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@
|
|||
<tr>
|
||||
<th>Action</th>
|
||||
<th>Chemist Name</th>
|
||||
<th>Speciality</th>
|
||||
<th>MSL Code</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -187,7 +186,7 @@
|
|||
<div id="actual-section" class="d-none">
|
||||
<h5 class="mt-3">Actual Values</h5>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="field-upload_invoice">Upload Invoice <span class="text-danger">*</span></label>
|
||||
<label class="form-label" for="field-upload_invoice">Upload Invoice</label>
|
||||
<input type="file" class="form-control" id="field-upload_invoice" name="upload_invoice" accept=".jpg,.jpeg,.png,.pdf,image/jpeg,image/png,application/pdf">
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
|
|
@ -358,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);
|
||||
$('<input type="hidden">').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');
|
||||
|
|
@ -371,6 +392,7 @@
|
|||
$('#report-title').text('POB Details (Without Visit)');
|
||||
$('#contact-label').html((type === 'ebt_chemist' ? 'Select Chemist' : 'Select Doctor') + ' <span class="text-danger">*</span>');
|
||||
$('#report-form')[0].reset();
|
||||
unlockReportUnits();
|
||||
$('#field-submission-id').val('');
|
||||
$('#field-record_date').prop('readonly', false);
|
||||
$('#field-contact_type').val(type);
|
||||
|
|
@ -394,6 +416,7 @@
|
|||
$('#report-title').text(mode === 'upload' ? 'Upload Invoice' : 'Edit Order');
|
||||
$('#contact-label').html((type === 'ebt_chemist' ? 'Select Chemist' : 'Select Doctor') + ' <span class="text-danger">*</span>');
|
||||
$('#report-form')[0].reset();
|
||||
unlockReportUnits();
|
||||
$('#field-submission-id').val(submission.id || '');
|
||||
$('#field-contact_type').val(type);
|
||||
initContactSearch(type);
|
||||
|
|
@ -412,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 || '';
|
||||
}
|
||||
|
||||
|
|
@ -428,24 +452,28 @@
|
|||
var child = document.createElement('tr');
|
||||
child.className = 'pob-child';
|
||||
child.setAttribute('data-open', 'yes');
|
||||
child.innerHTML = '<td colspan="4">Loading...</td>';
|
||||
var colSpan = row.closest('table') && row.closest('table').id === 'chemist-table' ? 3 : 4;
|
||||
child.innerHTML = '<td colspan="' + colSpan + '">Loading...</td>';
|
||||
row.after(child);
|
||||
$.getJSON('/form/pobhistory', { form_id: formId, contact_id: contactId }, function (data) {
|
||||
var submissions = (data && data.submissions) || [];
|
||||
var orderRows = submissions.map(function (submission) {
|
||||
var info = submission.formdata || {};
|
||||
var open = info.submission_status === 'Order Taken' || info.submission_status === 'Rejected';
|
||||
var button = open
|
||||
? '<button type="button" class="btn btn-warning btn-xs edit-order me-1" data-submission="' + esc(JSON.stringify(submission)) + '">Edit</button>'
|
||||
+ '<button type="button" class="btn btn-warning btn-xs upload-invoice" data-submission="' + esc(JSON.stringify(submission)) + '">Upload Invoice</button>'
|
||||
: '';
|
||||
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 = '<button type="button" class="btn btn-warning btn-xs edit-order" data-submission="' + payload + '">Edit</button>';
|
||||
} else if (workflowStatus === 'rejected') {
|
||||
button = '<button type="button" class="btn btn-warning btn-xs upload-invoice" data-submission="' + payload + '">Upload Invoice</button>';
|
||||
}
|
||||
return '<tr><td>' + button + '</td><td>' + esc(info.record_date || '') + '</td><td>' + esc(info.submission_status || '') + '</td><td>' + esc(info.total_pob_value || '') + '</td><td>' + esc(info.total_actual_pob_value || '') + '</td></tr>';
|
||||
}).join('');
|
||||
child.innerHTML = '<td colspan="4" class="p-0"><div class="table-responsive"><table class="table table-sm mb-0 pob-orders"><thead class="table-light"><tr><th>Action</th><th>Date</th><th>Status</th><th>Total Value</th><th>Actual Total</th></tr></thead><tbody>'
|
||||
child.innerHTML = '<td colspan="' + colSpan + '" class="p-0"><div class="table-responsive"><table class="table table-sm mb-0 pob-orders"><thead class="table-light"><tr><th>Action</th><th>Date</th><th>Status</th><th>Total Value</th><th>Actual Total</th></tr></thead><tbody>'
|
||||
+ (orderRows || '<tr><td colspan="5" class="text-muted">No orders yet.</td></tr>')
|
||||
+ '</tbody></table></div></td>';
|
||||
}).fail(function () {
|
||||
child.innerHTML = '<td colspan="4" class="text-danger">Could not load orders.</td>';
|
||||
child.innerHTML = '<td colspan="' + colSpan + '" class="text-danger">Could not load orders.</td>';
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -453,8 +481,9 @@
|
|||
var body = document.querySelector('#' + tableId + ' tbody');
|
||||
var rows = contacts[type] || [];
|
||||
body.innerHTML = '';
|
||||
var chemist = type === 'ebt_chemist';
|
||||
if (!rows.length) {
|
||||
body.innerHTML = '<tr><td colspan="4" class="text-muted">No ' + (type === 'ebt_doctor' ? 'doctors' : 'chemists') + ' yet.</td></tr>';
|
||||
body.innerHTML = '<tr><td colspan="' + (chemist ? 3 : 4) + '" class="text-muted">No ' + (chemist ? 'chemists' : 'doctors') + ' yet.</td></tr>';
|
||||
return;
|
||||
}
|
||||
rows.forEach(function (item) {
|
||||
|
|
@ -463,7 +492,7 @@
|
|||
tr.setAttribute('data-contact', item.value);
|
||||
tr.innerHTML = '<td><button type="button" class="btn btn-info btn-xs report-btn">Report</button></td>'
|
||||
+ '<td class="pob-toggle"><i class="mdi mdi-arrow-down-drop-circle"></i> ' + esc(item.display || contact.name || '') + '</td>'
|
||||
+ '<td>' + esc(contact.speciality || '') + '</td>'
|
||||
+ (chemist ? '' : '<td>' + esc(contact.speciality || '') + '</td>')
|
||||
+ '<td>' + esc(contact.msl_code || '') + '</td>';
|
||||
tr.querySelector('.report-btn').addEventListener('click', function () {
|
||||
showReport(type, item.value);
|
||||
|
|
@ -515,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();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
@php
|
||||
$chemistTypes = ['Attached','Floating','Semi','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',
|
||||
|
|
@ -8,7 +8,7 @@ $therapies = [
|
|||
'Asthalin Syrup No. of Units per Month' => 'asthalin_syrup_units',
|
||||
'Levolin Syrup No. of Units per Month' => 'levolin_syrup_units',
|
||||
'Deriphyllin No. of Units per Month' => 'deriphyllin_units',
|
||||
'Beclomethasone Tablet No. of Units per Month' => 'beclomethasone_units',
|
||||
'Betnesol Tablet No. of Units per Month' => 'beclomethasone_units',
|
||||
'Methylprednisolone Tablet No. of Units per Month' => 'methylprednisolone_units',
|
||||
'Prednisolone Tablet No. of Units per Month' => 'prednisolone_units',
|
||||
'Cough Syrups No. of Units per Month' => 'cough_syrups_units',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
@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'];
|
||||
$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)'];
|
||||
|
|
@ -30,7 +30,7 @@ $products = ['Asthalin Inhaler' => 'asthalin_inhaler','Asthalin Rotacap' => 'ast
|
|||
<button type="button" class="section-head">Chemist details</button>
|
||||
<div class="section-body">
|
||||
<div class="mb-2"><label class="form-label">Attached Chemist Name (If any)</label><input type="text" class="form-control" name="attached_chemist_name"></div>
|
||||
<div class="mb-2"><label class="form-label">No. of Staff</label><input type="number" min="0" step="1" class="form-control" name="no_of_staff"></div>
|
||||
<div class="mb-2"><label class="form-label">No. of Staff</label><input type="number" min="0" max="999" step="1" inputmode="numeric" class="form-control" id="field-no_of_staff" name="no_of_staff"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ $products = ['Asthalin Inhaler' => 'asthalin_inhaler','Asthalin Rotacap' => 'ast
|
|||
'Asthalin Syrup No. of Units per Month' => 'asthalin_syrup_units',
|
||||
'Levolin Syrup No. of Units per Month' => 'levolin_syrup_units',
|
||||
'Deriphyllin No. of Units per Month' => 'deriphyllin_units',
|
||||
'Beclomethasone Tablet No. of Units per Month' => 'beclomethasone_units',
|
||||
'Betnesol Tablet No. of Units per Month' => 'beclomethasone_units',
|
||||
'Methylprednisolone Tablet No. of Units per Month' => 'methylprednisolone_units',
|
||||
'Prednisolone Tablet No. of Units per Month' => 'prednisolone_units',
|
||||
'Cough Syrups No. of Units per Month' => 'cough_syrups_units',
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
<div class="col-12">
|
||||
<div class="alert alert-warning mb-0">This entry has been replaced and can no longer be edited.</div>
|
||||
</div>
|
||||
@else
|
||||
<form method="POST" action="/form/submit" id="report-form" enctype="multipart/form-data">
|
||||
<input type="hidden" name="form_id" value="{{$form_id}}">
|
||||
<input type="hidden" name="user_id" value="{{$form->user_id}}">
|
||||
<input type="hidden" name="record_status" value="Active">
|
||||
<input type="hidden" name="base_submission_id" value="{{ $baseSubmissionId }}">
|
||||
@if($reasonQuestionCode !== '')
|
||||
<input type="hidden" name="reason_field_code" value="{{ $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
|
||||
<div class="col-md-12 mb-2">
|
||||
<label for="field-{{$question->code}}" class="form-label">{{$question->question}} {!!$question->required == 'yes' ? '<span class="text-danger">*</span>':''!!} </label>
|
||||
<label for="field-{{$question->code}}" class="form-label">{{$question->question}} {!!$fieldRequired ? '<span class="text-danger">*</span>':''!!} </label>
|
||||
@switch($question->type)
|
||||
@case('hidden')
|
||||
<input type="{{$question->type}}" id="field-{{$question->code}}" name="{{$question->code}}" value="{{isset($question->value)?$question->value:''}}">
|
||||
|
|
@ -83,13 +115,13 @@
|
|||
@case('email')
|
||||
@case('tel')
|
||||
@case('date')
|
||||
<input type="{{$question->type}}" id="field-{{$question->code}}" name="{{$question->code}}" class="form-control" {{$question->required == 'yes'?'required':'' }} value="{{isset($question->value)?$question->value:''}}">
|
||||
<input type="{{$question->type}}" id="field-{{$question->code}}" name="{{$question->code}}" class="form-control" {{ $fieldRequired ? 'required' : '' }} {{ $fieldDisabled ? 'disabled' : '' }} value="{{isset($question->value)?$question->value:''}}">
|
||||
@break
|
||||
@case('number')
|
||||
<input type="{{$question->type}}" min="0" max="9999999999" step="1" id="field-{{$question->code}}" name="{{$question->code}}" class="form-control" {{$question->required == 'yes'?'required':'' }} value="{{isset($question->value)?$question->value:0}}">
|
||||
<input type="{{$question->type}}" min="0" max="9999999999" step="1" id="field-{{$question->code}}" name="{{$question->code}}" class="form-control" {{ $fieldRequired ? 'required' : '' }} {{ $fieldDisabled ? 'disabled' : '' }} value="{{isset($question->value)?$question->value:0}}">
|
||||
@break
|
||||
@case('textarea')
|
||||
<textarea id="field-{{$question->code}}" name="{{$question->code}}" class="form-control" {{$question->required == 'yes'?'required':'' }}>{{isset($question->value)?$question->value:''}}</textarea>
|
||||
<textarea id="field-{{$question->code}}" name="{{$question->code}}" class="form-control" {{ $fieldRequired ? 'required' : '' }} {{ $fieldDisabled ? 'disabled' : '' }}>{{isset($question->value)?$question->value:''}}</textarea>
|
||||
@break
|
||||
|
||||
|
||||
|
|
@ -124,7 +156,7 @@
|
|||
@break --}}
|
||||
|
||||
@case('dropdown')
|
||||
<select class="form-control" id="field-{{ $question->code }}" name="{{ $question->code }}" data-initial-value="{{ isset($question->value) ? $question->value : '' }}" {{ $question->required == 'yes' ? 'required' : '' }}>
|
||||
<select class="form-control" id="field-{{ $question->code }}" name="{{ $question->code }}" data-initial-value="{{ isset($question->value) ? $question->value : '' }}" {{ $fieldRequired ? 'required' : '' }} {{ $fieldDisabled ? 'disabled' : '' }}>
|
||||
<option value=""> -- Select -- </option>
|
||||
@foreach ($question->options as $option)
|
||||
@php
|
||||
|
|
@ -204,6 +236,7 @@
|
|||
</div>
|
||||
@endif
|
||||
</form>
|
||||
@endif
|
||||
|
||||
|
||||
</div> <!-- end card-body -->
|
||||
|
|
@ -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!'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -175,6 +175,14 @@
|
|||
</a>
|
||||
</li>
|
||||
{{-- @endif --}}
|
||||
@if(\App\Http\Controllers\AdherencepobdrivereportController::canDownload(auth()->user()))
|
||||
<li>
|
||||
<a href="/adherencepobdrivereport/report">
|
||||
<i data-feather="file-text"></i>
|
||||
<span>Adherence Pack POB Drive</span>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if(auth()->user()->domain == 'gennovadc')
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue