feat: migrate OPD template flow to DB and apply related updates
This commit is contained in:
parent
145f9fba69
commit
300ec0e5cf
|
|
@ -452,6 +452,9 @@ $routes->get('Opd/opd_lettre_print/(:num)', 'Opd::opd_lettre_print/$1');
|
|||
$routes->get('Opd/opd_lettre_pdf/(:num)', 'Opd::opd_lettre_pdf/$1');
|
||||
$routes->get('Opd/print_template_builder', 'Opd::print_template_builder');
|
||||
$routes->post('Opd/print_template_save', 'Opd::print_template_save');
|
||||
$routes->post('Opd/print_template_rename', 'Opd::print_template_rename');
|
||||
$routes->post('Opd/print_template_delete', 'Opd::print_template_delete');
|
||||
$routes->match(['get', 'post'], 'Opd/paper_print_settings', 'Opd::paper_print_settings');
|
||||
|
||||
$routes->get('opd_print/invoice_print_pdf/(:num)', 'Opd::invoice_print_pdf/$1');
|
||||
$routes->get('opd_print/opd_PDF_print/(:num)', 'Opd::opd_pdf_print/$1');
|
||||
|
|
|
|||
|
|
@ -3357,135 +3357,6 @@ class Diagnosis extends BaseController
|
|||
return '';
|
||||
}
|
||||
|
||||
private function extractTextWithDocumentIntelligence(string $absolutePath, string $endpoint, string $key): string
|
||||
{
|
||||
try {
|
||||
$client = service('curlrequest', $this->httpOptions());
|
||||
$bytes = @file_get_contents($absolutePath);
|
||||
if ($bytes === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$analyzeUrl = $endpoint . '/documentintelligence/documentModels/prebuilt-read:analyze?api-version=2024-02-29-preview';
|
||||
$submit = $client->post($analyzeUrl, [
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'Ocp-Apim-Subscription-Key' => $key,
|
||||
],
|
||||
'body' => $bytes,
|
||||
]);
|
||||
|
||||
if ($submit->getStatusCode() < 200 || $submit->getStatusCode() >= 300) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$operationLocation = trim((string) $submit->getHeaderLine('Operation-Location'));
|
||||
if ($operationLocation === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
for ($i = 0; $i < 12; $i++) {
|
||||
usleep(800000);
|
||||
$poll = $client->get($operationLocation, [
|
||||
'headers' => [
|
||||
'Ocp-Apim-Subscription-Key' => $key,
|
||||
],
|
||||
]);
|
||||
|
||||
if ($poll->getStatusCode() < 200 || $poll->getStatusCode() >= 300) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$decoded = json_decode((string) $poll->getBody(), true);
|
||||
if (! is_array($decoded)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$status = strtolower((string) ($decoded['status'] ?? ''));
|
||||
if ($status === 'succeeded') {
|
||||
$lines = [];
|
||||
$pages = $decoded['analyzeResult']['pages'] ?? [];
|
||||
foreach ($pages as $page) {
|
||||
$pageLines = $page['lines'] ?? [];
|
||||
foreach ($pageLines as $line) {
|
||||
$content = trim((string) ($line['content'] ?? ''));
|
||||
if ($content !== '') {
|
||||
$lines[] = $content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return trim(implode("\n", $lines));
|
||||
}
|
||||
|
||||
if ($status === 'failed') {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function extractTextWithAzureVisionLlm(string $absolutePath): string
|
||||
{
|
||||
$endpoint = rtrim($this->readSetting('AZURE_OPENAI_ENDPOINT'), '/');
|
||||
$apiKey = trim($this->readSetting('AZURE_OPENAI_API_KEY'));
|
||||
$deployment = trim($this->readSetting('AZURE_OPENAI_DEPLOYMENT'));
|
||||
$apiVersion = trim($this->readSetting('AZURE_OPENAI_API_VERSION'));
|
||||
|
||||
if ($endpoint === '' || $apiKey === '' || $deployment === '') {
|
||||
return '';
|
||||
}
|
||||
if ($apiVersion === '') {
|
||||
$apiVersion = '2024-10-21';
|
||||
}
|
||||
|
||||
$mime = $this->detectMimeType($absolutePath);
|
||||
if (! str_starts_with($mime, 'image/')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$bytes = @file_get_contents($absolutePath);
|
||||
if ($bytes === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$dataUrl = 'data:' . $mime . ';base64,' . base64_encode($bytes);
|
||||
$url = $endpoint . '/openai/deployments/' . rawurlencode($deployment) . '/chat/completions?api-version=' . rawurlencode($apiVersion);
|
||||
|
||||
try {
|
||||
$client = service('curlrequest', $this->httpOptions());
|
||||
$response = $client->post($url, [
|
||||
'headers' => [
|
||||
'api-key' => $apiKey,
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'json' => [
|
||||
'messages' => [[
|
||||
'role' => 'user',
|
||||
'content' => [
|
||||
['type' => 'text', 'text' => 'Read and return all text from this medical lab report image exactly as plain text.'],
|
||||
['type' => 'image_url', 'image_url' => ['url' => $dataUrl]],
|
||||
],
|
||||
]],
|
||||
'temperature' => 0,
|
||||
'max_tokens' => 3000,
|
||||
],
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$decoded = json_decode((string) $response->getBody(), true);
|
||||
return trim((string) ($decoded['choices'][0]['message']['content'] ?? ''));
|
||||
} catch (\Throwable $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
|
|
@ -3724,99 +3595,6 @@ class Diagnosis extends BaseController
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function parseLabValuesWithAzureOpenAi(string $ocrText, string $panelName): array
|
||||
{
|
||||
$endpoint = rtrim($this->readSetting('AZURE_OPENAI_ENDPOINT'), '/');
|
||||
$apiKey = trim($this->readSetting('AZURE_OPENAI_API_KEY'));
|
||||
$deployment = trim($this->readSetting('AZURE_OPENAI_DEPLOYMENT'));
|
||||
$apiVersion = trim($this->readSetting('AZURE_OPENAI_API_VERSION'));
|
||||
|
||||
if ($endpoint === '' || $apiKey === '' || $deployment === '') {
|
||||
return [
|
||||
'provider' => 'azure-openai',
|
||||
'model' => '',
|
||||
'values' => [],
|
||||
'raw' => [],
|
||||
'error' => 'Azure OpenAI settings are missing.',
|
||||
];
|
||||
}
|
||||
if ($apiVersion === '') {
|
||||
$apiVersion = '2024-10-21';
|
||||
}
|
||||
|
||||
$schemaHint = '{"panel_name":"string","values":[{"test_name":"string","value":"string","unit":"string","reference_range":"string","abnormal_flag":"H|L|N|","raw_line":"string"}] }';
|
||||
$prompt = "You are an expert lab report parser for Indian hospitals.\n" .
|
||||
"Extract only measurable pathology values from OCR text.\n" .
|
||||
"Return strict JSON only, no markdown, no comments, with schema: " . $schemaHint . "\n" .
|
||||
"If uncertain, keep empty string. panel_name input: " . $panelName . "\n\n" .
|
||||
"OCR_TEXT:\n" . mb_substr($ocrText, 0, 14000);
|
||||
|
||||
$url = $endpoint . '/openai/deployments/' . rawurlencode($deployment) . '/chat/completions?api-version=' . rawurlencode($apiVersion);
|
||||
|
||||
$attempts = $this->aiMaxRetries();
|
||||
$lastError = 'Azure AI parsing failed.';
|
||||
|
||||
for ($attempt = 1; $attempt <= $attempts; $attempt++) {
|
||||
try {
|
||||
$client = service('curlrequest', $this->httpOptions());
|
||||
$response = $client->post($url, [
|
||||
'headers' => [
|
||||
'api-key' => $apiKey,
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'json' => [
|
||||
'messages' => [[
|
||||
'role' => 'user',
|
||||
'content' => $prompt,
|
||||
]],
|
||||
'temperature' => 0,
|
||||
'response_format' => ['type' => 'json_object'],
|
||||
'max_tokens' => 2200,
|
||||
],
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
|
||||
$lastError = 'Azure OpenAI HTTP ' . $response->getStatusCode();
|
||||
continue;
|
||||
}
|
||||
|
||||
$decoded = json_decode((string) $response->getBody(), true);
|
||||
$content = trim((string) ($decoded['choices'][0]['message']['content'] ?? ''));
|
||||
if ($content === '') {
|
||||
$lastError = 'Azure OpenAI returned empty content.';
|
||||
continue;
|
||||
}
|
||||
|
||||
$json = json_decode($content, true);
|
||||
if (! is_array($json)) {
|
||||
$lastError = 'Azure OpenAI returned invalid JSON content.';
|
||||
continue;
|
||||
}
|
||||
|
||||
return [
|
||||
'provider' => 'azure-openai',
|
||||
'model' => (string) ($decoded['model'] ?? $deployment),
|
||||
'values' => is_array($json['values'] ?? null) ? $json['values'] : [],
|
||||
'raw' => $decoded,
|
||||
'error' => '',
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
$lastError = $e->getMessage() !== '' ? $e->getMessage() : 'Azure OpenAI request timeout/error.';
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'provider' => 'azure-openai',
|
||||
'model' => '',
|
||||
'values' => [],
|
||||
'raw' => [],
|
||||
'error' => $lastError,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -483,8 +483,8 @@ class Opd_prescription extends BaseController
|
|||
$draft = $this->generateClinicalTextWithAi($prompt);
|
||||
|
||||
if ($draft !== null && trim($draft) !== '') {
|
||||
$provider = $this->lastAiProvider === 'azure' ? 'azure' : 'ai';
|
||||
$providerLabel = $provider === 'azure' ? 'Azure OpenAI' : 'AI';
|
||||
$provider = $this->lastAiProvider === 'ai-server' ? 'ai-server' : 'ai';
|
||||
$providerLabel = $provider === 'ai-server' ? 'AI Server' : 'AI';
|
||||
return $this->response->setJSON([
|
||||
'update' => 1,
|
||||
'draft_text' => trim($draft),
|
||||
|
|
@ -579,8 +579,8 @@ class Opd_prescription extends BaseController
|
|||
$aiMode = 'fallback';
|
||||
$message = 'AI unavailable. Template draft prepared from entered data.';
|
||||
} else {
|
||||
$aiMode = $this->lastAiProvider === 'azure' ? 'azure' : 'ai';
|
||||
$message = 'Full clinical draft prepared using ' . ($aiMode === 'azure' ? 'Azure OpenAI' : 'AI') . '.';
|
||||
$aiMode = $this->lastAiProvider === 'ai-server' ? 'ai-server' : 'ai';
|
||||
$message = 'Full clinical draft prepared using ' . ($aiMode === 'ai-server' ? 'AI Server' : 'AI') . '.';
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
|
|
@ -2597,37 +2597,12 @@ class Opd_prescription extends BaseController
|
|||
|
||||
$targetLanguageName = $supportedLanguages[$targetLang];
|
||||
|
||||
$prompt = "You are a clinical translation assistant for OPD patient advice in India.\n"
|
||||
. "Translate the following English patient advice into {$targetLanguageName}.\n"
|
||||
. "Keep meaning medically accurate, simple, and patient-friendly.\n"
|
||||
. "Return ONLY valid JSON object: {\"translated_text\":\"...\"}.\n"
|
||||
. "Do not include markdown, extra keys, or explanation.\n"
|
||||
. "English Advice: {$sourceText}";
|
||||
|
||||
$this->lastAiProvider = 'none';
|
||||
$aiText = $this->generateClinicalTextWithAi($prompt, 220);
|
||||
if ($aiText === null || trim($aiText) === '') {
|
||||
return $this->response->setJSON([
|
||||
'update' => 0,
|
||||
'error_text' => 'AI unavailable. Check AI settings.',
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
|
||||
$parsed = $this->parseJsonFromAiResponse($aiText);
|
||||
$translatedText = trim((string) ($parsed['translated_text'] ?? ''));
|
||||
|
||||
if ($translatedText === '') {
|
||||
$translatedText = trim(strip_tags((string) $aiText));
|
||||
$translatedText = preg_replace('/```(?:json)?|```/i', '', $translatedText) ?? $translatedText;
|
||||
$translatedText = trim($translatedText);
|
||||
}
|
||||
|
||||
$translatedText = $this->translateAdviceWithAiServer($sourceText, $targetLang);
|
||||
if ($translatedText === '') {
|
||||
return $this->response->setJSON([
|
||||
'update' => 0,
|
||||
'error_text' => 'Translation failed. Try again.',
|
||||
'error_text' => 'AI server unavailable. Check DIAGNOSIS_AI_SERVER_URL and token settings.',
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
|
|
@ -2635,7 +2610,7 @@ class Opd_prescription extends BaseController
|
|||
|
||||
return $this->response->setJSON([
|
||||
'update' => 1,
|
||||
'provider' => $this->lastAiProvider,
|
||||
'provider' => 'ai-server',
|
||||
'target_lang' => $targetLang,
|
||||
'target_language_name' => $targetLanguageName,
|
||||
'translated_text' => $translatedText,
|
||||
|
|
@ -3016,8 +2991,8 @@ class Opd_prescription extends BaseController
|
|||
$this->lastAiProvider = 'none';
|
||||
$aiDraft = $this->generateComplaintDraftWithAi($finalComplaints, $currentText);
|
||||
if ($aiDraft !== null && trim($aiDraft) !== '') {
|
||||
$provider = $this->lastAiProvider === 'azure' ? 'azure' : 'ai';
|
||||
$providerLabel = $provider === 'azure' ? 'Azure OpenAI' : 'AI';
|
||||
$provider = $this->lastAiProvider === 'ai-server' ? 'ai-server' : 'ai';
|
||||
$providerLabel = $provider === 'ai-server' ? 'AI Server' : 'AI';
|
||||
return $this->response->setJSON([
|
||||
'update' => 1,
|
||||
'draft_text' => trim($aiDraft),
|
||||
|
|
@ -5374,59 +5349,10 @@ class Opd_prescription extends BaseController
|
|||
}
|
||||
|
||||
/**
|
||||
* @return array{endpoint:string,api_key:string,deployment:string,api_version:string}
|
||||
*/
|
||||
private function getAzureOpenAiConfig(): array
|
||||
{
|
||||
$endpoint = $this->readAiSettingValue(
|
||||
['AZURE_OPENAI_ENDPOINT', 'AI_AZURE_OPENAI_ENDPOINT', 'APP_AZURE_OPENAI_ENDPOINT', 'H_AZURE_OPENAI_ENDPOINT'],
|
||||
['AZURE_OPENAI_ENDPOINT']
|
||||
);
|
||||
|
||||
$apiKey = $this->readAiSettingValue(
|
||||
['AZURE_OPENAI_API_KEY', 'AI_AZURE_OPENAI_API_KEY', 'APP_AZURE_OPENAI_API_KEY', 'H_AZURE_OPENAI_API_KEY'],
|
||||
['AZURE_OPENAI_API_KEY']
|
||||
);
|
||||
|
||||
$deployment = $this->readAiSettingValue(
|
||||
['AZURE_OPENAI_DEPLOYMENT', 'AI_AZURE_OPENAI_DEPLOYMENT', 'APP_AZURE_OPENAI_DEPLOYMENT', 'H_AZURE_OPENAI_DEPLOYMENT'],
|
||||
['AZURE_OPENAI_DEPLOYMENT']
|
||||
);
|
||||
|
||||
$apiVersion = $this->readAiSettingValue(
|
||||
['AZURE_OPENAI_API_VERSION', 'AI_AZURE_OPENAI_API_VERSION', 'APP_AZURE_OPENAI_API_VERSION', 'H_AZURE_OPENAI_API_VERSION'],
|
||||
['AZURE_OPENAI_API_VERSION']
|
||||
);
|
||||
|
||||
if ($apiVersion === '') {
|
||||
$apiVersion = '2024-10-21';
|
||||
}
|
||||
|
||||
return [
|
||||
'endpoint' => rtrim($endpoint, '/'),
|
||||
'api_key' => $apiKey,
|
||||
'deployment' => $deployment,
|
||||
'api_version' => $apiVersion,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $constCandidates
|
||||
* @param array<int, string> $settingCandidates
|
||||
*/
|
||||
private function readAiSettingValue(array $constCandidates, array $settingCandidates): string
|
||||
private function readAiSettingValue(array $settingCandidates): string
|
||||
{
|
||||
foreach ($constCandidates as $constName) {
|
||||
if (! defined($constName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = trim((string) constant($constName));
|
||||
if ($value !== '') {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->db->tableExists('hospital_setting') && ! empty($settingCandidates)) {
|
||||
$row = $this->db->table('hospital_setting')
|
||||
->select('s_value')
|
||||
|
|
@ -5462,61 +5388,6 @@ Output only one plain text sentence starting with 'Chief complaints:' and no mar
|
|||
return $this->generateClinicalTextWithAi($prompt, 180);
|
||||
}
|
||||
|
||||
private function generateClinicalTextWithAzureOpenAi(string $prompt, int $maxOutputTokens = 300): ?string
|
||||
{
|
||||
$cfg = $this->getAzureOpenAiConfig();
|
||||
if ($cfg['endpoint'] === '' || $cfg['api_key'] === '' || $cfg['deployment'] === '' || trim($prompt) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$httpOptions = [
|
||||
'timeout' => 20,
|
||||
'http_errors' => false,
|
||||
];
|
||||
if (defined('ENVIRONMENT') && in_array((string) ENVIRONMENT, ['development', 'testing'], true)) {
|
||||
$httpOptions['verify'] = false;
|
||||
}
|
||||
|
||||
$client = service('curlrequest', $httpOptions);
|
||||
$url = $cfg['endpoint'] . '/openai/deployments/' . rawurlencode($cfg['deployment'])
|
||||
. '/chat/completions?api-version=' . rawurlencode($cfg['api_version']);
|
||||
|
||||
$response = $client->post($url, [
|
||||
'headers' => [
|
||||
'api-key' => $cfg['api_key'],
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'json' => [
|
||||
'messages' => [
|
||||
['role' => 'system', 'content' => 'You are a clinical OPD assistant. Return plain text only, no markdown.'],
|
||||
['role' => 'user', 'content' => $prompt],
|
||||
],
|
||||
'temperature' => 0.2,
|
||||
'top_p' => 0.9,
|
||||
'max_tokens' => max(64, $maxOutputTokens),
|
||||
],
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decoded = json_decode((string) $response->getBody(), true);
|
||||
$text = trim((string) ($decoded['choices'][0]['message']['content'] ?? ''));
|
||||
|
||||
$text = trim($text);
|
||||
if ($text === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->lastAiProvider = 'azure';
|
||||
return preg_replace('/\s+/', ' ', $text) ?: $text;
|
||||
} catch (\Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function buildClinicalPrompt(string $mode, string $text): string
|
||||
{
|
||||
if ($mode === 'hinglish_to_english') {
|
||||
|
|
@ -5639,7 +5510,84 @@ Input: " . $text;
|
|||
|
||||
private function generateClinicalTextWithAi(string $prompt, int $maxOutputTokens = 300): ?string
|
||||
{
|
||||
return $this->generateClinicalTextWithAzureOpenAi($prompt, $maxOutputTokens);
|
||||
unset($prompt, $maxOutputTokens);
|
||||
return null;
|
||||
}
|
||||
|
||||
private function resolveAiServerBaseUrl(): string
|
||||
{
|
||||
$base = rtrim($this->readAiSettingValue(['DIAGNOSIS_AI_SERVER_URL']), '/');
|
||||
if ($base !== '') {
|
||||
return $base;
|
||||
}
|
||||
|
||||
$candidates = [
|
||||
trim($this->readAiSettingValue(['DIAGNOSIS_AI_PARSE_ENDPOINT'])),
|
||||
trim($this->readAiSettingValue(['DIAGNOSIS_AI_OCR_ENDPOINT'])),
|
||||
];
|
||||
|
||||
foreach ($candidates as $url) {
|
||||
if ($url === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parts = @parse_url($url);
|
||||
if (! is_array($parts) || empty($parts['scheme']) || empty($parts['host'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$base = (string) $parts['scheme'] . '://' . (string) $parts['host'];
|
||||
if (! empty($parts['port'])) {
|
||||
$base .= ':' . (int) $parts['port'];
|
||||
}
|
||||
|
||||
return rtrim($base, '/');
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function translateAdviceWithAiServer(string $sourceText, string $targetLang): string
|
||||
{
|
||||
$base = $this->resolveAiServerBaseUrl();
|
||||
if ($base === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$token = trim($this->readAiSettingValue(['DIAGNOSIS_AI_PARSE_TOKEN']));
|
||||
|
||||
try {
|
||||
$options = [
|
||||
'timeout' => 20,
|
||||
'http_errors' => false,
|
||||
];
|
||||
if (defined('ENVIRONMENT') && in_array((string) ENVIRONMENT, ['development', 'testing'], true)) {
|
||||
$options['verify'] = false;
|
||||
}
|
||||
|
||||
$headers = ['Accept' => 'application/json'];
|
||||
if ($token !== '') {
|
||||
$headers['Authorization'] = 'Bearer ' . $token;
|
||||
}
|
||||
|
||||
$client = service('curlrequest', $options);
|
||||
$url = $base . '/translate?text=' . rawurlencode($sourceText) . '&target_lang=' . rawurlencode($targetLang);
|
||||
$response = $client->get($url, ['headers' => $headers]);
|
||||
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$decoded = json_decode((string) $response->getBody(), true);
|
||||
$translated = trim((string) ($decoded['translated_text'] ?? ''));
|
||||
if ($translated === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$this->lastAiProvider = 'ai-server';
|
||||
return $translated;
|
||||
} catch (\Throwable $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use App\Models\DoctorModel;
|
|||
|
||||
class Doctor extends BaseController
|
||||
{
|
||||
private const OPD_TEMPLATE_KEY_PREFIX = 'OPD_PRINT_TEMPLATE__';
|
||||
|
||||
public function index(): string
|
||||
{
|
||||
$doctorModel = new DoctorModel();
|
||||
|
|
@ -23,6 +25,7 @@ class Doctor extends BaseController
|
|||
return view('Setting/Doctor/Doctor_V', [
|
||||
'errors' => session('errors'),
|
||||
'template_options' => $templateContext['options'],
|
||||
'opd_print_template_options' => $templateContext['opd_print_options'],
|
||||
'template_fields' => $templateContext['fields'],
|
||||
]);
|
||||
}
|
||||
|
|
@ -42,6 +45,7 @@ class Doctor extends BaseController
|
|||
'errors' => $this->validator->getErrors(),
|
||||
'formData' => $this->request->getPost(),
|
||||
'template_options' => $templateContext['options'],
|
||||
'opd_print_template_options' => $templateContext['opd_print_options'],
|
||||
'template_fields' => $templateContext['fields'],
|
||||
]);
|
||||
}
|
||||
|
|
@ -80,8 +84,7 @@ class Doctor extends BaseController
|
|||
if (! in_array($dbField, $templateContext['fields'], true)) {
|
||||
continue;
|
||||
}
|
||||
$templateName = strtolower(trim((string) $this->request->getPost($inputField)));
|
||||
$templateName = preg_replace('/[^a-z0-9_\-]/', '', $templateName) ?? '';
|
||||
$templateName = $this->normalizeTemplateId((string) $this->request->getPost($inputField));
|
||||
$data[$dbField] = $templateName;
|
||||
}
|
||||
|
||||
|
|
@ -99,6 +102,7 @@ class Doctor extends BaseController
|
|||
'errors' => $errors,
|
||||
'formData' => $this->request->getPost(),
|
||||
'template_options' => $templateContext['options'],
|
||||
'opd_print_template_options' => $templateContext['opd_print_options'],
|
||||
'template_fields' => $templateContext['fields'],
|
||||
]);
|
||||
}
|
||||
|
|
@ -123,6 +127,7 @@ class Doctor extends BaseController
|
|||
'doc_ipd_fee_list' => $doctorModel->getDoctorIpdFees($id),
|
||||
'doc_ipd_fee_type' => $doctorModel->getIpdFeeTypes(),
|
||||
'template_options' => $templateContext['options'],
|
||||
'opd_print_template_options' => $templateContext['opd_print_options'],
|
||||
'template_fields' => $templateContext['fields'],
|
||||
]);
|
||||
}
|
||||
|
|
@ -165,8 +170,7 @@ class Doctor extends BaseController
|
|||
if (! in_array($dbField, $templateContext['fields'], true)) {
|
||||
continue;
|
||||
}
|
||||
$templateName = strtolower(trim((string) $this->request->getPost($inputField)));
|
||||
$templateName = preg_replace('/[^a-z0-9_\-]/', '', $templateName) ?? '';
|
||||
$templateName = $this->normalizeTemplateId((string) $this->request->getPost($inputField));
|
||||
$data[$dbField] = $templateName;
|
||||
}
|
||||
|
||||
|
|
@ -573,59 +577,67 @@ class Doctor extends BaseController
|
|||
];
|
||||
|
||||
if (! $this->db->tableExists('doctor_master')) {
|
||||
return ['fields' => [], 'options' => []];
|
||||
return ['fields' => [], 'options' => [], 'opd_print_options' => []];
|
||||
}
|
||||
|
||||
$doctorFields = $this->db->getFieldNames('doctor_master') ?? [];
|
||||
$availableFields = array_values(array_intersect($allTemplateFields, $doctorFields));
|
||||
|
||||
$options = [];
|
||||
|
||||
$templateDir = APPPATH . 'Views/billing/opd_templates';
|
||||
if (is_dir($templateDir)) {
|
||||
$htmlFiles = glob($templateDir . '/*.html') ?: [];
|
||||
foreach ($htmlFiles as $file) {
|
||||
$name = strtolower(trim((string) basename((string) $file, '.html')));
|
||||
$name = preg_replace('/[^a-z0-9_\-]/', '', $name) ?? '';
|
||||
if ($name !== '') {
|
||||
$options[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
$phpFiles = glob($templateDir . '/*.php') ?: [];
|
||||
foreach ($phpFiles as $file) {
|
||||
$name = strtolower(trim((string) basename((string) $file, '.php')));
|
||||
$name = preg_replace('/[^a-z0-9_\-]/', '', $name) ?? '';
|
||||
if ($name !== '') {
|
||||
$options[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($availableFields as $field) {
|
||||
$rows = $this->db->table('doctor_master')
|
||||
->select($field . ' as template_name')
|
||||
->where($field . ' IS NOT NULL', null, false)
|
||||
->where($field . ' !=', '')
|
||||
->distinct()
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$name = strtolower(trim((string) ($row['template_name'] ?? '')));
|
||||
$name = preg_replace('/[^a-z0-9_\-]/', '', $name) ?? '';
|
||||
if ($name !== '') {
|
||||
$options[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
$options = $this->collectStoredTemplateOptions();
|
||||
$opdPrintOptions = $options;
|
||||
|
||||
$options = array_values(array_unique($options));
|
||||
sort($options);
|
||||
$opdPrintOptions = array_values(array_unique($opdPrintOptions));
|
||||
sort($opdPrintOptions);
|
||||
|
||||
return [
|
||||
'fields' => $availableFields,
|
||||
'options' => $options,
|
||||
'opd_print_options' => $opdPrintOptions,
|
||||
];
|
||||
}
|
||||
|
||||
private function normalizeTemplateId(string $templateId): string
|
||||
{
|
||||
$templateId = strtolower(trim($templateId));
|
||||
$templateId = str_replace([' ', '.'], '_', $templateId);
|
||||
$templateId = preg_replace('/[^a-z0-9_\-]+/', '_', $templateId) ?? '';
|
||||
$templateId = preg_replace('/_{2,}/', '_', $templateId) ?? $templateId;
|
||||
|
||||
return trim($templateId, '_-');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function collectStoredTemplateOptions(): array
|
||||
{
|
||||
if (! $this->db->tableExists('hospital_setting')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->db->table('hospital_setting')
|
||||
->select('s_name')
|
||||
->like('s_name', self::OPD_TEMPLATE_KEY_PREFIX, 'after')
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
$options = [];
|
||||
foreach ($rows as $row) {
|
||||
$storageKey = (string) ($row['s_name'] ?? '');
|
||||
if (! str_starts_with($storageKey, self::OPD_TEMPLATE_KEY_PREFIX)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$templateId = substr($storageKey, strlen(self::OPD_TEMPLATE_KEY_PREFIX));
|
||||
$templateId = $this->normalizeTemplateId((string) $templateId);
|
||||
if ($templateId !== '') {
|
||||
$options[] = $templateId;
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class CreateLabAiExtractionTables extends Migration
|
|||
'ai_provider' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 50,
|
||||
'default' => 'azure-openai',
|
||||
'default' => 'ai-server',
|
||||
],
|
||||
'model_name' => [
|
||||
'type' => 'VARCHAR',
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@
|
|||
<?php
|
||||
$templateFieldSet = array_flip($template_fields ?? []);
|
||||
$templateOptions = $template_options ?? [];
|
||||
$opdPrintTemplateOptions = $opd_print_template_options ?? [];
|
||||
?>
|
||||
<?php if (! empty($templateFieldSet)) : ?>
|
||||
<div class="row g-3 mt-1">
|
||||
|
|
@ -75,7 +76,7 @@
|
|||
<label class="form-label">OPD Print Template</label>
|
||||
<select class="form-select" name="tmpl_opd_print_format">
|
||||
<option value="">-- Select Template --</option>
|
||||
<?php foreach ($templateOptions as $tmpl) : ?>
|
||||
<?php foreach ($opdPrintTemplateOptions as $tmpl) : ?>
|
||||
<option value="<?= esc($tmpl) ?>" <?= combo_checked($tmpl, strtolower(trim((string) ($formData['tmpl_opd_print_format'] ?? '')))) ?>><?= esc($tmpl) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ if (!empty($data) && !empty($data[0])) {
|
|||
<?php
|
||||
$templateFieldSet = array_flip($template_fields ?? []);
|
||||
$templateOptions = $template_options ?? [];
|
||||
$opdPrintTemplateOptions = $opd_print_template_options ?? [];
|
||||
?>
|
||||
<?php if (! empty($templateFieldSet)) : ?>
|
||||
<div class="row g-3 mt-1">
|
||||
|
|
@ -97,7 +98,7 @@ if (!empty($data) && !empty($data[0])) {
|
|||
<label class="form-label">OPD Print Template</label>
|
||||
<select class="form-select" name="tmpl_opd_print_format">
|
||||
<option value="">-- Select Template --</option>
|
||||
<?php foreach ($templateOptions as $tmpl) : ?>
|
||||
<?php foreach ($opdPrintTemplateOptions as $tmpl) : ?>
|
||||
<option value="<?= esc($tmpl) ?>" <?= combo_checked($tmpl, strtolower(trim((string) ($data[0]->opd_print_format ?? '')))) ?>><?= esc($tmpl) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@
|
|||
$('#advice_hi').val((data.translated_text || '').toString());
|
||||
var langName = (data.target_language_name || '').toString();
|
||||
var provider = (data.provider || '').toString();
|
||||
var providerLabel = provider === 'azure' ? 'Azure OpenAI' : 'AI';
|
||||
var providerLabel = provider === 'ai-server' ? 'AI Server' : 'AI';
|
||||
setMsg('ok', 'Translated to ' + (langName || 'selected language') + ' using ' + providerLabel + '.');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -207,8 +207,9 @@ if ($user && method_exists($user, 'inGroup')) {
|
|||
<div class="col-sm-6">
|
||||
<a href="<?= base_url('opd_print/invoice_print_pdf') ?>/<?= esc($opd_master[0]->opd_id ?? 0) ?>" target="_blank" class="btn btn-outline-secondary">Print Invoice</a>
|
||||
<a href="<?= base_url('opd_print/opd_PDF_print') ?>/<?= esc($opd_master[0]->opd_id ?? 0) ?>" target="_blank" class="btn btn-outline-secondary">Print OPD HEAD in Letter Head</a>
|
||||
<a href="<?= base_url('opd_print/opd_Cont_print') ?>/<?= esc($opd_master[0]->opd_id ?? 0) ?>" target="_blank" class="btn btn-outline-secondary">Print Cont. Paper</a>
|
||||
<a href="<?= base_url('opd_print/opd_blank_print') ?>/<?= esc($opd_master[0]->opd_id ?? 0) ?>" target="_blank" class="btn btn-outline-secondary">Print OPD HEAD in Blank Page</a>
|
||||
<a href="<?= base_url('opd_print/opd_Cont_print') ?>/<?= esc($opd_master[0]->opd_id ?? 0) ?>" target="_blank" class="btn btn-outline-secondary">Print Cont. Paper</a>
|
||||
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<?php if (($refund_status ?? 0) == 1) : ?>
|
||||
|
|
|
|||
|
|
@ -608,7 +608,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
if (isNewMedicine && (provider === 'azure' || provider === 'local-brand-fallback')) {
|
||||
if (isNewMedicine && (provider === 'ai-server' || provider === 'local-brand-fallback')) {
|
||||
if (window.confirm('Details prepared for a new medicine. Save this new master entry now?')) {
|
||||
saveMedicine(buildMedicinePayload(), function() {
|
||||
setMsg('ok', 'New medicine saved with suggested generic/salt details.');
|
||||
|
|
|
|||
162
app/Views/billing/opd_paper_print_settings.php
Normal file
162
app/Views/billing/opd_paper_print_settings.php
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
<?php
|
||||
$settings = $settings ?? [];
|
||||
$notice = (string) ($notice ?? '');
|
||||
$noticeType = (string) ($notice_type ?? 'success');
|
||||
$pageSize = strtoupper((string) ($settings['page_size'] ?? 'A4'));
|
||||
?>
|
||||
|
||||
<section class="content-header">
|
||||
<div class="clearfix">
|
||||
<div style="float:left;">
|
||||
<h1>OPD Paper Print Settings</h1>
|
||||
</div>
|
||||
<div style="float:right; margin-top:8px;">
|
||||
<a class="btn btn-sm btn-info" href="javascript:load_form_div('<?= base_url('Opd/print_template_builder') ?>?mode=list','maindiv','OPD Template List');">Template Builder</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="content">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Dedicated OPD PDF page/header/footer settings</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div id="opd_paper_notice">
|
||||
<?php if ($notice !== ''): ?>
|
||||
<div class="alert alert-<?= esc($noticeType) ?> py-2 mb-3"><?= esc($notice) ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<form method="post" action="<?= base_url('Opd/paper_print_settings') ?>" id="opd_paper_print_form">
|
||||
<?= csrf_field() ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<label>Page Size</label>
|
||||
<select class="form-control" name="page_size">
|
||||
<option value="A4" <?= $pageSize === 'A4' ? 'selected' : '' ?>>A4</option>
|
||||
<option value="A4-L" <?= $pageSize === 'A4-L' ? 'selected' : '' ?>>A4 Landscape</option>
|
||||
<option value="A5" <?= $pageSize === 'A5' ? 'selected' : '' ?>>A5</option>
|
||||
<option value="A6" <?= $pageSize === 'A6' ? 'selected' : '' ?>>A6</option>
|
||||
<option value="LETTER" <?= $pageSize === 'LETTER' ? 'selected' : '' ?>>Letter</option>
|
||||
<option value="LEGAL" <?= $pageSize === 'LEGAL' ? 'selected' : '' ?>>Legal</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h4 style="margin-top:0;">Page & Margin (cm)</h4>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<label>Top</label>
|
||||
<input type="number" step="0.1" min="0" max="25" class="form-control" name="page_margin_top_cm" value="<?= esc((string) ($settings['page_margin_top_cm'] ?? '6.1')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label>Bottom</label>
|
||||
<input type="number" step="0.1" min="0" max="25" class="form-control" name="page_margin_bottom_cm" value="<?= esc((string) ($settings['page_margin_bottom_cm'] ?? '2.5')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label>Left</label>
|
||||
<input type="number" step="0.1" min="0" max="25" class="form-control" name="page_margin_left_cm" value="<?= esc((string) ($settings['page_margin_left_cm'] ?? '0.7')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label>Right</label>
|
||||
<input type="number" step="0.1" min="0" max="25" class="form-control" name="page_margin_right_cm" value="<?= esc((string) ($settings['page_margin_right_cm'] ?? '0.7')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label>Header</label>
|
||||
<input type="number" step="0.1" min="0" max="25" class="form-control" name="margin_header_cm" value="<?= esc((string) ($settings['margin_header_cm'] ?? '0.5')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label>Footer</label>
|
||||
<input type="number" step="0.1" min="0" max="25" class="form-control" name="margin_footer_cm" value="<?= esc((string) ($settings['margin_footer_cm'] ?? '1.5')) ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h4 style="margin-top:0;">Header / Footer HTML</h4>
|
||||
<div class="alert alert-info" style="padding:8px 10px; font-size:12px;">
|
||||
Use variables with <b>{{name}}</b> or <b>{name}</b>.<br>
|
||||
Hospital: <code>H_Name</code>, <code>H_address_1</code>, <code>H_address_2</code>, <code>H_phone_No</code>, <code>H_Email</code>, <code>H_logo</code>.<br>
|
||||
Common: <code>print_time</code>, <code>current_date</code>, <code>doctor_name</code>.<br>
|
||||
OPD: <code>pName</code>, <code>age_sex</code>, <code>phoneno</code>, <code>uhid_no</code>, <code>opd_no</code>, <code>opd_date</code>, <code>Complaint</code>, <code>diagnosis</code>, <code>medical</code>, <code>advice</code>.<br>
|
||||
mPDF page tokens like <code>{PAGENO}</code> and <code>{nbpg}</code> are supported.
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<label>Header HTML / mPDF tags</label>
|
||||
<textarea class="form-control" name="header_html" rows="7" style="font-family:Consolas,Monaco,monospace;"><?= esc((string) ($settings['header_html'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
<div class="col-md-12" style="margin-top:10px;">
|
||||
<label>Footer HTML / mPDF tags</label>
|
||||
<textarea class="form-control" name="footer_html" rows="5" style="font-family:Consolas,Monaco,monospace;"><?= esc((string) ($settings['footer_html'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:12px;">
|
||||
<button type="submit" class="btn btn-primary btn-sm">Save OPD Print Settings</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var form = document.getElementById('opd_paper_print_form');
|
||||
var noticeBox = document.getElementById('opd_paper_notice');
|
||||
|
||||
function showNotice(type, message) {
|
||||
if (!noticeBox) {
|
||||
return;
|
||||
}
|
||||
var safeType = (type === 'success') ? 'success' : 'danger';
|
||||
var safeText = String(message || '').replace(/</g, '<').replace(/>/g, '>');
|
||||
noticeBox.innerHTML = '<div class="alert alert-' + safeType + ' py-2 mb-3">' + safeText + '</div>';
|
||||
}
|
||||
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
|
||||
form.addEventListener('submit', function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
var submitBtn = form.querySelector('button[type="submit"]');
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = true;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: form.action,
|
||||
method: 'POST',
|
||||
data: $(form).serialize(),
|
||||
dataType: 'json',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
.done(function (result) {
|
||||
var status = (result && result.status) ? result.status : 'error';
|
||||
showNotice(status === 'success' ? 'success' : 'danger', (result && result.notice) ? result.notice : 'Save failed.');
|
||||
|
||||
if (result && result.csrfName && result.csrfHash) {
|
||||
var csrfInput = form.querySelector('input[name="' + result.csrfName + '"]');
|
||||
if (csrfInput) {
|
||||
csrfInput.value = result.csrfHash;
|
||||
}
|
||||
}
|
||||
})
|
||||
.fail(function () {
|
||||
showNotice('danger', 'Save failed.');
|
||||
})
|
||||
.always(function () {
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<div style="font-size:12px; line-height:1.45;">
|
||||
{{painscale_img}}
|
||||
{{vital_content}}
|
||||
{{Complaint}}
|
||||
{{diagnosis}}
|
||||
{{Finding_Examinations}}
|
||||
{{medical}}
|
||||
{{investigation}}
|
||||
{{Prescriber_Remarks}}
|
||||
{{advice}}
|
||||
{{next_visit}}
|
||||
{{refer_to}}
|
||||
</div>
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<style>
|
||||
@page {
|
||||
margin-top: 3.2cm;
|
||||
margin-bottom: 1.2cm;
|
||||
margin-left: 0.5cm;
|
||||
margin-right: 0.5cm;
|
||||
margin-header: 0.5cm;
|
||||
margin-footer: 0.5cm;
|
||||
header: html_myHeader;
|
||||
footer: html_myFooter;
|
||||
}
|
||||
body { font-family: freeserif, dejavusans, sans-serif; font-size: 10pt; color: #111; }
|
||||
.part-divider { margin: 6px 0; border-top: 1px solid #666; }
|
||||
</style>
|
||||
<htmlpageheader name="myHeader">
|
||||
{{hospital_section}}
|
||||
</htmlpageheader>
|
||||
<htmlpagefooter name="myFooter">
|
||||
<p style="text-align:right; font-size:9px;">Print Time : {{print_time}}</p>
|
||||
</htmlpagefooter>
|
||||
{{patient_section}}
|
||||
<div class="part-divider"></div>
|
||||
{{content_section}}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<style>
|
||||
@page {
|
||||
margin-top: 3.2cm;
|
||||
margin-bottom: 1.2cm;
|
||||
margin-left: 0.5cm;
|
||||
margin-right: 0.5cm;
|
||||
margin-header: 0.5cm;
|
||||
margin-footer: 0.5cm;
|
||||
header: html_myHeader;
|
||||
footer: html_myFooter;
|
||||
}
|
||||
body { font-family: freeserif, dejavusans, sans-serif; font-size: 10pt; color: #111; }
|
||||
.part-divider { margin: 6px 0; border-top: 1px solid #666; }
|
||||
</style>
|
||||
<htmlpageheader name="myHeader">
|
||||
{{hospital_section}}
|
||||
</htmlpageheader>
|
||||
<htmlpagefooter name="myFooter">
|
||||
<p style="text-align:right; font-size:9px;">Print Time : {{print_time}}</p>
|
||||
</htmlpagefooter>
|
||||
{{patient_section}}
|
||||
<div class="part-divider"></div>
|
||||
{{content_section}}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
Patient Name :
|
||||
<td>
|
||||
<td>
|
||||
{{pName}}
|
||||
</td>
|
||||
<tr>
|
||||
</table>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<table width="100%" style="font-size:10pt; border:1px solid #777; border-collapse:collapse;" cellspacing="0" cellpadding="4">
|
||||
<tr>
|
||||
<td width="50%" valign="top" style="border:1px solid #777;">
|
||||
<strong>{{pName}}</strong><br>
|
||||
{{pRelative}}<br>
|
||||
{{age_sex}}<br>
|
||||
{{p_address}}<br>
|
||||
Phone: {{phoneno}}
|
||||
</td>
|
||||
<td width="50%" valign="top" style="border:1px solid #777;">
|
||||
UHID: {{uhid_no}}<br>
|
||||
OPD: {{opd_no}}<br>
|
||||
SR: {{opd_sr_no}}<br>
|
||||
Date: {{opd_date}}<br>
|
||||
{{exp_date}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<table width="100%" style="font-size:10pt; border:1px solid #777; border-collapse:collapse;" cellspacing="0" cellpadding="4">
|
||||
<tr>
|
||||
<td width="33.3%" valign="top" style="border:1px solid #777;">
|
||||
Name : <strong>{{pName}}</strong><br>
|
||||
{{pRelative}}<br>
|
||||
Gender/Age : <b>{{age_sex}}</b><br>
|
||||
Mob : {{phoneno}}<br>
|
||||
Address : {{p_address}}
|
||||
</td>
|
||||
<td width="33.3%" valign="top" style="border:1px solid #777;">
|
||||
UHID : {{uhid_no}}<br>
|
||||
Sr No.: <b>{{opd_sr_no}}</b><br>
|
||||
OPD No.: <b>{{opd_no}}</b><br>
|
||||
<b>Date: {{opd_date}}</b><br>
|
||||
{{exp_date}}
|
||||
</td>
|
||||
<td width="33.3%" valign="top" style="border:1px solid #777;">
|
||||
<b>Department:</b><br>
|
||||
{{SpecName}}<br>
|
||||
{{opd_fee_desc}}<br>
|
||||
<b>No. of Visit:</b> {{total_no_visit}}<br>
|
||||
{{last_opdvisit_date}}<br>
|
||||
Book Time : {{str_opd_book_date}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
@ -6,12 +6,20 @@
|
|||
<div style="float:right; margin-top:8px;">
|
||||
<div class="btn-group btn-group-sm" role="group" aria-label="Template Navigation">
|
||||
<a class="btn btn-info" href="javascript:load_form_div('<?= base_url('Opd/print_template_builder') ?>?mode=list','maindiv','OPD Template List');">Template List</a>
|
||||
<a class="btn btn-default" href="#opd-paper-settings">Paper Print Settings</a>
|
||||
<button type="button" class="btn btn-primary" disabled>Edit Template</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php
|
||||
$paperSettings = $paper_settings ?? [];
|
||||
$paperPageSize = strtoupper((string) ($paperSettings['page_size'] ?? 'A4'));
|
||||
$paperCustomWidth = (string) ($paperSettings['custom_width_mm'] ?? '210');
|
||||
$paperCustomHeight = (string) ($paperSettings['custom_height_mm'] ?? '297');
|
||||
?>
|
||||
|
||||
<section class="content">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
|
|
@ -28,6 +36,7 @@
|
|||
<div class="col-sm-6">
|
||||
<label>Template Name</label>
|
||||
<input type="text" id="tmpl_name" class="form-control" value="<?= esc($selected_name ?? 'default') ?>" placeholder="default">
|
||||
<input type="hidden" id="tmpl_original_name" value="<?= esc($selected_name ?? '') ?>">
|
||||
</div>
|
||||
<div class="col-sm-6" style="padding-top:25px;">
|
||||
<button type="button" class="btn btn-default" id="btn_new_part">New</button>
|
||||
|
|
@ -35,24 +44,9 @@
|
|||
<button type="button" class="btn btn-primary" id="btn_save_part">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-top:8px;">
|
||||
<div class="col-sm-8">
|
||||
<label>Existing Templates</label>
|
||||
<select id="tmpl_existing" class="form-control">
|
||||
<option value="">-- Select existing template --</option>
|
||||
<?php foreach (($template_names ?? []) as $nm) : ?>
|
||||
<option value="<?= esc($nm) ?>" <?= ($selected_name ?? '') === $nm ? 'selected' : '' ?>><?= esc($nm) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-4" style="padding-top:25px;">
|
||||
<button type="button" class="btn btn-info" id="btn_use_existing">Use Selected</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<label>HTML Content</label>
|
||||
<textarea id="tmpl_content" class="form-control" rows="20" style="font-family:Consolas,monospace;"><?= esc($template_content ?? '') ?></textarea>
|
||||
<div class="form-group" style="margin-top:10px;">
|
||||
<label>Template HTML Content (for template id save/load)</label>
|
||||
<textarea id="tmpl_content" class="form-control" rows="10" style="font-family:Consolas,monospace;"><?= esc($template_content ?? '') ?></textarea>
|
||||
</div>
|
||||
<div id="tmpl_msg" class="text-muted"></div>
|
||||
</div>
|
||||
|
|
@ -76,6 +70,101 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top:10px;">
|
||||
<div class="col-md-12">
|
||||
<div class="box box-success" id="opd-paper-settings">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">OPD Paper Print Settings</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div id="opd_paper_msg" class="text-muted" style="margin-bottom:8px;"></div>
|
||||
<div class="form-group">
|
||||
<label>Page Size</label>
|
||||
<select id="opd_page_size" class="form-control input-sm">
|
||||
<option value="A4" <?= $paperPageSize === 'A4' ? 'selected' : '' ?>>A4</option>
|
||||
<option value="A4-L" <?= $paperPageSize === 'A4-L' ? 'selected' : '' ?>>A4 Landscape</option>
|
||||
<option value="A5" <?= $paperPageSize === 'A5' ? 'selected' : '' ?>>A5</option>
|
||||
<option value="A6" <?= $paperPageSize === 'A6' ? 'selected' : '' ?>>A6</option>
|
||||
<option value="LETTER" <?= $paperPageSize === 'LETTER' ? 'selected' : '' ?>>Letter</option>
|
||||
<option value="LEGAL" <?= $paperPageSize === 'LEGAL' ? 'selected' : '' ?>>Legal</option>
|
||||
<option value="CUSTOM" <?= $paperPageSize === 'CUSTOM' ? 'selected' : '' ?>>Custom (mm)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row" id="opd_custom_size_row" style="display:none; margin-top:-2px; margin-bottom:8px;">
|
||||
<div class="col-xs-6">
|
||||
<label>Custom Width (mm)</label>
|
||||
<input type="number" step="1" min="20" max="600" id="opd_custom_width" class="form-control input-sm" value="<?= esc($paperCustomWidth) ?>">
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<label>Custom Height (mm)</label>
|
||||
<input type="number" step="1" min="20" max="1000" id="opd_custom_height" class="form-control input-sm" value="<?= esc($paperCustomHeight) ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top:6px;">
|
||||
<div class="col-md-2 col-sm-4 col-xs-6" style="margin-bottom:8px;">
|
||||
<label>Top (cm)</label>
|
||||
<input type="number" step="0.1" min="0" max="25" id="opd_margin_top" class="form-control input-sm" value="<?= esc((string) ($paperSettings['page_margin_top_cm'] ?? '6.1')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-4 col-xs-6" style="margin-bottom:8px;">
|
||||
<label>Bottom (cm)</label>
|
||||
<input type="number" step="0.1" min="0" max="25" id="opd_margin_bottom" class="form-control input-sm" value="<?= esc((string) ($paperSettings['page_margin_bottom_cm'] ?? '2.5')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-4 col-xs-6" style="margin-bottom:8px;">
|
||||
<label>Left (cm)</label>
|
||||
<input type="number" step="0.1" min="0" max="25" id="opd_margin_left" class="form-control input-sm" value="<?= esc((string) ($paperSettings['page_margin_left_cm'] ?? '0.7')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-4 col-xs-6" style="margin-bottom:8px;">
|
||||
<label>Right (cm)</label>
|
||||
<input type="number" step="0.1" min="0" max="25" id="opd_margin_right" class="form-control input-sm" value="<?= esc((string) ($paperSettings['page_margin_right_cm'] ?? '0.7')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-4 col-xs-6" style="margin-bottom:8px;">
|
||||
<label>Header (cm)</label>
|
||||
<input type="number" step="0.1" min="0" max="25" id="opd_margin_header" class="form-control input-sm" value="<?= esc((string) ($paperSettings['margin_header_cm'] ?? '0.5')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-4 col-xs-6" style="margin-bottom:8px;">
|
||||
<label>Footer (cm)</label>
|
||||
<input type="number" step="0.1" min="0" max="25" id="opd_margin_footer" class="form-control input-sm" value="<?= esc((string) ($paperSettings['margin_footer_cm'] ?? '1.5')) ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="margin-top:8px;">
|
||||
<label>Header HTML</label>
|
||||
<textarea id="opd_header_html" class="form-control input-sm" rows="4" style="font-family:Consolas,monospace;"><?= esc((string) ($paperSettings['header_html'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="margin-top:8px;">
|
||||
<label>Footer HTML</label>
|
||||
<textarea id="opd_footer_html" class="form-control input-sm" rows="4" style="font-family:Consolas,monospace;"><?= esc((string) ($paperSettings['footer_html'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="margin-top:8px;">
|
||||
<label>HTML Content</label>
|
||||
<textarea id="opd_paper_html_content" class="form-control input-sm" rows="8" style="font-family:Consolas,monospace;"><?= esc((string) ($paperSettings['paper_html_content'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="small text-muted" style="margin-bottom:8px;">
|
||||
<strong>How it works:</strong><br>
|
||||
1. Margin values (cm) auto-populate the CSS @page block<br>
|
||||
2. <strong>Header HTML:</strong> Enter HTML for top of page (logo, hospital name, address, doctor info using {{variables}})<br>
|
||||
3. <strong>Footer HTML:</strong> Enter HTML for bottom of page (print time, page number, etc. using {{variables}})<br>
|
||||
4. <strong>HTML Content:</strong> Enter main body content between header and footer (patient info, diagnosis, etc.)<br><br>
|
||||
<strong>Available variables:</strong><br>
|
||||
Hospital: {{H_Name}}, {{H_address_1}}, {{H_address_2}}, {{H_phone_No}}, {{H_Email}}, {{H_logo}}<br>
|
||||
Patient/OPD: {{pName}}, {{age_sex}}, {{phoneno}}, {{uhid_no}}, {{opd_no}}, {{opd_date}}<br>
|
||||
Medical: {{Complaint}}, {{diagnosis}}, {{medical}}, {{advice}}, {{doctor_name}}, {{doctor_sign_html}}<br>
|
||||
Dates: {{print_time}}, {{current_date}}<br>
|
||||
Margins (cm): {{MarginTop}}, {{MarginBottom}}, {{MarginLeft}}, {{MarginRight}}, {{MarginHeader}}, {{MarginFooter}}
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-success btn-sm" id="btn_save_opd_paper">Save OPD Print Settings</button>
|
||||
<button type="button" class="btn btn-default btn-sm" id="btn_reset_opd_paper">Reset</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
|
|
@ -101,31 +190,107 @@
|
|||
el.textContent = msg || '';
|
||||
}
|
||||
|
||||
function setPaperMsg(msg, ok) {
|
||||
var el = document.getElementById('opd_paper_msg');
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
el.className = ok ? 'text-success' : 'text-danger';
|
||||
el.textContent = msg || '';
|
||||
}
|
||||
|
||||
function normalizeTemplateId(value) {
|
||||
value = (value || '').trim().toLowerCase().replace(/[ .]+/g, '_').replace(/[^a-z0-9_\-]+/g, '_').replace(/_+/g, '_');
|
||||
return value.replace(/^[_-]+|[_-]+$/g, '');
|
||||
}
|
||||
|
||||
document.getElementById('btn_load_part').addEventListener('click', function () {
|
||||
var name = (document.getElementById('tmpl_name').value || 'default').trim();
|
||||
var name = normalizeTemplateId(document.getElementById('tmpl_name').value || 'default');
|
||||
if (!name) { name = 'default'; }
|
||||
document.getElementById('tmpl_name').value = name;
|
||||
load_form_div('<?= base_url('Opd/print_template_builder') ?>?mode=edit&name=' + encodeURIComponent(name), 'maindiv', 'OPD Template Edit');
|
||||
});
|
||||
|
||||
document.getElementById('btn_new_part').addEventListener('click', function () {
|
||||
document.getElementById('tmpl_name').value = '';
|
||||
document.getElementById('tmpl_content').value = '';
|
||||
document.getElementById('tmpl_original_name').value = '';
|
||||
setMsg('Enter new template name, then write HTML and click Save.', true);
|
||||
});
|
||||
|
||||
document.getElementById('btn_use_existing').addEventListener('click', function () {
|
||||
var selected = document.getElementById('tmpl_existing').value || '';
|
||||
if (!selected) {
|
||||
setMsg('Please select an existing template.', false);
|
||||
function toggleCustomSize() {
|
||||
var pageSize = document.getElementById('opd_page_size');
|
||||
var customRow = document.getElementById('opd_custom_size_row');
|
||||
if (!pageSize || !customRow) {
|
||||
return;
|
||||
}
|
||||
document.getElementById('tmpl_name').value = selected;
|
||||
load_form_div('<?= base_url('Opd/print_template_builder') ?>?mode=edit&name=' + encodeURIComponent(selected), 'maindiv', 'OPD Template Edit');
|
||||
});
|
||||
customRow.style.display = (pageSize.value === 'CUSTOM') ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function buildTemplateFromPaperFields() {
|
||||
var top = document.getElementById('opd_margin_top');
|
||||
var bottom = document.getElementById('opd_margin_bottom');
|
||||
var left = document.getElementById('opd_margin_left');
|
||||
var right = document.getElementById('opd_margin_right');
|
||||
var marginHeader = document.getElementById('opd_margin_header');
|
||||
var marginFooter = document.getElementById('opd_margin_footer');
|
||||
var header = document.getElementById('opd_header_html');
|
||||
var footer = document.getElementById('opd_footer_html');
|
||||
var body = document.getElementById('opd_paper_html_content');
|
||||
|
||||
var pageBlock = '<style>@page {\n'
|
||||
+ 'margin-top: ' + ((top && top.value) || '6.1') + 'cm;\n'
|
||||
+ 'margin-bottom: ' + ((bottom && bottom.value) || '2.5') + 'cm;\n'
|
||||
+ 'margin-left: ' + ((left && left.value) || '0.7') + 'cm;\n'
|
||||
+ 'margin-right: ' + ((right && right.value) || '0.7') + 'cm;\n'
|
||||
+ 'margin-header: ' + ((marginHeader && marginHeader.value) || '0.5') + 'cm;\n'
|
||||
+ 'margin-footer: ' + ((marginFooter && marginFooter.value) || '1.5') + 'cm;\n'
|
||||
+ 'header: html_myHeader;\n'
|
||||
+ 'footer: html_myFooter;\n'
|
||||
+ '}\n</style>\n\n';
|
||||
|
||||
var headerBlock = '';
|
||||
var headerText = (header && header.value) ? header.value.trim() : '';
|
||||
if (headerText !== '') {
|
||||
if (/<\s*htmlpageheader\b/i.test(headerText)) {
|
||||
headerBlock = headerText + '\n\n';
|
||||
} else {
|
||||
headerBlock = '<htmlpageheader name="myHeader">\n' + headerText + '\n</htmlpageheader>\n\n';
|
||||
}
|
||||
}
|
||||
|
||||
var footerBlock = '';
|
||||
var footerText = (footer && footer.value) ? footer.value.trim() : '';
|
||||
if (footerText !== '') {
|
||||
if (/<\s*htmlpagefooter\b/i.test(footerText)) {
|
||||
footerBlock = footerText + '\n\n';
|
||||
} else {
|
||||
footerBlock = '<htmlpagefooter name="myFooter">\n' + footerText + '\n</htmlpagefooter>\n\n';
|
||||
}
|
||||
}
|
||||
|
||||
var bodyText = (body && body.value) ? body.value.trim() : '';
|
||||
return pageBlock + headerBlock + footerBlock + bodyText;
|
||||
}
|
||||
|
||||
var pageSizeSelect = document.getElementById('opd_page_size');
|
||||
if (pageSizeSelect) {
|
||||
pageSizeSelect.addEventListener('change', toggleCustomSize);
|
||||
}
|
||||
toggleCustomSize();
|
||||
|
||||
document.getElementById('btn_save_part').addEventListener('click', function () {
|
||||
var name = (document.getElementById('tmpl_name').value || '').trim();
|
||||
var name = normalizeTemplateId(document.getElementById('tmpl_name').value || '');
|
||||
var originalName = normalizeTemplateId(document.getElementById('tmpl_original_name').value || '');
|
||||
var content = document.getElementById('tmpl_content').value || '';
|
||||
document.getElementById('tmpl_name').value = name;
|
||||
if (!content.trim()) {
|
||||
content = buildTemplateFromPaperFields();
|
||||
var tmplContentEl = document.getElementById('tmpl_content');
|
||||
if (tmplContentEl && content.trim()) {
|
||||
tmplContentEl.value = content;
|
||||
}
|
||||
}
|
||||
if (!name) {
|
||||
setMsg('Template name required', false);
|
||||
return;
|
||||
|
|
@ -138,22 +303,191 @@
|
|||
};
|
||||
payload[csrf.name] = csrf.value;
|
||||
|
||||
$.post('<?= base_url('Opd/print_template_save') ?>', payload, function (res) {
|
||||
if (res && res.csrfName && res.csrfHash) {
|
||||
var tokenInput = document.querySelector('input[name="' + res.csrfName + '"]');
|
||||
if (tokenInput) tokenInput.value = res.csrfHash;
|
||||
}
|
||||
if (!res || Number(res.update || 0) !== 1) {
|
||||
setMsg((res && res.error_text) ? res.error_text : 'Unable to save template', false);
|
||||
return;
|
||||
}
|
||||
setMsg('Saved: ' + (res.section || 'full') + '/' + (res.name || name), true);
|
||||
setTimeout(function () {
|
||||
load_form_div('<?= base_url('Opd/print_template_builder') ?>?mode=edit&name=' + encodeURIComponent(res.name || name), 'maindiv', 'OPD Template Edit');
|
||||
}, 250);
|
||||
}, 'json').fail(function () {
|
||||
setMsg('Unable to save template', false);
|
||||
});
|
||||
var saveTemplate = function () {
|
||||
$.post('<?= base_url('Opd/print_template_save') ?>', payload, function (res) {
|
||||
if (res && res.csrfName && res.csrfHash) {
|
||||
var tokenInput = document.querySelector('input[name="' + res.csrfName + '"]');
|
||||
if (tokenInput) tokenInput.value = res.csrfHash;
|
||||
}
|
||||
if (!res || Number(res.update || 0) !== 1) {
|
||||
setMsg((res && res.error_text) ? res.error_text : 'Unable to save template', false);
|
||||
return;
|
||||
}
|
||||
document.getElementById('tmpl_original_name').value = (res.name || name);
|
||||
setMsg('Saved: ' + (res.section || 'full') + '/' + (res.name || name), true);
|
||||
setTimeout(function () {
|
||||
load_form_div('<?= base_url('Opd/print_template_builder') ?>?mode=edit&name=' + encodeURIComponent(res.name || name), 'maindiv', 'OPD Template Edit');
|
||||
}, 250);
|
||||
}, 'json').fail(function () {
|
||||
setMsg('Unable to save template', false);
|
||||
});
|
||||
};
|
||||
|
||||
if (originalName && originalName !== name) {
|
||||
var renamePayload = {
|
||||
old_name: originalName,
|
||||
new_name: name
|
||||
};
|
||||
var renameCsrf = getCsrfPair();
|
||||
renamePayload[renameCsrf.name] = renameCsrf.value;
|
||||
|
||||
$.post('<?= base_url('Opd/print_template_rename') ?>', renamePayload, function (renameRes) {
|
||||
if (renameRes && renameRes.csrfName && renameRes.csrfHash) {
|
||||
var renameTokenInput = document.querySelector('input[name="' + renameRes.csrfName + '"]');
|
||||
if (renameTokenInput) renameTokenInput.value = renameRes.csrfHash;
|
||||
}
|
||||
|
||||
if (!renameRes || Number(renameRes.update || 0) !== 1) {
|
||||
setMsg((renameRes && renameRes.error_text) ? renameRes.error_text : 'Unable to rename template', false);
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('tmpl_original_name').value = name;
|
||||
saveTemplate();
|
||||
}, 'json').fail(function () {
|
||||
setMsg('Unable to rename template', false);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
saveTemplate();
|
||||
});
|
||||
|
||||
var savePaperBtn = document.getElementById('btn_save_opd_paper');
|
||||
if (savePaperBtn) {
|
||||
savePaperBtn.addEventListener('click', function () {
|
||||
var csrf = getCsrfPair();
|
||||
var currentTemplateName = normalizeTemplateId(document.getElementById('tmpl_name').value || '');
|
||||
var payload = {
|
||||
template_name: currentTemplateName,
|
||||
page_size: (document.getElementById('opd_page_size').value || 'A4'),
|
||||
custom_width_mm: document.getElementById('opd_custom_width').value || '210',
|
||||
custom_height_mm: document.getElementById('opd_custom_height').value || '297',
|
||||
page_margin_top_cm: document.getElementById('opd_margin_top').value || '6.1',
|
||||
page_margin_bottom_cm: document.getElementById('opd_margin_bottom').value || '2.5',
|
||||
page_margin_left_cm: document.getElementById('opd_margin_left').value || '0.7',
|
||||
page_margin_right_cm: document.getElementById('opd_margin_right').value || '0.7',
|
||||
margin_header_cm: document.getElementById('opd_margin_header').value || '0.5',
|
||||
margin_footer_cm: document.getElementById('opd_margin_footer').value || '1.5',
|
||||
header_html: document.getElementById('opd_header_html').value || '',
|
||||
footer_html: document.getElementById('opd_footer_html').value || '',
|
||||
paper_html_content: document.getElementById('opd_paper_html_content').value || ''
|
||||
};
|
||||
payload[csrf.name] = csrf.value;
|
||||
|
||||
savePaperBtn.disabled = true;
|
||||
|
||||
$.ajax({
|
||||
url: '<?= base_url('Opd/paper_print_settings') ?>',
|
||||
method: 'POST',
|
||||
data: payload,
|
||||
dataType: 'json',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
}).done(function (res) {
|
||||
if (res && res.csrfName && res.csrfHash) {
|
||||
var tokenInput = document.querySelector('input[name="' + res.csrfName + '"]');
|
||||
if (tokenInput) tokenInput.value = res.csrfHash;
|
||||
}
|
||||
if (!res || res.status !== 'success') {
|
||||
setPaperMsg((res && res.notice) ? res.notice : 'Unable to save OPD print settings', false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep template library in sync when user saves paper settings from edit page.
|
||||
if (currentTemplateName) {
|
||||
var tmplCsrf = getCsrfPair();
|
||||
var templatePayload = {
|
||||
section: 'full',
|
||||
name: currentTemplateName,
|
||||
content: buildTemplateFromPaperFields()
|
||||
};
|
||||
templatePayload[tmplCsrf.name] = tmplCsrf.value;
|
||||
|
||||
$.post('<?= base_url('Opd/print_template_save') ?>', templatePayload, function (tmplRes) {
|
||||
if (tmplRes && tmplRes.csrfName && tmplRes.csrfHash) {
|
||||
var tmplTokenInput = document.querySelector('input[name="' + tmplRes.csrfName + '"]');
|
||||
if (tmplTokenInput) tmplTokenInput.value = tmplRes.csrfHash;
|
||||
}
|
||||
|
||||
if (!tmplRes || Number(tmplRes.update || 0) !== 1) {
|
||||
setPaperMsg((res.notice || 'OPD print settings saved.') + ' But template id save failed.', false);
|
||||
setMsg((tmplRes && tmplRes.error_text) ? tmplRes.error_text : 'Unable to save template id', false);
|
||||
return;
|
||||
}
|
||||
|
||||
setMsg('Saved: full/' + (tmplRes.name || currentTemplateName), true);
|
||||
setPaperMsg((res.notice || 'OPD print settings saved.') + ' Template id synced: ' + (tmplRes.name || currentTemplateName), true);
|
||||
}, 'json').fail(function () {
|
||||
setPaperMsg((res.notice || 'OPD print settings saved.') + ' But template id save failed.', false);
|
||||
setMsg('Unable to save template id', false);
|
||||
});
|
||||
} else {
|
||||
setPaperMsg((res.notice || 'OPD print settings saved.'), true);
|
||||
}
|
||||
}).fail(function () {
|
||||
setPaperMsg('Unable to save OPD print settings', false);
|
||||
}).always(function () {
|
||||
savePaperBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var resetPaperBtn = document.getElementById('btn_reset_opd_paper');
|
||||
if (resetPaperBtn) {
|
||||
resetPaperBtn.addEventListener('click', function () {
|
||||
var csrf = getCsrfPair();
|
||||
var currentTemplateName = normalizeTemplateId(document.getElementById('tmpl_name').value || '');
|
||||
var payload = {
|
||||
reset: 1,
|
||||
template_name: currentTemplateName
|
||||
};
|
||||
payload[csrf.name] = csrf.value;
|
||||
|
||||
resetPaperBtn.disabled = true;
|
||||
|
||||
$.ajax({
|
||||
url: '<?= base_url('Opd/paper_print_settings') ?>',
|
||||
method: 'POST',
|
||||
data: payload,
|
||||
dataType: 'json',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
}).done(function (res) {
|
||||
if (res && res.csrfName && res.csrfHash) {
|
||||
var tokenInput = document.querySelector('input[name="' + res.csrfName + '"]');
|
||||
if (tokenInput) tokenInput.value = res.csrfHash;
|
||||
}
|
||||
|
||||
if (!res || res.status !== 'success') {
|
||||
setPaperMsg((res && res.notice) ? res.notice : 'Unable to reset OPD print settings', false);
|
||||
return;
|
||||
}
|
||||
|
||||
var settings = res.settings || {};
|
||||
document.getElementById('opd_page_size').value = settings.page_size || 'A4';
|
||||
document.getElementById('opd_custom_width').value = settings.custom_width_mm || '210';
|
||||
document.getElementById('opd_custom_height').value = settings.custom_height_mm || '297';
|
||||
document.getElementById('opd_margin_top').value = settings.page_margin_top_cm || '6.1';
|
||||
document.getElementById('opd_margin_bottom').value = settings.page_margin_bottom_cm || '2.5';
|
||||
document.getElementById('opd_margin_left').value = settings.page_margin_left_cm || '0.7';
|
||||
document.getElementById('opd_margin_right').value = settings.page_margin_right_cm || '0.7';
|
||||
document.getElementById('opd_margin_header').value = settings.margin_header_cm || '0.5';
|
||||
document.getElementById('opd_margin_footer').value = settings.margin_footer_cm || '1.5';
|
||||
document.getElementById('opd_header_html').value = settings.header_html || '';
|
||||
document.getElementById('opd_footer_html').value = settings.footer_html || '';
|
||||
document.getElementById('opd_paper_html_content').value = settings.paper_html_content || '';
|
||||
toggleCustomSize();
|
||||
|
||||
setPaperMsg((res.notice || 'OPD print settings reset.'), true);
|
||||
}).fail(function () {
|
||||
setPaperMsg('Unable to reset OPD print settings', false);
|
||||
}).always(function () {
|
||||
resetPaperBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<?= csrf_field() ?>
|
||||
<div id="tmpl_list_msg" class="text-muted" style="margin-bottom:8px;"></div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped">
|
||||
|
|
@ -33,7 +34,7 @@
|
|||
<tr>
|
||||
<th style="width: 70px;">#</th>
|
||||
<th>Template Name</th>
|
||||
<th style="width: 140px;">Action</th>
|
||||
<th style="width: 220px;">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -48,6 +49,8 @@
|
|||
<td><?= esc($nm) ?></td>
|
||||
<td>
|
||||
<a class="btn btn-primary btn-xs" href="javascript:load_form_div('<?= base_url('Opd/print_template_builder') ?>?mode=edit&name=<?= esc($nm) ?>','maindiv','OPD Template Edit');">Edit</a>
|
||||
<button type="button" class="btn btn-default btn-xs btn_rename_template" data-name="<?= esc($nm) ?>">Rename</button>
|
||||
<button type="button" class="btn btn-danger btn-xs btn_delete_template" data-name="<?= esc($nm) ?>">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
|
@ -61,6 +64,28 @@
|
|||
|
||||
<script>
|
||||
(function () {
|
||||
function normalizeTemplateId(value) {
|
||||
value = (value || '').trim().toLowerCase().replace(/[ .]+/g, '_').replace(/[^a-z0-9_\-]+/g, '_').replace(/_+/g, '_');
|
||||
return value.replace(/^[_-]+|[_-]+$/g, '');
|
||||
}
|
||||
|
||||
function getCsrfPair() {
|
||||
var input = document.querySelector('input[name^="csrf_"]');
|
||||
if (!input) {
|
||||
return { name: 'csrf_test_name', value: '' };
|
||||
}
|
||||
return { name: input.getAttribute('name'), value: input.value };
|
||||
}
|
||||
|
||||
function setListMsg(msg, ok) {
|
||||
var el = document.getElementById('tmpl_list_msg');
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
el.className = ok ? 'text-success' : 'text-danger';
|
||||
el.textContent = msg || '';
|
||||
}
|
||||
|
||||
var btn = document.getElementById('btn_new_template');
|
||||
if (!btn) {
|
||||
return;
|
||||
|
|
@ -71,17 +96,102 @@
|
|||
if (name === null) {
|
||||
return;
|
||||
}
|
||||
name = (name || '').trim().toLowerCase().replace(/[^a-z0-9_\-]/g, '');
|
||||
name = normalizeTemplateId(name);
|
||||
if (!name) {
|
||||
var msg = document.getElementById('tmpl_list_msg');
|
||||
if (msg) {
|
||||
msg.className = 'text-danger';
|
||||
msg.textContent = 'Valid template name required.';
|
||||
}
|
||||
setListMsg('Valid template name required.', false);
|
||||
return;
|
||||
}
|
||||
|
||||
load_form_div('<?= base_url('Opd/print_template_builder') ?>?mode=edit&name=' + encodeURIComponent(name), 'maindiv', 'OPD Template Edit');
|
||||
});
|
||||
|
||||
var renameButtons = document.querySelectorAll('.btn_rename_template');
|
||||
renameButtons.forEach(function (renameBtn) {
|
||||
renameBtn.addEventListener('click', function () {
|
||||
var oldName = (renameBtn.getAttribute('data-name') || '').trim();
|
||||
if (!oldName) {
|
||||
return;
|
||||
}
|
||||
|
||||
var newName = window.prompt('Enter new template name (letters, numbers, _ or -):', oldName);
|
||||
if (newName === null) {
|
||||
return;
|
||||
}
|
||||
newName = normalizeTemplateId(newName);
|
||||
if (!newName) {
|
||||
setListMsg('Valid new template name required.', false);
|
||||
return;
|
||||
}
|
||||
if (newName === oldName) {
|
||||
setListMsg('New template name is same as current name.', false);
|
||||
return;
|
||||
}
|
||||
|
||||
var csrf = getCsrfPair();
|
||||
var payload = {
|
||||
old_name: oldName,
|
||||
new_name: newName
|
||||
};
|
||||
payload[csrf.name] = csrf.value;
|
||||
|
||||
$.post('<?= base_url('Opd/print_template_rename') ?>', payload, function (res) {
|
||||
if (res && res.csrfName && res.csrfHash) {
|
||||
var tokenInput = document.querySelector('input[name="' + res.csrfName + '"]');
|
||||
if (tokenInput) {
|
||||
tokenInput.value = res.csrfHash;
|
||||
}
|
||||
}
|
||||
|
||||
if (!res || Number(res.update || 0) !== 1) {
|
||||
setListMsg((res && res.error_text) ? res.error_text : 'Unable to rename template', false);
|
||||
return;
|
||||
}
|
||||
|
||||
setListMsg('Template renamed: ' + oldName + ' -> ' + (res.new_name || newName), true);
|
||||
load_form_div('<?= base_url('Opd/print_template_builder') ?>?mode=list', 'maindiv', 'OPD Template List');
|
||||
}, 'json').fail(function () {
|
||||
setListMsg('Unable to rename template', false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var deleteButtons = document.querySelectorAll('.btn_delete_template');
|
||||
deleteButtons.forEach(function (deleteBtn) {
|
||||
deleteBtn.addEventListener('click', function () {
|
||||
var templateName = (deleteBtn.getAttribute('data-name') || '').trim();
|
||||
if (!templateName) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm('Are you sure you want to delete template "' + templateName + '"? This action cannot be undone.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var csrf = getCsrfPair();
|
||||
var payload = {
|
||||
template_name: templateName
|
||||
};
|
||||
payload[csrf.name] = csrf.value;
|
||||
|
||||
$.post('<?= base_url('Opd/print_template_delete') ?>', payload, function (res) {
|
||||
if (res && res.csrfName && res.csrfHash) {
|
||||
var tokenInput = document.querySelector('input[name="' + res.csrfName + '"]');
|
||||
if (tokenInput) {
|
||||
tokenInput.value = res.csrfHash;
|
||||
}
|
||||
}
|
||||
|
||||
if (!res || Number(res.update || 0) !== 1) {
|
||||
setListMsg((res && res.error_text) ? res.error_text : 'Unable to delete template', false);
|
||||
return;
|
||||
}
|
||||
|
||||
setListMsg('Template deleted: ' + templateName, true);
|
||||
load_form_div('<?= base_url('Opd/print_template_builder') ?>?mode=list', 'maindiv', 'OPD Template List');
|
||||
}, 'json').fail(function () {
|
||||
setListMsg('Unable to delete template', false);
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
<H1>Hello World</h1>
|
||||
|
|
@ -1 +0,0 @@
|
|||
<H1>Hello World</h1>
|
||||
|
|
@ -1 +0,0 @@
|
|||
<h1>Test</h1>
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
$opd = $opd_master[0] ?? null;
|
||||
$patient = $patient_master[0] ?? null;
|
||||
$doctor = $doctor_master[0] ?? null;
|
||||
|
||||
$pName = strtoupper(trim((string) (($patient->title ?? '') . ' ' . ($patient->p_fname ?? ''))));
|
||||
$pRelative = trim((string) (($patient->p_relative ?? '') . ' ' . ($patient->p_rname ?? '')));
|
||||
$ageSex = trim((string) (($patient->xgender ?? '') . ' / ' . ($patient->age ?? '')));
|
||||
$phone = (string) ($patient->mphone1 ?? '');
|
||||
$pAddress = trim((string) (($patient->add1 ?? '') . ', ' . ($patient->add2 ?? '') . ', ' . ($patient->city ?? '')), ' ,');
|
||||
|
||||
$uhidNo = (string) ($patient->p_code ?? '');
|
||||
$opdSrNo = (string) ($opd->opd_no ?? '');
|
||||
$opdNo = (string) ($opd->opd_code ?? '');
|
||||
$opdDate = (string) ($opd->str_apointment_date ?? '');
|
||||
$expDate = (string) ($opd->opd_Exp_Date ?? '');
|
||||
|
||||
$specName = trim((string) ($doctor->SpecName ?? $opd->doc_spec ?? ''));
|
||||
$opdFeeDesc = trim((string) ($opd->opd_fee_desc ?? ''));
|
||||
$totalNoVisit = (string) ($opd->no_visit ?? '');
|
||||
$lastVisitDate = trim((string) (($old_opd[0]->str_apointment_date ?? '') ?: ''));
|
||||
$bookTime = (string) ($opd->apointment_date ?? '');
|
||||
|
||||
$lastVisitLine = $lastVisitDate !== '' ? ('<br>Last Visit : ' . esc($lastVisitDate)) : '';
|
||||
$expDateLine = $expDate !== '' ? ('<b>Valid Upto : </b>' . esc($expDate)) : '';
|
||||
?>
|
||||
<style>
|
||||
@page {
|
||||
margin-top: 5.5cm;
|
||||
margin-bottom: 1.2cm;
|
||||
margin-left: 0.5cm;
|
||||
margin-right: 0.5cm;
|
||||
margin-header: 0.5cm;
|
||||
margin-footer: 0.5cm;
|
||||
header: html_myHeader;
|
||||
footer: html_myFooter;
|
||||
}
|
||||
body { font-family: freeserif, dejavusans, sans-serif; font-size: 10pt; }
|
||||
</style>
|
||||
|
||||
<htmlpageheader name="myHeader">
|
||||
<p align="center"></p>
|
||||
</htmlpageheader>
|
||||
|
||||
<htmlpagefooter name="myFooter">
|
||||
<p align="center">Print Time : <?= date('d-m-Y H:i:s') ?></p>
|
||||
</htmlpagefooter>
|
||||
|
||||
<table width="100%" border="0" style="font-size:10pt;">
|
||||
<tr>
|
||||
<td width="33.3%" valign="top">
|
||||
Name : <strong><?= esc($pName) ?></strong><br>
|
||||
<?= esc($pRelative) ?><br>
|
||||
Gender/Age : <b><?= esc($ageSex) ?></b><br>
|
||||
Mob : <?= esc($phone) ?><br>
|
||||
Address : <?= esc($pAddress) ?>
|
||||
</td>
|
||||
<td width="33.3%" valign="top">
|
||||
UHID : <?= esc($uhidNo) ?><br>
|
||||
Sr No.: <b><?= esc($opdSrNo) ?></b><br>
|
||||
OPD No.: <b><?= esc($opdNo) ?></b><br>
|
||||
<br><b>Date: <?= esc($opdDate) ?></b><br>
|
||||
<?= $expDateLine ?>
|
||||
</td>
|
||||
<td width="33.3%" valign="top">
|
||||
<b>DEPARTMENT :</b><br>
|
||||
<?= esc($specName) ?><br>
|
||||
<?= esc($opdFeeDesc) ?><br>
|
||||
<b>No. of Visit</b> : <?= esc($totalNoVisit) ?>
|
||||
<?= $lastVisitLine ?><br>
|
||||
Book Time : <?= esc($bookTime) ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
84
app/Views/billing/opd_templates/printonletterhead.html
Normal file
84
app/Views/billing/opd_templates/printonletterhead.html
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<style>@page {
|
||||
margin-top: 4cm;
|
||||
margin-bottom: 2.5cm;
|
||||
margin-left: 0.7cm;
|
||||
margin-right: 0.7cm;
|
||||
margin-header: 0.5cm;
|
||||
margin-footer: 1.5cm;
|
||||
header: html_myHeader;
|
||||
footer: html_myFooter;
|
||||
}
|
||||
</style>
|
||||
|
||||
<htmlpageheader name="myHeader">
|
||||
<table cellspacing="0" style="font-size: 10px;width:100%;border-style: inset;">
|
||||
<tr>
|
||||
<td style="width: 120px;vertical-align: top;">
|
||||
<img style="width: 100px;vertical-align: top;" src="{{H_logo_abs}}" />
|
||||
</td>
|
||||
<td style="width: 400px;vertical-align: top;text-align: left;">
|
||||
<p align="center" style="font-size: 20px;">{{H_Name}}</p>
|
||||
<p align="center" style="font-size: 12px">{{H_address_1}}, {{H_address_2}}<br>Phone: {{H_phone_No}}</p>
|
||||
</td>
|
||||
<td style="width: 300px;vertical-align: top;text-align: right;">
|
||||
<p align="right" style="font-size: 22px;">Dr. {{doctor_name}}</p>
|
||||
{{doctor_sign_html}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr/>
|
||||
</htmlpageheader>
|
||||
|
||||
<htmlpagefooter name="myFooter">
|
||||
<p align="right" style="font-size: 10px;">Prescription Generated: {{print_time}}</p>
|
||||
<p style="font-size: 9px;">Please ensure doctor registration number and signature/stamp are present for regulatory compliance.</p>
|
||||
</htmlpagefooter>
|
||||
|
||||
<table width="100%" style="font-size: 11px; border-collapse: collapse;">
|
||||
<tr>
|
||||
<td width="50%">
|
||||
<strong>Name:</strong> {{pName}}<br>
|
||||
<strong>S/o:</strong> [Father Name]<br>
|
||||
<strong>Gender/Age:</strong> {{age_sex}}<br>
|
||||
<strong>Mob:</strong> {{phoneno}}<br>
|
||||
<strong>Address:</strong> [Address]
|
||||
</td>
|
||||
<td width="50%">
|
||||
<strong>UHID:</strong> {{uhid_no}}<br>
|
||||
<strong>Sr No.:</strong> [Sr No]<br>
|
||||
<strong>OPD No.:</strong> {{opd_no}}<br>
|
||||
<strong>Date & Time:</strong> {{opd_date}} 12:00 AM<br>
|
||||
<strong>Valid Upto:</strong> [Valid Date]
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table width="100%" style="font-size: 11px; border-collapse: collapse; margin-top: 10px;">
|
||||
<tr>
|
||||
<td width="33%">
|
||||
<strong>DEPARTMENT:</strong><br>{{Complaint}}
|
||||
</td>
|
||||
<td width="33%">
|
||||
<strong>DIAGNOSIS:</strong><br>{{diagnosis}}
|
||||
</td>
|
||||
<td width="33%">
|
||||
<strong>Last Visit:</strong> [Date]<br>
|
||||
<strong>No. of Visit:</strong> [Count]
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3 style="margin: 15px 0 10px 0;">Rx :</h3>
|
||||
|
||||
{{medical}}
|
||||
|
||||
<p style="margin-top: 20px; font-size: 10px;">
|
||||
<strong>Investigation:</strong> {{investigation}}<br>
|
||||
<strong>Advice:</strong> {{advice}}
|
||||
</p>
|
||||
|
||||
<div style="margin-top: 40px; text-align: right;">
|
||||
<p><strong>Dr. {{doctor_name}}</strong></p>
|
||||
{{doctor_sign_html}}
|
||||
<p style="font-size: 10px; margin-top: 5px;"><strong>Reg. No.: 80982</strong></p>
|
||||
</div>
|
||||
86
app/Views/billing/opd_templates/printonplainpaper.html
Normal file
86
app/Views/billing/opd_templates/printonplainpaper.html
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
<style>@page {
|
||||
margin-top: 4cm;
|
||||
margin-bottom: 2.5cm;
|
||||
margin-left: 0.7cm;
|
||||
margin-right: 0.7cm;
|
||||
margin-header: 0.5cm;
|
||||
margin-footer: 1.5cm;
|
||||
header: html_myHeader;
|
||||
footer: html_myFooter;
|
||||
}
|
||||
</style>
|
||||
|
||||
<htmlpageheader name="myHeader">
|
||||
<table cellspacing="0" style="font-size: 10px;width:100%;border-style: inset;">
|
||||
<tr>
|
||||
<td style="width: 120px;vertical-align: top;">
|
||||
<img style="width: 100px;vertical-align: top;" src="assets/images/{{H_logo}}" />
|
||||
</td>
|
||||
<td style="width: 400px;vertical-align: top;text-align: left;">
|
||||
<p align="center" style="font-size: 20px;">{{H_Name}}</p>
|
||||
<p align="center" style="font-size: 12px">{{H_address_1}}, {{H_address_2}}<br>
|
||||
Phone: {{H_phone_No}}
|
||||
</p>
|
||||
</td>
|
||||
<td style="width: 300px;vertical-align: top;text-align: right;">
|
||||
<p align="right" style="font-size: 22px;">Dr. {{doctor_name}}</p>
|
||||
{{doctor_sign_html}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr/>
|
||||
</htmlpageheader>
|
||||
|
||||
<htmlpagefooter name="myFooter">
|
||||
<p align="right" style="font-size: 10px;">Prescription Generated: {{print_time}}</p>
|
||||
<p style="font-size: 9px;">Please ensure doctor registration number and signature/stamp are present for regulatory compliance.</p>
|
||||
</htmlpagefooter>
|
||||
|
||||
<table width="100%" style="font-size: 11px; border-collapse: collapse;">
|
||||
<tr>
|
||||
<td width="50%">
|
||||
<strong>Name:</strong> {{pName}}<br>
|
||||
<strong>S/o:</strong> [Father Name]<br>
|
||||
<strong>Gender/Age:</strong> {{age_sex}}<br>
|
||||
<strong>Mob:</strong> {{phoneno}}<br>
|
||||
<strong>Address:</strong> [Address]
|
||||
</td>
|
||||
<td width="50%">
|
||||
<strong>UHID:</strong> {{uhid_no}}<br>
|
||||
<strong>Sr No.:</strong> [Sr No]<br>
|
||||
<strong>OPD No.:</strong> {{opd_no}}<br>
|
||||
<strong>Date & Time:</strong> {{opd_date}} 12:00 AM<br>
|
||||
<strong>Valid Upto:</strong> [Valid Date]
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table width="100%" style="font-size: 11px; border-collapse: collapse; margin-top: 10px;">
|
||||
<tr>
|
||||
<td width="33%">
|
||||
<strong>DEPARTMENT:</strong><br>{{Complaint}}
|
||||
</td>
|
||||
<td width="33%">
|
||||
<strong>DIAGNOSIS:</strong><br>{{diagnosis}}
|
||||
</td>
|
||||
<td width="33%">
|
||||
<strong>Last Visit:</strong> [Date]<br>
|
||||
<strong>No. of Visit:</strong> [Count]
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3 style="margin: 15px 0 10px 0;">Rx :</h3>
|
||||
|
||||
{{medical}}
|
||||
|
||||
<p style="margin-top: 20px; font-size: 10px;">
|
||||
<strong>Investigation:</strong> {{investigation}}<br>
|
||||
<strong>Advice:</strong> {{advice}}
|
||||
</p>
|
||||
|
||||
<div style="margin-top: 40px; text-align: right;">
|
||||
<p><strong>Dr. {{doctor_name}}</strong></p>
|
||||
{{doctor_sign_html}}
|
||||
<p style="font-size: 10px; margin-top: 5px;"><strong>Reg. No.: 80982</strong></p>
|
||||
</div>
|
||||
|
|
@ -1 +0,0 @@
|
|||
<h1>Test again</h1>
|
||||
|
|
@ -5,7 +5,7 @@ $studyName = trim((string) ($study_name ?? ''));
|
|||
$findingsHtml = (string) ($findings_html ?? '');
|
||||
$impressionHtml = (string) ($impression_html ?? '');
|
||||
$summaryText = trim((string) ($summary_text ?? ''));
|
||||
$provider = trim((string) ($provider ?? 'azure-openai'));
|
||||
$provider = trim((string) ($provider ?? 'ai-server'));
|
||||
$model = trim((string) ($model ?? ''));
|
||||
$findingsInputId = 'ai_findings_' . $batchId;
|
||||
$impressionInputId = 'ai_impression_' . $batchId;
|
||||
|
|
@ -18,8 +18,8 @@ if ($providerKey === 'local-xray-fallback') {
|
|||
} elseif ($providerKey === 'regex-fallback') {
|
||||
$providerLabel = 'Regex Fallback';
|
||||
$providerBadgeClass = 'bg-warning text-dark';
|
||||
} elseif ($providerKey === 'azure-openai') {
|
||||
$providerLabel = 'Azure OpenAI';
|
||||
} elseif ($providerKey === 'ai-server') {
|
||||
$providerLabel = 'AI Server';
|
||||
$providerBadgeClass = 'bg-info text-dark';
|
||||
}
|
||||
$isFallback = in_array($providerKey, ['local-xray-fallback', 'regex-fallback'], true);
|
||||
|
|
|
|||
BIN
public/assets/images/hospital_logo_20260319_082713_10b1d94f.png
Normal file
BIN
public/assets/images/hospital_logo_20260319_082713_10b1d94f.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
BIN
public/uploads/patient/profile_1_1773908930.jpg
Normal file
BIN
public/uploads/patient/profile_1_1773908930.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
Loading…
Reference in New Issue
Block a user