first commit

This commit is contained in:
=
2026-09-21 16:33:51 -04:00
commit a899479dc9
204 changed files with 21132 additions and 0 deletions
@@ -0,0 +1,383 @@
import 'dart:typed_data';
import 'package:flutter/services.dart' show rootBundle;
import 'package:intl/intl.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
class InsuranceAuthPdfService {
/// Genera el documento PDF de Autorización de Seguro en formato Media Carta Horizontal
/// (8.5 x 5.5 pulgadas / 612 x 396 pt), idéntico a AlfaMedical.
static Future<Uint8List> buildInsuranceAuthPdf({
required String doctorName,
required String specialty,
required String subspecialty,
required String generalContact,
required String arsName,
required String patientName,
required String surgeryText,
required String diagnosisText,
String? anesthesia,
String? daysOfStay,
DateTime? date,
pw.Font? fontRegular,
pw.Font? fontBold,
pw.Font? fontTitle,
String? logoSvg,
}) async {
final pdf = pw.Document(title: 'Autorización de seguro - $patientName');
final formattedDate = DateFormat('dd/MM/yyyy').format(date ?? DateTime.now());
// Formato exacto e idéntico a AlfaMedical (ReportLab):
// pagesize = (8.5*inch, 5.5*inch)
// leftMargin = 72, rightMargin = 72, topMargin = 18, bottomMargin = 18.
const pageFormat = PdfPageFormat(
8.5 * PdfPageFormat.inch,
5.5 * PdfPageFormat.inch,
marginLeft: 72,
marginRight: 72,
marginTop: 18,
marginBottom: 18,
);
// Tipografías estándar con fallback a fuentes base PDF si no hay conexión a internet
pw.Font regular = fontRegular ?? pw.Font.helvetica();
pw.Font bold = fontBold ?? pw.Font.helveticaBold();
pw.Font titleFont = fontTitle ?? pw.Font.timesBoldItalic();
if (fontRegular == null || fontBold == null || fontTitle == null) {
try {
regular = await PdfGoogleFonts.robotoRegular();
bold = await PdfGoogleFonts.robotoBold();
titleFont = await PdfGoogleFonts.playfairDisplayItalic();
} catch (_) {
regular = pw.Font.helvetica();
bold = pw.Font.helveticaBold();
titleFont = pw.Font.timesBoldItalic();
}
}
String? svgContent = logoSvg;
if (svgContent == null) {
try {
svgContent = await rootBundle.loadString('assets/images/hidalyx_logo_blue.svg');
} catch (_) {
svgContent = null;
}
}
pdf.addPage(
pw.Page(
pageFormat: pageFormat,
build: (pw.Context context) {
return pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.stretch,
children: [
// 1. Cabecera con Nombre del Doctor y Badge Sutil de Hidalyx Med
pw.Stack(
children: [
// Nombre y especialidad del doctor centrado
pw.Align(
alignment: pw.Alignment.center,
child: pw.Column(
children: [
pw.Text(
doctorName.isNotEmpty ? doctorName : 'Dr. Luis A. Castro Sosa',
style: pw.TextStyle(
font: titleFont,
fontSize: 22,
fontWeight: pw.FontWeight.bold,
color: PdfColor.fromHex('1E293B'),
),
),
pw.SizedBox(height: 3),
if (specialty.isNotEmpty)
pw.Text(
specialty.toUpperCase(),
style: pw.TextStyle(
font: bold,
fontSize: 10.5,
letterSpacing: 0.5,
color: PdfColor.fromHex('0F172A'),
),
),
if (subspecialty.isNotEmpty) ...[
pw.SizedBox(height: 2),
pw.Text(
subspecialty.toUpperCase(),
textAlign: pw.TextAlign.center,
style: pw.TextStyle(
font: regular,
fontSize: 8.5,
color: PdfColor.fromHex('475569'),
),
),
],
pw.SizedBox(height: 3),
pw.Text(
'Fecha: $formattedDate',
style: pw.TextStyle(
font: regular,
fontSize: 9.5,
color: PdfColor.fromHex('334155'),
),
),
],
),
),
// Badge sutil de Hidalyx Med en la esquina superior derecha
pw.Positioned(
top: 0,
right: 0,
child: pw.Row(
mainAxisSize: pw.MainAxisSize.min,
crossAxisAlignment: pw.CrossAxisAlignment.center,
children: [
if (svgContent != null && svgContent.isNotEmpty) ...[
pw.Opacity(
opacity: 0.6,
child: pw.SvgImage(
svg: svgContent,
width: 7.5,
height: 7.5,
),
),
pw.SizedBox(width: 3),
],
pw.Text(
'hidalyx.com',
style: pw.TextStyle(
font: regular,
fontSize: 6.0,
color: PdfColor.fromHex('94A3B8'),
),
),
],
),
),
],
),
pw.SizedBox(height: 6),
// Línea divisoria decorativa del membrete
pw.Container(
height: 1,
color: PdfColor.fromHex('CBD5E1'),
),
pw.SizedBox(height: 8),
// 2. Encabezado de la Orden Médica (Símbolo Rx)
pw.Row(
crossAxisAlignment: pw.CrossAxisAlignment.center,
children: [
pw.Container(
padding: const pw.EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: pw.BoxDecoration(
color: PdfColor.fromHex('E2E8F0'),
borderRadius: pw.BorderRadius.circular(4),
),
child: pw.Text(
'Rx',
style: pw.TextStyle(
font: bold,
fontSize: 12,
color: PdfColor.fromHex('00A65A'),
),
),
),
pw.SizedBox(width: 8),
pw.Text(
'SOLICITUD DE AUTORIZACIÓN QUIRÚRGICA',
style: pw.TextStyle(
font: bold,
fontSize: 10,
letterSpacing: 0.5,
color: PdfColor.fromHex('475569'),
),
),
],
),
pw.SizedBox(height: 8),
// 3. Contenido Formal idéntico a AlfaMedical
pw.Expanded(
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.stretch,
children: [
// Señores ARS
pw.RichText(
text: pw.TextSpan(
children: [
pw.TextSpan(
text: 'Señores ARS: ',
style: pw.TextStyle(font: bold, fontSize: 11, color: PdfColor.fromHex('1E293B')),
),
pw.TextSpan(
text: arsName.isNotEmpty ? arsName : '____________________________________',
style: pw.TextStyle(font: regular, fontSize: 11, color: PdfColor.fromHex('0F172A')),
),
],
),
),
pw.SizedBox(height: 6),
// Favor autorizar al Sr. (a)
pw.RichText(
text: pw.TextSpan(
children: [
pw.TextSpan(
text: 'Favor autorizar al Sr. (a): ',
style: pw.TextStyle(font: bold, fontSize: 11, color: PdfColor.fromHex('1E293B')),
),
pw.TextSpan(
text: patientName.isNotEmpty ? patientName : '___________________________',
style: pw.TextStyle(font: regular, fontSize: 11, color: PdfColor.fromHex('0F172A')),
),
],
),
),
pw.SizedBox(height: 6),
// La cirugía de:
pw.RichText(
text: pw.TextSpan(
children: [
pw.TextSpan(
text: 'La cirugía de: ',
style: pw.TextStyle(font: bold, fontSize: 11, color: PdfColor.fromHex('1E293B')),
),
pw.TextSpan(
text: surgeryText.trim().isNotEmpty ? surgeryText.trim() : '___________________________',
style: pw.TextStyle(font: regular, fontSize: 11, color: PdfColor.fromHex('0F172A')),
),
],
),
),
pw.SizedBox(height: 6),
// Diagnóstico
pw.RichText(
text: pw.TextSpan(
children: [
pw.TextSpan(
text: 'Diagnóstico: ',
style: pw.TextStyle(font: bold, fontSize: 11, color: PdfColor.fromHex('1E293B')),
),
pw.TextSpan(
text: diagnosisText.trim().isNotEmpty ? diagnosisText.trim() : '___________________________',
style: pw.TextStyle(font: regular, fontSize: 11, color: PdfColor.fromHex('0F172A')),
),
],
),
),
pw.SizedBox(height: 6),
// Bajo Anestesia y Días de ingreso
if ((anesthesia != null && anesthesia.trim().isNotEmpty) ||
(daysOfStay != null && daysOfStay.trim().isNotEmpty)) ...[
pw.RichText(
text: pw.TextSpan(
children: [
if (anesthesia != null && anesthesia.trim().isNotEmpty) ...[
pw.TextSpan(
text: 'Bajo Anestesia: ',
style: pw.TextStyle(font: bold, fontSize: 11, color: PdfColor.fromHex('1E293B')),
),
pw.TextSpan(
text: anesthesia.trim(),
style: pw.TextStyle(font: regular, fontSize: 11, color: PdfColor.fromHex('0F172A')),
),
],
if (daysOfStay != null && daysOfStay.trim().isNotEmpty) ...[
pw.TextSpan(
text: ' Con ',
style: pw.TextStyle(font: bold, fontSize: 11, color: PdfColor.fromHex('1E293B')),
),
pw.TextSpan(
text: '${daysOfStay.trim()} Días de ingreso',
style: pw.TextStyle(font: regular, fontSize: 11, color: PdfColor.fromHex('0F172A')),
),
],
],
),
),
],
],
),
),
pw.SizedBox(height: 6),
// 4. Pie de página con Contacto del Consultorio
if (generalContact.isNotEmpty) ...[
pw.Container(
padding: const pw.EdgeInsets.only(top: 4),
child: pw.Text(
generalContact.toUpperCase(),
textAlign: pw.TextAlign.center,
style: pw.TextStyle(
font: regular,
fontSize: 6.8,
color: PdfColor.fromHex('64748B'),
lineSpacing: 1.5,
),
),
),
],
pw.SizedBox(height: 3),
// Confirmación institucional
pw.Center(
child: pw.Text(
'Documento médico emitido electrónicamente',
style: pw.TextStyle(
font: regular,
fontSize: 6.0,
color: PdfColor.fromHex('94A3B8'),
),
),
),
],
);
},
),
);
return pdf.save();
}
/// Envía a imprimir directamente o abre el diálogo nativo de impresión/PDF.
static Future<void> printInsuranceAuth({
required String doctorName,
required String specialty,
required String subspecialty,
required String generalContact,
required String arsName,
required String patientName,
required String surgeryText,
required String diagnosisText,
String? anesthesia,
String? daysOfStay,
DateTime? date,
}) async {
final pdfBytes = await buildInsuranceAuthPdf(
doctorName: doctorName,
specialty: specialty,
subspecialty: subspecialty,
generalContact: generalContact,
arsName: arsName,
patientName: patientName,
surgeryText: surgeryText,
diagnosisText: diagnosisText,
anesthesia: anesthesia,
daysOfStay: daysOfStay,
date: date,
);
await Printing.layoutPdf(
onLayout: (PdfPageFormat format) async => pdfBytes,
name: 'Autorizacion_Seguro_${patientName.replaceAll(' ', '_')}',
);
}
}