feat: update storestock flow and ABDM bridge handling
This commit is contained in:
parent
1790be3fca
commit
1bac1f6e04
|
|
@ -7133,22 +7133,9 @@ class Opd_prescription extends BaseController
|
||||||
$code = trim((string) $this->request->getPost('investigation_code'));
|
$code = trim((string) $this->request->getPost('investigation_code'));
|
||||||
$name = trim((string) $this->request->getPost('investigation_name'));
|
$name = trim((string) $this->request->getPost('investigation_name'));
|
||||||
|
|
||||||
// Profile chips may send only investigation name; resolve code from master to avoid unique-key collisions.
|
// Resolve profile shorthand names (CBC/LFT/KFT etc.) to canonical codes.
|
||||||
if ($code === '' && $name !== '' && $this->db->tableExists('investigation')) {
|
if (($code === '' || $code === '0') && $name !== '') {
|
||||||
$invFields = $this->db->getFieldNames('investigation');
|
$code = $this->resolveInvestigationCodeByText($name, $this->buildInvestigationLookupMaps());
|
||||||
$invCodeField = $this->resolveFirstField($invFields, ['Code', 'code']);
|
|
||||||
$invNameField = $this->resolveFirstField($invFields, ['Name', 'name']);
|
|
||||||
if ($invCodeField !== null && $invNameField !== null) {
|
|
||||||
$resolved = $this->db->table('investigation')
|
|
||||||
->select($invCodeField . ' as code')
|
|
||||||
->where($invNameField, $name)
|
|
||||||
->limit(1)
|
|
||||||
->get()
|
|
||||||
->getRowArray();
|
|
||||||
if (! empty($resolved['code'])) {
|
|
||||||
$code = trim((string) $resolved['code']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($opdId <= 0 || ($code === '' && $name === '')) {
|
if ($opdId <= 0 || ($code === '' && $name === '')) {
|
||||||
|
|
@ -7229,6 +7216,303 @@ class Opd_prescription extends BaseController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function investigation_add_bulk()
|
||||||
|
{
|
||||||
|
$startedAt = microtime(true);
|
||||||
|
$respond = function (array $payload, int $statusCode = 200) use ($startedAt) {
|
||||||
|
$payload['elapsed_ms'] = (int) round((microtime(true) - $startedAt) * 1000);
|
||||||
|
return $this->response->setStatusCode($statusCode)->setJSON($payload);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (! $this->request->isAJAX()) {
|
||||||
|
return $respond(['update' => 0, 'error_text' => 'Invalid request'], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$opdId = (int) $this->request->getPost('opd_id');
|
||||||
|
$sessionId = (int) $this->request->getPost('opd_session_id');
|
||||||
|
|
||||||
|
$rawTests = $this->request->getPost('tests');
|
||||||
|
$tests = [];
|
||||||
|
if (is_string($rawTests) && $rawTests !== '') {
|
||||||
|
$decoded = json_decode($rawTests, true);
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
$tests = $decoded;
|
||||||
|
}
|
||||||
|
} elseif (is_array($rawTests)) {
|
||||||
|
$tests = $rawTests;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($opdId <= 0 || empty($tests)) {
|
||||||
|
return $respond(['update' => 0, 'error_text' => 'Invalid bulk payload']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = $this->findExistingTable(['opd_prescription_investigation']);
|
||||||
|
if ($table === null) {
|
||||||
|
return $respond(['update' => 0, 'error_text' => 'Investigation table not found']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sessionId = $this->ensurePrescriptionSession($opdId, $sessionId);
|
||||||
|
if ($sessionId <= 0) {
|
||||||
|
return $respond(['update' => 0, 'error_text' => 'Unable to create prescription session']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $this->db->getFieldNames($table);
|
||||||
|
$hasOpdPreId = in_array('opd_pre_id', $fields, true);
|
||||||
|
$hasOpdId = in_array('opd_id', $fields, true);
|
||||||
|
$hasCode = in_array('investigation_code', $fields, true);
|
||||||
|
$hasName = in_array('investigation_name', $fields, true);
|
||||||
|
|
||||||
|
$existingRowsBuilder = $this->db->table($table);
|
||||||
|
if ($hasOpdPreId) {
|
||||||
|
$existingRowsBuilder->where('opd_pre_id', $sessionId);
|
||||||
|
} elseif ($hasOpdId) {
|
||||||
|
$existingRowsBuilder->where('opd_id', $opdId);
|
||||||
|
} else {
|
||||||
|
return $respond(['update' => 0, 'error_text' => 'Invalid investigation table structure']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$existingRows = $existingRowsBuilder->get()->getResultArray();
|
||||||
|
$existingByCode = [];
|
||||||
|
$existingByName = [];
|
||||||
|
foreach ($existingRows as $row) {
|
||||||
|
$exCode = trim((string) ($row['investigation_code'] ?? ''));
|
||||||
|
$exName = trim((string) ($row['investigation_name'] ?? ''));
|
||||||
|
if ($exCode !== '' && $exCode !== '0') {
|
||||||
|
$existingByCode[strtolower($exCode)] = true;
|
||||||
|
}
|
||||||
|
if ($exName !== '') {
|
||||||
|
$existingByName[strtolower($exName)] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$invLookup = $this->buildInvestigationLookupMaps();
|
||||||
|
|
||||||
|
$seenPayload = [];
|
||||||
|
$insertRows = [];
|
||||||
|
$added = 0;
|
||||||
|
$skipped = 0;
|
||||||
|
|
||||||
|
foreach ($tests as $test) {
|
||||||
|
$name = trim((string) (($test['name'] ?? $test['investigation_name'] ?? '')));
|
||||||
|
$code = trim((string) (($test['code'] ?? $test['investigation_code'] ?? '')));
|
||||||
|
|
||||||
|
if ($name === '' && $code === '') {
|
||||||
|
$skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($code === '' || $code === '0') && $name !== '') {
|
||||||
|
$code = $this->resolveInvestigationCodeByText($name, $invLookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
$payloadKey = strtolower($code . '|' . $name);
|
||||||
|
if (isset($seenPayload[$payloadKey])) {
|
||||||
|
$skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$seenPayload[$payloadKey] = true;
|
||||||
|
|
||||||
|
$duplicateByCode = ($code !== '' && isset($existingByCode[strtolower($code)]));
|
||||||
|
$duplicateByName = ($name !== '' && isset($existingByName[strtolower($name)]));
|
||||||
|
if ($duplicateByCode || $duplicateByName) {
|
||||||
|
$skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = [];
|
||||||
|
if ($hasOpdId) {
|
||||||
|
$row['opd_id'] = $opdId;
|
||||||
|
}
|
||||||
|
if ($hasOpdPreId) {
|
||||||
|
$row['opd_pre_id'] = $sessionId;
|
||||||
|
}
|
||||||
|
if ($hasCode) {
|
||||||
|
$row['investigation_code'] = ($code === '' || $code === '0') ? '' : $code;
|
||||||
|
}
|
||||||
|
if ($hasName) {
|
||||||
|
$row['investigation_name'] = $name;
|
||||||
|
}
|
||||||
|
$insertRows[] = $row;
|
||||||
|
|
||||||
|
if ($code !== '' && $code !== '0') {
|
||||||
|
$existingByCode[strtolower($code)] = true;
|
||||||
|
}
|
||||||
|
if ($name !== '') {
|
||||||
|
$existingByName[strtolower($name)] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($insertRows as $row) {
|
||||||
|
try {
|
||||||
|
$this->db->table($table)->insert($row);
|
||||||
|
$added++;
|
||||||
|
$insertId = (int) $this->db->insertID();
|
||||||
|
$this->auditClinicalUpdate('opd_prescription_investigation', 'added', $insertId, null, $row);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$msg = strtolower($e->getMessage());
|
||||||
|
if (strpos($msg, 'duplicate entry') !== false) {
|
||||||
|
$skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = [
|
||||||
|
'update' => 1,
|
||||||
|
'opd_session_id' => $sessionId,
|
||||||
|
'added_count' => $added,
|
||||||
|
'skipped_count' => $skipped,
|
||||||
|
'total_count' => count($tests),
|
||||||
|
'error_text' => $added > 0 ? 'Investigations added' : 'No new investigations added',
|
||||||
|
'csrfName' => csrf_token(),
|
||||||
|
'csrfHash' => csrf_hash(),
|
||||||
|
];
|
||||||
|
|
||||||
|
log_message(
|
||||||
|
'info',
|
||||||
|
'investigation_add_bulk opd_id={opd_id} session_id={session_id} total={total} added={added} skipped={skipped} elapsed_ms={elapsed_ms}',
|
||||||
|
[
|
||||||
|
'opd_id' => $opdId,
|
||||||
|
'session_id' => $sessionId,
|
||||||
|
'total' => count($tests),
|
||||||
|
'added' => $added,
|
||||||
|
'skipped' => $skipped,
|
||||||
|
'elapsed_ms' => (int) round((microtime(true) - $startedAt) * 1000),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return $respond($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildInvestigationLookupMaps(): array
|
||||||
|
{
|
||||||
|
$out = [
|
||||||
|
'by_name' => [],
|
||||||
|
'by_short' => [],
|
||||||
|
'by_alias' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (! $this->db->tableExists('investigation')) {
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
$invFields = $this->db->getFieldNames('investigation');
|
||||||
|
$invCodeField = $this->resolveFirstField($invFields, ['Code', 'code']);
|
||||||
|
$invNameField = $this->resolveFirstField($invFields, ['Name', 'name']);
|
||||||
|
$invShortField = $this->resolveFirstField($invFields, ['short_name', 'shortName', 'short']);
|
||||||
|
|
||||||
|
if ($invCodeField === null || $invNameField === null) {
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
$select = $invCodeField . ' as code, ' . $invNameField . ' as name';
|
||||||
|
if ($invShortField !== null) {
|
||||||
|
$select .= ', ' . $invShortField . ' as short_name';
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $this->db->table('investigation')->select($select)->get()->getResultArray();
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$code = trim((string) ($row['code'] ?? ''));
|
||||||
|
if ($code === '' || $code === '0') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = trim((string) ($row['name'] ?? ''));
|
||||||
|
$short = trim((string) ($row['short_name'] ?? ''));
|
||||||
|
|
||||||
|
$nameKey = $this->normalizeInvestigationLookupKey($name);
|
||||||
|
if ($nameKey !== '' && ! isset($out['by_name'][$nameKey])) {
|
||||||
|
$out['by_name'][$nameKey] = $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
$shortKey = $this->normalizeInvestigationLookupKey($short);
|
||||||
|
if ($shortKey !== '' && ! isset($out['by_short'][$shortKey])) {
|
||||||
|
$out['by_short'][$shortKey] = $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/\(([A-Za-z0-9\-\s\.]{2,20})\)/', $name, $m) === 1) {
|
||||||
|
$aliasKey = $this->normalizeInvestigationLookupKey((string) ($m[1] ?? ''));
|
||||||
|
if ($aliasKey !== '' && ! isset($out['by_alias'][$aliasKey])) {
|
||||||
|
$out['by_alias'][$aliasKey] = $code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$acronym = $this->buildInvestigationAcronym($name);
|
||||||
|
if ($acronym !== '' && ! isset($out['by_alias'][$acronym])) {
|
||||||
|
$out['by_alias'][$acronym] = $code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveInvestigationCodeByText(string $text, array $lookup): string
|
||||||
|
{
|
||||||
|
$key = $this->normalizeInvestigationLookupKey($text);
|
||||||
|
if ($key === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($lookup['by_name'][$key])) {
|
||||||
|
return (string) $lookup['by_name'][$key];
|
||||||
|
}
|
||||||
|
if (isset($lookup['by_short'][$key])) {
|
||||||
|
return (string) $lookup['by_short'][$key];
|
||||||
|
}
|
||||||
|
if (isset($lookup['by_alias'][$key])) {
|
||||||
|
return (string) $lookup['by_alias'][$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common profile aliases seen in legacy shortcuts.
|
||||||
|
$aliasFallback = [
|
||||||
|
'kft' => 'rft',
|
||||||
|
];
|
||||||
|
$fallback = $aliasFallback[$key] ?? '';
|
||||||
|
if ($fallback !== '' && isset($lookup['by_alias'][$fallback])) {
|
||||||
|
return (string) $lookup['by_alias'][$fallback];
|
||||||
|
}
|
||||||
|
if ($fallback !== '' && isset($lookup['by_short'][$fallback])) {
|
||||||
|
return (string) $lookup['by_short'][$fallback];
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalizeInvestigationLookupKey(string $value): string
|
||||||
|
{
|
||||||
|
$value = trim(strtolower($value));
|
||||||
|
if ($value === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return (string) (preg_replace('/[^a-z0-9]+/', '', $value) ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildInvestigationAcronym(string $name): string
|
||||||
|
{
|
||||||
|
$plain = preg_replace('/\([^)]*\)/', ' ', $name) ?? $name;
|
||||||
|
$plain = trim((string) preg_replace('/\s+/', ' ', $plain));
|
||||||
|
if ($plain === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$stopWords = [
|
||||||
|
'and', 'of', 'for', 'the', 'test', 'panel', 'routine', 'view', 'complete', 'count',
|
||||||
|
];
|
||||||
|
|
||||||
|
$parts = preg_split('/\s+/', strtolower($plain)) ?: [];
|
||||||
|
$acronym = '';
|
||||||
|
foreach ($parts as $part) {
|
||||||
|
$part = trim((string) preg_replace('/[^a-z0-9]/', '', $part));
|
||||||
|
if ($part === '' || in_array($part, $stopWords, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$acronym .= substr($part, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return strlen($acronym) >= 2 ? $acronym : '';
|
||||||
|
}
|
||||||
|
|
||||||
public function investigation_remove($id)
|
public function investigation_remove($id)
|
||||||
{
|
{
|
||||||
$table = $this->findExistingTable(['opd_prescription_investigation']);
|
$table = $this->findExistingTable(['opd_prescription_investigation']);
|
||||||
|
|
@ -7247,6 +7531,81 @@ class Opd_prescription extends BaseController
|
||||||
return $this->response->setJSON(['update' => 1, 'error_text' => 'Investigation removed']);
|
return $this->response->setJSON(['update' => 1, 'error_text' => 'Investigation removed']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function investigation_remove_bulk()
|
||||||
|
{
|
||||||
|
$startedAt = microtime(true);
|
||||||
|
$respond = function (array $payload, int $statusCode = 200) use ($startedAt) {
|
||||||
|
$payload['elapsed_ms'] = (int) round((microtime(true) - $startedAt) * 1000);
|
||||||
|
return $this->response->setStatusCode($statusCode)->setJSON($payload);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (! $this->request->isAJAX()) {
|
||||||
|
return $respond(['update' => 0, 'error_text' => 'Invalid request'], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rawIds = $this->request->getPost('ids');
|
||||||
|
$ids = [];
|
||||||
|
|
||||||
|
if (is_array($rawIds)) {
|
||||||
|
$ids = $rawIds;
|
||||||
|
} elseif (is_string($rawIds) && trim($rawIds) !== '') {
|
||||||
|
$decoded = json_decode($rawIds, true);
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
$ids = $decoded;
|
||||||
|
} else {
|
||||||
|
$ids = explode(',', $rawIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = array_values(array_unique(array_filter(array_map(static function ($id) {
|
||||||
|
return (int) $id;
|
||||||
|
}, $ids), static function ($id) {
|
||||||
|
return $id > 0;
|
||||||
|
})));
|
||||||
|
|
||||||
|
if (empty($ids)) {
|
||||||
|
return $respond(['update' => 0, 'error_text' => 'No investigations selected']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = $this->findExistingTable(['opd_prescription_investigation']);
|
||||||
|
if ($table === null) {
|
||||||
|
return $respond(['update' => 0, 'error_text' => 'Investigation table not found']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $this->db->getFieldNames($table);
|
||||||
|
if (! in_array('id', $fields, true)) {
|
||||||
|
return $respond(['update' => 0, 'error_text' => 'Investigation id field missing']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $this->db->table($table)->whereIn('id', $ids)->get()->getResultArray();
|
||||||
|
$this->db->table($table)->whereIn('id', $ids)->delete();
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$this->auditClinicalUpdate('opd_prescription_investigation', 'removed', (int) ($row['id'] ?? 0), $row, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = [
|
||||||
|
'update' => 1,
|
||||||
|
'removed_count' => count($rows),
|
||||||
|
'requested_count' => count($ids),
|
||||||
|
'error_text' => 'Investigations removed',
|
||||||
|
'csrfName' => csrf_token(),
|
||||||
|
'csrfHash' => csrf_hash(),
|
||||||
|
];
|
||||||
|
|
||||||
|
log_message(
|
||||||
|
'info',
|
||||||
|
'investigation_remove_bulk requested={requested} removed={removed} elapsed_ms={elapsed_ms}',
|
||||||
|
[
|
||||||
|
'requested' => count($ids),
|
||||||
|
'removed' => count($rows),
|
||||||
|
'elapsed_ms' => (int) round((microtime(true) - $startedAt) * 1000),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return $respond($result);
|
||||||
|
}
|
||||||
|
|
||||||
public function medicine_search()
|
public function medicine_search()
|
||||||
{
|
{
|
||||||
$q = trim((string) $this->request->getGet('q'));
|
$q = trim((string) $this->request->getGet('q'));
|
||||||
|
|
|
||||||
|
|
@ -341,6 +341,7 @@ class Patient extends BaseController
|
||||||
$txnId = trim((string) ($body['txnId'] ?? $body['txn_id'] ?? $this->request->getPost('txnId') ?? $this->request->getPost('txn_id') ?? ''));
|
$txnId = trim((string) ($body['txnId'] ?? $body['txn_id'] ?? $this->request->getPost('txnId') ?? $this->request->getPost('txn_id') ?? ''));
|
||||||
$otp = trim((string) ($body['otp'] ?? $this->request->getPost('otp') ?? ''));
|
$otp = trim((string) ($body['otp'] ?? $this->request->getPost('otp') ?? ''));
|
||||||
$mobile = trim((string) ($body['mobile'] ?? $this->request->getPost('mobile') ?? ''));
|
$mobile = trim((string) ($body['mobile'] ?? $this->request->getPost('mobile') ?? ''));
|
||||||
|
$requestedPid = (int) ($body['p_id'] ?? $this->request->getPost('p_id') ?? 0);
|
||||||
|
|
||||||
if ($txnId === '' || $otp === '') {
|
if ($txnId === '' || $otp === '') {
|
||||||
return $this->response->setJSON(['ok' => 0, 'error_text' => 'txnId and otp are required']);
|
return $this->response->setJSON(['ok' => 0, 'error_text' => 'txnId and otp are required']);
|
||||||
|
|
@ -352,6 +353,10 @@ class Patient extends BaseController
|
||||||
return $this->response->setStatusCode(500)->setJSON(['ok' => 0, 'error_text' => $e->getMessage()]);
|
return $this->response->setStatusCode(500)->setJSON(['ok' => 0, 'error_text' => $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! empty($result['ok']) && (int) $result['ok'] === 1) {
|
||||||
|
$result = $this->enrichAbhaVerifyResult($result, $requestedPid);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->response->setJSON($result);
|
return $this->response->setJSON($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -388,6 +393,7 @@ class Patient extends BaseController
|
||||||
$body = $isJson ? ($this->request->getJSON(true) ?? []) : [];
|
$body = $isJson ? ($this->request->getJSON(true) ?? []) : [];
|
||||||
$txnId = trim((string) ($body['txnId'] ?? $body['txn_id'] ?? $this->request->getPost('txnId') ?? $this->request->getPost('txn_id') ?? ''));
|
$txnId = trim((string) ($body['txnId'] ?? $body['txn_id'] ?? $this->request->getPost('txnId') ?? $this->request->getPost('txn_id') ?? ''));
|
||||||
$otp = trim((string) ($body['otp'] ?? $this->request->getPost('otp') ?? ''));
|
$otp = trim((string) ($body['otp'] ?? $this->request->getPost('otp') ?? ''));
|
||||||
|
$requestedPid = (int) ($body['p_id'] ?? $this->request->getPost('p_id') ?? 0);
|
||||||
|
|
||||||
if ($txnId === '' || $otp === '') {
|
if ($txnId === '' || $otp === '') {
|
||||||
return $this->response->setJSON(['ok' => 0, 'error_text' => 'txnId and otp are required']);
|
return $this->response->setJSON(['ok' => 0, 'error_text' => 'txnId and otp are required']);
|
||||||
|
|
@ -399,6 +405,10 @@ class Patient extends BaseController
|
||||||
return $this->response->setStatusCode(500)->setJSON(['ok' => 0, 'error_text' => $e->getMessage()]);
|
return $this->response->setStatusCode(500)->setJSON(['ok' => 0, 'error_text' => $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! empty($result['ok']) && (int) $result['ok'] === 1) {
|
||||||
|
$result = $this->enrichAbhaVerifyResult($result, $requestedPid);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->response->setJSON($result);
|
return $this->response->setJSON($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1332,6 +1342,286 @@ class Patient extends BaseController
|
||||||
return (int) $this->db->insertID();
|
return (int) $this->db->insertID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $result
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
private function enrichAbhaVerifyResult(array $result, int $requestedPid): array
|
||||||
|
{
|
||||||
|
$payload = [];
|
||||||
|
if (isset($result['data']) && is_array($result['data'])) {
|
||||||
|
$payload = $result['data'];
|
||||||
|
} elseif (is_array($result)) {
|
||||||
|
$payload = $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$profile = $this->pickGatewayAbhaProfile($payload);
|
||||||
|
$abhaNumberRaw = trim((string) ($profile['ABHANumber'] ?? $payload['ABHANumber'] ?? ''));
|
||||||
|
$abhaDigits = preg_replace('/\D/', '', $abhaNumberRaw);
|
||||||
|
$abhaAddress = trim((string) ($profile['preferredAbhaAddress'] ?? $payload['abha_address'] ?? ''));
|
||||||
|
$gatewayName = trim((string) ($profile['name'] ?? $payload['name'] ?? ''));
|
||||||
|
$gatewayMobile = trim((string) ($profile['mobile'] ?? $payload['mobile'] ?? $payload['mobileNumber'] ?? ''));
|
||||||
|
$gatewayDob = trim((string) ($profile['dob'] ?? $payload['dob'] ?? ''));
|
||||||
|
$gatewayGender = trim((string) ($profile['gender'] ?? $payload['gender'] ?? ''));
|
||||||
|
$gatewayPhoto = trim((string) ($profile['profilePhoto'] ?? $payload['profilePhoto'] ?? ''));
|
||||||
|
|
||||||
|
$patient = $this->findPatientForAbhaProfile($requestedPid, $abhaDigits, $gatewayMobile);
|
||||||
|
$patientId = (int) ($patient['id'] ?? 0);
|
||||||
|
$before = $patient !== null ? $this->buildPatientSnapshot($patient) : null;
|
||||||
|
|
||||||
|
$photoMeta = [
|
||||||
|
'saved' => false,
|
||||||
|
'path' => '',
|
||||||
|
'error' => '',
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($patientId > 0 && $this->db->tableExists('patient_master')) {
|
||||||
|
$updates = [];
|
||||||
|
|
||||||
|
if ($abhaDigits !== '') {
|
||||||
|
$abhaField = $this->resolvePatientAbhaIdField();
|
||||||
|
if ($abhaField !== null) {
|
||||||
|
$updates[$abhaField] = $abhaDigits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pmFields = $this->db->getFieldNames('patient_master') ?? [];
|
||||||
|
if ($abhaAddress !== '' && in_array('abha_address', $pmFields, true)) {
|
||||||
|
$updates['abha_address'] = $abhaAddress;
|
||||||
|
}
|
||||||
|
if (in_array('abdm_linked_at', $pmFields, true) && ($abhaDigits !== '' || $abhaAddress !== '')) {
|
||||||
|
$updates['abdm_linked_at'] = date('Y-m-d H:i:s');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($updates !== []) {
|
||||||
|
$this->db->table('patient_master')->where('id', $patientId)->update($updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($gatewayPhoto !== '') {
|
||||||
|
$photoMeta = $this->saveGatewayProfilePhotoToPatient($patientId, $gatewayPhoto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$afterPatient = null;
|
||||||
|
if ($patientId > 0 && $this->db->tableExists('patient_master')) {
|
||||||
|
$afterPatient = $this->db->table('patient_master')->where('id', $patientId)->get()->getRowArray();
|
||||||
|
}
|
||||||
|
$after = $afterPatient !== null ? $this->buildPatientSnapshot($afterPatient) : $before;
|
||||||
|
|
||||||
|
$result['gateway_user'] = [
|
||||||
|
'name' => $gatewayName,
|
||||||
|
'mobile' => $gatewayMobile,
|
||||||
|
'dob' => $gatewayDob,
|
||||||
|
'gender' => $gatewayGender,
|
||||||
|
'abha_number' => $abhaDigits,
|
||||||
|
'abha_address' => $abhaAddress,
|
||||||
|
'photo_base64' => $gatewayPhoto,
|
||||||
|
];
|
||||||
|
|
||||||
|
$result['hms_patient'] = [
|
||||||
|
'requested_patient_id' => $requestedPid > 0 ? $requestedPid : null,
|
||||||
|
'matched_patient_id' => $patientId > 0 ? $patientId : null,
|
||||||
|
'before' => $before,
|
||||||
|
'after' => $after,
|
||||||
|
'comparison' => [
|
||||||
|
'name_match_before' => $before !== null
|
||||||
|
? mb_strtolower(trim((string) ($before['name'] ?? ''))) === mb_strtolower($gatewayName)
|
||||||
|
: null,
|
||||||
|
'name_match_after' => $after !== null
|
||||||
|
? mb_strtolower(trim((string) ($after['name'] ?? ''))) === mb_strtolower($gatewayName)
|
||||||
|
: null,
|
||||||
|
'mobile_match_before' => $before !== null
|
||||||
|
? preg_replace('/\D/', '', (string) ($before['mobile'] ?? '')) === preg_replace('/\D/', '', $gatewayMobile)
|
||||||
|
: null,
|
||||||
|
'mobile_match_after' => $after !== null
|
||||||
|
? preg_replace('/\D/', '', (string) ($after['mobile'] ?? '')) === preg_replace('/\D/', '', $gatewayMobile)
|
||||||
|
: null,
|
||||||
|
'abha_match_before' => $before !== null
|
||||||
|
? preg_replace('/\D/', '', (string) ($before['abha_id'] ?? '')) === $abhaDigits
|
||||||
|
: null,
|
||||||
|
'abha_match_after' => $after !== null
|
||||||
|
? preg_replace('/\D/', '', (string) ($after['abha_id'] ?? '')) === $abhaDigits
|
||||||
|
: null,
|
||||||
|
'dob_match_before' => $before !== null
|
||||||
|
? trim((string) ($before['dob'] ?? '')) === trim($gatewayDob)
|
||||||
|
: null,
|
||||||
|
'dob_match_after' => $after !== null
|
||||||
|
? trim((string) ($after['dob'] ?? '')) === trim($gatewayDob)
|
||||||
|
: null,
|
||||||
|
'gender_match_before' => $before !== null
|
||||||
|
? $this->normalizeGenderForCompare((string) ($before['gender'] ?? '')) === $this->normalizeGenderForCompare($gatewayGender)
|
||||||
|
: null,
|
||||||
|
'gender_match_after' => $after !== null
|
||||||
|
? $this->normalizeGenderForCompare((string) ($after['gender'] ?? '')) === $this->normalizeGenderForCompare($gatewayGender)
|
||||||
|
: null,
|
||||||
|
],
|
||||||
|
'photo_update' => $photoMeta,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $payload
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
private function pickGatewayAbhaProfile(array $payload): array
|
||||||
|
{
|
||||||
|
if (isset($payload['gateway_patient']) && is_array($payload['gateway_patient'])) {
|
||||||
|
return $payload['gateway_patient'];
|
||||||
|
}
|
||||||
|
if (isset($payload['ABHAProfile']) && is_array($payload['ABHAProfile'])) {
|
||||||
|
return $payload['ABHAProfile'];
|
||||||
|
}
|
||||||
|
if (isset($payload['profile']) && is_array($payload['profile'])) {
|
||||||
|
return $payload['profile'];
|
||||||
|
}
|
||||||
|
if (isset($payload['accounts'][0]) && is_array($payload['accounts'][0])) {
|
||||||
|
return $payload['accounts'][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>|null
|
||||||
|
*/
|
||||||
|
private function findPatientForAbhaProfile(int $requestedPid, string $abhaDigits, string $mobile): ?array
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('patient_master')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($requestedPid > 0) {
|
||||||
|
$row = $this->db->table('patient_master')->where('id', $requestedPid)->get()->getRowArray();
|
||||||
|
if ($row !== null) {
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$abhaField = $this->resolvePatientAbhaIdField();
|
||||||
|
if ($abhaDigits !== '' && $abhaField !== null) {
|
||||||
|
$row = $this->db->table('patient_master')->where($abhaField, $abhaDigits)->get()->getRowArray();
|
||||||
|
if ($row !== null) {
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$mobileDigits = preg_replace('/\D/', '', $mobile);
|
||||||
|
if ($mobileDigits !== '') {
|
||||||
|
$row = $this->db->table('patient_master')->where('mphone1', $mobileDigits)->get()->getRowArray();
|
||||||
|
if ($row !== null) {
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $row
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
private function buildPatientSnapshot(array $row): array
|
||||||
|
{
|
||||||
|
$abhaField = $this->resolvePatientAbhaIdField();
|
||||||
|
$abhaId = $abhaField !== null ? trim((string) ($row[$abhaField] ?? '')) : '';
|
||||||
|
$abhaAddress = trim((string) ($row['abha_address'] ?? ''));
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => (int) ($row['id'] ?? 0),
|
||||||
|
'p_code' => trim((string) ($row['p_code'] ?? '')),
|
||||||
|
'name' => trim((string) ($row['p_fname'] ?? '')),
|
||||||
|
'mobile' => trim((string) ($row['mphone1'] ?? '')),
|
||||||
|
'dob' => trim((string) ($row['dob'] ?? '')),
|
||||||
|
'gender' => trim((string) ($row['gender'] ?? '')),
|
||||||
|
'abha_id' => $abhaId,
|
||||||
|
'abha_address' => $abhaAddress,
|
||||||
|
'profile_picture' => trim((string) ($row['profile_picture'] ?? '')),
|
||||||
|
'profile_file_id' => (int) ($row['profile_file_id'] ?? 0),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{saved:bool,path:string,error:string}
|
||||||
|
*/
|
||||||
|
private function saveGatewayProfilePhotoToPatient(int $pid, string $photoBase64): array
|
||||||
|
{
|
||||||
|
$raw = trim($photoBase64);
|
||||||
|
if ($pid <= 0 || $raw === '') {
|
||||||
|
return ['saved' => false, 'path' => '', 'error' => 'Missing patient id or photo.'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$mime = 'image/jpeg';
|
||||||
|
$encoded = $raw;
|
||||||
|
if (str_starts_with($raw, 'data:image')) {
|
||||||
|
$parts = explode('base64,', $raw, 2);
|
||||||
|
if (count($parts) !== 2) {
|
||||||
|
return ['saved' => false, 'path' => '', 'error' => 'Invalid image data URI.'];
|
||||||
|
}
|
||||||
|
if (preg_match('/data:(image\/[a-zA-Z0-9.+-]+);/i', $parts[0], $m) === 1) {
|
||||||
|
$mime = strtolower((string) ($m[1] ?? 'image/jpeg'));
|
||||||
|
}
|
||||||
|
$encoded = $parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
$binary = base64_decode($encoded, true);
|
||||||
|
if ($binary === false) {
|
||||||
|
return ['saved' => false, 'path' => '', 'error' => 'Unable to decode photo base64.'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$ext = match ($mime) {
|
||||||
|
'image/png' => 'png',
|
||||||
|
'image/gif' => 'gif',
|
||||||
|
'image/webp' => 'webp',
|
||||||
|
default => 'jpg',
|
||||||
|
};
|
||||||
|
|
||||||
|
$uploadPath = rtrim(FCPATH, '\\/') . '/uploads/patient';
|
||||||
|
if (! is_dir($uploadPath) && ! mkdir($uploadPath, 0755, true) && ! is_dir($uploadPath)) {
|
||||||
|
return ['saved' => false, 'path' => '', 'error' => 'Unable to create upload directory.'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileName = 'abha_profile_' . $pid . '_' . time() . '.' . $ext;
|
||||||
|
$absolutePath = $uploadPath . '/' . $fileName;
|
||||||
|
if (file_put_contents($absolutePath, $binary) === false) {
|
||||||
|
return ['saved' => false, 'path' => '', 'error' => 'Unable to write photo file.'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$publicPath = '/uploads/patient/' . $fileName;
|
||||||
|
$insertId = $this->insertFileUploadRecordFromData($pid, 'profile', $publicPath, $mime, strlen($binary));
|
||||||
|
|
||||||
|
$updates = [];
|
||||||
|
if ($insertId > 0) {
|
||||||
|
$updates['profile_file_id'] = $insertId;
|
||||||
|
}
|
||||||
|
if ($this->db->fieldExists('profile_picture', 'patient_master')) {
|
||||||
|
$updates['profile_picture'] = $publicPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($updates !== []) {
|
||||||
|
$this->db->table('patient_master')->where('id', $pid)->update($updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['saved' => true, 'path' => $publicPath, 'error' => ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalizeGenderForCompare(string $value): string
|
||||||
|
{
|
||||||
|
$v = strtoupper(trim($value));
|
||||||
|
if ($v === '1' || $v === 'M' || $v === 'MALE') {
|
||||||
|
return 'M';
|
||||||
|
}
|
||||||
|
if ($v === '2' || $v === 'F' || $v === 'FEMALE') {
|
||||||
|
return 'F';
|
||||||
|
}
|
||||||
|
if ($v === '3' || $v === 'O' || $v === 'OTHER') {
|
||||||
|
return 'O';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $v;
|
||||||
|
}
|
||||||
|
|
||||||
private function parseDate(?string $date): ?string
|
private function parseDate(?string $date): ?string
|
||||||
{
|
{
|
||||||
if ($date === null || trim($date) === '') {
|
if ($date === null || trim($date) === '') {
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ class AbdmGatewaySettings extends BaseController
|
||||||
}
|
}
|
||||||
|
|
||||||
$gwUrl = trim((string) $this->request->getPost('gateway_url'));
|
$gwUrl = trim((string) $this->request->getPost('gateway_url'));
|
||||||
$token = trim((string) $this->request->getPost('api_token'));
|
$token = $this->sanitizeBearerToken((string) $this->request->getPost('api_token'));
|
||||||
$hfrId = trim((string) $this->request->getPost('hfr_id'));
|
$hfrId = trim((string) $this->request->getPost('hfr_id'));
|
||||||
$hmsName = trim((string) $this->request->getPost('hms_name'));
|
$hmsName = trim((string) $this->request->getPost('hms_name'));
|
||||||
|
|
||||||
|
|
@ -118,7 +118,7 @@ class AbdmGatewaySettings extends BaseController
|
||||||
|
|
||||||
// Use posted values first, then stored settings
|
// Use posted values first, then stored settings
|
||||||
$gwUrl = trim((string) $this->request->getPost('gateway_url'));
|
$gwUrl = trim((string) $this->request->getPost('gateway_url'));
|
||||||
$token = trim((string) $this->request->getPost('api_token'));
|
$token = $this->sanitizeBearerToken((string) $this->request->getPost('api_token'));
|
||||||
$hfrId = trim((string) $this->request->getPost('hfr_id'));
|
$hfrId = trim((string) $this->request->getPost('hfr_id'));
|
||||||
|
|
||||||
if ($gwUrl === '') {
|
if ($gwUrl === '') {
|
||||||
|
|
@ -412,4 +412,16 @@ class AbdmGatewaySettings extends BaseController
|
||||||
}
|
}
|
||||||
return mb_substr($key, 0, 4) . str_repeat('*', mb_strlen($key) - 8) . mb_substr($key, -4);
|
return mb_substr($key, 0, 4) . str_repeat('*', mb_strlen($key) - 8) . mb_substr($key, -4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function sanitizeBearerToken(string $token): string
|
||||||
|
{
|
||||||
|
$token = trim($token);
|
||||||
|
if ($token === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (stripos($token, 'Bearer ') === 0) {
|
||||||
|
$token = trim(substr($token, 7));
|
||||||
|
}
|
||||||
|
return trim($token, " \t\n\r\0\x0B\"'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use Mpdf\Mpdf;
|
||||||
class Storestock extends BaseController
|
class Storestock extends BaseController
|
||||||
{
|
{
|
||||||
protected $db;
|
protected $db;
|
||||||
|
protected array $procedureExistsCache = [];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
|
@ -80,6 +81,96 @@ class Storestock extends BaseController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function normalizeUiDate(string $value): string
|
||||||
|
{
|
||||||
|
$value = trim($value);
|
||||||
|
if ($value === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$dt = \DateTime::createFromFormat('d/m/Y', $value);
|
||||||
|
if ($dt instanceof \DateTime) {
|
||||||
|
return $dt->format('Y-m-d');
|
||||||
|
}
|
||||||
|
|
||||||
|
$dt = \DateTime::createFromFormat('Y-m-d', $value);
|
||||||
|
return $dt instanceof \DateTime ? $dt->format('Y-m-d') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function purchaseReturnInvoiceNoColumn(): string
|
||||||
|
{
|
||||||
|
$fields = $this->db->getFieldNames('purchase_return_invoice') ?? [];
|
||||||
|
if (in_array('p_r_invoice_no', $fields, true)) {
|
||||||
|
return 'p_r_invoice_no';
|
||||||
|
}
|
||||||
|
|
||||||
|
return in_array('Invoice_no', $fields, true) ? 'Invoice_no' : 'id';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fetchPurchaseReturnItems(int $invId): array
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
$invId <= 0
|
||||||
|
|| ! $this->db->tableExists('purchase_return_invoice_item')
|
||||||
|
|| ! $this->db->tableExists('purchase_invoice_item')
|
||||||
|
) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->db->table('purchase_return_invoice_item r')
|
||||||
|
->select('p.*, r.purchase_inv_id, r.qty AS r_qty, r.id AS r_id', false)
|
||||||
|
->join('purchase_invoice_item p', 'r.purchase_item_id=p.id', 'inner')
|
||||||
|
->where('r.purchase_inv_id', $invId)
|
||||||
|
->orderBy('r.id', 'ASC')
|
||||||
|
->get()
|
||||||
|
->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildPurchaseReturnItemList(int $invId): string
|
||||||
|
{
|
||||||
|
return view('storestock/purchase_return_invoice_item', [
|
||||||
|
'purchase_return_invoice_item' => $this->fetchPurchaseReturnItems($invId),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function procedureExists(string $procedureName): bool
|
||||||
|
{
|
||||||
|
if (array_key_exists($procedureName, $this->procedureExistsCache)) {
|
||||||
|
return $this->procedureExistsCache[$procedureName];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$row = $this->db->query(
|
||||||
|
'SELECT ROUTINE_NAME FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = DATABASE() AND ROUTINE_TYPE = ? AND ROUTINE_NAME = ? LIMIT 1',
|
||||||
|
['PROCEDURE', $procedureName]
|
||||||
|
)->getRowArray();
|
||||||
|
|
||||||
|
$this->procedureExistsCache[$procedureName] = ! empty($row['ROUTINE_NAME']);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->procedureExistsCache[$procedureName] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->procedureExistsCache[$procedureName];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function runOptionalProcedure(string $procedureName, array $arguments = []): void
|
||||||
|
{
|
||||||
|
if (! $this->procedureExists($procedureName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = 'CALL ' . $procedureName . '(' . implode(', ', array_fill(0, count($arguments), '?')) . ')';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->db->query($sql, $arguments);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
log_message('warning', 'Optional procedure failed: {procedure} - {message}', [
|
||||||
|
'procedure' => $procedureName,
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function buildDashboardInsights(): array
|
private function buildDashboardInsights(): array
|
||||||
{
|
{
|
||||||
$todayIndents = (int) $this->safeScalar(
|
$todayIndents = (int) $this->safeScalar(
|
||||||
|
|
@ -618,7 +709,7 @@ class Storestock extends BaseController
|
||||||
if ($insert_id > 0) {
|
if ($insert_id > 0) {
|
||||||
// Call stored procs
|
// Call stored procs
|
||||||
$this->db->query("CALL p_stock_update_purchase_id_store({$inv_id})");
|
$this->db->query("CALL p_stock_update_purchase_id_store({$inv_id})");
|
||||||
$this->db->query("CALL p_update_med_GST_store({$inv_id})");
|
$this->runOptionalProcedure('p_update_med_GST_store', [$inv_id]);
|
||||||
|
|
||||||
$data['inv_stock_item'] = $this->db->query(
|
$data['inv_stock_item'] = $this->db->query(
|
||||||
"SELECT i.*, IFNULL(DATEDIFF(expiry, CURDATE()), 1000) AS no_day,
|
"SELECT i.*, IFNULL(DATEDIFF(expiry, CURDATE()), 1000) AS no_day,
|
||||||
|
|
@ -717,7 +808,7 @@ class Storestock extends BaseController
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call stored procedure to recalculate GST
|
// Call stored procedure to recalculate GST
|
||||||
$this->db->query("CALL p_update_med_GST_store({$inv_id})");
|
$this->runOptionalProcedure('p_update_med_GST_store', [$inv_id]);
|
||||||
|
|
||||||
$data['invoice_stock_master'] = $this->db->query(
|
$data['invoice_stock_master'] = $this->db->query(
|
||||||
"SELECT *, (discount_amount + item_discount_amount) AS inv_disc_total,
|
"SELECT *, (discount_amount + item_discount_amount) AS inv_disc_total,
|
||||||
|
|
@ -751,7 +842,7 @@ class Storestock extends BaseController
|
||||||
return $err;
|
return $err;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->db->query("CALL p_update_med_GST_store({$inv_id})");
|
$this->runOptionalProcedure('p_update_med_GST_store', [$inv_id]);
|
||||||
|
|
||||||
$data['inv_items'] = $this->db->query(
|
$data['inv_items'] = $this->db->query(
|
||||||
"SELECT i.indent_id, i.id, i.item_Name, i.formulation,
|
"SELECT i.indent_id, i.id, i.item_Name, i.formulation,
|
||||||
|
|
@ -948,4 +1039,570 @@ class Storestock extends BaseController
|
||||||
}
|
}
|
||||||
return view('storestock/main_store_dashboard');
|
return view('storestock/main_store_dashboard');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Purchase
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public function Purchase()
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['purchase_list'] = [];
|
||||||
|
if ($this->db->tableExists('purchase_invoice_stock')) {
|
||||||
|
$data['purchase_list'] = $this->db->table('purchase_invoice_stock p')
|
||||||
|
->select("p.id, p.Invoice_no, p.date_of_invoice, DATE_FORMAT(p.date_of_invoice,'%d-%m-%Y') AS str_date_of_invoice, p.sid, IFNULL(s.name_supplier,'-') AS name_supplier, IFNULL(s.short_name,'-') AS short_name, IFNULL(s.gst_no,'-') AS gst_no, IFNULL(p.T_Net_Amount,0) AS tamount, IFNULL(p.inv_status,0) AS inv_status, IFNULL(p.ischallan,0) AS ischallan", false)
|
||||||
|
->join('stock_supplier s', 'p.sid=s.sid', 'left')
|
||||||
|
->orderBy('p.id', 'DESC')
|
||||||
|
->limit(50)
|
||||||
|
->get()
|
||||||
|
->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('storestock/purchase', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function PurchaseInvoice()
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
$searchRaw = (string) ($this->request->getPost('txtsearch') ?? $this->request->getGet('txtsearch') ?? '');
|
||||||
|
$search = trim(preg_replace('/[^A-Za-z0-9_ \-]/', '', $searchRaw));
|
||||||
|
|
||||||
|
$data['purchase_list'] = [];
|
||||||
|
if ($this->db->tableExists('purchase_invoice_stock')) {
|
||||||
|
$builder = $this->db->table('purchase_invoice_stock p')
|
||||||
|
->select("p.id, p.Invoice_no, p.date_of_invoice, DATE_FORMAT(p.date_of_invoice,'%d-%m-%Y') AS str_date_of_invoice, p.sid, IFNULL(s.name_supplier,'-') AS name_supplier, IFNULL(s.short_name,'-') AS short_name, IFNULL(s.gst_no,'-') AS gst_no, IFNULL(p.T_Net_Amount,0) AS tamount, IFNULL(p.inv_status,0) AS inv_status, IFNULL(p.ischallan,0) AS ischallan", false)
|
||||||
|
->join('stock_supplier s', 'p.sid=s.sid', 'left');
|
||||||
|
|
||||||
|
if ($search !== '') {
|
||||||
|
$builder->groupStart();
|
||||||
|
$builder->like('p.Invoice_no', $search);
|
||||||
|
if (ctype_digit($search)) {
|
||||||
|
$builder->orWhere('p.id', (int) $search);
|
||||||
|
}
|
||||||
|
$builder->groupEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['purchase_list'] = $builder->orderBy('p.id', 'DESC')->limit(50)->get()->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('storestock/purchase_supp_list', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function PurchaseNew()
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['supplier_data'] = $this->db->tableExists('stock_supplier')
|
||||||
|
? $this->db->table('stock_supplier')->orderBy('name_supplier', 'ASC')->get()->getResult()
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return view('storestock/purchase_new_invoice', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function CreatePurchase()
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->request->isAJAX() || ! $this->db->tableExists('purchase_invoice_stock')) {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'insertid' => 0,
|
||||||
|
'show_text' => 'Invalid request',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sid = (int) ($this->request->getPost('input_supplier') ?? 0);
|
||||||
|
$invoiceNo = trim((string) ($this->request->getPost('input_invoicecode') ?? ''));
|
||||||
|
$invoiceDate = $this->normalizeUiDate((string) ($this->request->getPost('datepicker_invoice') ?? ''));
|
||||||
|
$billType = (int) ($this->request->getPost('cbo_billtype') ?? 0);
|
||||||
|
|
||||||
|
if ($sid <= 0 || $invoiceNo === '' || $invoiceDate === '') {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'insertid' => 0,
|
||||||
|
'show_text' => 'Supplier, invoice no and invoice date are required',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $this->db->getFieldNames('purchase_invoice_stock') ?? [];
|
||||||
|
$insert = [];
|
||||||
|
if (in_array('sid', $fields, true)) {
|
||||||
|
$insert['sid'] = $sid;
|
||||||
|
}
|
||||||
|
if (in_array('Invoice_no', $fields, true)) {
|
||||||
|
$insert['Invoice_no'] = $invoiceNo;
|
||||||
|
}
|
||||||
|
if (in_array('date_of_invoice', $fields, true)) {
|
||||||
|
$insert['date_of_invoice'] = $invoiceDate;
|
||||||
|
}
|
||||||
|
if (in_array('ischallan', $fields, true)) {
|
||||||
|
$insert['ischallan'] = $billType;
|
||||||
|
}
|
||||||
|
if (in_array('inv_status', $fields, true)) {
|
||||||
|
$insert['inv_status'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->table('purchase_invoice_stock')->insert($insert);
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'insertid' => (int) $this->db->insertID(),
|
||||||
|
'show_text' => 'Added Successfully',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function PurchaseMasterEdit(int $invId)
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['supplier_data'] = $this->db->tableExists('stock_supplier')
|
||||||
|
? $this->db->table('stock_supplier')->orderBy('name_supplier', 'ASC')->get()->getResult()
|
||||||
|
: [];
|
||||||
|
$data['inv_master_data'] = $this->db->query(
|
||||||
|
'SELECT p.*, s.name_supplier FROM purchase_invoice_stock p JOIN stock_supplier s ON p.sid=s.sid WHERE p.id = ?',
|
||||||
|
[$invId]
|
||||||
|
)->getResult();
|
||||||
|
$data['purchase_item'] = $this->db->query(
|
||||||
|
'SELECT * FROM purchase_invoice_item_stock WHERE purchase_id = ?',
|
||||||
|
[$invId]
|
||||||
|
)->getResult();
|
||||||
|
|
||||||
|
if (empty($data['inv_master_data'])) {
|
||||||
|
return $this->response->setStatusCode(404)->setBody('<div class="alert alert-warning m-3">Purchase invoice not found.</div>');
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('storestock/purchase_invoice_master_edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function PurchaseInvoiceEdit(int $invId)
|
||||||
|
{
|
||||||
|
return $this->PurchaseMasterEdit($invId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function UpdatePurchase()
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->request->isAJAX() || ! $this->db->tableExists('purchase_invoice_stock')) {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'insertid' => 0,
|
||||||
|
'show_text' => 'Invalid request',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$recId = (int) ($this->request->getPost('hid_purchaseid') ?? 0);
|
||||||
|
$sid = (int) ($this->request->getPost('input_supplier') ?? 0);
|
||||||
|
$invoiceNo = trim((string) ($this->request->getPost('input_invoicecode') ?? ''));
|
||||||
|
$invoiceDate = $this->normalizeUiDate((string) ($this->request->getPost('datepicker_invoice') ?? ''));
|
||||||
|
$billType = (int) ($this->request->getPost('cbo_billtype') ?? 0);
|
||||||
|
|
||||||
|
if ($recId <= 0 || $sid <= 0 || $invoiceNo === '' || $invoiceDate === '') {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'insertid' => 0,
|
||||||
|
'show_text' => 'Please fill supplier, invoice number and invoice date.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$update = [];
|
||||||
|
$fields = $this->db->getFieldNames('purchase_invoice_stock') ?? [];
|
||||||
|
if (in_array('sid', $fields, true)) {
|
||||||
|
$update['sid'] = $sid;
|
||||||
|
}
|
||||||
|
if (in_array('Invoice_no', $fields, true)) {
|
||||||
|
$update['Invoice_no'] = $invoiceNo;
|
||||||
|
}
|
||||||
|
if (in_array('date_of_invoice', $fields, true)) {
|
||||||
|
$update['date_of_invoice'] = $invoiceDate;
|
||||||
|
}
|
||||||
|
if (in_array('ischallan', $fields, true)) {
|
||||||
|
$update['ischallan'] = $billType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($update !== []) {
|
||||||
|
$this->db->table('purchase_invoice_stock')->where('id', $recId)->update($update);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'insertid' => 1,
|
||||||
|
'show_text' => 'Data Update',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function UpdatePurchaseInvoiceStatus(int $purchaseInvId, int $invStatus)
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->db->tableExists('purchase_invoice_stock')) {
|
||||||
|
$this->db->table('purchase_invoice_stock')->where('id', $purchaseInvId)->update([
|
||||||
|
'inv_status' => $invStatus,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to(site_url('Storestock/PurchaseMasterEdit/' . $purchaseInvId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function purchase_invoice_delete(int $invId)
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($invId <= 0 || ! $this->db->tableExists('purchase_invoice_stock')) {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'is_delete' => 0,
|
||||||
|
'show_text' => 'Invalid invoice',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$invoice = $this->db->table('purchase_invoice_stock')->where('id', $invId)->get()->getRow();
|
||||||
|
if (! $invoice) {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'is_delete' => 0,
|
||||||
|
'show_text' => 'Invoice not found',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->db->tableExists('purchase_invoice_item_stock')) {
|
||||||
|
$count = $this->db->table('purchase_invoice_item_stock')->where('purchase_id', $invId)->countAllResults();
|
||||||
|
if ($count > 0) {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'is_delete' => 0,
|
||||||
|
'show_text' => 'Invoice has items, empty the item list first',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((float) ($invoice->T_Net_Amount ?? 0) > 0) {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'is_delete' => 0,
|
||||||
|
'show_text' => 'Cannot delete invoice with amount',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->table('purchase_invoice_stock')->where('id', $invId)->delete();
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'is_delete' => 1,
|
||||||
|
'show_text' => 'Deleted Successfully',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function print_purchase(int $invId)
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
$invoice = $this->db->query(
|
||||||
|
'SELECT p.id, p.Invoice_no, p.date_of_invoice, DATE_FORMAT(p.date_of_invoice,\'%d/%m/%Y\') AS str_date_of_invoice, p.sid, IFNULL(s.name_supplier,\'-\') AS name_supplier, IFNULL(s.short_name,\'-\') AS short_name, IFNULL(s.gst_no,\'-\') AS gst_no
|
||||||
|
FROM purchase_invoice_stock p JOIN stock_supplier s ON p.sid=s.sid WHERE p.id = ?',
|
||||||
|
[$invId]
|
||||||
|
)->getRow();
|
||||||
|
|
||||||
|
if (! $invoice) {
|
||||||
|
return $this->response->setStatusCode(404)->setBody('Purchase invoice not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $this->db->query('SELECT * FROM purchase_invoice_item_stock WHERE purchase_id = ? ORDER BY id ASC', [$invId])->getResult();
|
||||||
|
|
||||||
|
return view('storestock/purchase_print', [
|
||||||
|
'purchase_invoice' => $invoice,
|
||||||
|
'purchase_invoice_item' => $items,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Purchase Return
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public function Purchase_return()
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('storestock/purchase_return');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function PurchaseReturnNew()
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
$supplierData = [];
|
||||||
|
if ($this->db->tableExists('med_supplier')) {
|
||||||
|
$supplierData = $this->db->query('SELECT * FROM med_supplier ORDER BY name_supplier')->getResult();
|
||||||
|
} elseif ($this->db->tableExists('stock_supplier')) {
|
||||||
|
$supplierData = $this->db->query('SELECT * FROM stock_supplier ORDER BY name_supplier')->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('storestock/purchase_return_new', [
|
||||||
|
'supplier_data' => $supplierData,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function PurchaseReturnInvoice()
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = [];
|
||||||
|
if ($this->db->tableExists('purchase_return_invoice')) {
|
||||||
|
$searchRaw = (string) ($this->request->getPost('txtsearch') ?? $this->request->getGet('txtsearch') ?? '');
|
||||||
|
$search = preg_replace('/[^A-Za-z0-9_ \-]/', '', trim($searchRaw));
|
||||||
|
$invNoCol = $this->purchaseReturnInvoiceNoColumn();
|
||||||
|
|
||||||
|
$builder = $this->db->table('purchase_return_invoice p')
|
||||||
|
->select("p.id, p.{$invNoCol} AS p_r_invoice_no, p.date_of_invoice, DATE_FORMAT(p.date_of_invoice,'%d-%m-%Y') AS str_date_of_invoice, p.sid, IFNULL(s.name_supplier,'-') AS name_supplier, IFNULL(s.short_name,'-') AS short_name", false)
|
||||||
|
->join('med_supplier s', 'p.sid=s.sid', 'left');
|
||||||
|
|
||||||
|
if ($search !== '') {
|
||||||
|
$builder->groupStart();
|
||||||
|
$builder->like("p.{$invNoCol}", $search);
|
||||||
|
if (ctype_digit($search)) {
|
||||||
|
$builder->orWhere('p.id', (int) $search);
|
||||||
|
}
|
||||||
|
$builder->groupEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $builder->orderBy('p.id', 'DESC')->limit(50)->get()->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('storestock/purchase_return_supplier_list', [
|
||||||
|
'purchase_return_invoice' => $rows,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function PurchaseReturnInvoiceEdit(int $invId)
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($invId <= 0 || ! $this->db->tableExists('purchase_return_invoice')) {
|
||||||
|
return $this->response->setStatusCode(404)->setBody('<div class="alert alert-warning m-3">Purchase return invoice not found.</div>');
|
||||||
|
}
|
||||||
|
|
||||||
|
$invNoCol = $this->purchaseReturnInvoiceNoColumn();
|
||||||
|
$invoice = $this->db->table('purchase_return_invoice p')
|
||||||
|
->select("p.id, p.{$invNoCol} AS Invoice_no, p.date_of_invoice, DATE_FORMAT(p.date_of_invoice,'%d/%m/%Y') AS str_date_of_invoice, p.sid, p.status AS inv_status, IFNULL(s.name_supplier,'-') AS name_supplier, IFNULL(s.short_name,'-') AS short_name, IFNULL(s.gst_no,'-') AS gst_no", false)
|
||||||
|
->join('med_supplier s', 'p.sid=s.sid', 'left')
|
||||||
|
->where('p.id', $invId)
|
||||||
|
->get()
|
||||||
|
->getRow();
|
||||||
|
|
||||||
|
if (! $invoice) {
|
||||||
|
return $this->response->setStatusCode(404)->setBody('<div class="alert alert-warning m-3">Purchase return invoice not found.</div>');
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $this->fetchPurchaseReturnItems($invId);
|
||||||
|
|
||||||
|
return view('storestock/purchase_return_invoice_edit', [
|
||||||
|
'purchase_return_invoice' => $invoice,
|
||||||
|
'purchase_return_invoice_item' => $items,
|
||||||
|
'content' => $this->buildPurchaseReturnItemList($invId),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function PurchaseReturn_invoice_item_list(int $invId)
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
$invoice = null;
|
||||||
|
if ($invId > 0 && $this->db->tableExists('purchase_return_invoice')) {
|
||||||
|
$invoice = $this->db->table('purchase_return_invoice')->where('id', $invId)->get()->getRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('storestock/purchase_return_invoice_item', [
|
||||||
|
'purchase_return_invoice_item' => $this->fetchPurchaseReturnItems($invId),
|
||||||
|
'purchase_return_invoice' => $invoice ? [$invoice] : [],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create_purchase_return()
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->request->isAJAX() || ! $this->db->tableExists('purchase_return_invoice')) {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'insertid' => 0,
|
||||||
|
'show_text' => 'Invalid request',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sid = (int) ($this->request->getPost('input_supplier') ?? 0);
|
||||||
|
$invoiceDate = $this->normalizeUiDate((string) ($this->request->getPost('datepicker_invoice') ?? ''));
|
||||||
|
|
||||||
|
if ($sid <= 0 || $invoiceDate === '') {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'insertid' => 0,
|
||||||
|
'show_text' => 'Supplier and date are required',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $this->db->getFieldNames('purchase_return_invoice') ?? [];
|
||||||
|
$insert = [];
|
||||||
|
if (in_array('sid', $fields, true)) {
|
||||||
|
$insert['sid'] = $sid;
|
||||||
|
}
|
||||||
|
if (in_array('date_of_invoice', $fields, true)) {
|
||||||
|
$insert['date_of_invoice'] = $invoiceDate;
|
||||||
|
}
|
||||||
|
if (in_array('status', $fields, true)) {
|
||||||
|
$insert['status'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->table('purchase_return_invoice')->insert($insert);
|
||||||
|
$insertId = (int) $this->db->insertID();
|
||||||
|
|
||||||
|
if ($insertId > 0 && in_array('p_r_invoice_no', $fields, true)) {
|
||||||
|
$invoiceNo = 'PR' . date('ym') . str_pad((string) ($insertId % 1000), 3, '0', STR_PAD_LEFT);
|
||||||
|
$this->db->table('purchase_return_invoice')->where('id', $insertId)->update(['p_r_invoice_no' => $invoiceNo]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'insertid' => $insertId,
|
||||||
|
'show_text' => $insertId > 0 ? 'Added Successfully' : 'Unable to create purchase return invoice',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add_remove_item()
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
$invId = (int) ($this->request->getPost('inv_id') ?? 0);
|
||||||
|
$itemId = (int) ($this->request->getPost('itemid') ?? 0);
|
||||||
|
$qty = (float) ($this->request->getPost('rqty') ?? 0);
|
||||||
|
$batchNo = trim((string) ($this->request->getPost('rbatch_no') ?? ''));
|
||||||
|
$expiry = trim((string) ($this->request->getPost('rexpiry_dt') ?? ''));
|
||||||
|
|
||||||
|
if ($invId <= 0 || $itemId <= 0 || $qty <= 0 || ! $this->db->tableExists('purchase_return_invoice_item') || ! $this->db->tableExists('purchase_invoice_item')) {
|
||||||
|
return $this->response->setJSON(['update' => 0, 'msg_text' => 'Invalid return input', 'content' => '']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sourceItem = $this->db->table('purchase_invoice_item')->where('id', $itemId)->get()->getRow();
|
||||||
|
if (! $sourceItem) {
|
||||||
|
return $this->response->setJSON(['update' => 0, 'msg_text' => 'No item found', 'content' => '']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$insert = [];
|
||||||
|
$fields = $this->db->getFieldNames('purchase_return_invoice_item') ?? [];
|
||||||
|
if (in_array('purchase_inv_id', $fields, true)) {
|
||||||
|
$insert['purchase_inv_id'] = $invId;
|
||||||
|
}
|
||||||
|
if (in_array('purchase_item_id', $fields, true)) {
|
||||||
|
$insert['purchase_item_id'] = $itemId;
|
||||||
|
}
|
||||||
|
if (in_array('item_code', $fields, true)) {
|
||||||
|
$insert['item_code'] = $sourceItem->item_code ?? 0;
|
||||||
|
}
|
||||||
|
if (in_array('Item_name', $fields, true)) {
|
||||||
|
$insert['Item_name'] = $sourceItem->Item_name ?? ($sourceItem->item_name ?? '');
|
||||||
|
}
|
||||||
|
if (in_array('batch_no_r', $fields, true)) {
|
||||||
|
$insert['batch_no_r'] = $batchNo;
|
||||||
|
}
|
||||||
|
if (in_array('expiry_date_r', $fields, true)) {
|
||||||
|
$insert['expiry_date_r'] = $expiry !== '' ? $expiry : null;
|
||||||
|
}
|
||||||
|
if (in_array('qty', $fields, true)) {
|
||||||
|
$insert['qty'] = $qty;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->table('purchase_return_invoice_item')->insert($insert);
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'update' => 1,
|
||||||
|
'msg_text' => 'Item Added',
|
||||||
|
'content' => $this->buildPurchaseReturnItemList($invId),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remove_item_invoice(int $itemId)
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = null;
|
||||||
|
if ($itemId > 0 && $this->db->tableExists('purchase_return_invoice_item')) {
|
||||||
|
$row = $this->db->table('purchase_return_invoice_item')->where('id', $itemId)->get()->getRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $row) {
|
||||||
|
return $this->response->setJSON(['update' => 0, 'msg_text' => 'No item found', 'content' => '']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$invId = (int) ($row->purchase_inv_id ?? 0);
|
||||||
|
$this->db->table('purchase_return_invoice_item')->where('id', $itemId)->delete();
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'update' => 1,
|
||||||
|
'msg_text' => 'Item Removed',
|
||||||
|
'content' => $this->buildPurchaseReturnItemList($invId),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function print_purchase_return(int $invId)
|
||||||
|
{
|
||||||
|
if ($err = $this->ensureStoreAccess()) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($invId <= 0 || ! $this->db->tableExists('purchase_return_invoice')) {
|
||||||
|
return $this->response->setStatusCode(404)->setBody('Purchase return invoice not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
$invNoCol = $this->purchaseReturnInvoiceNoColumn();
|
||||||
|
$invoice = $this->db->table('purchase_return_invoice p')
|
||||||
|
->select("p.id, p.{$invNoCol} AS Invoice_no, p.date_of_invoice, DATE_FORMAT(p.date_of_invoice,'%d/%m/%Y') AS str_date_of_invoice, p.sid, IFNULL(s.name_supplier,'-') AS name_supplier, IFNULL(s.short_name,'-') AS short_name, IFNULL(s.gst_no,'-') AS gst_no", false)
|
||||||
|
->join('med_supplier s', 'p.sid=s.sid', 'left')
|
||||||
|
->where('p.id', $invId)
|
||||||
|
->get()
|
||||||
|
->getRow();
|
||||||
|
|
||||||
|
if (! $invoice) {
|
||||||
|
return $this->response->setStatusCode(404)->setBody('Purchase return invoice not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $this->fetchPurchaseReturnItems($invId);
|
||||||
|
$html = view('storestock/purchase_return_invoice_item_print', [
|
||||||
|
'purchase_return_invoice' => [$invoice],
|
||||||
|
'purchase_return_invoice_item' => $items,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$mpdf = new Mpdf([
|
||||||
|
'mode' => 'utf-8',
|
||||||
|
'format' => 'A4',
|
||||||
|
'tempDir' => WRITEPATH . 'cache/mpdf',
|
||||||
|
]);
|
||||||
|
$mpdf->showWatermarkText = false;
|
||||||
|
$mpdf->WriteHTML($html);
|
||||||
|
|
||||||
|
$filename = 'Return_Invoice-' . $invId . '-' . date('YmdHis') . '.pdf';
|
||||||
|
return $this->response
|
||||||
|
->setHeader('Content-Type', 'application/pdf')
|
||||||
|
->setBody($mpdf->Output($filename, 'S'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
|
||||||
$config = config('AbdmConnector');
|
$config = config('AbdmConnector');
|
||||||
|
|
||||||
$this->baseUrl = rtrim((string) ($config->eatriaBridgeUrl ?? 'https://abdm-bridge.e-atria.in/api'), '/');
|
$this->baseUrl = rtrim((string) ($config->eatriaBridgeUrl ?? 'https://abdm-bridge.e-atria.in/api'), '/');
|
||||||
$this->token = (string) ($config->eatriaBridgeToken ?? '');
|
$this->token = $this->sanitizeBearerToken((string) ($config->eatriaBridgeToken ?? ''));
|
||||||
$this->hfrId = '';
|
$this->hfrId = '';
|
||||||
$this->timeoutSec = (int) ($config->eatriaBridgeTimeoutSec ?? 30);
|
$this->timeoutSec = (int) ($config->eatriaBridgeTimeoutSec ?? 30);
|
||||||
|
|
||||||
|
|
@ -41,14 +41,19 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
|
||||||
if ($db->tableExists('hospital_setting')) {
|
if ($db->tableExists('hospital_setting')) {
|
||||||
$rows = $db->table('hospital_setting')
|
$rows = $db->table('hospital_setting')
|
||||||
->select('s_name, s_value')
|
->select('s_name, s_value')
|
||||||
->whereIn('s_name', ['EATRIA_BRIDGE_TOKEN', 'EATRIA_BRIDGE_URL', 'ABDM_HFR_ID'])
|
->whereIn('s_name', ['EATRIA_BRIDGE_TOKEN', 'ABDM_BRIDGE_TOKEN', 'EATRIA_BRIDGE_URL', 'ABDM_HFR_ID'])
|
||||||
->get()
|
->get()
|
||||||
->getResultArray();
|
->getResultArray();
|
||||||
|
|
||||||
$dbSettings = array_column($rows, 's_value', 's_name');
|
$dbSettings = array_column($rows, 's_value', 's_name');
|
||||||
|
|
||||||
if (! empty($dbSettings['EATRIA_BRIDGE_TOKEN'])) {
|
$dbToken = (string) ($dbSettings['EATRIA_BRIDGE_TOKEN'] ?? '');
|
||||||
$this->token = trim($dbSettings['EATRIA_BRIDGE_TOKEN']);
|
if ($dbToken === '') {
|
||||||
|
// Backward compatibility: older setups may store the key under ABDM_BRIDGE_TOKEN.
|
||||||
|
$dbToken = (string) ($dbSettings['ABDM_BRIDGE_TOKEN'] ?? '');
|
||||||
|
}
|
||||||
|
if ($dbToken !== '') {
|
||||||
|
$this->token = $this->sanitizeBearerToken($dbToken);
|
||||||
}
|
}
|
||||||
if (! empty($dbSettings['EATRIA_BRIDGE_URL'])) {
|
if (! empty($dbSettings['EATRIA_BRIDGE_URL'])) {
|
||||||
$this->baseUrl = rtrim(trim($dbSettings['EATRIA_BRIDGE_URL']), '/');
|
$this->baseUrl = rtrim(trim($dbSettings['EATRIA_BRIDGE_URL']), '/');
|
||||||
|
|
@ -67,6 +72,21 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
|
||||||
return 'eatria_bridge';
|
return 'eatria_bridge';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function sanitizeBearerToken(string $token): string
|
||||||
|
{
|
||||||
|
$token = trim($token);
|
||||||
|
if ($token === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operators sometimes paste "Bearer <token>" into settings.
|
||||||
|
if (stripos($token, 'Bearer ') === 0) {
|
||||||
|
$token = trim(substr($token, 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
return trim($token, " \t\n\r\0\x0B\"'");
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Internal HTTP helper
|
// Internal HTTP helper
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
@ -221,6 +241,9 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
|
||||||
public function validateAbha(string $abhaId, array $fullPayload = []): array
|
public function validateAbha(string $abhaId, array $fullPayload = []): array
|
||||||
{
|
{
|
||||||
$body = $fullPayload !== [] ? $fullPayload : ['abha_id' => $abhaId];
|
$body = $fullPayload !== [] ? $fullPayload : ['abha_id' => $abhaId];
|
||||||
|
if ($this->hfrId !== '' && empty($body['hfr_id'])) {
|
||||||
|
$body['hfr_id'] = $this->hfrId;
|
||||||
|
}
|
||||||
return $this->post('/v3/abha/validate', $body);
|
return $this->post('/v3/abha/validate', $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,6 +257,9 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
|
||||||
$body = [
|
$body = [
|
||||||
'aadhaar' => (string) ($payload['aadhaar'] ?? $payload['loginId'] ?? ''),
|
'aadhaar' => (string) ($payload['aadhaar'] ?? $payload['loginId'] ?? ''),
|
||||||
];
|
];
|
||||||
|
if ($this->hfrId !== '' && empty($body['hfr_id'])) {
|
||||||
|
$body['hfr_id'] = $this->hfrId;
|
||||||
|
}
|
||||||
return $this->post('/v3/abha/aadhaar/generate-otp', $body);
|
return $this->post('/v3/abha/aadhaar/generate-otp', $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,6 +276,9 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
|
||||||
if (preg_match('/^\d{10}$/', $mobile)) {
|
if (preg_match('/^\d{10}$/', $mobile)) {
|
||||||
$body['mobile'] = $mobile;
|
$body['mobile'] = $mobile;
|
||||||
}
|
}
|
||||||
|
if ($this->hfrId !== '' && empty($body['hfr_id'])) {
|
||||||
|
$body['hfr_id'] = $this->hfrId;
|
||||||
|
}
|
||||||
return $this->post('/v3/abha/aadhaar/verify-otp', $body);
|
return $this->post('/v3/abha/aadhaar/verify-otp', $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,6 +289,9 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
|
||||||
$body = [
|
$body = [
|
||||||
'mobile' => (string) ($payload['mobile'] ?? $payload['loginId'] ?? ''),
|
'mobile' => (string) ($payload['mobile'] ?? $payload['loginId'] ?? ''),
|
||||||
];
|
];
|
||||||
|
if ($this->hfrId !== '' && empty($body['hfr_id'])) {
|
||||||
|
$body['hfr_id'] = $this->hfrId;
|
||||||
|
}
|
||||||
return $this->post('/v3/abha/mobile/generate-otp', $body);
|
return $this->post('/v3/abha/mobile/generate-otp', $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,6 +302,9 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
|
||||||
'txnId' => (string) ($payload['txnId'] ?? $payload['txn_id'] ?? ''),
|
'txnId' => (string) ($payload['txnId'] ?? $payload['txn_id'] ?? ''),
|
||||||
'otp' => (string) ($payload['otp'] ?? ''),
|
'otp' => (string) ($payload['otp'] ?? ''),
|
||||||
];
|
];
|
||||||
|
if ($this->hfrId !== '' && empty($body['hfr_id'])) {
|
||||||
|
$body['hfr_id'] = $this->hfrId;
|
||||||
|
}
|
||||||
return $this->post('/v3/abha/mobile/verify-otp', $body);
|
return $this->post('/v3/abha/mobile/verify-otp', $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -296,6 +296,12 @@ $paymentHistoryRows = $payment_history_rows ?? [];
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<?php
|
||||||
|
$appointmentDateRaw = (string) ($opd_master[0]->apointment_date ?? '');
|
||||||
|
$appointmentTimestamp = $appointmentDateRaw !== '' ? strtotime($appointmentDateRaw) : false;
|
||||||
|
$appointmentDateValue = $appointmentTimestamp !== false ? date('Y-m-d', $appointmentTimestamp) : '';
|
||||||
|
$appointmentDateDisplay = $appointmentTimestamp !== false ? date('d/m/Y', $appointmentTimestamp) : '';
|
||||||
|
?>
|
||||||
<div class="row g-3 mt-2">
|
<div class="row g-3 mt-2">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label>Change Doctor</label>
|
<label>Change Doctor</label>
|
||||||
|
|
@ -306,8 +312,8 @@ $paymentHistoryRows = $payment_history_rows ?? [];
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label>Date <?= esc(MysqlDate_to_str($opd_master[0]->apointment_date ?? '')) ?></label>
|
<label>Date <?= esc($appointmentDateDisplay) ?></label>
|
||||||
<input class="form-control datepicker" id="datepicker_opddate" name="datepicker_opddate" type="text" value="<?= esc(MysqlDate_to_str($opd_master[0]->apointment_date ?? '')) ?>" />
|
<input class="form-control" id="datepicker_opddate" name="datepicker_opddate" type="date" value="<?= esc($appointmentDateValue) ?>" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label>OPD Fee <?= esc($opd_master[0]->opd_fee_gross_amount ?? '') ?> / <?= esc($opd_master[0]->opd_fee_amount ?? '') ?></label>
|
<label>OPD Fee <?= esc($opd_master[0]->opd_fee_gross_amount ?? '') ?> / <?= esc($opd_master[0]->opd_fee_amount ?? '') ?></label>
|
||||||
|
|
|
||||||
|
|
@ -1576,6 +1576,8 @@
|
||||||
var legacyInvestigationProfileList = [];
|
var legacyInvestigationProfileList = [];
|
||||||
var legacyInvestigationShortTests = [];
|
var legacyInvestigationShortTests = [];
|
||||||
var investigationBatchActive = false;
|
var investigationBatchActive = false;
|
||||||
|
var investigationListLoading = false;
|
||||||
|
var investigationListPending = false;
|
||||||
var activeMedicineScope = 'all';
|
var activeMedicineScope = 'all';
|
||||||
var medicineSuggestRows = [];
|
var medicineSuggestRows = [];
|
||||||
var medicineSearchCache = {};
|
var medicineSearchCache = {};
|
||||||
|
|
@ -6589,37 +6591,6 @@
|
||||||
return (name || '').toString().trim().toLowerCase();
|
return (name || '').toString().trim().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addInvestigationEntry(name, code, done) {
|
|
||||||
name = (name || '').trim();
|
|
||||||
code = (code || '').trim();
|
|
||||||
if (!name) {
|
|
||||||
if (typeof done === 'function') {
|
|
||||||
done(false);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureSession(function(sid) {
|
|
||||||
apiPost('<?= base_url('Opd_prescription/investigation_add') ?>', {
|
|
||||||
opd_id: $('#opd_id').val(),
|
|
||||||
opd_session_id: sid,
|
|
||||||
investigation_code: code,
|
|
||||||
investigation_name: name
|
|
||||||
}, function(data) {
|
|
||||||
if (data.update == 1) {
|
|
||||||
$('#opd_session_id').val(data.opd_session_id || sid);
|
|
||||||
if (typeof done === 'function') {
|
|
||||||
done(true);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (typeof done === 'function') {
|
|
||||||
done(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function batchAddInvestigations(tests) {
|
function batchAddInvestigations(tests) {
|
||||||
tests = Array.isArray(tests) ? tests : [];
|
tests = Array.isArray(tests) ? tests : [];
|
||||||
if (!tests.length) {
|
if (!tests.length) {
|
||||||
|
|
@ -6663,41 +6634,56 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
investigationBatchActive = true;
|
investigationBatchActive = true;
|
||||||
|
ensureSession(function(sid) {
|
||||||
var idx = 0;
|
apiPost('<?= base_url('Opd_prescription/investigation_add_bulk') ?>', {
|
||||||
var added = 0;
|
opd_id: $('#opd_id').val(),
|
||||||
function next() {
|
opd_session_id: sid,
|
||||||
if (idx >= queue.length) {
|
tests: queue
|
||||||
|
}, function(data) {
|
||||||
investigationBatchActive = false;
|
investigationBatchActive = false;
|
||||||
loadInvestigationList();
|
if (data.update == 1) {
|
||||||
$('.jsError').removeClass('text-danger text-muted').addClass('text-success').text(added + ' test(s) added.');
|
$('#opd_session_id').val(data.opd_session_id || sid);
|
||||||
markDirty('Investigation shortcuts applied');
|
loadInvestigationList();
|
||||||
return;
|
var addedCount = parseInt(data.added_count || '0', 10);
|
||||||
}
|
var skippedCount = parseInt(data.skipped_count || '0', 10);
|
||||||
var row = queue[idx++] || {};
|
var msg = addedCount + ' test(s) added.';
|
||||||
var key = normalizeInvestigationKey(row.name || '');
|
if (skippedCount > 0) {
|
||||||
addInvestigationEntry((row.name || '').toString(), (row.code || '').toString(), function(ok) {
|
msg += ' ' + skippedCount + ' duplicate/empty skipped.';
|
||||||
if (ok) {
|
|
||||||
added++;
|
|
||||||
if (key) {
|
|
||||||
existing[key] = true;
|
|
||||||
}
|
}
|
||||||
|
$('.jsError').removeClass('text-danger text-muted').addClass('text-success').text(msg);
|
||||||
|
if (addedCount > 0) {
|
||||||
|
markDirty('Investigation shortcuts applied');
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
next();
|
$('.jsError').removeClass('text-success text-muted').addClass('text-danger').text(data.error_text || 'Unable to add investigations.');
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
next();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadInvestigationList() {
|
function loadInvestigationList() {
|
||||||
var opdId = $('#opd_id').val();
|
var opdId = $('#opd_id').val();
|
||||||
var sid = parseInt($('#opd_session_id').val() || '0', 10);
|
var sid = parseInt($('#opd_session_id').val() || '0', 10);
|
||||||
if (sid <= 0) {
|
if (sid <= 0) {
|
||||||
|
investigationListLoading = false;
|
||||||
|
investigationListPending = false;
|
||||||
renderInvestigation([]);
|
renderInvestigation([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (investigationListLoading) {
|
||||||
|
investigationListPending = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
investigationListLoading = true;
|
||||||
apiGet('<?= base_url('Opd_prescription/investigation_list') ?>/' + opdId + '/' + sid, function(data) {
|
apiGet('<?= base_url('Opd_prescription/investigation_list') ?>/' + opdId + '/' + sid, function(data) {
|
||||||
|
investigationListLoading = false;
|
||||||
renderInvestigation(data.rows || []);
|
renderInvestigation(data.rows || []);
|
||||||
|
if (investigationListPending) {
|
||||||
|
investigationListPending = false;
|
||||||
|
loadInvestigationList();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6753,15 +6739,11 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
addInvestigationEntry(name, code, function(ok) {
|
batchAddInvestigationRows([{ name: name, code: code }]);
|
||||||
if (ok) {
|
$('#investigation_name').val('');
|
||||||
$('#investigation_name').val('');
|
if ($.fn && $.fn.select2 && $('#investigation_name_select2').length) {
|
||||||
if ($.fn && $.fn.select2 && $('#investigation_name_select2').length) {
|
$('#investigation_name_select2').val(null).trigger('change');
|
||||||
$('#investigation_name_select2').val(null).trigger('change');
|
}
|
||||||
}
|
|
||||||
loadInvestigationList();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#btn_clear_investigation').on('click', function() {
|
$('#btn_clear_investigation').on('click', function() {
|
||||||
|
|
@ -6777,24 +6759,23 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var index = 0;
|
apiPost('<?= base_url('Opd_prescription/investigation_remove_bulk') ?>', {
|
||||||
function removeNext() {
|
ids: ids
|
||||||
if (index >= ids.length) {
|
}, function(data) {
|
||||||
|
if (data.update == 1) {
|
||||||
loadInvestigationList();
|
loadInvestigationList();
|
||||||
$('.jsError').removeClass('text-danger text-muted').addClass('text-success').text('All investigations removed.');
|
$('.jsError').removeClass('text-danger text-muted').addClass('text-success').text('All investigations removed.');
|
||||||
markDirty('Investigation list cleared');
|
markDirty('Investigation list cleared');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
$('.jsError').removeClass('text-success text-muted').addClass('text-danger').text(data.error_text || 'Unable to remove investigations.');
|
||||||
var id = ids[index++];
|
});
|
||||||
apiPost('<?= base_url('Opd_prescription/investigation_remove') ?>/' + id, {}, function() {
|
|
||||||
removeNext();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
removeNext();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', '.inv-quick-chip', function() {
|
$(document).off('click', '.inv-quick-chip');
|
||||||
|
$(document).off('click', '.inv-profile-chip');
|
||||||
|
|
||||||
|
$(document).off('click.invQuickChip', '.inv-quick-chip').on('click.invQuickChip', '.inv-quick-chip', function() {
|
||||||
var test = ($(this).data('test') || '').toString().trim();
|
var test = ($(this).data('test') || '').toString().trim();
|
||||||
var code = ($(this).data('code') || '').toString().trim();
|
var code = ($(this).data('code') || '').toString().trim();
|
||||||
if (!test) {
|
if (!test) {
|
||||||
|
|
@ -6807,7 +6788,7 @@
|
||||||
batchAddInvestigations([test]);
|
batchAddInvestigations([test]);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', '.inv-profile-chip', function() {
|
$(document).off('click.invProfileChip', '.inv-profile-chip').on('click.invProfileChip', '.inv-profile-chip', function() {
|
||||||
var key = ($(this).data('profile') || '').toString().trim();
|
var key = ($(this).data('profile') || '').toString().trim();
|
||||||
if (legacyInvestigationProfiles[key] && legacyInvestigationProfiles[key].length) {
|
if (legacyInvestigationProfiles[key] && legacyInvestigationProfiles[key].length) {
|
||||||
batchAddInvestigationRows(legacyInvestigationProfiles[key]);
|
batchAddInvestigationRows(legacyInvestigationProfiles[key]);
|
||||||
|
|
@ -6947,7 +6928,7 @@
|
||||||
loadMedicineDoseMasters();
|
loadMedicineDoseMasters();
|
||||||
$('#advise_investigation_notes').val($('#investigation').val() || '');
|
$('#advise_investigation_notes').val($('#investigation').val() || '');
|
||||||
|
|
||||||
$(document).on('click', '.btn-del-invest', function() {
|
$(document).off('click.investRemoveSingle', '.btn-del-invest').on('click.investRemoveSingle', '.btn-del-invest', function() {
|
||||||
var id = $(this).data('id');
|
var id = $(this).data('id');
|
||||||
apiPost('<?= base_url('Opd_prescription/investigation_remove') ?>/' + id, {}, function() {
|
apiPost('<?= base_url('Opd_prescription/investigation_remove') ?>/' + id, {}, function() {
|
||||||
loadInvestigationList();
|
loadInvestigationList();
|
||||||
|
|
|
||||||
|
|
@ -170,11 +170,37 @@
|
||||||
}
|
}
|
||||||
function clearMsg(id) { document.getElementById(id).innerHTML = ''; }
|
function clearMsg(id) { document.getElementById(id).innerHTML = ''; }
|
||||||
|
|
||||||
|
function parseXhrJson(xhr) {
|
||||||
|
if (!xhr) return {};
|
||||||
|
if (xhr.responseJSON && typeof xhr.responseJSON === 'object') {
|
||||||
|
return xhr.responseJSON;
|
||||||
|
}
|
||||||
|
var raw = xhr.responseText || '';
|
||||||
|
if (raw === '') return {};
|
||||||
|
try {
|
||||||
|
return JSON.parse(raw);
|
||||||
|
} catch (e) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickErrorMessage(res, fallbackText) {
|
||||||
|
var payload = (res && (res.data || res)) || {};
|
||||||
|
var profile = payload.profile || payload.ABHAProfile || {};
|
||||||
|
return (
|
||||||
|
(res && (res.error_text || res.message || res.error)) ||
|
||||||
|
payload.message ||
|
||||||
|
profile.message ||
|
||||||
|
profile.code ||
|
||||||
|
fallbackText
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function postAbdm(url, data, cb) {
|
function postAbdm(url, data, cb) {
|
||||||
var csrf = csrfPair();
|
var csrf = csrfPair();
|
||||||
data[csrf.name] = csrf.value;
|
data[csrf.name] = csrf.value;
|
||||||
$.post(url, data, function (res) { updateCsrf(res); cb(res || {}); }, 'json')
|
$.post(url, data, function (res) { updateCsrf(res); cb(res || {}); }, 'json')
|
||||||
.fail(function (xhr) { cb(xhr && xhr.responseJSON ? xhr.responseJSON : {}); });
|
.fail(function (xhr) { cb(parseXhrJson(xhr)); });
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableBtn(id) { $('#' + id).prop('disabled', true); }
|
function disableBtn(id) { $('#' + id).prop('disabled', true); }
|
||||||
|
|
@ -256,7 +282,7 @@
|
||||||
AADHAAR OTP FLOW
|
AADHAAR OTP FLOW
|
||||||
================================================================ */
|
================================================================ */
|
||||||
|
|
||||||
$('#btn_aadhaar_send_otp').on('click', function () {
|
$('#btn_aadhaar_send_otp').off('click.abhaOtp').on('click.abhaOtp', function () {
|
||||||
clearMsg('aadhaar_step1_msg');
|
clearMsg('aadhaar_step1_msg');
|
||||||
var aadhaar = $('#abha_aadhaar_input').val().trim();
|
var aadhaar = $('#abha_aadhaar_input').val().trim();
|
||||||
if (!/^\d{12}$/.test(aadhaar)) {
|
if (!/^\d{12}$/.test(aadhaar)) {
|
||||||
|
|
@ -284,19 +310,19 @@
|
||||||
$('#aadhaar-step2').show();
|
$('#aadhaar-step2').show();
|
||||||
setTimeout(function () { $('#abha_aadhaar_otp').trigger('focus'); }, 100);
|
setTimeout(function () { $('#abha_aadhaar_otp').trigger('focus'); }, 100);
|
||||||
} else {
|
} else {
|
||||||
var errMsg = res.error_text || res.message || res.error || 'Failed to send OTP. Please try again.';
|
var errMsg = pickErrorMessage(res, 'Failed to send OTP. Please try again.');
|
||||||
showMsg('aadhaar_step1_msg', 'danger', '<i class="bi bi-x-circle me-1"></i>' + $('<div>').text(errMsg).html());
|
showMsg('aadhaar_step1_msg', 'danger', '<i class="bi bi-x-circle me-1"></i>' + $('<div>').text(errMsg).html());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#btn_aadhaar_back').on('click', function () {
|
$('#btn_aadhaar_back').off('click.abhaOtp').on('click.abhaOtp', function () {
|
||||||
$('#aadhaar-step2').hide();
|
$('#aadhaar-step2').hide();
|
||||||
$('#aadhaar-step1').show();
|
$('#aadhaar-step1').show();
|
||||||
clearMsg('aadhaar_step2_msg');
|
clearMsg('aadhaar_step2_msg');
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#btn_aadhaar_resend').on('click', function () {
|
$('#btn_aadhaar_resend').off('click.abhaOtp').on('click.abhaOtp', function () {
|
||||||
var aadhaar = $('#abha_aadhaar_input').val().trim();
|
var aadhaar = $('#abha_aadhaar_input').val().trim();
|
||||||
if (!/^\d{12}$/.test(aadhaar)) {
|
if (!/^\d{12}$/.test(aadhaar)) {
|
||||||
showMsg('aadhaar_step2_msg', 'warning', 'Go back and re-enter the Aadhaar number.');
|
showMsg('aadhaar_step2_msg', 'warning', 'Go back and re-enter the Aadhaar number.');
|
||||||
|
|
@ -320,13 +346,13 @@
|
||||||
showMsg('aadhaar_step2_msg', 'success', '<i class="bi bi-check-circle me-1"></i>OTP resent. Enter the new OTP above.');
|
showMsg('aadhaar_step2_msg', 'success', '<i class="bi bi-check-circle me-1"></i>OTP resent. Enter the new OTP above.');
|
||||||
setTimeout(function () { $('#abha_aadhaar_otp').trigger('focus'); }, 100);
|
setTimeout(function () { $('#abha_aadhaar_otp').trigger('focus'); }, 100);
|
||||||
} else {
|
} else {
|
||||||
var errMsg = res.error_text || res.message || res.error || 'Resend failed.';
|
var errMsg = pickErrorMessage(res, 'Resend failed.');
|
||||||
showMsg('aadhaar_step2_msg', 'danger', '<i class="bi bi-x-circle me-1"></i>' + $('<div>').text(errMsg).html());
|
showMsg('aadhaar_step2_msg', 'danger', '<i class="bi bi-x-circle me-1"></i>' + $('<div>').text(errMsg).html());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#btn_aadhaar_verify_otp').on('click', function () {
|
$('#btn_aadhaar_verify_otp').off('click.abhaOtp').on('click.abhaOtp', function () {
|
||||||
clearMsg('aadhaar_step2_msg');
|
clearMsg('aadhaar_step2_msg');
|
||||||
var otp = $('#abha_aadhaar_otp').val().trim();
|
var otp = $('#abha_aadhaar_otp').val().trim();
|
||||||
var mobile = $('#abha_aadhaar_mobile').val().trim();
|
var mobile = $('#abha_aadhaar_mobile').val().trim();
|
||||||
|
|
@ -345,7 +371,7 @@
|
||||||
disableBtn('btn_aadhaar_verify_otp');
|
disableBtn('btn_aadhaar_verify_otp');
|
||||||
$('#btn_aadhaar_verify_otp').html('<span class="spinner-border spinner-border-sm me-1"></span>Verifying…');
|
$('#btn_aadhaar_verify_otp').html('<span class="spinner-border spinner-border-sm me-1"></span>Verifying…');
|
||||||
|
|
||||||
postAbdm('<?= base_url('billing/patient/abha_aadhaar_verify_otp') ?>', { txnId: _aadhaarTxnId, otp: otp, mobile: mobile }, function (res) {
|
postAbdm('<?= base_url('billing/patient/abha_aadhaar_verify_otp') ?>', { txnId: _aadhaarTxnId, otp: otp, mobile: mobile, p_id: _patientId }, function (res) {
|
||||||
enableBtn('btn_aadhaar_verify_otp');
|
enableBtn('btn_aadhaar_verify_otp');
|
||||||
$('#btn_aadhaar_verify_otp').html('<i class="bi bi-check-circle me-1"></i>Verify OTP & Link ABHA');
|
$('#btn_aadhaar_verify_otp').html('<i class="bi bi-check-circle me-1"></i>Verify OTP & Link ABHA');
|
||||||
|
|
||||||
|
|
@ -360,14 +386,14 @@
|
||||||
$('#btn_aadhaar_verify_otp').prop('disabled', true).text('Linked ✓');
|
$('#btn_aadhaar_verify_otp').prop('disabled', true).text('Linked ✓');
|
||||||
setTimeout(function () { bootstrap.Modal.getInstance(document.getElementById('abhaOtpModal')).hide(); }, 1800);
|
setTimeout(function () { bootstrap.Modal.getInstance(document.getElementById('abhaOtpModal')).hide(); }, 1800);
|
||||||
} else {
|
} else {
|
||||||
var errMsg = res.error_text || res.message || res.error || 'OTP verification failed.';
|
var errMsg = pickErrorMessage(res, 'OTP verification failed.');
|
||||||
showMsg('aadhaar_step2_msg', 'danger', '<i class="bi bi-x-circle me-1"></i>' + $('<div>').text(errMsg).html());
|
showMsg('aadhaar_step2_msg', 'danger', '<i class="bi bi-x-circle me-1"></i>' + $('<div>').text(errMsg).html());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Allow Enter key in OTP field */
|
/* Allow Enter key in OTP field */
|
||||||
$('#abha_aadhaar_otp').on('keydown', function (e) {
|
$('#abha_aadhaar_otp').off('keydown.abhaOtp').on('keydown.abhaOtp', function (e) {
|
||||||
if (e.key === 'Enter') $('#btn_aadhaar_verify_otp').trigger('click');
|
if (e.key === 'Enter') $('#btn_aadhaar_verify_otp').trigger('click');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -375,7 +401,7 @@
|
||||||
MOBILE OTP FLOW
|
MOBILE OTP FLOW
|
||||||
================================================================ */
|
================================================================ */
|
||||||
|
|
||||||
$('#btn_mobile_send_otp').on('click', function () {
|
$('#btn_mobile_send_otp').off('click.abhaOtp').on('click.abhaOtp', function () {
|
||||||
clearMsg('mobile_step1_msg');
|
clearMsg('mobile_step1_msg');
|
||||||
var mobile = $('#abha_mobile_input').val().trim();
|
var mobile = $('#abha_mobile_input').val().trim();
|
||||||
if (!/^\d{10}$/.test(mobile)) {
|
if (!/^\d{10}$/.test(mobile)) {
|
||||||
|
|
@ -397,19 +423,19 @@
|
||||||
$('#mobile-step2').show();
|
$('#mobile-step2').show();
|
||||||
setTimeout(function () { $('#abha_mobile_otp').trigger('focus'); }, 100);
|
setTimeout(function () { $('#abha_mobile_otp').trigger('focus'); }, 100);
|
||||||
} else {
|
} else {
|
||||||
var errMsg = res.error_text || res.message || res.error || 'Failed to send OTP.';
|
var errMsg = pickErrorMessage(res, 'Failed to send OTP.');
|
||||||
showMsg('mobile_step1_msg', 'danger', '<i class="bi bi-x-circle me-1"></i>' + $('<div>').text(errMsg).html());
|
showMsg('mobile_step1_msg', 'danger', '<i class="bi bi-x-circle me-1"></i>' + $('<div>').text(errMsg).html());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#btn_mobile_back').on('click', function () {
|
$('#btn_mobile_back').off('click.abhaOtp').on('click.abhaOtp', function () {
|
||||||
$('#mobile-step2').hide();
|
$('#mobile-step2').hide();
|
||||||
$('#mobile-step1').show();
|
$('#mobile-step1').show();
|
||||||
clearMsg('mobile_step2_msg');
|
clearMsg('mobile_step2_msg');
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#btn_mobile_verify_otp').on('click', function () {
|
$('#btn_mobile_verify_otp').off('click.abhaOtp').on('click.abhaOtp', function () {
|
||||||
clearMsg('mobile_step2_msg');
|
clearMsg('mobile_step2_msg');
|
||||||
var otp = $('#abha_mobile_otp').val().trim();
|
var otp = $('#abha_mobile_otp').val().trim();
|
||||||
if (!/^\d{6}$/.test(otp)) {
|
if (!/^\d{6}$/.test(otp)) {
|
||||||
|
|
@ -423,7 +449,7 @@
|
||||||
disableBtn('btn_mobile_verify_otp');
|
disableBtn('btn_mobile_verify_otp');
|
||||||
$('#btn_mobile_verify_otp').html('<span class="spinner-border spinner-border-sm me-1"></span>Verifying…');
|
$('#btn_mobile_verify_otp').html('<span class="spinner-border spinner-border-sm me-1"></span>Verifying…');
|
||||||
|
|
||||||
postAbdm('<?= base_url('billing/patient/abha_mobile_verify_otp') ?>', { txnId: _mobileTxnId, otp: otp }, function (res) {
|
postAbdm('<?= base_url('billing/patient/abha_mobile_verify_otp') ?>', { txnId: _mobileTxnId, otp: otp, p_id: _patientId }, function (res) {
|
||||||
enableBtn('btn_mobile_verify_otp');
|
enableBtn('btn_mobile_verify_otp');
|
||||||
$('#btn_mobile_verify_otp').html('<i class="bi bi-check-circle me-1"></i>Verify OTP & Link ABHA');
|
$('#btn_mobile_verify_otp').html('<i class="bi bi-check-circle me-1"></i>Verify OTP & Link ABHA');
|
||||||
|
|
||||||
|
|
@ -438,13 +464,13 @@
|
||||||
$('#btn_mobile_verify_otp').prop('disabled', true).text('Linked ✓');
|
$('#btn_mobile_verify_otp').prop('disabled', true).text('Linked ✓');
|
||||||
setTimeout(function () { bootstrap.Modal.getInstance(document.getElementById('abhaOtpModal')).hide(); }, 1800);
|
setTimeout(function () { bootstrap.Modal.getInstance(document.getElementById('abhaOtpModal')).hide(); }, 1800);
|
||||||
} else {
|
} else {
|
||||||
var errMsg = res.error_text || res.message || res.error || 'OTP verification failed.';
|
var errMsg = pickErrorMessage(res, 'OTP verification failed.');
|
||||||
showMsg('mobile_step2_msg', 'danger', '<i class="bi bi-x-circle me-1"></i>' + $('<div>').text(errMsg).html());
|
showMsg('mobile_step2_msg', 'danger', '<i class="bi bi-x-circle me-1"></i>' + $('<div>').text(errMsg).html());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#abha_mobile_otp').on('keydown', function (e) {
|
$('#abha_mobile_otp').off('keydown.abhaOtp').on('keydown.abhaOtp', function (e) {
|
||||||
if (e.key === 'Enter') $('#btn_mobile_verify_otp').trigger('click');
|
if (e.key === 'Enter') $('#btn_mobile_verify_otp').trigger('click');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,30 @@ $stats = $stats ?? [];
|
||||||
$lists = $lists ?? [];
|
$lists = $lists ?? [];
|
||||||
$stockValue = (float) ($stats['stock_value'] ?? 0);
|
$stockValue = (float) ($stats['stock_value'] ?? 0);
|
||||||
?>
|
?>
|
||||||
<section class="content-header">
|
|
||||||
<h1>Store Stock <small>Dashboard</small></h1>
|
|
||||||
</section>
|
|
||||||
<section class="content">
|
<section class="content">
|
||||||
|
<div class="module-shell-head">
|
||||||
|
<div class="module-shell-title">
|
||||||
|
<span class="main">Store Stock</span>
|
||||||
|
<span class="sub" id="subtitle">Dashboard</span>
|
||||||
|
</div>
|
||||||
|
<div class="module-nav-tabs">
|
||||||
|
<a class="tab active" href="javascript:load_form('<?= base_url('Storestock') ?>','Hospital Stock');">
|
||||||
|
<i class="fa fa-home"></i> Dashboard
|
||||||
|
</a>
|
||||||
|
<a class="tab" href="javascript:load_form('<?= base_url('Storestock/Indent_List') ?>','Store : Indent');">
|
||||||
|
<i class="fa fa-shopping-cart"></i> Indent
|
||||||
|
</a>
|
||||||
|
<a class="tab" href="javascript:load_form('<?= base_url('Storestock/Report_2') ?>','Day Report :Store');">
|
||||||
|
<i class="fa fa-line-chart"></i> Day Report
|
||||||
|
</a>
|
||||||
|
<a class="tab" href="javascript:load_form('<?= base_url('Storestock/store_stock') ?>','Store Stock : Store');">
|
||||||
|
<i class="fa fa-barcode"></i> Store Stock
|
||||||
|
</a>
|
||||||
|
<a class="tab" href="javascript:load_form('<?= base_url('Storestock/main_store') ?>','Store Main : Store');">
|
||||||
|
<i class="fa fa-desktop"></i> Store Main
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="module-hero">
|
<div class="module-hero">
|
||||||
<h4>Hospital Stock Control Center</h4>
|
<h4>Hospital Stock Control Center</h4>
|
||||||
<p>Track indents, monitor stock movement, and jump to master controls from one screen.</p>
|
<p>Track indents, monitor stock movement, and jump to master controls from one screen.</p>
|
||||||
|
|
@ -67,14 +87,7 @@ $stockValue = (float) ($stats['stock_value'] ?? 0);
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="box" style="margin-top:1rem;">
|
|
||||||
<div class="box-header">
|
|
||||||
<h3 class="box-title">Workspace</h3>
|
|
||||||
</div>
|
|
||||||
<div class="box-body">
|
|
||||||
<div id="maindiv" class="row"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="insight-grid">
|
<div class="insight-grid">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
|
|
@ -147,5 +160,6 @@ $stockValue = (float) ($stats['stock_value'] ?? 0);
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
131
app/Views/storestock/dashboard_sub.php
Normal file
131
app/Views/storestock/dashboard_sub.php
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
<div class="module-hero">
|
||||||
|
<h4>Hospital Stock Control Center</h4>
|
||||||
|
<p>Track indents, monitor stock movement, and jump to master controls from one screen.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="label">Indents Today</span>
|
||||||
|
<span class="value"><?= (int) ($stats['today_indents'] ?? 0) ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="label">Indents This Month</span>
|
||||||
|
<span class="value"><?= (int) ($stats['month_indents'] ?? 0) ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="label">Low Stock Items</span>
|
||||||
|
<span class="value"><?= (int) ($stats['low_stock_count'] ?? 0) ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="label">Near Expiry (60 Days)</span>
|
||||||
|
<span class="value"><?= (int) ($stats['near_expiry_count'] ?? 0) ?></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats-grid" style="grid-template-columns: repeat(2, minmax(180px, 1fr));">
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="label">Pending Purchase Invoices</span>
|
||||||
|
<span class="value"><?= (int) ($stats['pending_purchase'] ?? 0) ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="label">Approx. Current Stock Value</span>
|
||||||
|
<span class="value">Rs. <?= number_format($stockValue, 2) ?></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action-grid">
|
||||||
|
<a class="action-card" href="javascript:load_form_div('/Storestock/Indent_List','maindiv','Store : Indent');">
|
||||||
|
<span class="icon"><i class="fa fa-shopping-cart"></i></span>
|
||||||
|
<h5>Indent Desk</h5>
|
||||||
|
<p>Create and process departmental item requests.</p>
|
||||||
|
</a>
|
||||||
|
<a class="action-card" href="javascript:load_form_div('/Storestock/Report_2','maindiv','Day Report :Store');">
|
||||||
|
<span class="icon"><i class="fa fa-line-chart"></i></span>
|
||||||
|
<h5>Day Report</h5>
|
||||||
|
<p>Review daily issue trend and transaction summary.</p>
|
||||||
|
</a>
|
||||||
|
<a class="action-card" href="javascript:load_form_div('/Storestock/store_stock','maindiv','Store Stock : Store');">
|
||||||
|
<span class="icon"><i class="fa fa-barcode"></i></span>
|
||||||
|
<h5>Stock Search</h5>
|
||||||
|
<p>Find real-time availability with reorder controls.</p>
|
||||||
|
</a>
|
||||||
|
<a class="action-card" href="javascript:load_form_div('/Storestock/main_store','maindiv','Store Main : Store');">
|
||||||
|
<span class="icon"><i class="fa fa-cogs"></i></span>
|
||||||
|
<h5>Store Controls</h5>
|
||||||
|
<p>Open masters, inventory, and procurement tools.</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="insight-grid">
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header">
|
||||||
|
<h3 class="box-title">Low Stock Alerts</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table class="table table-bordered table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Item</th>
|
||||||
|
<th style="width:120px;">Current Unit</th>
|
||||||
|
<th style="width:140px;">Reorder Threshold</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php $lowStockItems = $lists['low_stock_items'] ?? []; ?>
|
||||||
|
<?php if (empty($lowStockItems)) { ?>
|
||||||
|
<tr><td colspan="3" class="text-center text-muted">No low-stock items right now.</td></tr>
|
||||||
|
<?php } ?>
|
||||||
|
<?php foreach ($lowStockItems as $row) { ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= esc($row['item_name'] ?? '') ?></td>
|
||||||
|
<td><?= esc($row['cur_units'] ?? 0) ?></td>
|
||||||
|
<td><?= esc($row['re_order_qty'] ?? 0) ?> x <?= esc($row['packing'] ?? 1) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php } ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header">
|
||||||
|
<h3 class="box-title">Near Expiry Alerts</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table class="table table-bordered table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Item / Batch</th>
|
||||||
|
<th style="width:100px;">Expiry</th>
|
||||||
|
<th style="width:90px;">Days Left</th>
|
||||||
|
<th style="width:95px;">Cur Packs</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php $nearExpiryItems = $lists['near_expiry_items'] ?? []; ?>
|
||||||
|
<?php if (empty($nearExpiryItems)) { ?>
|
||||||
|
<tr><td colspan="4" class="text-center text-muted">No near-expiry batches in next 60 days.</td></tr>
|
||||||
|
<?php } ?>
|
||||||
|
<?php foreach ($nearExpiryItems as $row) { ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= esc($row['item_name'] ?? '') ?> <span class="text-muted">(<?= esc($row['batch_no'] ?? '-') ?>)</span></td>
|
||||||
|
<td><?= esc($row['expiry_str'] ?? '-') ?></td>
|
||||||
|
<td>
|
||||||
|
<span class="mini-badge <?= ((int) ($row['days_left'] ?? 0)) <= 15 ? 'danger' : 'warn' ?>">
|
||||||
|
<?= (int) ($row['days_left'] ?? 0) ?>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td><?= esc($row['cur_packs'] ?? 0) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php } ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -1,40 +1,66 @@
|
||||||
<?= $this->include('storestock/_niceadmin_theme') ?>
|
<?= $this->include('storestock/_niceadmin_theme') ?>
|
||||||
<div class="storestock-ui">
|
<div class="storestock-ui">
|
||||||
<div class="col-md-12">
|
<section class="content">
|
||||||
<div class="box">
|
<div class="module-shell-head">
|
||||||
<div class="box-header">
|
<div class="module-shell-title">
|
||||||
<h3 class="box-title">Day Report - Store</h3>
|
<span class="main">Store Stock</span>
|
||||||
|
<span class="sub">Day Report</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">
|
<div class="module-nav-tabs">
|
||||||
<form class="form-day-report" method="post">
|
<a class="tab" href="javascript:load_form('<?= base_url('Storestock') ?>','Hospital Stock');">
|
||||||
<?= csrf_field() ?>
|
<i class="fa fa-home"></i> Dashboard
|
||||||
<div class="row">
|
</a>
|
||||||
<div class="col-md-3">
|
<a class="tab " href="javascript:load_form('<?= base_url('Storestock/Indent_List') ?>','Store : Indent');">
|
||||||
<div class="form-group">
|
<i class="fa fa-shopping-cart"></i> Indent
|
||||||
<label>From Date</label>
|
</a>
|
||||||
<input type="date" class="form-control input-sm" id="date_from" name="date_from"
|
<a class="tab active" href="javascript:load_form('<?= base_url('Storestock/Report_2') ?>','Day Report :Store');">
|
||||||
value="<?= date('Y-m-d') ?>">
|
<i class="fa fa-line-chart"></i> Day Report
|
||||||
</div>
|
</a>
|
||||||
</div>
|
<a class="tab" href="javascript:load_form('<?= base_url('Storestock/store_stock') ?>','Store Stock : Store');">
|
||||||
<div class="col-md-3">
|
<i class="fa fa-barcode"></i> Store Stock
|
||||||
<div class="form-group">
|
</a>
|
||||||
<label>To Date</label>
|
<a class="tab" href="javascript:load_form('<?= base_url('Storestock/main_store') ?>','Store Main : Store');">
|
||||||
<input type="date" class="form-control input-sm" id="date_to" name="date_to"
|
<i class="fa fa-desktop"></i> Store Main
|
||||||
value="<?= date('Y-m-d') ?>">
|
</a>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<div class="form-group" style="margin-top:24px;">
|
|
||||||
<button type="submit" class="btn btn-info btn-sm">Generate Report</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div id="day_report_result" class="row"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="workspace-shell">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header">
|
||||||
|
<h3 class="box-title">Day Report - Store</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<form class="form-day-report" method="post">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>From Date</label>
|
||||||
|
<input type="date" class="form-control input-sm" id="date_from" name="date_from"
|
||||||
|
value="<?= date('Y-m-d') ?>">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>To Date</label>
|
||||||
|
<input type="date" class="form-control input-sm" id="date_to" name="date_to"
|
||||||
|
value="<?= date('Y-m-d') ?>">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="form-group" style="margin-top:24px;">
|
||||||
|
<button type="submit" class="btn btn-info btn-sm">Generate Report</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div id="day_report_result" class="row"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('form.form-day-report').on('submit', function (e) {
|
$('form.form-day-report').on('submit', function (e) {
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,79 @@
|
||||||
<?= $this->include('storestock/_niceadmin_theme') ?>
|
<?= $this->include('storestock/_niceadmin_theme') ?>
|
||||||
<div class="storestock-ui">
|
<div class="storestock-ui">
|
||||||
<div class="col-md-12">
|
<section class="content">
|
||||||
<div class="box">
|
<div class="module-shell-head">
|
||||||
<div class="box-header">
|
<div class="module-shell-title">
|
||||||
<div class="toolbar-row">
|
<span class="main">Store Stock</span>
|
||||||
<div class="left">
|
<span class="sub">Indent</span>
|
||||||
<h3 class="box-title" style="margin-right:.65rem;">Indent List</h3>
|
|
||||||
<button type="button" class="pill-btn active">Live Queue</button>
|
|
||||||
<button type="button" class="pill-btn" onclick="$('#indent-grid').DataTable().ajax.reload();">Refresh</button>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<a class="btn btn-primary btn-sm" href="javascript:load_form_div('/Storestock/new_indent','maindiv');">
|
|
||||||
<i class="fa fa-plus"></i> New Indent
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">
|
<div class="module-nav-tabs">
|
||||||
<div class="table-wrap">
|
<a class="tab" href="javascript:load_form('<?= base_url('Storestock') ?>','Hospital Stock');">
|
||||||
<table class="table table-bordered table-striped" id="indent-grid" width="100%">
|
<i class="fa fa-home"></i> Dashboard
|
||||||
<thead>
|
</a>
|
||||||
<tr>
|
<a class="tab active" href="javascript:load_form('<?= base_url('Storestock/Indent_List') ?>','Store : Indent');">
|
||||||
<th>Indent No.</th>
|
<i class="fa fa-shopping-cart"></i> Indent
|
||||||
<th>Department</th>
|
</a>
|
||||||
<th>Date</th>
|
<a class="tab" href="javascript:load_form('<?= base_url('Storestock/Report_2') ?>','Day Report :Store');">
|
||||||
<th style="display:none;">ID</th>
|
<i class="fa fa-line-chart"></i> Day Report
|
||||||
</tr>
|
</a>
|
||||||
</thead>
|
<a class="tab" href="javascript:load_form('<?= base_url('Storestock/store_stock') ?>','Store Stock : Store');">
|
||||||
<thead>
|
<i class="fa fa-barcode"></i> Store Stock
|
||||||
<tr>
|
</a>
|
||||||
<td><input type="text" data-column="0" class="form-control input-sm" placeholder="Indent No."></td>
|
<a class="tab" href="javascript:load_form('<?= base_url('Storestock/main_store') ?>','Store Main : Store');">
|
||||||
<td><input type="text" data-column="1" class="form-control input-sm" placeholder="Department"></td>
|
<i class="fa fa-desktop"></i> Store Main
|
||||||
<td><input type="date" data-column="2" class="form-control input-sm"></td>
|
</a>
|
||||||
<td style="display:none;"></td>
|
</div>
|
||||||
</tr>
|
</div>
|
||||||
</thead>
|
|
||||||
<tbody></tbody>
|
<div class="workspace-shell">
|
||||||
</table>
|
<div class="box">
|
||||||
|
<div class="box-header">
|
||||||
|
<h3 class="box-title">Workspace</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body" id="maindiv">
|
||||||
|
<div class="box" style="margin-bottom:0;">
|
||||||
|
<div class="box-header">
|
||||||
|
<div class="toolbar-row">
|
||||||
|
<div class="left">
|
||||||
|
<h3 class="box-title" style="margin-right:.65rem;">Indent List</h3>
|
||||||
|
<button type="button" class="pill-btn active">Live Queue</button>
|
||||||
|
<button type="button" class="pill-btn" onclick="$('#indent-grid').DataTable().ajax.reload();">Refresh</button>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<a class="btn btn-primary btn-sm" href="javascript:load_form_div('/Storestock/new_indent','maindiv');">
|
||||||
|
<i class="fa fa-plus"></i> New Indent
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table class="table table-bordered table-striped" id="indent-grid" width="100%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Indent No.</th>
|
||||||
|
<th>Department</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th style="display:none;">ID</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td><input type="text" data-column="0" class="form-control input-sm" placeholder="Indent No."></td>
|
||||||
|
<td><input type="text" data-column="1" class="form-control input-sm" placeholder="Department"></td>
|
||||||
|
<td><input type="date" data-column="2" class="form-control input-sm"></td>
|
||||||
|
<td style="display:none;"></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
@ -50,7 +84,7 @@ $(document).ready(function () {
|
||||||
"processing": true,
|
"processing": true,
|
||||||
"serverSide": true,
|
"serverSide": true,
|
||||||
"ajax": {
|
"ajax": {
|
||||||
url: "Storestock/getIndentTable",
|
url: "<?= base_url('Storestock/getIndentTable') ?>",
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
type: "post",
|
type: "post",
|
||||||
data: function (d) {
|
data: function (d) {
|
||||||
|
|
|
||||||
97
app/Views/storestock/purchase.php
Normal file
97
app/Views/storestock/purchase.php
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
<?= $this->include('storestock/_niceadmin_theme') ?>
|
||||||
|
<div class="storestock-ui">
|
||||||
|
<section class="content-header">
|
||||||
|
<h1>
|
||||||
|
Purchase
|
||||||
|
<small>Store</small>
|
||||||
|
</h1>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="content">
|
||||||
|
<div class="module-hero">
|
||||||
|
<h4>Store Purchase Control Center</h4>
|
||||||
|
<p>Track supplier invoices, open edits in a persistent sub panel, and keep the invoice list visible while you work.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header d-flex justify-content-between align-items-center">
|
||||||
|
<h3 class="box-title mb-0">Purchase Invoices</h3>
|
||||||
|
<button type="button" class="btn btn-warning btn-sm" id="btnNewPurchase">New Purchase / Challan Invoice</button>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<form id="purchase-search-form" class="row g-2 align-items-center" method="post" action="javascript:void(0)">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input class="form-control" type="text" id="txtsearch" name="txtsearch" placeholder="Search by invoice no. or supplier">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button type="submit" class="btn btn-info">Search Purchase Invoice</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button type="button" id="btnResetPurchase" class="btn btn-outline-secondary">Reset</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header">
|
||||||
|
<h3 class="box-title">Recent Purchase Invoices</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body" id="purchase-main-panel">
|
||||||
|
<?= $this->include('storestock/purchase_supp_list') ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="purchase-sub-main" style="display:none;" class="mt-3">
|
||||||
|
<div id="searchresult"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
window.openStorestockPurchaseSubView = function (url, title) {
|
||||||
|
var $root = $('#purchase-sub-main');
|
||||||
|
var $listPanel = $('#purchase-main-panel');
|
||||||
|
|
||||||
|
if ($root.length) {
|
||||||
|
$listPanel.hide();
|
||||||
|
$root.show();
|
||||||
|
load_form_div(url, 'searchresult', title || 'Purchase : Store');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
load_form_div(url, 'maindiv', title || 'Purchase : Store');
|
||||||
|
};
|
||||||
|
|
||||||
|
window.closeStorestockPurchaseSubView = function () {
|
||||||
|
var $root = $('#purchase-sub-main');
|
||||||
|
var $listPanel = $('#purchase-main-panel');
|
||||||
|
|
||||||
|
if ($root.length) {
|
||||||
|
$('#searchresult').empty();
|
||||||
|
$root.hide();
|
||||||
|
$listPanel.show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$('#btnNewPurchase').off('click').on('click', function () {
|
||||||
|
openStorestockPurchaseSubView('<?= base_url('Storestock/PurchaseNew') ?>', 'New Purchase / Challan Invoice : Store');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#btnResetPurchase').off('click').on('click', function () {
|
||||||
|
$('#txtsearch').val('');
|
||||||
|
$('#purchase-search-form').trigger('submit');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#purchase-search-form').off('submit').on('submit', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
$.post('<?= base_url('Storestock/PurchaseInvoice') ?>', $(this).serialize(), function (html) {
|
||||||
|
$('#purchase-main-panel').html(html || '');
|
||||||
|
}).fail(function () {
|
||||||
|
$('#purchase-main-panel').html('<div class="alert alert-danger mb-0">Failed to load purchase list.</div>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
136
app/Views/storestock/purchase_invoice_master_edit.php
Normal file
136
app/Views/storestock/purchase_invoice_master_edit.php
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
<?php $invoice = $inv_master_data[0] ?? null; ?>
|
||||||
|
|
||||||
|
<?php if (! $invoice): ?>
|
||||||
|
<div class="alert alert-warning mb-0">Purchase invoice not found.</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header d-flex flex-wrap justify-content-between align-items-center gap-2">
|
||||||
|
<div>
|
||||||
|
<h3 class="box-title mb-0">Purchase Invoice / Supplier : <?= esc((string) ($invoice->name_supplier ?? '-')) ?></h3>
|
||||||
|
<small class="text-muted">
|
||||||
|
Invoice No.: <?= esc((string) ($invoice->Invoice_no ?? '')) ?> |
|
||||||
|
Invoice Date: <?= esc((string) ($invoice->str_date_of_invoice ?? '')) ?>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group btn-group-sm">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" onclick="closeStorestockPurchaseSubView();">Back to List</button>
|
||||||
|
<button type="button" class="btn btn-warning" onclick="openStorestockPurchaseSubView('<?= base_url('Storestock/PurchaseMasterEdit/' . (int) ($invoice->id ?? 0)) ?>','Purchase : Edit');">Reload Invoice</button>
|
||||||
|
<a href="<?= base_url('Storestock/print_purchase/' . (int) ($invoice->id ?? 0)) ?>" target="_blank" class="btn btn-secondary"><i class="fa fa-print"></i> Print</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<form id="storestock-purchase-edit-form" class="row g-3" method="post" action="javascript:void(0)">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<input type="hidden" id="hid_purchaseid" name="hid_purchaseid" value="<?= (int) ($invoice->id ?? 0) ?>">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Supplier</label>
|
||||||
|
<select class="form-select" id="input_supplier" name="input_supplier" <?= ((int) ($invoice->inv_status ?? 0)) === 0 ? '' : 'disabled' ?>>
|
||||||
|
<?php foreach (($supplier_data ?? []) as $row): ?>
|
||||||
|
<option value="<?= (int) ($row->sid ?? 0) ?>" <?= ((int) ($row->sid ?? 0)) === (int) ($invoice->sid ?? 0) ? 'selected' : '' ?>><?= esc($row->name_supplier ?? '') ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label class="form-label">Bill Type</label>
|
||||||
|
<select class="form-select" name="cbo_billtype" id="cbo_billtype" <?= ((int) ($invoice->inv_status ?? 0)) === 0 ? '' : 'disabled' ?>>
|
||||||
|
<option value="0" <?= ((int) ($invoice->ischallan ?? 0)) === 0 ? 'selected' : '' ?>>Invoice</option>
|
||||||
|
<option value="1" <?= ((int) ($invoice->ischallan ?? 0)) === 1 ? 'selected' : '' ?>>Challan</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label class="form-label">Invoice No.</label>
|
||||||
|
<input class="form-control" name="input_invoicecode" type="text" value="<?= esc((string) ($invoice->Invoice_no ?? '')) ?>" <?= ((int) ($invoice->inv_status ?? 0)) === 0 ? '' : 'readonly' ?>>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label class="form-label">Date of Invoice</label>
|
||||||
|
<input class="form-control" name="datepicker_invoice" type="text" value="<?= esc((string) ($invoice->str_date_of_invoice ?? '')) ?>" <?= ((int) ($invoice->inv_status ?? 0)) === 0 ? '' : 'readonly' ?>>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2 d-flex align-items-end gap-2">
|
||||||
|
<button type="button" class="btn btn-primary" id="btn_update_purchase" <?= ((int) ($invoice->inv_status ?? 0)) === 0 ? '' : 'disabled' ?>>Update</button>
|
||||||
|
<button type="button" class="btn btn-outline-secondary" id="btn_toggle_status">Toggle Status</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="table-responsive mt-4">
|
||||||
|
<table class="table table-bordered table-striped table-sm align-middle">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Item Name</th>
|
||||||
|
<th>Batch No</th>
|
||||||
|
<th>Exp.</th>
|
||||||
|
<th>MRP</th>
|
||||||
|
<th>Qty.</th>
|
||||||
|
<th>Rate</th>
|
||||||
|
<th>Amount</th>
|
||||||
|
<th>Disc.</th>
|
||||||
|
<th>Tax Amount</th>
|
||||||
|
<th>CGST</th>
|
||||||
|
<th>SGST</th>
|
||||||
|
<th>Net Amount</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (empty($purchase_item)): ?>
|
||||||
|
<tr><td colspan="13" class="text-center text-muted">No items added yet.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php $srno = 0; foreach (($purchase_item ?? []) as $row): $srno++; ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= $srno ?></td>
|
||||||
|
<td><?= esc((string) ($row->Item_name ?? $row->item_name ?? '')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->batch_no ?? '-')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->expiry_date ?? '-')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->mrp ?? '0')) ?></td>
|
||||||
|
<td><?= esc((string) ((float) ($row->qty ?? 0))) ?><?= ((float) ($row->qty_free ?? 0)) > 0 ? ' + ' . esc((string) ((float) ($row->qty_free ?? 0))) : '' ?></td>
|
||||||
|
<td><?= esc((string) ($row->purchase_price ?? '0')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->amount ?? '0')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->discount ?? $row->disc_amount ?? '0')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->taxable_amount ?? '0')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->CGST_per ?? '0')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->SGST_per ?? '0')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->net_amount ?? '0')) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<th colspan="9" class="text-end">Totals</th>
|
||||||
|
<th><?= esc((string) ($invoice->Taxable_Amt ?? '0')) ?></th>
|
||||||
|
<th><?= esc((string) ($invoice->CGST_Amt ?? '0')) ?></th>
|
||||||
|
<th><?= esc((string) ($invoice->SGST_Amt ?? '0')) ?></th>
|
||||||
|
<th><?= esc((string) ($invoice->T_Net_Amount ?? '0')) ?></th>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
$('#btn_update_purchase').off('click').on('click', function () {
|
||||||
|
$.post('<?= base_url('Storestock/UpdatePurchase') ?>', $('#storestock-purchase-edit-form').serialize(), function (data) {
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
|
notify('error', 'Please Attention', 'Unexpected response');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((data.insertid || 0) === 0) {
|
||||||
|
notify('error', 'Please Attention', data.show_text || 'Please check input');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
notify('success', 'Update Success', data.show_text || 'Data Update');
|
||||||
|
openStorestockPurchaseSubView('<?= base_url('Storestock/PurchaseMasterEdit/' . (int) ($invoice->id ?? 0)) ?>', 'Purchase : Edit');
|
||||||
|
}, 'json').fail(function () {
|
||||||
|
notify('error', 'Please Attention', 'Unable to update invoice');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#btn_toggle_status').off('click').on('click', function () {
|
||||||
|
var nextStatus = <?= ((int) ($invoice->inv_status ?? 0)) === 0 ? 1 : 0 ?>;
|
||||||
|
openStorestockPurchaseSubView('<?= base_url('Storestock/UpdatePurchaseInvoiceStatus/' . (int) ($invoice->id ?? 0)) ?>/' + nextStatus, 'Purchase : Edit');
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<?php endif; ?>
|
||||||
60
app/Views/storestock/purchase_new_invoice.php
Normal file
60
app/Views/storestock/purchase_new_invoice.php
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header d-flex justify-content-between align-items-center">
|
||||||
|
<h3 class="box-title mb-0">New Purchase Invoice</h3>
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="closeStorestockPurchaseSubView();">Back to List</button>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<form id="storestock-purchase-new-form" class="row g-3" method="post" action="javascript:void(0)">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Supplier</label>
|
||||||
|
<select class="form-select" id="input_supplier" name="input_supplier">
|
||||||
|
<?php foreach (($supplier_data ?? []) as $row): ?>
|
||||||
|
<option value="<?= (int) ($row->sid ?? 0) ?>"><?= esc($row->name_supplier ?? '') ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label class="form-label">Bill Type</label>
|
||||||
|
<select class="form-select" name="cbo_billtype" id="cbo_billtype">
|
||||||
|
<option value="0">Invoice</option>
|
||||||
|
<option value="1">Challan</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="form-label">Invoice / Challan No.</label>
|
||||||
|
<input class="form-control" name="input_invoicecode" placeholder="Invoice No." type="text">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="form-label">Date of Invoice</label>
|
||||||
|
<input class="form-control" name="datepicker_invoice" id="datepicker_invoice" type="text" value="<?= date('d/m/Y') ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<button type="button" class="btn btn-primary" id="btn_create_purchase">Create</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
$('#btn_create_purchase').off('click').on('click', function () {
|
||||||
|
$.post('<?= base_url('Storestock/CreatePurchase') ?>', $('#storestock-purchase-new-form').serialize(), function (data) {
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
|
notify('error', 'Please Attention', 'Unexpected response');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((data.insertid || 0) <= 0) {
|
||||||
|
notify('error', 'Please Attention', data.show_text || 'Unable to create purchase invoice');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
notify('success', 'Please Attention', 'Invoice Added : ID->' + data.insertid);
|
||||||
|
openStorestockPurchaseSubView('<?= base_url('Storestock/PurchaseMasterEdit') ?>/' + data.insertid, 'Purchase : Edit');
|
||||||
|
}, 'json').fail(function () {
|
||||||
|
notify('error', 'Please Attention', 'Unable to create purchase invoice');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
51
app/Views/storestock/purchase_print.php
Normal file
51
app/Views/storestock/purchase_print.php
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php $invoice = $purchase_invoice ?? null; ?>
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Purchase Invoice</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, Helvetica, sans-serif; margin: 18px; color: #1f2937; }
|
||||||
|
.title { font-size: 22px; font-weight: 700; color: #0f2f67; }
|
||||||
|
.sub { margin-top: 4px; color: #6b7280; font-size: 13px; }
|
||||||
|
table { width: 100%; border-collapse: collapse; margin-top: 14px; font-size: 13px; }
|
||||||
|
th, td { border: 1px solid #d1d5db; padding: 6px 8px; text-align: left; }
|
||||||
|
th { background: #f3f4f6; }
|
||||||
|
.right { text-align: right; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="title">Purchase Invoice</div>
|
||||||
|
<div class="sub">Supplier: <?= esc((string) ($invoice->name_supplier ?? '-')) ?> | Invoice No.: <?= esc((string) ($invoice->Invoice_no ?? '')) ?> | Date: <?= esc((string) ($invoice->str_date_of_invoice ?? '')) ?></div>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Item</th>
|
||||||
|
<th>Batch</th>
|
||||||
|
<th>Exp.</th>
|
||||||
|
<th class="right">Qty</th>
|
||||||
|
<th class="right">Rate</th>
|
||||||
|
<th class="right">Amount</th>
|
||||||
|
<th class="right">Net</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php $srno = 0; foreach (($purchase_invoice_item ?? []) as $row): $srno++; ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= $srno ?></td>
|
||||||
|
<td><?= esc((string) ($row->Item_name ?? $row->item_name ?? '')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->batch_no ?? '-')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->expiry_date ?? '-')) ?></td>
|
||||||
|
<td class="right"><?= esc((string) ((float) ($row->qty ?? 0))) ?></td>
|
||||||
|
<td class="right"><?= esc((string) ($row->purchase_price ?? '0')) ?></td>
|
||||||
|
<td class="right"><?= esc((string) ($row->amount ?? '0')) ?></td>
|
||||||
|
<td class="right"><?= esc((string) ($row->net_amount ?? '0')) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
24
app/Views/storestock/purchase_return_invoice_edit.php
Normal file
24
app/Views/storestock/purchase_return_invoice_edit.php
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php $invoice = $purchase_return_invoice ?? null; ?>
|
||||||
|
<?php if (! $invoice): ?>
|
||||||
|
<div class="alert alert-warning mb-0">Purchase return invoice not found.</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header d-flex flex-wrap justify-content-between align-items-center gap-2">
|
||||||
|
<div>
|
||||||
|
<h3 class="box-title mb-0">Purchase Return Invoice / Supplier : <?= esc((string) ($invoice->name_supplier ?? '-')) ?></h3>
|
||||||
|
<small class="text-muted">
|
||||||
|
Invoice No.: <?= esc((string) ($invoice->Invoice_no ?? '')) ?> |
|
||||||
|
Invoice Date: <?= esc((string) ($invoice->str_date_of_invoice ?? '')) ?>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group btn-group-sm">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" onclick="load_form_div('<?= base_url('Storestock/Purchase_return') ?>','searchresult','Purchase Return');">Back</button>
|
||||||
|
<button type="button" class="btn btn-warning" onclick="load_form_div('<?= base_url('Storestock/PurchaseReturnInvoiceEdit/' . (int) ($invoice->id ?? 0)) ?>','searchresult','Purchase Return Invoice Edit');">Reload Invoice</button>
|
||||||
|
<a href="<?= base_url('Storestock/print_purchase_return/' . (int) ($invoice->id ?? 0)) ?>" target="_blank" class="btn btn-secondary"><i class="fa fa-print"></i> Print</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-body pt-3">
|
||||||
|
<?= $content ?? '' ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
28
app/Views/storestock/purchase_return_invoice_item.php
Normal file
28
app/Views/storestock/purchase_return_invoice_item.php
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php $rows = $purchase_return_invoice_item ?? []; ?>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table id="purchase_return_report_list" class="table table-bordered table-striped table-sm align-middle">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Item</th>
|
||||||
|
<th>Batch</th>
|
||||||
|
<th>Expiry</th>
|
||||||
|
<th>Qty</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (empty($rows)): ?>
|
||||||
|
<tr><td colspan="5" class="text-center text-muted">No return items found.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php $srno = 0; foreach ($rows as $row): $srno++; ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= $srno ?></td>
|
||||||
|
<td><?= esc((string) ($row->Item_name ?? '')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->batch_no_r ?? $row->batch_no ?? '-')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->expiry_date_r ?? $row->expiry_date ?? '-')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->r_qty ?? $row->qty ?? '0')) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
50
app/Views/storestock/purchase_return_new.php
Normal file
50
app/Views/storestock/purchase_return_new.php
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header d-flex justify-content-between align-items-center">
|
||||||
|
<h3 class="box-title mb-0">New Purchase Return Invoice</h3>
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="load_form_div('<?= base_url('Storestock/Purchase_return') ?>','searchresult','Purchase Return');">Back</button>
|
||||||
|
</div>
|
||||||
|
<div class="box-body pt-3">
|
||||||
|
<form id="new-purchase-return-form" class="row g-2" method="post" action="javascript:void(0)">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Supplier</label>
|
||||||
|
<select class="form-select" id="input_supplier" name="input_supplier">
|
||||||
|
<?php foreach (($supplier_data ?? []) as $row): ?>
|
||||||
|
<option value="<?= (int) ($row->sid ?? 0) ?>"><?= esc($row->name_supplier ?? '') ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="form-label">Date of Invoice</label>
|
||||||
|
<input class="form-control" name="datepicker_invoice" id="datepicker_invoice" type="text" value="<?= date('d/m/Y') ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<button type="button" class="btn btn-primary" id="btn_create_purchase_return" accesskey="C">Create</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
$('#btn_create_purchase_return').off('click').on('click', function () {
|
||||||
|
$.post('<?= base_url('Storestock/create_purchase_return') ?>', $('#new-purchase-return-form').serialize(), function (data) {
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
|
notify('error', 'Please Attention', 'Unexpected response');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((data.insertid || 0) <= 0) {
|
||||||
|
notify('error', 'Please Attention', data.show_text || 'Unable to create purchase return invoice');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
notify('success', 'Please Attention', 'Return Invoice : ID->' + data.insertid);
|
||||||
|
load_form_div('<?= base_url('Storestock/PurchaseReturnInvoiceEdit') ?>/' + data.insertid, 'searchresult', 'Purchase Return Invoice Edit');
|
||||||
|
}, 'json').fail(function () {
|
||||||
|
notify('error', 'Please Attention', 'Unable to create purchase return invoice');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
51
app/Views/storestock/purchase_supp_list.php
Normal file
51
app/Views/storestock/purchase_supp_list.php
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php $rows = $purchase_list ?? []; ?>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table id="purchase-report-list" class="table table-bordered table-striped table-sm align-middle">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Invoice ID</th>
|
||||||
|
<th>Supplier</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Amount</th>
|
||||||
|
<th style="width:220px;">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (empty($rows)): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center text-muted">No purchase invoices found.</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php foreach ($rows as $row): ?>
|
||||||
|
<tr class="<?= ((int) ($row->ischallan ?? 0)) === 1 ? 'text-danger' : '' ?>">
|
||||||
|
<td><?= esc((string) ($row->Invoice_no ?? '')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->name_supplier ?? '')) ?></td>
|
||||||
|
<td><?= esc((string) ($row->str_date_of_invoice ?? $row->date_of_invoice ?? '')) ?></td>
|
||||||
|
<td><?= esc((string) number_format((float) ($row->tamount ?? 0), 2)) ?></td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-warning btn-sm" onclick="openStorestockPurchaseSubView('<?= base_url('Storestock/PurchaseMasterEdit/' . (int) ($row->id ?? 0)) ?>','Purchase : Edit');">View & Edit</button>
|
||||||
|
<a href="<?= base_url('Storestock/print_purchase/' . (int) ($row->id ?? 0)) ?>" target="_blank" class="btn btn-secondary btn-sm"><i class="fa fa-print"></i> Print</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
if (window.jQuery && $.fn.DataTable) {
|
||||||
|
if ($.fn.DataTable.isDataTable('#purchase-report-list')) {
|
||||||
|
$('#purchase-report-list').DataTable().destroy();
|
||||||
|
}
|
||||||
|
$('#purchase-report-list').DataTable({
|
||||||
|
pageLength: 25,
|
||||||
|
order: [[0, 'desc']]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
Loading…
Reference in New Issue
Block a user