Conheça as melhorias da versão 8.0, 8.1, 8.2!
Clique aqui para saber mais
TText não é exibido Estou criando um formulário que terá alguns subformulários. Todos os campos TTexto não estão aparecendo. Alguma dica? O código é longo: ...
AA
TText não é exibido  
Estou criando um formulário que terá alguns subformulários. Todos os campos TTexto não estão aparecendo. Alguma dica?

O código é longo:
 
  1. <?php
 
  1. <?php
  2. /**
  3. * Este arquivo é parte do DPS - Departamento de Pessoal do Sistema PCES
  4. * A ideia aqui é que a evolução seja chamada por um botão no prontuário abrindo uma página modal
  5. */
  6. use Adianti\Control\TPage;
  7. use Adianti\Core\AdiantiCoreApplication;
  8. use Adianti\Database\TTransaction;
  9. use Adianti\Widget\Form\TForm;
  10. use Adianti\Widget\Form\TEntry;
  11. use Adianti\Widget\Dialog\TMessage;
  12. use Adianti\Widget\Dialog\TQuestion;
  13. use Adianti\Widget\Dialog\TToast;
  14. use Adianti\Widget\Form\TCombo;
  15. use Adianti\Widget\Form\THidden;
  16. use Adianti\Widget\Form\THtmlEditor;
  17. use Adianti\Widget\Wrapper\TDBUniqueSearch;
  18. use Adianti\Control\TAction;
  19. use Adianti\Widget\Form\TLabel;
  20. use Adianti\Widget\Form\TText;
  21. class FormProntuarioEncontrado extends TPage
  22. {
  23. private $form;
  24. private $datagrid; // listing
  25. private $pageNavigation;
  26. private $formAnexo;
  27. private $formLaudo;
  28. private $nome_pessoa;
  29. private $formgrid;
  30. private $paginaAtual;
  31. private $grid_anexo;
  32. use Adianti\Base\AdiantiStandardFormTrait; // Standard form methods
  33. use Adianti\base\AdiantiStandardListTrait;
  34. use Adianti\Base\Meutrait; // Trait com métodos auxiliares
  35. public function __construct($param = null)
  36. {
  37. parent::__construct();
  38. $this->setDatabase('dps'); // define o banco de dados
  39. $this->setActiveRecord('Prontuario'); // define o registro ativo
  40. $this->addFilterField('id', '=', 'id'); // filterField, operator, formField
  41. // cria o formulário de identificação $this->form
  42. $this->form = new BootstrapFormBuilder('form_prontuario_encontrado');
  43. $this->form->generateAria(); // automatic aria-label
  44. $this->form->setFieldSizes('100%');
  45. $this->form->setProperty('style', 'margin:0;border:0;margin-bottom:30px;');
  46. $this->form->setClientValidation(true);
  47. $achou = false; // variável para verificar se o prontuário já existe
  48. $tamanho_btn = "width: 120px; color: black";
  49. $prontuario = null; // Inicializa $prontuario como null para evitar warnings posteriores
  50. $id_prontuario = null;
  51. $id_pessoa_atual = null;
  52. // Verifica que página foi chamada
  53. if (isset($param['current_tab']) && $param['current_tab'] !== '') {
  54. TSession::setValue('pagina_atual', $param['current_tab']);
  55. }
  56. $this->paginaAtual = TSession::getValue('pagina_atual') ?? 2;
  57. $this->form->setCurrentPage($this->paginaAtual); // Define a página atual do formulário
  58. if (isset($param['key']) && $param['key']) {
  59. $id = $param['key'];
  60. TTransaction::open('dps'); // Abre a transação para acessar o banco de dados
  61. $prontuario = Prontuario::find($id); // Busca o prontuário pelo ID
  62. if ($prontuario) { // Verifica se o prontuário foi encontrado
  63. $achou = true;
  64. $id_prontuario = $prontuario->id;
  65. $id_pessoa_atual = $prontuario->id_pessoa;
  66. }
  67. TTransaction::close(); // Fecha a transação
  68. }
  69. if (isset($param['id_prontuario']) && $param['id_prontuario']) {
  70. $id_prontuario = $param['id_prontuario'];
  71. }
  72. // Criando campos
  73. $id = new THidden('id');
  74. $id_pessoa = new THidden('id_pessoa'); // campo oculto para o ID da pessoa
  75. $pessoa = new TEntry('pessoa');
  76. TTransaction::open('permission'); // Abre a transação para acessar o banco de dados
  77. if ($achou && $prontuario) { // Garante que $prontuario não é null aqui
  78. // Procura o nome da pessoa pelo id_pessoa na tabela Pessoa
  79. $this->nome_pessoa = $this->RetornaNome($prontuario->id);
  80. $id->setValue($prontuario->id); // Define o ID do prontuário no campo oculto
  81. $id_pessoa->setValue($prontuario->id_pessoa); // Define o ID da pessoa no campo oculto
  82. }
  83. //Cria uma div para exibir alguns dados da pessoa
  84. $div_pessoa = new TElement('div');
  85. $div_pessoa->setProperty('id', 'div_pessoa');
  86. $div_pessoa->setProperty('class', 'row'); // Define a classe CSS para a div
  87. $div_pessoa->style = 'margin-bottom: -20px;'; // Define o estilo de margem para a div
  88. if (isset($id_pessoa_atual) && $id_pessoa_atual) {
  89. $cfg_pessoa = 'background-color: #CCCCCC; color: #4d4d4dff; padding: 5px; border-radius: 6px;';
  90. $pessoa_obj = Pessoa::find($id_pessoa_atual); // Busca a pessoa pelo ID
  91. if ($pessoa_obj) { // Verifica se a pessoa_obj foi encontrada
  92. $genero = $pessoa_obj->sexo ?: 'Não Informado'; // Verifica se o gênero está definido
  93. $div_pessoa->add("<div class='col-sm-2'><div><strong>Gênero:</strong><br></div> <div style='{$cfg_pessoa}'>{$genero}</div> &nbsp;&nbsp;</div> ");
  94. $num_funcional = $pessoa_obj->numero_funcional ?: '&nbsp;'; // Verifica se o número funcional está definido
  95. $div_pessoa->add("<div class='col-sm-2'><div><strong>Número Funcional:</strong><br></div> <div style='{$cfg_pessoa}'>{$num_funcional}</div> &nbsp;&nbsp;</div> ");
  96. // Acessa o método get_estado_civil() e verifica se o objeto retornado é válido antes de acessar a descrição
  97. $estado_civil_obj = $pessoa_obj->get_estado_civil();
  98. $estado_civil = $estado_civil_obj ? $estado_civil_obj->descricao : 'Não Informado';
  99. $div_pessoa->add("<div class='col-sm-2'><div><strong>Estado Civil:</strong><br></div> <div style='{$cfg_pessoa}'>{$estado_civil}</div> &nbsp;&nbsp;</div> ");
  100. // Acessa o método get_grau_instrucao() e verifica se o objeto retornado é válido antes de acessar a descrição
  101. $grau_instrucao_obj = $pessoa_obj->get_grau_instrucao();
  102. $grau_instrucao = $grau_instrucao_obj ? $grau_instrucao_obj->descricao : 'Não Informado';
  103. $div_pessoa->add("<div class='col-sm-4'><div><strong>Grau de Instrução:</strong><br></div> <div style='{$cfg_pessoa}'>{$grau_instrucao}</div> &nbsp;&nbsp;</div> ");
  104. //Converte a data de nascimento para o formato desejado
  105. if ($pessoa_obj->data_nascimento) {
  106. $data_nascimento = new DateTime($pessoa_obj->data_nascimento);
  107. $div_pessoa->add("<div class='col-sm-2'><div><strong>Data Nascimento:</strong><br></div> <div style='{$cfg_pessoa}'>" . $data_nascimento->format('d/m/Y') . "</div> &nbsp;&nbsp;</div> ");
  108. } else {
  109. $div_pessoa->add("<div class='col-sm-2'><div><strong>Data Nascimento:</strong><br></div> <div style='{$cfg_pessoa}'>Não Informada</div> &nbsp;&nbsp;</div> ");
  110. }
  111. } else {
  112. $div_pessoa->add("<div class='col-sm-12'><div style='color: red;'>Dados da Pessoa não encontrados.</div></div>");
  113. }
  114. }
  115. TTransaction::close(); // Fecha a transação
  116. $id_situacao = new TDBUniqueSearch('id_situacao', 'dps', 'Situacao', 'id', 'descricao');
  117. $id_situacao->setMinLength(0); // Define o tamanho mínimo de caracteres para pesquisa
  118. $id_situacao->setChangeAction(new TAction(array($this, 'onChangeSituacao'), ['static' => '1']));
  119. $outra_situacao = new TEntry('outra_situacao');
  120. $id_funcao = new TDBUniqueSearch('id_funcao', 'dps', 'Funcao', 'id', 'descricao');
  121. $id_funcao->setMinLength(0); // Define o tamanho mínimo de caracteres para pesquisa
  122. $id_chefia = new TDBUniqueSearch('id_chefia', 'permission', 'Pessoa', 'id', 'nome');
  123. $id_chefia->setMinLength(0); // Define o tamanho mínimo de caracteres para pesquisa
  124. $id_setor = new TDBUniqueSearch('id_setor', 'permission', 'Setor', 'id', 'nome');
  125. $id_setor->setMinLength(0); // Define o tamanho mínimo de caracteres para pesquisa
  126. $id_plano_saude = new TDBUniqueSearch('id_plano_saude', 'dps', 'PlanosSaude', 'id', 'nome');
  127. $id_plano_saude->setMinLength(0); // Define o tamanho mínimo de caracteres para pesquisa
  128. $exercicio = new TEntry('exercicio'); // Corrigido para 'exercicio' minúsculo para consistência
  129. $anamnese = new THtmlEditor('anamnese');
  130. $anamnese->setSize('100%', 500); // Define o tamanho do campo de anamnese
  131. $obs = new THtmlEditor('obs');
  132. $obs->setSize('100%', 200); // Define o tamanho do campo de observações
  133. // Definindo os placeholders
  134. $id_situacao->placeholder = 'Selecione uma situação';
  135. $outra_situacao->placeholder = 'Descreva outra situação';
  136. $id_funcao->placeholder = 'Selecione uma função';
  137. $id_chefia->placeholder = 'Selecione nome da chefia';
  138. $id_setor->placeholder = 'Selecione um setor';
  139. $id_plano_saude->placeholder = 'Selecione um plano de saúde';
  140. $espaco = 'margin-top: 20px;'; // define o tamanho do campo de exercício
  141. // insira os campos no formulário
  142. $this->form->appendPage('FICHA DE IDENTIFICAÇÃO'); // cria uma nova página no formulário
  143. //Possibilita controlar a aba corrente mantendo após salvar
  144. $div_outra_situacao = new TElement('div');
  145. $div_outra_situacao->setProperty('id', 'div_outra_situacao');
  146. $div_outra_situacao->add("<span>Outra Situação:</span><br>");
  147. $div_outra_situacao->add($outra_situacao);
  148. $this->form->setFields([$outra_situacao]); // Adiciona o campo ao formulário
  149. $div_outra_situacao->style = 'display: none;'; // Esconde o campo de outra situação inicialmente
  150. // Lê os dados do prontuário se já existir
  151. //Busca os dados da pessoa
  152. $this->form->addFields([$div_pessoa]); // Adiciona a div ao formulário
  153. $row = $this->form->addFields(['Função', $id_funcao], ['Situação', $id_situacao], [$div_outra_situacao]);
  154. $row->layout = ['col-sm-3', 'col-sm-2', 'col-sm-5']; // define o layout da linha
  155. $row = $this->form->addFields(['Chefia', $id_chefia], ['Setor', $id_setor], ['Plano de Saúde', $id_plano_saude], ['Exercício', $exercicio]);
  156. $row->layout = ['col-sm-4', 'col-sm-4', 'col-sm-2', 'col-sm-2']; // define o layout da linha
  157. $row->style = $espaco; // aplica o estilo de espaçamento
  158. $row = $this->form->addFields(['Observações', $obs]);
  159. $row->style = "$espaco width: 60%; margin-left: 20%;"; // aplica o estilo de espaçamento
  160. $this->form->appendPage('ANAMNESE'); // cria uma nova página no formulário
  161. $row = $this->form->addFields([$anamnese]);
  162. $this->form->addFields([$id, $id_pessoa]); // campo oculto para o ID do prontuário
  163. $this->form->addFields([new THidden('current_tab')]); //Cria um campo invisível para armazenar a aba corrente
  164. $this->form->setTabFunction("$('[name=current_tab]').val($(this).attr('data-current_page'));"); //Captura o valor da aba corrente
  165. $this->form->appendPage('PRONTUÁRIO E ANEXOS'); // cria uma nova página no formulário
  166. //Aqui teremos um subformulário para anexos e um datagrid para exibir os anexos já existentes
  167. $this->formAnexo = new BootstrapFormBuilder('subform_anexos');
  168. $id_anexo = new THidden('id_anexo'); // campo oculto para o ID do anexo
  169. $descricao_anexo = new TEntry('descricao');
  170. $descricao_anexo->setMaxLength(100);
  171. $descricao_anexo->setProperty('placeholder', 'Descrição do documento');
  172. $descricao_anexo->setTip('Informe uma breve descrição do documento');
  173. $obs_anexo = new TText('obs_anexo');
  174. $id_prontuario_anexo = new THidden('id_prontuario_anexo');
  175. $id_prontuario_anexo->setValue($id_prontuario);
  176. $dados_arquivo = new TFile('dados_arquivo');
  177. $dados_arquivo->setLabel('Anexar Documento');
  178. $dados_arquivo->setAllowedExtensions(['pdf', 'jpg', 'png', 'svg', 'gif'], 'Somente arquivos PDF, JPG, PNG, SVG e GIF são permitidos.');
  179. $enviar_arquivo = new TButton('enviar_arquivo');
  180. $enviar_arquivo->setLabel('Enviar');
  181. $enviar_arquivo->setAction(new TAction([$this, 'onEnviarArquivo'], ['id_prontuario' => $id_prontuario]), '');
  182. $enviar_arquivo->class = 'btn btn-sm btn-primary';
  183. $enviar_arquivo->style = 'width: 33px; margin-top: 2.55vh;'; // Define o estilo do botão
  184. $enviar_arquivo->title = 'Enviar arquivo';
  185. $enviar_arquivo->setImage('fa:upload');
  186. $enviar_arquivo->setProperty('id', 'btn_enviar_arquivo'); // Define o ID do botão para uso em JavaScript
  187. //Insere os campos no subformulário
  188. $this->formAnexo->addFields([$id_anexo], [$id_prontuario_anexo]);
  189. //Primeira linha do subformulário
  190. $div_descricao = $this->addCampo(new TLabel('Descrição do Documento:'), $descricao_anexo, '100%');
  191. $div_arquivo = $this->addCampo(new TLabel('Escolha o arquivo:'), $dados_arquivo, '100%');
  192. $row = $this->formAnexo->addFields([$div_descricao], [$div_arquivo], [$enviar_arquivo]); // Campo de observações do anexo
  193. $row->layout = ['col-sm-6', 'col-sm-5', 'col-sm-1']; // define o layout da linha
  194. //Cria datagrid com os anexos já existentes na tabela anexo
  195. // Preenche dataGrid com os anexos já existentes para o id_prontuario
  196. // Cria a grid para exibir os anexos
  197. $this->grid_anexo = new BootstrapDatagridWrapper(new TDataGrid);
  198. $this->grid_anexo->setId('datagrid_anexos');
  199. $this->grid_anexo->style = 'width: 100%';
  200. $this->grid_anexo->setHeight(250); // Define a altura da grid
  201. $this->grid_anexo->makeScrollable();
  202. $col_descricao = new TDataGridColumn('descricao', 'Descrição', 'left', '40%');
  203. $col_data = new TDataGridColumn('data_upload', 'Data/hora', 'center', '20%');
  204. $col_nome_arquivo = new TDataGridColumn('nome_arquivo', 'Nome do Arquivo', 'left', '40%');
  205. $col_data->setTransformer(function ($value, $object, $row) {
  206. if ($value) {
  207. $data = new DateTime($value);
  208. return $data->format('d/m/Y - H:i:s');
  209. }
  210. return '';
  211. });
  212. $this->grid_anexo->addColumn($col_descricao);
  213. $this->grid_anexo->addColumn($col_nome_arquivo);
  214. $this->grid_anexo->addColumn($col_data);
  215. $this->grid_anexo->addAction(new TDataGridAction([$this, 'onDownloadAnexo'], ['id' => '{id}', 'id_prontuario' => $id_prontuario]), 'Visualizar/baixar', 'fa:file-image green');
  216. $this->grid_anexo->addAction(new TDataGridAction([$this, 'onDeleteAnexo'], ['id' => '{id}', 'id_prontuario' => $id_prontuario]), 'Excluir', 'fa:trash red');
  217. $this->grid_anexo->createModel(); // Cria o modelo da grid
  218. $panel = new TPanelGroup('ANEXOS');
  219. $panel->add($this->grid_anexo);
  220. $panel->style = 'width: 95%; margin: auto;';
  221. $pesquisa = new TEntry('pesquisa');
  222. $pesquisa->setProperty('placeholder', 'Pesquisar...');
  223. $pesquisa->style = 'width: 300px; margin-right: 4px;';
  224. $pesquisa->setTip('Pesquisar anexos por parte da descrição ou parte do nome do arquivo');
  225. $pesquisa->setProperty('onkeypress', 'if(event.keyCode == 13) { document.getElementById("btn_find").click(); return false; }'); // Ação ao pressionar Enter
  226. $btnf = new TButton('find');
  227. $btnf->setAction(new TAction([$this, 'onCarrega'], ['id_prontuario' => $id_prontuario]), '');
  228. $btnf->style = 'height: 37px; margin-right:4px;';
  229. $btnf->setImage('fa:search');
  230. $btnf->setProperty('title', 'Pesquisar pela descrição ou nome do arquivo');
  231. $btnf->setProperty('id', 'btn_find'); // Define o ID do botão para uso em JavaScript
  232. $this->form->setFields([$pesquisa, $btnf]);
  233. $div_formgrid = new TElement('div');
  234. $div_formgrid->setProperty('id', 'div_formgrid');
  235. $div_formgrid->style = 'width: 100%; display: flex; justify-content: space-between; align-items: center;';
  236. $div_pesquisa = new TElement('div');
  237. $div_pesquisa->setProperty('id', 'div_pesquisa');
  238. $div_pesquisa->style = 'width: 80%; align-items: center;';
  239. $div_pesquisa->add($pesquisa);
  240. $div_btn = new TElement('div');
  241. $div_btn->setProperty('id', 'div_btn');
  242. $div_btn->style = 'width: 20%; display: flex; justify-content: flex-end;';
  243. $div_btn->add($btnf);
  244. $div_formgrid->add($div_pesquisa);
  245. $div_formgrid->add($div_btn);
  246. $panel->addHeaderWidget($div_formgrid); // Adiciona o painel de pesquisa ao cabeçalho do painel de anexos
  247. $pesquisa->setProperty('id', 'inp_pesquisa');
  248. $descricao_anexo->setProperty('id', 'inp_desc_anexo');
  249. $dados_arquivo->setProperty('id', 'inp_arquivo');
  250. $obs_anexo->setProperty('id', 'inp_obs_anexo');
  251. $this->formAnexo->addContent([$panel]);
  252. //Insere subform no formulário principal
  253. $this->form->addContent([$this->formAnexo]);
  254. $this->formAnexo->setFields([$descricao_anexo], [$dados_arquivo]);
  255. //Cria aba para emissão e controle de laudos
  256. $this->form->appendPage('CONTROLE DE LAUDOS');
  257. //Aqui teremos um subformulário para laudos e um datagrid para exibir os laudos já existentes
  258. $this->formLaudo = new BootstrapFormBuilder('subform_laudos');
  259. $id_laudo = new THidden('id_laudo'); // campo oculto para o ID do laudo
  260. $descricao_laudo = new TEntry('descricao');
  261. $descricao_laudo->setSize('100%');
  262. $descricao_laudo->setProperty('placeholder', 'Descrição do laudo');
  263. $descricao_laudo->setTip('Informe uma breve descrição do laudo');
  264. $obs_laudo = new TText('obs_laudo');
  265. $obs_laudo->setProperty('placeholder', 'Observações sobre o laudo');
  266. $obs_laudo->setTip('Informe observações adicionais sobre o laudo');
  267. $obs_laudo->setSize('100%', 100); // Define o tamanho do campo de observações do laudo
  268. $id_prontuario_laudo = new THidden('id_prontuario_laudo');
  269. $id_prontuario_laudo->setValue($id_prontuario);
  270. //Insere os campos no subformulário
  271. $this->formLaudo->addFields([$id_laudo], [$id_prontuario_laudo]);
  272. $this->formLaudo->addFields(['Descrição do laudo', $descricao_laudo]);
  273. $this->formLaudo->addFields(['Observações', $obs_laudo]); // <-- LINHA CORRIGIDA
  274. $this->form->addContent([$this->formLaudo]);
  275. // Define o método de ação do formulário principal
  276. $btn = $this->form->addAction('Salvar', new TAction([$this, 'onSalvaProntuario'], ['id_prontuario' => $id_prontuario]), 'fa:save');
  277. $btn->class = 'btn btn-sm btn-primary';
  278. $btn->style = $tamanho_btn;
  279. $btn = $this->form->addAction('Evoluções', new TAction(['ListaEvolucao', 'onEdit'], ['id_prontuario' => $id_prontuario]), 'fa:rectangle-list');
  280. $btn->class = 'btn btn-sm btn-info';
  281. $btn->style = $tamanho_btn;
  282. // Passa o id_prontuario para o botão "Evoluir" de forma segura
  283. $btn_novo = $this->form->addAction("<i class='fa-solid fa-plus'></i><span> Evoluir</span>", new TAction(['FormEvolucao', 'onEdit'], ['register_state' => 'false', 'id_prontuario' => $id_prontuario]), '');
  284. $btn_novo->class = 'btn btn-sm btn-success';
  285. $btn_novo->style = $tamanho_btn;
  286. // wrap the page content using vertical box
  287. $vbox = new TVBox;
  288. $vbox->style = 'width: 70%; margin: auto;';
  289. $vbox->add($this->form);
  290. TScript::create(<<<'JS'
  291. (function(){
  292. function isEnter(e){ return (e.key === 'Enter' || e.keyCode === 13); }
  293. // Evita submit “global” e decide para onde mandar
  294. $(document).on('keydown', 'input, textarea, select', function(e){
  295. if(!isEnter(e)) return;
  296. var name = this.getAttribute('name') || '';
  297. var formEl = $(this).closest('form');
  298. var handled = false;
  299. // 1) Se estiver no campo de PESQUISA dos anexos
  300. if (name === 'pesquisa') {
  301. $('#btn_find').trigger('click');
  302. handled = true;
  303. }
  304. // 2) Se estiver em campos do SUBFORM de anexos -> Enviar arquivo
  305. var camposSub = ['descricao','dados_arquivo','obs_anexo'];
  306. if (!handled && camposSub.indexOf(name) !== -1) {
  307. $('#btn_enviar_arquivo').trigger('click');
  308. handled = true;
  309. }
  310. // 3) Caso contrário: Salvar formulário principal
  311. if (!handled) {
  312. $('#btn_salvar').trigger('click');
  313. handled = true;
  314. }
  315. if (handled){
  316. e.preventDefault();
  317. e.stopPropagation();
  318. return false;
  319. }
  320. });
  321. // Bônus: no input de pesquisa, ENTER não deve submeter o form principal
  322. $(document).on('keydown', '#inp_pesquisa', function(e){
  323. if (e.key === 'Enter'){ e.preventDefault(); }
  324. });
  325. })();
  326. JS);
  327. // Finaliza a criação do formulário
  328. parent::add($vbox);
  329. }
  330. public function onSearchEvolucao($param)
  331. {
  332. try {
  333. // Acessar os campos do subformulário diretamente de $param.
  334. $data_subform = (object) $param;
  335. //Carrega o formulário principal com os dados do prontuário através do ID da pessoa usando loadPage
  336. if (isset($data_subform->id_prontuario) && $data_subform->id_prontuario) {
  337. TApplication::loadPage('FormProntuarioEncontrado', 'onEdit', ['id' => $data_subform->id_prontuario, 'key' => $data_subform->id_prontuario, 'current_tab' => 1, 'register_state' => 'false']);
  338. } else {
  339. throw new Exception('ID da pessoa não encontrado no subformulário. Certifique-se de que o prontuário principal foi salvo ou carregado.');
  340. }
  341. // Muda para a página do subformulário de evolução
  342. // Verifica se o ID do prontuário está definido
  343. if (isset($data_subform->id_prontuario) && $data_subform->id_prontuario) {
  344. // Redireciona para a página de pesquisa de evolução
  345. // TApplication::loadPage('ListaEvolucao', 'onSearch', ['id_prontuario' => $data_subform->id_prontuario]);
  346. // Para demonstração, vamos apenas exibir uma mensagem com o ID do prontuário.
  347. // new TMessage('info', 'Pesquisando evoluções para o prontuário ID: ' . $data_subform->id_prontuario); // Comentado para não exibir em produção
  348. if (isset($this->formAnexo)) { // Verifica se $this->formAnexo está inicializado
  349. $this->formAnexo->setData($data_subform); // Mantém os dados do subformulário no formulário principal
  350. }
  351. } else {
  352. new TMessage('warning', 'ID do prontuário não encontrado no subformulário. Certifique-se de que o prontuário principal foi salvo ou carregado.');
  353. }
  354. } catch (Exception $e) {
  355. new TMessage('error', $e->getMessage());
  356. }
  357. }
  358. // Adicione esta nova função para limpar o subformulário de evolução
  359. public function onClearSubformEvolucao($param)
  360. {
  361. // Cria um objeto vazio para limpar os campos
  362. $clear_data = new StdClass();
  363. $clear_data->id_evolucao = null;
  364. $clear_data->data_atendimento = null;
  365. $clear_data->id_tipo_atendimento = null;
  366. $clear_data->evolucao = null;
  367. $clear_data->id_profissional = null;
  368. $clear_data->id_motivo = null;
  369. // Mantém o id_prontuario se ele já estiver definido
  370. if (isset($param['id_prontuario'])) {
  371. $clear_data->id_prontuario = $param['id_prontuario'];
  372. } else {
  373. // Se o id_prontuario não estiver nos parâmetros, tenta obter do formulário principal
  374. $mainFormData = $this->form->getData();
  375. if (isset($mainFormData->id)) {
  376. $clear_data->id_prontuario = $mainFormData->id;
  377. }
  378. }
  379. if (isset($this->formAnexo)) { // Verifica se $this->formAnexo está inicializado
  380. $this->formAnexo->setData($clear_data);
  381. }
  382. TToast::show('info', 'Subformulário de evolução limpo.');
  383. }
  384. public function onSalvaProntuario($param)
  385. {
  386. try {
  387. $data = array_merge($param); // Combina os dados do formulário com os parâmetros recebidos
  388. TTransaction::open('dps');
  389. $object = new Prontuario;
  390. $object->id_pessoa = $param['id_pessoa'] ?? null; // Obtém o ID da pessoa do parâmetro ou do formulário
  391. $object->id = $param['id'] ?? null; // Obtém o ID do prontuário do parâmetro ou do formulário
  392. $object->id_situacao = $param['id_situacao'] ?? null; // Obtém o ID da situação do parâmetro ou do formulário
  393. $object->outra_situacao = $param['outra_situacao'] ?? null; // Obtém a outra situação do parâmetro ou do formulário
  394. $object->id_funcao = $param['id_funcao'] ?? null; // Obtém o ID da função do parâmetro ou do formulário
  395. $object->id_chefia = $param['id_chefia'] ?? null; // Obtém o ID da chefia do parâmetro ou do formulário
  396. $object->id_setor = $param['id_setor'] ?? null; // Obtém o ID do setor do parâmetro ou do formulário
  397. $object->id_plano_saude = $param['id_plano_saude'] ?? null; // Obtém o ID do plano de saúde do parâmetro ou do formulário
  398. $object->exercicio = $param['exercicio'] ?? null; // Obtém o exercício do parâmetro ou do formulário
  399. $object->anamnese = $param['anamnese'] ?? null; // Obtém a anamnese do parâmetro ou do formulário
  400. $object->obs = $param['obs'] ?? null; // Obtém as observações do parâmetro ou do formulário
  401. $object->store();
  402. TTransaction::close();
  403. $object->key = $object->id; // Define a chave do objeto para o ID do prontuário salvo
  404. // Atualiza o formulário com os dados salvos
  405. $this->onCarrega(['id_prontuario' => $param['id']]); // Recarrega os dados do prontuário
  406. $this->onCarregaOutraSituacao($param['id_situacao'] ?? null); // Chama o método para carregar outra situação
  407. TToast::show('success', 'Prontuário salvo com sucesso!');
  408. } catch (Exception $e) {
  409. TTransaction::rollback();
  410. new TMessage('error', $e->getMessage());
  411. }
  412. }
  413. public function onChangeSituacao($param)
  414. {
  415. $data = $this->form->getData(); // lê os dados do formulário
  416. $this->onCarregaOutraSituacao($param['id_situacao']);
  417. // Mantém os dados do formulário
  418. TForm::sendData('form_prontuario_encontrado', $data, false, false);
  419. }
  420. public function onCarregaOutraSituacao($id_situacao)
  421. {
  422. try {
  423. // Se id_situacao for igual a 4, exibe o campo de outra situação
  424. if ($id_situacao == '4') {
  425. TScript::create("
  426. document.getElementById('div_outra_situacao').style.display = 'inline-block';
  427. document.getElementById('div_outra_situacao').style.width = '100%';
  428. ");
  429. } else {
  430. TScript::create("
  431. document.getElementById('div_outra_situacao').style.display = 'none';
  432. ");
  433. }
  434. } catch (Exception $e) // in case of exception
  435. {
  436. // shows the exception error message
  437. new TMessage('error', $e->getMessage());
  438. // undo all pending operations
  439. TTransaction::rollback();
  440. }
  441. }
  442. public function onCarrega($param)
  443. {
  444. try {
  445. if (empty($this->database)) {
  446. throw new Exception(AdiantiCoreTranslator::translate('^1 was not defined. You must call ^2 in ^3', AdiantiCoreTranslator::translate('Database'), 'setDatabase()', AdiantiCoreTranslator::translate('Constructor')));
  447. }
  448. if (empty($this->activeRecord)) {
  449. throw new Exception(AdiantiCoreTranslator::translate('^1 was not defined. You must call ^2 in ^3', 'Active Record', 'setActiveRecord()', AdiantiCoreTranslator::translate('Constructor')));
  450. }
  451. if (isset($param['id_prontuario'])) {
  452. // get the parameter $id_prontuario
  453. $param['key'] = $param['id_prontuario'];
  454. }
  455. // open a transaction with database
  456. TTransaction::open($this->database);
  457. $class = $this->activeRecord;
  458. // instantiates object
  459. $object = new $class($param['key']); // Cria uma nova instância do objeto com o ID fornecido
  460. if ($object) { // Verifica se o objeto Prontuario foi encontrado
  461. // fill the form with the active record data
  462. $this->form->setFormTitle("<span style='font-size: 1.5em; text-transform: uppercase;'><strong>PRONTUÁRIO DE {$this->RetornaNome($object->id)}</strong></span>");
  463. if (isset($param['key'])) {
  464. // get the parameter $key
  465. $key = $param['key'];
  466. // pegue o current_tab atual do request ou do form/sessão
  467. $tabAtual = $param['current_tab'] ?? 0;
  468. // garanta que o objeto enviado ao front tem o campo
  469. $object->current_tab = $tabAtual;
  470. // IMPORTANTE: use os dois últimos parâmetros para não disparar eventos e manter os campos
  471. TForm::sendData('form_prontuario_encontrado', $object, false, true);
  472. // $this->form->setData($object);
  473. // close the transaction
  474. $this->onCarregaOutraSituacao($object->id_situacao); // Chama o método para carregar arma com o ID do objeto
  475. } else {
  476. new TMessage('warning', 'Prontuário não encontrado para o ID fornecido.');
  477. $this->form->clear(true);
  478. }
  479. //fecha a transação
  480. TTransaction::close(); // Fecha a transação
  481. // Carrega os anexos do prontuário
  482. $this->grid_anexo->clear();
  483. $parametros = [];
  484. $parametros['id_prontuario'] = $key; // Define o ID do prontuário para filtrar os anexos
  485. $parametros['pesquisa'] = $param['pesquisa'] ?? ''; // Obtém o termo de pesquisa, se fornecido
  486. $parametros['current_tab'] = $tabAtual;
  487. $this->onRecarregaAnexos($parametros); // Chama o método para recarregar os anexos do prontuário
  488. // Mantem a aba corrente após carregar os dados
  489. if (isset($param['current_tab']) && $param['current_tab'] !== '') {
  490. $this->form->setCurrentPage($param['current_tab']); // Muda para a página do prontuário
  491. } else {
  492. $this->form->setCurrentPage(0); // Muda para a primeira página se current_tab não estiver definido
  493. }
  494. TTransaction::close(); // Fecha a transação
  495. return $object;
  496. } else {
  497. $this->form->clear(true);
  498. }
  499. } catch (Exception $e) // in case of exception
  500. {
  501. // shows the exception error message
  502. new TMessage('error', $e->getMessage());
  503. // undo all pending operations
  504. TTransaction::rollback();
  505. }
  506. }
  507. public function onEnviarArquivo($param)
  508. {
  509. try {
  510. $arquivo_temporario = '';
  511. $id = null;
  512. $id_prontuario = $param['id_prontuario'] ?? null;
  513. if (empty($param['descricao'])) {
  514. throw new Exception('Descrição do arquivo não informada.');
  515. }
  516. if (empty($param['dados_arquivo'])) {
  517. throw new Exception('Arquivo não informado.');
  518. }
  519. // Determina o tipo MIME com base na extensão do arquivo
  520. $extensao = pathinfo($param['dados_arquivo'], PATHINFO_EXTENSION);
  521. $tipos_permitidos = [
  522. 'pdf' => 'application/pdf',
  523. 'jpg' => 'image/jpeg',
  524. 'jpeg' => 'image/jpeg',
  525. 'png' => 'image/png',
  526. 'svg' => 'image/svg+xml',
  527. 'gif' => 'image/gif'
  528. ];
  529. $param['tipo_mime'] = $tipos_permitidos[strtolower($extensao)] ?? null;
  530. if ($param['tipo_mime'] === null) {
  531. throw new Exception('Tipo de arquivo não informado.');
  532. }
  533. if (!array_key_exists(strtolower($extensao), $tipos_permitidos)) {
  534. throw new Exception('Tipo de arquivo não permitido. Apenas PDF, JPG, PNG, SVG e GIF são aceitos.');
  535. }
  536. $arquivo_temporario = 'tmp/' . $param['dados_arquivo']; // Obtém o caminho do arquivo temporário
  537. if (!file_exists($arquivo_temporario)) {
  538. throw new Exception('Arquivo temporário não encontrado.');
  539. }
  540. $binary_content = file_get_contents($arquivo_temporario);
  541. TTransaction::open('dps'); // abre transação
  542. $conn = TTransaction::get(); // pega conexão PDO
  543. $conid = TTransaction::get(); // pega o nome do banco de dados
  544. $id = null;
  545. $id = $conid->query('SELECT MAX(id) AS max_id FROM dps.tb_anexos')->fetch(PDO::FETCH_ASSOC);
  546. $sql = 'INSERT INTO dps.tb_anexos
  547. (id, id_prontuario, descricao, obs, tipo_mime, dados_arquivo, data_upload, nome_arquivo, id_system_users)
  548. VALUES (:id, :id_prontuario, :descricao, :obs, :tipo_mime, :dados_arquivo, :data_upload, :nome_arquivo, :id_system_users)';
  549. $stmt = $conn->prepare($sql);
  550. $stmt->bindValue(':id', $id['max_id'] + 1, PDO::PARAM_INT);
  551. $stmt->bindValue(':id_prontuario', $param['id_prontuario_anexo']);
  552. $stmt->bindValue(':descricao', $param['descricao']);
  553. $stmt->bindValue(':obs', $param['obs_anexo'] ?? null);
  554. $stmt->bindValue(':tipo_mime', $param['tipo_mime']);
  555. $stmt->bindValue(':data_upload', date('Y-m-d H:i:s'));
  556. $stmt->bindParam(':dados_arquivo', $binary_content, PDO::PARAM_LOB);
  557. $stmt->bindValue(':nome_arquivo', $param['dados_arquivo']);
  558. $stmt->bindValue(':id_system_users', TSession::getValue('userid'), PDO::PARAM_INT); // Obtém o ID do usuário logado
  559. $stmt->execute();
  560. TTransaction::close(); // fecha transação
  561. TToast::show('success', 'Arquivo salvo com sucesso!');
  562. // Exclui o arquivo temporário após o upload
  563. if (file_exists($arquivo_temporario)) {
  564. unlink($arquivo_temporario);
  565. }
  566. // Redireciona para a página do prontuário com o ID do prontuário anexo
  567. // Isso garante que a página seja recarregada com os dados atualizados
  568. } catch (Exception $e) {
  569. TTransaction::rollback();
  570. new TMessage('error', $e->getMessage());
  571. }
  572. $this->onCarrega(['id_prontuario' => $id_prontuario, 'current_tab' => 2]); // Recarrega os dados do prontuário
  573. TForm::sendData('form_prontuario_encontrado', (object) $param, false, true);
  574. }
  575. public function onRecarregaAnexos($param)
  576. {
  577. try {
  578. $this->grid_anexo->clear(); // Limpa a grid antes de adicionar os novos anexos
  579. if (isset($param['id_prontuario']) && $param['id_prontuario']) {
  580. $id_prontuario_anexo = $param['id_prontuario'];
  581. TTransaction::open('dps'); // Abre a transação para acessar o banco de dados
  582. $criteria = new TCriteria();
  583. $criteria->add(new TFilter('id_prontuario', '=', $id_prontuario_anexo)); // Filtra os anexos pelo ID do prontuário
  584. // Define filtro pela descricao ou nome do arquivo, se fornecido
  585. if (isset($param['pesquisa']) && $param['pesquisa'] !== '' && $param['pesquisa'] !== null) {
  586. $crit_pesquisa = new TCriteria();
  587. $crit_pesquisa->add(new TFilter('descricao', 'ilike', "%{$param['pesquisa']}%"));
  588. $crit_pesquisa->add(new TFilter('nome_arquivo', 'ilike', "%{$param['pesquisa']}%"), TExpression::OR_OPERATOR);
  589. $criteria->add($crit_pesquisa); // Adiciona o critério de pesquisa ao critério principal
  590. }
  591. $criteria->setProperty('order', 'data_upload DESC'); // Ordena por data de upload
  592. $repository = new TRepository('Anexo_dps');
  593. $anexos = $repository->load($criteria); // Carrega os anexos
  594. if ($anexos) { // Verifica se existem anexos
  595. foreach ($anexos as $anexo) {
  596. $this->grid_anexo->addItem($anexo); // Adiciona cada anexo à grid
  597. }
  598. TTransaction::close(); // Fecha a transação
  599. //recarega a grid de anexos
  600. return $this->grid_anexo; // Retorna a grid atualizada
  601. } else {
  602. TToast::show('warning', 'Nenhum anexo encontrado para o prontuário informado.');
  603. }
  604. } else {
  605. new TMessage('error', 'ID do prontuário não informado.');
  606. }
  607. } catch (Exception $e) {
  608. TTransaction::rollback();
  609. new TMessage('error', $e->getMessage());
  610. }
  611. }
  612. public function onDeleteAnexo($param)
  613. {
  614. try {
  615. $usuarioatual = TSession::getValue('userid'); // Obtém o ID do usuário logado
  616. if (isset($param['id_prontuario'])) {
  617. $id_prontuario = $param['id_prontuario'] ?? null;
  618. }
  619. //Verifica se quem gravou é um mesmo usuário
  620. if (isset($param['id']) && $param['id']) {
  621. $id = $param['id'];
  622. } else {
  623. throw new Exception('ID do anexo não informado.');
  624. }
  625. TTransaction::open('dps'); // Abre a transação para acessar o banco de dados
  626. $anexo = Anexo_dps::find($id); // Busca o anexo pelo ID
  627. if ($anexo && $anexo->id_system_users != $usuarioatual) { // Verifica se o anexo foi encontrado e se o usuário é o mesmo que gravou
  628. throw new Exception('Você não tem permissão para excluir este anexo. Somente o usuário que o anexou pode excluí-lo.');
  629. }
  630. TTransaction::close();
  631. //Questiona a exclusão do anexo
  632. // cria a ação para resposta positiva
  633. $action = new TAction([$this, 'onAnexoDeletado']);
  634. $action->setParameters($param); // repassa parâmetros recebidos para a ação
  635. // exibe o diálogo para o usuário
  636. new TQuestion('Deseja excluir o registro?', $action);
  637. } catch (Exception $e) {
  638. TTransaction::rollback();
  639. new TMessage('error', $e->getMessage());
  640. }
  641. $this->onCarrega(['id_prontuario' => $id_prontuario, 'current_tab' => 2]); // Recarrega os dados do prontuário
  642. TForm::sendData('form_prontuario_encontrado', (object) $param, false, true);
  643. }
  644. public function onAnexoDeletado($param)
  645. {
  646. try {
  647. if (isset($param['id_prontuario'])) {
  648. $id_prontuario = $param['id_prontuario'] ?? null;
  649. }
  650. if ($id_prontuario == null) {
  651. throw new Exception('ID do prontuário não informado.');
  652. }
  653. if (isset($param['id']) && $param['id']) {
  654. $id = $param['id'];
  655. TTransaction::open('dps'); // Abre a transação para acessar o banco de dados
  656. $anexo = Anexo_dps::find($id); // Busca o anexo pelo ID
  657. if ($anexo) { // Verifica se o anexo foi encontrado
  658. $anexo->delete(); // Deleta o anexo
  659. TToast::show('info', 'Anexo deletado com sucesso.');
  660. } else {
  661. new TMessage('warning', 'Anexo não encontrado para o ID fornecido.');
  662. }
  663. TTransaction::close();
  664. } else {
  665. new TMessage('error', 'ID do anexo não informado.');
  666. }
  667. $this->onCarrega(['id_prontuario' => $id_prontuario, 'current_tab' => 2]); // Recarrega os dados do prontuário
  668. TForm::sendData('form_prontuario_encontrado', (object) $param, false, true);
  669. } catch (Exception $e) {
  670. TTransaction::rollback();
  671. new TMessage('error', $e->getMessage());
  672. }
  673. }
  674. public function onDownloadAnexo($param)
  675. {
  676. try {
  677. if (empty($param['id'])) {
  678. throw new Exception('Parâmetro "id" ausente.');
  679. }
  680. $id = (int) $param['id'];
  681. TTransaction::open('dps');
  682. $conn = TTransaction::get();
  683. $stmt = $conn->prepare('SELECT nome_arquivo, tipo_mime, dados_arquivo
  684. FROM dps.tb_anexos
  685. WHERE id = :id');
  686. $stmt->execute([':id' => $id]);
  687. $row = $stmt->fetch(PDO::FETCH_ASSOC);
  688. TTransaction::close();
  689. if (!$row) {
  690. throw new Exception('Arquivo não encontrado.');
  691. }
  692. $filename = $row['nome_arquivo'] ?: "arquivo_$id";
  693. $filename = basename($filename);
  694. $mime = $row['tipo_mime'] ?: 'application/pdf';
  695. $data = $row['dados_arquivo'];
  696. if (is_resource($data)) {
  697. $data = stream_get_contents($data);
  698. }
  699. $outDir = 'app/output';
  700. if (!is_dir($outDir)) {
  701. @mkdir($outDir, 0775, true);
  702. }
  703. $file_path = $outDir . '/' . $filename;
  704. file_put_contents($file_path, $data);
  705. $pessoa = $this->RetornaNome($param['id_prontuario']) ?: '';
  706. $window = TWindow::create('ARQUIVO ANEXO AO PRONTUÁRIO DE ' . $pessoa, 0.8, 0.8);
  707. if (strpos($mime, 'image/') === 0) {
  708. // -------- IMAGEM: download + imprimir + preview --------
  709. $container = new TElement('div');
  710. $container->style = 'display:flex; flex-direction:column; height:100%; ';
  711. // Barra de ações
  712. $toolbar = new TElement('div');
  713. $toolbar->style = 'padding: 8px 20px; display:flex; gap:5px; border-bottom: 4px double #1a1818ff; justify-content: right; background: #3C3C3C;';
  714. // Botão salvar
  715. $estilo_btn = 'display:inline-block; padding:8px 14px; border-radius:8px; background: transparent; color: #FFFFFF; text-decoration:none; font-weight:600;';
  716. $btnSalvar = new TElement('a');
  717. $btnSalvar->href = $file_path;
  718. $btnSalvar->download = $filename;
  719. $btnSalvar->style = $estilo_btn;
  720. $btnSalvar->title = 'Baixar Arquivo';
  721. $btnSalvar->add("<i class='fa-solid fa-download'></i>");
  722. // Botão imprimir (abre nova aba e chama window.print)
  723. $btnImprimir = new TElement('a');
  724. $btnImprimir->href = '#';
  725. $btnImprimir->style = $estilo_btn;
  726. $btnImprimir->title = 'Imprimir arquivo';
  727. $btnImprimir->add("<i class='fa-solid fa-print'></i>");
  728. // JS seguro com json_encode para evitar problemas de escape
  729. $jsPrint = "var src=" . json_encode($file_path) . ";";
  730. $jsPrint .= "var title=" . json_encode('Impressão - ' . $filename) . ";";
  731. $jsPrint .= "var w=window.open('', '_blank');";
  732. $jsPrint .= "w.document.write('<!doctype html><html><head><meta charset=\"utf-8\"><title>'+title+'</title></head>";
  733. $jsPrint .= "<body style=\"margin:0;text-align:center;background:#fff;\">";
  734. $jsPrint .= "<img src=\"'+src+'\" alt=\"'+title+'\" style=\"max-width:100%;height:auto;\" onload=\"window.focus();window.print();\">";
  735. $jsPrint .= "</body></html>');";
  736. $jsPrint .= "w.document.close();";
  737. $btnImprimir->onclick = $jsPrint . ' return false;';
  738. // Link abrir em nova aba
  739. $linkNovaAba = new TElement('a');
  740. $linkNovaAba->href = $file_path;
  741. $linkNovaAba->target = '_blank';
  742. $linkNovaAba->style = $estilo_btn;
  743. $linkNovaAba->title = 'Abrir em nova aba';
  744. $linkNovaAba->add("<i class='fa-solid fa-external-link-alt'></i>");
  745. $toolbar->add($btnSalvar);
  746. $toolbar->add($btnImprimir);
  747. $toolbar->add($linkNovaAba);
  748. // Área de visualização
  749. $viewerWrap = new TElement('div');
  750. $viewerWrap->style = 'flex:1; overflow:auto;';
  751. $img = new TElement('img');
  752. $img->src = $file_path;
  753. $img->alt = $filename;
  754. $img->style = 'max-width:100%; height:auto; display:block;';
  755. $viewerWrap->add($img);
  756. $container->add($toolbar);
  757. $container->add($viewerWrap);
  758. $window->add($container);
  759. } elseif ($mime === 'application/pdf' || preg_match('/\.pdf$/i', $filename)) {
  760. // -------- PDF: mantém como está --------
  761. $object = new TElement('object');
  762. $object->data = $file_path;
  763. $object->type = 'application/pdf';
  764. $object->style = "width: 100%; height:calc(100% - 10px)";
  765. $object->add('O navegador não suporta a exibição deste conteúdo, <a style="color:#007bff;" target=_newwindow href="' . $object->data . '">clique aqui para baixar</a>...');
  766. $window->add($object);
  767. } else {
  768. // -------- Outros tipos: oferece download --------
  769. $box = new TElement('div');
  770. $box->style = 'padding:10px;';
  771. $msg = new TElement('div');
  772. $msg->style = 'margin-bottom:10px;';
  773. $msg->add('Tipo de arquivo não visualizável. Você pode baixá-lo abaixo:');
  774. $a = new TElement('a');
  775. $a->href = $file_path;
  776. $a->download = $filename;
  777. $a->style = 'display:inline-block; padding:8px 14px; border-radius:8px; background:#28a745; color:#fff; text-decoration:none; font-weight:600;';
  778. $a->add('Baixar arquivo');
  779. $box->add($msg);
  780. $box->add($a);
  781. $window->add($box);
  782. }
  783. $window->show();
  784. $this->onCarrega(['id_prontuario' => $param['id_prontuario'], 'current_tab' => 2]);
  785. } catch (Exception $e) {
  786. if (TTransaction::get()) {
  787. TTransaction::rollback();
  788. }
  789. new TMessage('error', $e->getMessage());
  790. }
  791. }
  792. public function RetornaNome($id_prontuario)
  793. {
  794. TTransaction::open('dps'); // Abre a transação para acessar o banco de dados
  795. $pessoa_achada = Prontuario::find($id_prontuario); // Busca a pessoa pelo ID
  796. if ($pessoa_achada) { // Verifica se a pessoa foi encontrada
  797. $pessoa = $pessoa_achada->get_pessoa(); // Obtém a pessoa associada ao prontuário
  798. if ($pessoa) {
  799. return $pessoa->nome; // Retorna o nome da pessoa
  800. } else {
  801. return 'Pessoa Não Encontrada';
  802. }
  803. } else {
  804. return 'Pessoa Não Encontrada';
  805. }
  806. TTransaction::close();
  807. }
  808. }
  809. ?>

Curso Dominando o Adianti Framework

O material mais completo de treinamento do Framework.
Curso em vídeo aulas + Livro completo + Códigos fontes do projeto ERPHouse.
Conteúdo Atualizado!


Dominando o Adianti Framework Quero me inscrever agora!

Comentários (1)


AA

Resolvido.